skill
Altium PcbLib — native footprint parser
Public Made by Adomby adom
Native Rust parser for Altium .PcbLib binary footprint libraries — used by chipsmith + concur for cross-source agreement.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
{
"schema_version": 1,
"type": "skill",
"slug": "altium-pcblib",
"title": "Altium PcbLib — native footprint parser",
"brief": "Native Rust parser for Altium .PcbLib binary footprint libraries — used by chipsmith + concur for cross-source agreement.",
"version": "1.0.1",
"tags": [],
"license": "MIT",
"discovery_triggers": [
"altium pcblib",
"altium intlib",
"parse altium binary footprint",
"altium ole2",
".PcbLib",
".IntLib",
"altium-pcblib",
"altium svg debug",
"altium-pcblib-svg",
"why is my altium pad rotated wrong",
"MFG vs IPC variant",
"altium fat overshoot",
"altium compound file",
"read altium footprint in rust"
],
"discovery_pitch": "Use when reading Altium .PcbLib / .IntLib binaries from Rust, debugging \"why is my altium pad rotated wrong\", or wiring a fourth source into a cross-source footprint check. Ships an authoritative SVG debug renderer.",
"sample_prompts": [
{
"label": "Add to Cargo",
"prompt": "Add altium-pcblib to my Cargo.toml as a git dependency"
},
{
"label": "Render SVG",
"prompt": "Render the parser's authoritative SVG for an Altium .PcbLib file"
},
{
"label": "Compare to KiCad",
"prompt": "Compare the Altium .PcbLib pad layout to the KiCad .kicad_mod for a chip and tell me where they disagree"
},
{
"label": "Debug rotation",
"prompt": "Why are my altium pads rotated 90 degrees wrong in chipsmith?"
},
{
"label": "Pick variant",
"prompt": "This .PcbLib has multiple variants — pick the one whose pad count matches the kicad footprint"
}
],
"source_path": "SKILL.md",
"readme": "## What this is\n\n`altium-pcblib` is a private Adom Rust crate that reads Altium Designer's\nbinary footprint libraries (`.PcbLib`, and the PCB stream embedded inside\n`.IntLib`) and emits per-pad JSON shaped exactly like KiCad's\n`parse_kicad_mod` output. One source of truth instead of every Adom tool\nre-implementing OLE2 / CFBF walking + Altium's reverse-engineered binary\nrecord format.\n\nAlready wired into:\n- **adom-chipsmith** Tab 4 cross-source agreement matrix — fourth column\n alongside KiCad / Fusion / Datasheet.\n- **concur** consensus check — auto-picks the right multi-variant\n footprint (IPC vs MFG) by pad-count agreement with other parsed sources.\n\nSource lives in the private GitHub repo `adom-inc/altium-pcblib`. Skills\non the wiki cover discoverability + integration; the source itself is\nnot published here.\n\n## Why a skill page, not a library page\n\nIn Adom-wiki terms a **library** is an EDA part bundle (symbol +\nfootprint + 3D + variants + pad↔pin mapping). `altium-pcblib` is a Rust\ncrate — so its wiki page is a **skill**: it teaches Claude/the team how\nto use the crate, while the source stays private.\n\n## Depending on it\n\nIn the workspace (chipsmith, concur, anything else under\n`/home/adom/project/`):\n\n```toml\n[dependencies]\naltium-pcblib = { path = \"../altium-pcblib\" }\n```\n\nOn a build container without the workspace checkout:\n\n```toml\n[dependencies]\naltium-pcblib = { git = \"https://github.com/adom-inc/altium-pcblib\", branch = \"main\" }\n```\n\nSingle direct dep (`cfb`, the OLE2 reader) plus `anyhow` and `serde_json`.\n\n## Public Rust API\n\n```rust\n// Parse from a file on disk.\npub fn parse_altium_pcblib_path(path: &Path) -> anyhow::Result<serde_json::Value>;\n\n// Parse from raw bytes (handy for include_bytes! tests and HTTP bodies).\npub fn parse_altium_pcblib_bytes(bytes: &[u8]) -> anyhow::Result<serde_json::Value>;\n\n// Render the parser's view of the footprint as standalone SVG. THIS IS THE\n// AUTHORITATIVE GROUND TRUTH — if a downstream tool draws something different,\n// the downstream tool is the bug.\npub fn render_footprint_svg(footprint: &serde_json::Value) -> String;\n\n// Render primary + every alternate variant, stacked vertically in one SVG.\npub fn render_all_footprints_svg(parsed: &serde_json::Value) -> String;\n```\n\nThe returned `Value` is shaped exactly like\n`adom-chipsmith::view_library::parse_kicad_mod_text` output:\n\n```json\n{\n \"module_name\": \"D8\",\n \"pad_count\": 8,\n \"pads\": [{\n \"number\": \"1\", \"type\": \"smd\", \"shape\": \"rect\",\n \"at\": [-2.4638, -1.905, 0.0],\n \"size\": [1.9812, 0.5588],\n \"layers\": [\"F.Cu\", \"F.Paste\", \"F.Mask\"],\n \"drill\": null\n }],\n \"silk_markers\": [],\n \"pin1_silk\": { \"found\": false },\n \"_source\": \"altium_pcblib\",\n \"_alternates\": [ /* other footprints, sorted by pad_count desc */ ]\n}\n```\n\n## CLI bin: `altium-pcblib-svg`\n\nSingle binary that ships with the crate. Use it whenever a downstream\nrenderer looks suspect:\n\n```bash\n# Primary footprint, SVG to stdout.\naltium-pcblib-svg /path/to/Foo.PcbLib > foo.svg\n\n# Primary + every alternate variant stacked vertically.\naltium-pcblib-svg /path/to/Foo.PcbLib --all -o foo-all.svg\n```\n\nSVG conventions:\n- 1 SVG unit = 1 mm. No scaling.\n- Y-down (matches both KiCad and our emitted JSON).\n- Rotation applied via `transform=\"rotate(deg cx cy)\"` clockwise on screen.\n- Pad numbers stay un-rotated for readability.\n- F.Cu pads orange, B.Cu blue, thru-hole green, drill holes white on top.\n- Origin crosshair at (0, 0) with 1 mm arms.\n\n## Format quirks worth knowing\n\nThese bit us during reverse-engineering — they're encoded in the parser\nalready, but if you extend it or debug a weird chip, here's what's going\non.\n\n### 1. FAT overshoot\n\nINA226AIDGSR's PcbLib declares 256 FAT entries but only has 213 sectors.\nThe `cfb` crate refuses with `Malformed FAT … FAT has N entries, but\nfile has only M sectors`. Fix: pad the input bytes to 1 MiB with `0xFF`\n(CFBF \"free sector\" sentinel) before opening. The unreferenced trailing\nsectors don't hold real data.\n\n### 2. Per-record-type sub-block count\n\nAltium records aren't uniform `[type][len][payload]`. Different record\ntypes carry different numbers of `[u32 len][bytes]` sub-blocks:\n\n- Pad (0x02): 6 sub-blocks (designator, layer-name pstring, four metadata blocks).\n- Text (0x05): 2 sub-blocks (main + style trailer).\n- Track / Arc / Via / Fill / Region / ComponentBody: 1 sub-block.\n\nIf you hit an unknown type byte mid-stream, the parser stops walking\ngracefully — pads always come first, so by then we already have what we\nneed.\n\n### 3. Pad block-5 layout (the 194-byte v3 / 202-byte v4 pad data)\n\n```\n[0] u8 layer (1 = TOP_CU, 32 = BOTTOM_CU, ...)\n[1] u8 net/flag (0x08 in v3, 0x0c in v4 — version probe)\n[2] u8 reserved\n[3..13] 10 bytes of 0xFF (component / net id sentinels)\n[13..17] i32 LE x position (units = 1 / 10000 mil = 2.54e-6 mm)\n[17..21] i32 LE y position (Altium Y-up — flipped to KiCad Y-down at parse)\n[21..29] u32 LE x size, y size — top layer\n[29..37] u32 LE x size, y size — mid layer (multi-layer thru-hole pads)\n[37..45] u32 LE x size, y size — bottom layer\n[45..49] u32 LE hole size (0 → SMD; > 0 → thru-hole)\n[49..52] u8 × 3 shape codes (1 round, 2 rect, 3 octagonal, 9 rounded-rect)\n[52..60] f64 LE rotation in degrees — ONLY in 202-byte v4 layout\n```\n\nPast offset 60 the layout drifts between Altium releases (paste / mask\noffsets, plated bool, GUIDs). The parser stops at 60.\n\n### 4. Two equally valid rotation conventions in the wild\n\nThis is the one that *looks* like a parser bug but isn't. Altium gives\nlibrary authors freedom in how to encode a rotated pad:\n\n- **Convention A (matches KiCad):** store the pad as a \"vertical stick\"\n (xsize < ysize), set rotation = 90° on the side pads.\n- **Convention B (post-rotation):** store the physical post-rotation\n dimensions (xsize > ysize for a horizontal pad), set rotation = 0.\n\nBoth render to the same physical pad on the PCB. A downstream tool that\ncompares `size[]` element-wise without applying rotation will see a 90°\nmismatch in convention B → KiCad. The fix lives in the consumer (compute\nthe post-rotation bbox before comparing), **not** in this parser. When in\ndoubt, render the authoritative SVG and compare visually.\n\n### 5. Y-axis flip\n\nAltium Y-up vs KiCad Y-down. The parser flips Y at parse time so emitted\n`at[1]` is in KiCad convention. **Do not flip again downstream.**\n\n### 6. Round shape with unequal w/h → \"oval\"\n\nKiCad's `circle` implies w == h. Altium's \"round\" (shape byte 1) covers\nboth true circles and stadium-shaped SMD pads with rounded ends. The\nparser specializes: shape 1 with `(xsize - ysize).abs() < 1e-3` →\n`circle`, otherwise → `oval` (KiCad's stadium).\n\n## Trouble-shooting checklist when downstream renders pads wrong\n\n1. **Run the authoritative SVG first.** `altium-pcblib-svg <path>.PcbLib > /tmp/auth.svg`. If pads are right in the SVG, the bug is downstream.\n2. **Check which variant got picked.** `parsed.module_name` plus `_alternates[*].module_name`. Manufacturing variants often have extra pads vs the IPC pinout variant; concur uses `parse_with_consensus_hint` to pick the right one.\n3. **Check rotation convention.** If pad 1 looks correct but pad 13 looks rotated 90° wrong, you're probably hitting Convention B vs A — your downstream is comparing or rendering size literally without applying rotation.\n4. **Check Y direction.** If pads are mirrored top-to-bottom, your downstream double-flipped Y. The parser already emits KiCad Y-down.\n5. **Scale.** SVG units are mm. If the rendered size is off by 1000×, the consumer is treating mm as µm.\n\n## Tested chip corpus\n\n| Chip | Package | Variants | Notes |\n|---|---|---|---|\n| LM358D | SOIC-8 | D8 / D8-L / D8-M | Sanity baseline. 8 horizontal pads, rot=0 throughout. |\n| INA226AIDGSR | VSSOP-10 | SOP50P490X110-10N | v3 layout (Convention B). Triggers FAT overshoot quirk. |\n| ADS1115IDGSR | VSSOP-10 | DGS0010A_L / _M / _N | 3 alternate density variants. |\n| DRV8316RRGFR | VQFN-40 | RGF0040E-IPC_A / _B / _C / -MFG | IPC variants are Convention A; MFG variant is Convention B + extra fiducials/test points. |\n",
"author": {
"name": "Kyle Bergstedt",
"email": "[email protected]"
},
"visibility": {
"public": true
},
"hero": {
"type": "image",
"path": "altium-pcblib-hero.png"
},
"metadata": {},
"created_at": "2026-05-28T05:29:36.057Z",
"updated_at": "2026-06-25T01:59:29.394Z",
"sub_skills": [],
"parent_app": null,
"description": "Native Rust parser for Altium .PcbLib binary footprint libraries — used by chipsmith + concur for cross-source agreement.",
"dependencies": {},
"scripts": {
"install": "./install.sh",
"uninstall": "./uninstall.sh"
},
"org": "adom"
}