altium-toolkit 0.1.1 → 0.1.16
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.
- package/README.md +24 -6
- package/docs/api.md +42 -4
- package/docs/model-format.md +95 -5
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +553 -0
- package/docs/testing.md +7 -2
- package/package.json +6 -2
- package/spec/library-scope.md +7 -1
- package/src/core/altium/AltiumParser.mjs +22 -325
- package/src/core/altium/NormalizedModelSchema.mjs +28 -0
- package/src/core/altium/PcbArcPrimitiveParser.mjs +87 -0
- package/src/core/altium/PcbBinaryPrimitiveParser.mjs +43 -370
- package/src/core/altium/PcbBoardRegionSemanticsParser.mjs +477 -0
- package/src/core/altium/PcbComponentAnnotationNormalizer.mjs +290 -0
- package/src/core/altium/PcbComponentBodyPlacementNormalizer.mjs +52 -0
- package/src/core/altium/PcbComponentPrimitiveIndexer.mjs +109 -0
- package/src/core/altium/PcbEmbeddedFontExtractor.mjs +484 -0
- package/src/core/altium/PcbFillPrimitiveParser.mjs +84 -0
- package/src/core/altium/PcbFontMetricsParser.mjs +308 -0
- package/src/core/altium/PcbGeometryFlipper.mjs +244 -0
- package/src/core/altium/PcbLayerIdCodec.mjs +136 -0
- package/src/core/altium/PcbLibModelParser.mjs +202 -0
- package/src/core/altium/PcbLibStreamExtractor.mjs +968 -0
- package/src/core/altium/PcbModelParser.mjs +618 -66
- package/src/core/altium/PcbOutlineRecovery.mjs +4 -112
- package/src/core/altium/PcbPadPrimitiveParser.mjs +347 -0
- package/src/core/altium/PcbPadShapeCodec.mjs +158 -0
- package/src/core/altium/PcbPadStackParser.mjs +903 -0
- package/src/core/altium/PcbPrimitiveOwnershipIndexParser.mjs +60 -0
- package/src/core/altium/PcbPrimitiveParameterParser.mjs +212 -0
- package/src/core/altium/PcbPrimitiveRecordSlicer.mjs +243 -0
- package/src/core/altium/PcbRawRecordRegistry.mjs +831 -0
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +317 -0
- package/src/core/altium/PcbRuleParser.mjs +587 -0
- package/src/core/altium/PcbStreamExtractor.mjs +127 -4
- package/src/core/altium/PcbTextPrimitiveParser.mjs +537 -0
- package/src/core/altium/PcbTrackPrimitiveParser.mjs +87 -0
- package/src/core/altium/PcbViaPrimitiveParser.mjs +88 -0
- package/src/core/altium/PcbViaStackParser.mjs +548 -0
- package/src/core/altium/PcbWideStringTableParser.mjs +108 -0
- package/src/core/altium/PrjPcbModelParser.mjs +797 -0
- package/src/core/altium/SchematicComponentTextResolver.mjs +355 -0
- package/src/parser.mjs +13 -0
- package/src/renderers.mjs +5 -0
- package/src/styles/altium-renderers.css +11 -6
- package/src/ui/PcbCopperPrimitiveSplitter.mjs +113 -0
- package/src/ui/PcbEdgeFacingGlyphNormalizer.mjs +6 -5
- package/src/ui/PcbEmbeddedFontFaceRenderer.mjs +126 -0
- package/src/ui/PcbFootprintPrimitiveSelector.mjs +27 -6
- package/src/ui/PcbRegionPrimitiveRenderer.mjs +243 -0
- package/src/ui/PcbSideResolvedRenderModel.mjs +336 -0
- package/src/ui/PcbSvgRenderer.mjs +101 -109
- package/src/ui/PcbTextPrimitiveRenderer.mjs +252 -0
- package/src/ui/SchematicSheetChromeRenderer.mjs +2 -93
- 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
|
+
}
|