altium-toolkit 0.1.1 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +25 -6
  2. package/docs/api.md +42 -4
  3. package/docs/model-format.md +95 -5
  4. package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +553 -0
  5. package/docs/testing.md +7 -2
  6. package/package.json +6 -2
  7. package/spec/library-scope.md +7 -1
  8. package/src/core/altium/AltiumParser.mjs +22 -325
  9. package/src/core/altium/NormalizedModelSchema.mjs +28 -0
  10. package/src/core/altium/PcbArcPrimitiveParser.mjs +87 -0
  11. package/src/core/altium/PcbBinaryPrimitiveParser.mjs +43 -370
  12. package/src/core/altium/PcbBoardRegionSemanticsParser.mjs +477 -0
  13. package/src/core/altium/PcbComponentAnnotationNormalizer.mjs +290 -0
  14. package/src/core/altium/PcbComponentBodyPlacementNormalizer.mjs +52 -0
  15. package/src/core/altium/PcbComponentPrimitiveIndexer.mjs +109 -0
  16. package/src/core/altium/PcbEmbeddedFontExtractor.mjs +484 -0
  17. package/src/core/altium/PcbFillPrimitiveParser.mjs +84 -0
  18. package/src/core/altium/PcbFontMetricsParser.mjs +308 -0
  19. package/src/core/altium/PcbGeometryFlipper.mjs +244 -0
  20. package/src/core/altium/PcbLayerIdCodec.mjs +136 -0
  21. package/src/core/altium/PcbLibModelParser.mjs +202 -0
  22. package/src/core/altium/PcbLibStreamExtractor.mjs +968 -0
  23. package/src/core/altium/PcbModelParser.mjs +618 -66
  24. package/src/core/altium/PcbOutlineRecovery.mjs +4 -112
  25. package/src/core/altium/PcbPadPrimitiveParser.mjs +347 -0
  26. package/src/core/altium/PcbPadShapeCodec.mjs +158 -0
  27. package/src/core/altium/PcbPadStackParser.mjs +903 -0
  28. package/src/core/altium/PcbPrimitiveOwnershipIndexParser.mjs +60 -0
  29. package/src/core/altium/PcbPrimitiveParameterParser.mjs +212 -0
  30. package/src/core/altium/PcbPrimitiveRecordSlicer.mjs +243 -0
  31. package/src/core/altium/PcbRawRecordRegistry.mjs +831 -0
  32. package/src/core/altium/PcbRegionPrimitiveParser.mjs +317 -0
  33. package/src/core/altium/PcbRuleParser.mjs +587 -0
  34. package/src/core/altium/PcbStreamExtractor.mjs +127 -4
  35. package/src/core/altium/PcbTextPrimitiveParser.mjs +537 -0
  36. package/src/core/altium/PcbTrackPrimitiveParser.mjs +87 -0
  37. package/src/core/altium/PcbViaPrimitiveParser.mjs +88 -0
  38. package/src/core/altium/PcbViaStackParser.mjs +548 -0
  39. package/src/core/altium/PcbWideStringTableParser.mjs +108 -0
  40. package/src/core/altium/PrjPcbModelParser.mjs +797 -0
  41. package/src/core/altium/SchematicComponentTextResolver.mjs +355 -0
  42. package/src/parser.mjs +13 -0
  43. package/src/renderers.mjs +5 -0
  44. package/src/styles/altium-renderers.css +11 -6
  45. package/src/ui/PcbCopperPrimitiveSplitter.mjs +113 -0
  46. package/src/ui/PcbEdgeFacingGlyphNormalizer.mjs +6 -5
  47. package/src/ui/PcbEmbeddedFontFaceRenderer.mjs +126 -0
  48. package/src/ui/PcbFootprintPrimitiveSelector.mjs +27 -6
  49. package/src/ui/PcbRegionPrimitiveRenderer.mjs +243 -0
  50. package/src/ui/PcbSideResolvedRenderModel.mjs +336 -0
  51. package/src/ui/PcbSvgRenderer.mjs +101 -109
  52. package/src/ui/PcbTextPrimitiveRenderer.mjs +252 -0
  53. package/src/ui/SchematicSheetChromeRenderer.mjs +2 -93
  54. package/src/ui/SchematicSheetZoneRenderer.mjs +104 -0
@@ -0,0 +1,202 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import { NormalizedModelSchema } from './NormalizedModelSchema.mjs'
6
+ import { ParserUtils } from './ParserUtils.mjs'
7
+
8
+ const { stripExtension } = ParserUtils
9
+
10
+ /**
11
+ * Normalizes extracted PcbLib stream data into the public parser model.
12
+ */
13
+ export class PcbLibModelParser {
14
+ /**
15
+ * Parses one extracted PcbLib into a normalized read-only model.
16
+ * @param {string} fileName
17
+ * @param {{ libraryHeader?: Record<string, string>, componentParamsToc?: Record<string, object>, sectionKeys?: Record<string, string>, footprints?: object[], embeddedFonts?: { fonts?: object[] }, streamNames?: string[], diagnostics?: Record<string, number> } | null} extraction
18
+ * @returns {{ schema: string, kind: 'pcb-library', fileType: 'PcbLib', fileName: string, summary: Record<string, number | string>, diagnostics: { severity: 'info' | 'warning', message: string }[], pcbLibrary: { libraryHeader: Record<string, string>, componentParamsToc: Record<string, object>, sectionKeys: Record<string, string>, footprints: object[], embeddedFonts: object[] }, bom: [] }}
19
+ */
20
+ static parse(fileName, extraction) {
21
+ const safeExtraction = extraction || {}
22
+ const footprints = Array.isArray(safeExtraction.footprints)
23
+ ? safeExtraction.footprints.map((footprint) =>
24
+ PcbLibModelParser.#normalizeFootprint(footprint)
25
+ )
26
+ : []
27
+ const embeddedFonts = Array.isArray(safeExtraction.embeddedFonts?.fonts)
28
+ ? safeExtraction.embeddedFonts.fonts
29
+ : []
30
+ const summary = PcbLibModelParser.#buildSummary(
31
+ fileName,
32
+ footprints,
33
+ embeddedFonts
34
+ )
35
+ const diagnostics = PcbLibModelParser.#buildDiagnostics(
36
+ footprints,
37
+ embeddedFonts,
38
+ safeExtraction
39
+ )
40
+
41
+ return NormalizedModelSchema.attach({
42
+ kind: 'pcb-library',
43
+ fileType: 'PcbLib',
44
+ fileName,
45
+ summary,
46
+ diagnostics,
47
+ pcbLibrary: {
48
+ libraryHeader: safeExtraction.libraryHeader || {},
49
+ componentParamsToc: safeExtraction.componentParamsToc || {},
50
+ sectionKeys: safeExtraction.sectionKeys || {},
51
+ footprints,
52
+ embeddedFonts
53
+ },
54
+ bom: []
55
+ })
56
+ }
57
+
58
+ /**
59
+ * Creates summary counters across all footprints.
60
+ * @param {string} fileName
61
+ * @param {object[]} footprints
62
+ * @param {object[]} embeddedFonts
63
+ * @returns {Record<string, number | string>}
64
+ */
65
+ static #buildSummary(fileName, footprints, embeddedFonts) {
66
+ return {
67
+ title: stripExtension(fileName),
68
+ footprintCount: footprints.length,
69
+ primitiveCount: PcbLibModelParser.#countFamily(
70
+ footprints,
71
+ 'primitiveCount'
72
+ ),
73
+ padCount: PcbLibModelParser.#countFamily(footprints, 'pads'),
74
+ trackCount: PcbLibModelParser.#countFamily(footprints, 'tracks'),
75
+ arcCount: PcbLibModelParser.#countFamily(footprints, 'arcs'),
76
+ viaCount: PcbLibModelParser.#countFamily(footprints, 'vias'),
77
+ fillCount: PcbLibModelParser.#countFamily(footprints, 'fills'),
78
+ textCount: PcbLibModelParser.#countFamily(footprints, 'texts'),
79
+ regionCount: PcbLibModelParser.#countFamily(footprints, 'regions'),
80
+ rawRecordCount: PcbLibModelParser.#countFamily(
81
+ footprints,
82
+ 'rawRecords'
83
+ ),
84
+ embeddedFontCount: embeddedFonts.length
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Builds parser diagnostics from extraction metadata.
90
+ * @param {object[]} footprints
91
+ * @param {object[]} embeddedFonts
92
+ * @param {{ streamNames?: string[], diagnostics?: Record<string, number> }} extraction
93
+ * @returns {{ severity: 'info' | 'warning', message: string }[]}
94
+ */
95
+ static #buildDiagnostics(footprints, embeddedFonts, extraction) {
96
+ const diagnostics = [
97
+ {
98
+ severity: 'info',
99
+ message:
100
+ 'Recovered ' +
101
+ footprints.length +
102
+ ' PCB library footprint definitions.'
103
+ }
104
+ ]
105
+
106
+ if (Array.isArray(extraction.streamNames)) {
107
+ diagnostics.push({
108
+ severity: 'info',
109
+ message:
110
+ 'Recovered ' +
111
+ extraction.streamNames.length +
112
+ ' PcbLib data streams from the compound document.'
113
+ })
114
+ }
115
+
116
+ if (embeddedFonts.length) {
117
+ diagnostics.push({
118
+ severity: 'info',
119
+ message:
120
+ 'Recovered ' +
121
+ embeddedFonts.length +
122
+ ' embedded PcbLib font payloads.'
123
+ })
124
+ }
125
+
126
+ if (extraction.diagnostics?.missingFootprintCount) {
127
+ diagnostics.push({
128
+ severity: 'warning',
129
+ message:
130
+ 'Skipped ' +
131
+ extraction.diagnostics.missingFootprintCount +
132
+ ' declared PcbLib footprint storages that were not found.'
133
+ })
134
+ }
135
+
136
+ return diagnostics
137
+ }
138
+
139
+ /**
140
+ * Normalizes one extracted footprint object with stable primitive arrays.
141
+ * @param {object} footprint
142
+ * @returns {object}
143
+ */
144
+ static #normalizeFootprint(footprint) {
145
+ const normalized = {
146
+ name: String(footprint.name || ''),
147
+ dataName: String(footprint.dataName || footprint.name || ''),
148
+ sourceStorage: String(footprint.sourceStorage || ''),
149
+ declaredPrimitiveCount: Number(
150
+ footprint.declaredPrimitiveCount || 0
151
+ ),
152
+ parameters: footprint.parameters || {},
153
+ componentParams: footprint.componentParams || {},
154
+ wideStrings: footprint.wideStrings || {},
155
+ primitiveOrder: Array.isArray(footprint.primitiveOrder)
156
+ ? footprint.primitiveOrder
157
+ : [],
158
+ unknownRecords: Array.isArray(footprint.unknownRecords)
159
+ ? footprint.unknownRecords
160
+ : [],
161
+ rawRecords: PcbLibModelParser.#array(footprint.rawRecords),
162
+ pads: PcbLibModelParser.#array(footprint.pads),
163
+ tracks: PcbLibModelParser.#array(footprint.tracks),
164
+ arcs: PcbLibModelParser.#array(footprint.arcs),
165
+ vias: PcbLibModelParser.#array(footprint.vias),
166
+ fills: PcbLibModelParser.#array(footprint.fills),
167
+ texts: PcbLibModelParser.#array(footprint.texts),
168
+ regions: PcbLibModelParser.#array(footprint.regions)
169
+ }
170
+
171
+ return {
172
+ ...normalized,
173
+ primitiveCount:
174
+ Number(footprint.primitiveCount) ||
175
+ normalized.primitiveOrder.length
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Counts either a scalar footprint field or an array-valued family.
181
+ * @param {object[]} footprints
182
+ * @param {string} key
183
+ * @returns {number}
184
+ */
185
+ static #countFamily(footprints, key) {
186
+ return footprints.reduce((sum, footprint) => {
187
+ const value = footprint[key]
188
+ return (
189
+ sum + (Array.isArray(value) ? value.length : Number(value) || 0)
190
+ )
191
+ }, 0)
192
+ }
193
+
194
+ /**
195
+ * Returns a safe array value.
196
+ * @param {unknown} value
197
+ * @returns {unknown[]}
198
+ */
199
+ static #array(value) {
200
+ return Array.isArray(value) ? value : []
201
+ }
202
+ }