Download

name: adom-lbr description: > adom-lbr — the EDA library translator. Bring a component in from ANY supported EDA tool and convert it to ANY other; the canonical adom-lbr JSON (symbol + footprint + 3D + pin↔pad map) is the hub. Import from KiCad OR Altium; export to NATIVE Altium (.SchLib/.PcbLib/.IntLib — real OLE2 binary, no Altium needed), KiCad (.kicad_sym + .kicad_mod), or EAGLE/Fusion .lbr. Merge a symbol into an existing .SchLib. adom-lbr manage opens the Manager: browse wiki components, view symbol/footprint/3D + the per-EDA (KiCad vs Altium) footprint layer stackup. Trigger words: adom-lbr, altium schlib pcblib intlib, convert to altium, kicad to altium, altium to kicad, any eda to any eda, eda translator, lbr, eagle library, fusion library, adom-lbr json, kicad convert, adom-lbr manager, layer stackup.

adom-lbr

adom-lbr generator / EDA translator. The adom-lbr JSON — Adom's own canonical part format (symbol + footprint + 3D + pin↔pad map) — is the hub; every EDA format is a converter around it.

  • Import a KiCad .kicad_sym + .kicad_mod into the adom-lbr JSON.
  • Export the adom-lbr JSON to native Altium .SchLib / .PcbLib / .IntLib (real OLE2/CFBF binary, no Altium at conversion time), an EAGLE/Fusion .lbr, or a KiCad .kicad_sym.
  • Merge a part's symbol into an existing .SchLib (install into a user's Altium library).

("lbr" here = Adom Library / the adom-lbr JSON, not EAGLE's .lbr — that's just one output.)

⚡ Altium user? The reliable workflow — READ THIS FIRST

Helping someone add parts to their real Altium library? The native generators (export-altium, export-intlib, encode-sym) have burned entire sessions producing symbols/footprints that render catastrophically wrong in Altium with NO error — pins dropped or flung thousands of mils away, footprints with copper pads and nothing else. Prefer Altium-authored bytes over generation. The golden path — quick AND correct:

  1. Get genuine bytes; don't generate.
    • From a vendor .IntLib the user provides — the reliable source. It's an OLE2 file whose SchLib/0.schlib + PCBLib/0.pcblib streams are zlib-compressed at offset 1 (zlib.decompress(data[1:])) → real Altium .SchLib/.PcbLib that render perfectly. Extract and use them directly. (adom-lbr import-altium foo.IntLib --mpn X also ingests one.) This needs only Python olefile+zlib — no special binary — so it ALWAYS works.
    • Many similar parts? Have the user perfect ONE in Altium and save it, then byte-clone the rest (copy the component's Data stream verbatim, edit only the name/value/footprint text records).
    • Footprints with real silk / fab / courtyard / 3D: use genuine footprints from IntLibs. The pad generator writes copper ONLY (see the footprint reality-check below).
  2. Standard package → standard KiCad footprint (SOT-223, 0402/0603/0805), never a ds2sf datasheet extraction (it mis-lays pads).
  3. Back up the user's file BEFORE overwriting it (pull_file + keep a copy). Originals have been lost this way with no recovery.
  4. Deploy with adom-desktop send_files, dest = the user's C:\Users\<user>\Documents\Altium\Libraries\{Symbols,Footprints}\ folder. Verify by pulling it back and comparing sha256.
  5. Tell the user to RESTART Altium. It caches file-based libraries and freezes already-placed parts; most "it's still broken/old" reports are stale caches. If the lib is open with unsaved edits (title-bar *), close WITHOUT saving — a Save clobbers your deployed file.
  6. Verify in Altium's Properties panel, not your own parser. Reader and writer can agree with each other and both be wrong (a 79-mil pin read as 66,630 mil). Get this ground truth EARLY.

Everything below is detail. The two must-read deep sections: "⚠️ Altium .SchLib symbols — READ FIRST" and the footprint reality-check callout.

Native Altium commands (both directions)

# INTO the hub
adom-lbr import-kicad  --sym part.kicad_sym --fp part.kicad_mod --mpn MPN   # KiCad  → MPN.adom-lbr.json
adom-lbr import-altium part.SchLib --pcblib part.PcbLib --mpn MPN           # Altium → MPN.adom-lbr.json
adom-lbr import-altium part.IntLib --mpn MPN                                # or one bundled .IntLib

# OUT of the hub → native Altium (real OLE2/CFBF, no Altium needed)
adom-lbr export-altium  part.adom-lbr.json --out-dir .                      # → MPN.SchLib + MPN.PcbLib
adom-lbr export-intlib  part.adom-lbr.json                                  # → MPN.IntLib (bundled)
adom-lbr export-kicad   part.adom-lbr.json --out-dir .                      # → MPN.kicad_sym + MPN.kicad_mod
adom-lbr add-to-schlib  existing.SchLib part.adom-lbr.json --out lib.SchLib # merge a symbol into a library

What carries across (both ways): symbol pins + body graphics; footprint pads; footprint silk / courtyard / fab graphics with their layers; the pin↔pad map; the footprint link. Value/Manufacturer/MPN/Package become Altium component property fields (Value drawn on the symbol; the rest hidden), and are read back out on import.

⚠️ Reality check on the CLI (altium-codec binary) — verified 2026-07-13. The neutral Footprint model is {name, pads} with no graphics fields, so encode-fp / encode-fp-many (and import-kicad, which drops F.SilkS/F.Fab/F.CrtYd) emit copper pads ONLY — no silk, fab, courtyard, keepout, or 3D. The "graphics carry across" claim above is the intended full pipeline, NOT what the CLI does today. If the user needs real footprint layers, do not generate — extract genuine full footprints from vendor .IntLibs (streams zlib-compressed at offset 1) and combine them with altium-codec merge-fp-libs <out.PcbLib> <spec.json> (spec = [{pcblib,name}]): it byte-copies each footprint's primitives (pads + tracks on Mech13 fab / Mech15 courtyard + text) and its embedded STEP model verbatim, renaming only the footprint so existing symbol links still resolve. Same clone-don't-generate rule as symbols (see the READ-FIRST symbol section below).

Footprint layer mapping (Altium)

Graphics carry a neutral layer role (silk / courtyard / fab), mapped on export to the house convention (from the C0402 reference footprint):

Role Altium layer Named
fab + silk (outline) Mechanical 13 Assembly Top
courtyard Mechanical 15 Courtyard Top
pads Top Layer

Altium stores a track/arc layer in TWO places — the layer byte and a tail class-pair [41]/[43] (Track) — and it renders from the tail pair, so the encoder patches both. Decode reads the layers back into the neutral roles. 3D STEP models attach to the footprint via Altium's model factory (bridge), not the native writer (the model checksum is a custom Altium algorithm).

KiCad round-trip fidelity

export-kicad writes a KiCad-valid .kicad_sym + .kicad_mod. Import captures what a naive converter drops: rectangle fill (fill outline vs none — a filled cap plate stays filled), and per-symbol pin-number / pin-name visibility ((pin_numbers (hide yes))), so a KiCad→JSON→KiCad round trip keeps solid vs hollow art and shown vs hidden pin labels. Footprint silk/courtyard/fab graphics keep their layers → F.SilkS / F.CrtYd / F.Fab.

adom-lbr Manager (adom-lbr manage)

A web app (tiny_http → webview) that browses wiki components and inspects any part. adom-lbr manage --port 8785 → open http://localhost:8785.

  • List ▸ click ▸ viewer. Lists every component on the wiki (wiki API); click a row to open its viewer.
  • On-the-fly render. Fetches the part FRESH each click — prefers the page's canonical adom-lbr JSON, else generates it from the page's .kicad_sym + .kicad_mod — and renders symbol + footprint + 3D on the spot.
  • Layer stackup. A table of the footprint's layers showing what each becomes in KiCad and Altium (e.g. Courtyard → F.CrtYd / Mechanical 15). Plus a per-layer show/hide filter on the footprint.
  • Interaction. Drag-to-pan, scroll-to-zoom, double-click-reset on the symbol & footprint; hover a pin or pad to highlight it and read its number + name.
  • Served relative-path so it works behind the code-server /proxy/<port>/ proxy; open it as a webview via adom-cli hydrogen webview open-or-refresh.

EAGLE / Fusion path

Fusion 360 Electronics shows "No Components" when an .lbr has an empty <packages> or <connects/>. adom-lbr's generate builds a COMPLETE deviceset: symbol + package + full pin-to-pad connects. KiCad users can round-trip any .lbr back to .kicad_sym with adom-lbr import.

Commands

adom-lbr generate --sym <path.kicad_sym> [-o output.lbr] [--fp <path.kicad_mod>] [--name <override>]
adom-lbr import <path.lbr> [-o output.kicad_sym]
adom-lbr lint <path.lbr>
adom-lbr validate <path.lbr>
adom-lbr check <path.kicad_sym>
adom-lbr manage [--port 8785]                 # the adom-lbr Manager web app
adom-lbr health
adom-lbr install
adom-lbr completions <bash|zsh|fish>

generate

Parses a KiCad .kicad_sym file and produces a valid EAGLE .lbr with:

  • Symbol (body rectangle + pins with correct direction/rotation/length)
  • Package (placeholder SMD pads for each pin number, courtyard outline)
  • Deviceset with gate, device, and complete <connect> mappings

Auto-lints the output before writing. Exits 2 if lint fails.

import

Parses an EAGLE .lbr and converts to KiCad .kicad_sym format. Handles:

  • Pin direction mapping (EAGLE pwr/in/out/io/pas/nc → KiCad power_in/input/output/bidirectional/passive/unconnected)
  • Pin rotation, length, position
  • Body rectangle from layer 94 wires
  • Description, reference prefix
  • Pin numbers from connect mappings
  • Strips EAGLE @N dedup suffixes back to clean names

lint

Checks an existing .lbr for Fusion 360 compatibility. Outputs JSON with passed, errors[], warnings[].

Checks:

  • empty-packages — packages section empty (fatal: Fusion shows "No Components")
  • empty-connects — device has no connect mappings (fatal)
  • device-no-package — device element missing package attribute (fatal)
  • duplicate-pin-name — EAGLE requires unique pin names; use @suffix for power/NC duplicates
  • connect-pad-missing — connect references a pad that doesn't exist in the package
  • malformed-xml — unbalanced angle brackets
  • no-symbol-layer — nothing on layer 94 (symbol body invisible)

validate

Same as lint but human-readable output. Exits 0 = valid, 2 = errors found.

check

Validates a .kicad_sym file by local Rust validation — checks structure, pins, properties, duplicate names. Use after import to verify the output before sending to a desktop KiCad instance.

Duplicate pin handling

EAGLE requires unique pin names. When KiCad has multiple pins with the same name (e.g., 6 IOVDD pins), the generator appends @N suffixes: IOVDD, IOVDD@2, IOVDD@3, etc. The import command strips these back.

Workflows

Fusion 360 user (has .kicad_sym, needs .lbr)

adom-lbr generate --sym RP2040.kicad_sym -o RP2040.lbr
# Send to Fusion via adom-desktop send_files + fusion_open_lbr

KiCad user (has .lbr, needs .kicad_sym)

adom-lbr import RP2040.lbr -o RP2040.kicad_sym
adom-lbr check RP2040.kicad_sym
# Send to KiCad via adom-desktop send_files + kicad_install_library

Pipeline (adom-symbol → both targets)

adom-symbol create <mpn> → .kicad_sym
adom-lbr generate --sym <mpn>.kicad_sym → .lbr (for Fusion)
# .kicad_sym already native for KiCad

Lint any .lbr before use

adom-lbr lint suspicious.lbr
# JSON output with error codes for programmatic handling

⚠️ Altium .SchLib symbols — READ FIRST (hard-won 2026-07-10..13)

The native Altium symbol encoder (export-altium / encode-sym / add-to-schlib) has repeatedly produced symbols that render catastrophically wrong in real Altium with NO error — pins dropped, or one pin shot thousands of mils off to the side — while the bytes looked fine to our own parser. Over one session it needed four separate byte-level fixes and was still wrong. So:

RULE 0 — DON'T generate the symbol; CLONE a known-good one.

Whenever a symbol must render in the user's real Altium, do not trust the codec's pin encoder. Instead reuse Altium-authored bytes:

  • From a vendor .IntLib the user provides: it's an OLE2 file whose SchLib/0.schlib and PCBLib/0.pcblib streams are zlib-compressed at offset 1 (zlib.decompress(data[1:])) → the real .SchLib/.PcbLib. Use those directly (Altium's own format = renders perfectly).
  • From one symbol the user fixes in Altium + saves: then byte-clone it to the rest with altium-codec replace-clone <in.SchLib> <src_comp> <out.SchLib> <specs.json> (specs = [{storage,libref,value,package,footprint,description}, …]). It copies the source component's Data stream verbatim (pins + graphics untouched) and edits only the metadata text records. This is the reliable fix and mirrors the machine-pin flow (user fixes one, we clone). Only generate from scratch when nothing Altium-authored exists — and then VERIFY in Altium (RULE 1) before trusting it.

RULE 1 — Verify in ALTIUM, never in your own parser.

Reader and writer can agree with each other and both be wrong. Ground truth = the Properties panel (Pin Length / Location) on the actual pin. A "Pin Length" of tens of thousands of mils = a byte bug. Get this ground truth EARLY instead of theorising for an hour.

RULE 2 — Altium CACHES file-based libraries and FREEZES placed instances.

After you overwrite a .SchLib/.PcbLib, the user must restart Altium (or the library panel serves the stale copy) and delete any already-placed part (placed instances don't update). Tell them this up front — most "it still looks broken/old" reports this session were stale caches, and they cost hours. Also: a title-bar * = unsaved edits; if you overwrite the file while it's open and the user then Saves, they clobber your fix — tell them to close WITHOUT saving, or restart.

RULE 3 — Never call altium-bridge verbs with empty/partial args.

altium_clone_symbol / altium_build_footprint with {} time out and pop "value cannot be null" / "save file error" dialogs inside the user's live Altium. Only call bridge verbs with complete, correct payloads and an appropriate active doc.

RULE 4 — Back up the user's original before overwriting it.

Pull + keep a copy first. This session the user's original SnapEDA symbol got overwritten with a broken codec version and couldn't be recovered.

Byte-level reference (only if you MUST generate — and still verify in Altium)

Binary pin record, calibrated vs genuine pins (STM32F072/ADS1220/caps). NOTE: even after all of these the encoder was STILL wrong on negative X (Altium's own save writes byte [19]=0xFF sign-extension; the codec writes 0x00 → giant pin), so treat this as incomplete → clone.

  • [14] conglom (0x04).
  • [15:17] length field — length in bits 6-15 (unit 1/25.6 mil), orient in bits 0-1, and bit 5 (0x20) is a REQUIRED flag. Genuine low bytes are only ever 0x20/0x22/0x38/0x3a, never 0x00. A round length + orient 0 (100 mil → 0x0A00, low byte 0x00) → GIANT pin. Encode: field = (len & 0xFFC0) | 0x20 | orient.
  • X and Y must be snapped to a 10-mil grid (multiple of 256 internal units) or the low bytes bleed into neighbouring fields (off-grid X = the 66,630-mil pin). Exact X/Y widths are context-dependent and NOT fully nailed — clone instead.
  • Pins before graphics; arcs → tessellated RECORD=6 polylines (a hand-rolled RECORD=12 arc dropped the pin). µ → ASCII ("uF" not "µF" — codec writes UTF-8 → shows as "µ"). Zero-width KiCad polylines are dropped by import-kicad (give leads a real width).
  • The altium-schlib READER decodes Y at the wrong offset — don't trust it either.

Non-symbol notes

Which binary? replace-clone and merge-fp-libs currently live in the lower-level altium-codec dev crate (/home/adom/project/altium-writer/altium-codec/), not yet folded into the shipped adom-lbr binary — cargo build --release there, or fall back to the manual olefile+zlib extraction (golden-path step 1), which needs no special binary and always works. adom-lbr itself ships import/export-altium, export-intlib, add-to-schlib, import-altium.

  • encode-fp-many <[Footprint]> <out.PcbLib> builds a multi-footprint PcbLib — but writes copper pads ONLY (no silk/fab/courtyard/keepout/3D; the model is {name,pads}). Pad placement is reliable; the footprint is just bare. For real layers use genuine footprints + merge-fp-libs (see the ⚠️ reality-check note up top).
  • merge-fp-libs <out.PcbLib> <spec.json> (spec=[{pcblib,name}]) — merge N genuine single-footprint PcbLibs into one, renaming each. Copies every primitive + embedded STEP verbatim; source[0] supplies the shared Library/* boilerplate (all sources must be from the same converter). The way to get full-layer footprints into a user's library without generating them.
  • Symbol model has a description field → RECORD=1 ComponentDescription.
  • For a STANDARD package (SOT-223, 0402/0603/0805), use the KiCad standard footprint, not a ds2sf datasheet extraction — ds2sf mis-laid the SOT-223 pads.