altium-toolkit 1.0.8 → 1.0.9
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 +18 -6
- package/docs/api.md +78 -16
- package/docs/model-format.md +229 -8
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +47 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1661 -104
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +59 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +57 -0
- package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
- package/docs/testing.md +9 -3
- package/package.json +1 -1
- package/spec/library-scope.md +7 -1
- package/src/core/altium/AltiumLayoutParser.mjs +104 -8
- package/src/core/altium/AltiumParser.mjs +191 -45
- package/src/core/altium/EmbeddedFileInventoryBuilder.mjs +255 -0
- package/src/core/altium/IntLibModelParser.mjs +240 -0
- package/src/core/altium/IntLibStreamExtractor.mjs +366 -0
- package/src/core/altium/LibraryRenderManifestBuilder.mjs +417 -0
- package/src/core/altium/LibrarySearchIndex.mjs +215 -0
- package/src/core/altium/NormalizedModelSchema.mjs +36 -0
- package/src/core/altium/PcbCustomPadShapeParser.mjs +244 -0
- package/src/core/altium/PcbDefaultsParser.mjs +171 -0
- package/src/core/altium/PcbDimensionParser.mjs +229 -0
- package/src/core/altium/PcbEmbeddedModelExtractor.mjs +232 -6
- package/src/core/altium/PcbExtendedPrimitiveInformationParser.mjs +256 -0
- package/src/core/altium/PcbLibModelParser.mjs +235 -14
- package/src/core/altium/PcbLibStreamExtractor.mjs +62 -4
- package/src/core/altium/PcbMaskPasteResolver.mjs +354 -0
- package/src/core/altium/PcbMechanicalLayerPairParser.mjs +204 -0
- package/src/core/altium/PcbModelParser.mjs +466 -28
- package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
- package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
- package/src/core/altium/PcbPadStackParser.mjs +58 -0
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +217 -0
- package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
- package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +5 -1
- package/src/core/altium/PcbRuleParser.mjs +354 -33
- package/src/core/altium/PcbSidecarRecordParser.mjs +177 -0
- package/src/core/altium/PcbSpecialStringResolver.mjs +220 -0
- package/src/core/altium/PcbStatisticsBuilder.mjs +532 -0
- package/src/core/altium/PcbStreamExtractor.mjs +111 -4
- package/src/core/altium/PcbTextPrimitiveParser.mjs +60 -0
- package/src/core/altium/PcbUnionParser.mjs +307 -0
- package/src/core/altium/PcbViaStackParser.mjs +98 -10
- package/src/core/altium/PcbViaStructureParser.mjs +335 -0
- package/src/core/altium/PrintableTextDecoder.mjs +53 -3
- package/src/core/altium/PrjPcbModelParser.mjs +257 -5
- package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +477 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +499 -0
- package/src/core/altium/ProjectOutJobDigestBuilder.mjs +109 -0
- package/src/core/altium/ProjectVariantViewBuilder.mjs +334 -0
- package/src/core/altium/SchematicBindingProvenanceParser.mjs +223 -0
- package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +312 -0
- package/src/core/altium/SchematicComponentTextResolver.mjs +72 -19
- package/src/core/altium/SchematicConnectivityQaBuilder.mjs +271 -0
- package/src/core/altium/SchematicCrossSheetConnectorParser.mjs +140 -0
- package/src/core/altium/SchematicDirectiveParser.mjs +312 -0
- package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +231 -0
- package/src/core/altium/SchematicHarnessParser.mjs +302 -0
- package/src/core/altium/SchematicImageParser.mjs +474 -3
- package/src/core/altium/SchematicImplementationParser.mjs +518 -0
- package/src/core/altium/SchematicNetlistBuilder.mjs +15 -2
- package/src/core/altium/SchematicOwnershipGraphParser.mjs +195 -0
- package/src/core/altium/SchematicPinParser.mjs +84 -1
- package/src/core/altium/SchematicPrimitiveParser.mjs +301 -0
- package/src/core/altium/SchematicProjectParameterResolver.mjs +361 -0
- package/src/core/altium/SchematicQaReportBuilder.mjs +284 -0
- package/src/core/altium/SchematicRecordTypeRegistry.mjs +137 -0
- package/src/core/altium/SchematicRepeatedChannelParser.mjs +229 -0
- package/src/core/altium/SchematicStreamExtractor.mjs +10 -1
- package/src/core/altium/SchematicTemplateParser.mjs +256 -0
- package/src/core/altium/SchematicTextParser.mjs +123 -0
- package/src/core/ole/OleCompoundDocument.mjs +20 -0
- package/src/parser.mjs +29 -0
- package/src/styles/altium-renderers.css +19 -0
- package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
- package/src/ui/PcbInteractionIndex.mjs +9 -4
- package/src/ui/PcbScene3dBuilder.mjs +137 -3
- package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
- package/src/ui/PcbSvgRenderer.mjs +1187 -34
- package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
- package/src/ui/SchematicNoteRenderer.mjs +9 -2
- package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
- package/src/ui/SchematicShapeRenderer.mjs +362 -0
- package/src/ui/SchematicSvgRenderer.mjs +1442 -92
- package/src/ui/SchematicTypography.mjs +48 -5
- package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves PCB text special strings against project parameters.
|
|
7
|
+
*/
|
|
8
|
+
export class PcbSpecialStringResolver {
|
|
9
|
+
/**
|
|
10
|
+
* Resolves one authored PCB text expression.
|
|
11
|
+
* @param {string} text
|
|
12
|
+
* @param {Record<string, string | number | boolean>} [parameters]
|
|
13
|
+
* @returns {{ rawText: string, resolvedText: string, parameterNames: string[], expressionParts: object[] } | null}
|
|
14
|
+
*/
|
|
15
|
+
static resolveText(text, parameters = {}) {
|
|
16
|
+
const rawText = String(text ?? '')
|
|
17
|
+
const expressionParts =
|
|
18
|
+
PcbSpecialStringResolver.#parseExpressionParts(rawText)
|
|
19
|
+
|
|
20
|
+
if (!expressionParts.length) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const normalizedParameters =
|
|
25
|
+
PcbSpecialStringResolver.#normalizeParameters(parameters)
|
|
26
|
+
const resolvedParts = []
|
|
27
|
+
const parameterNames = []
|
|
28
|
+
|
|
29
|
+
for (const part of expressionParts) {
|
|
30
|
+
if (part.type !== 'parameter') {
|
|
31
|
+
resolvedParts.push(part)
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parameter = normalizedParameters.get(part.name.toLowerCase())
|
|
36
|
+
|
|
37
|
+
if (!parameter) {
|
|
38
|
+
resolvedParts.push({
|
|
39
|
+
...part,
|
|
40
|
+
value: '.' + part.name,
|
|
41
|
+
unresolved: true
|
|
42
|
+
})
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
parameterNames.push(parameter.name)
|
|
47
|
+
resolvedParts.push({
|
|
48
|
+
type: 'parameter',
|
|
49
|
+
name: parameter.name,
|
|
50
|
+
value: String(parameter.value)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
!parameterNames.length &&
|
|
56
|
+
!resolvedParts.some((part) => part.type === 'parameter')
|
|
57
|
+
) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
rawText,
|
|
63
|
+
resolvedText: resolvedParts.map((part) => part.value).join(''),
|
|
64
|
+
parameterNames,
|
|
65
|
+
expressionParts: resolvedParts
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adds special-string metadata to text primitives.
|
|
71
|
+
* @param {object[]} texts
|
|
72
|
+
* @param {Record<string, string | number | boolean>} [parameters]
|
|
73
|
+
* @param {{ replaceText?: boolean }} [options]
|
|
74
|
+
* @returns {object[]}
|
|
75
|
+
*/
|
|
76
|
+
static annotateTexts(texts, parameters = {}, options = {}) {
|
|
77
|
+
return (texts || []).map((text) => {
|
|
78
|
+
const resolved = PcbSpecialStringResolver.resolveText(
|
|
79
|
+
text?.text,
|
|
80
|
+
parameters
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (!resolved) {
|
|
84
|
+
return text
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...text,
|
|
89
|
+
...(options.replaceText === true
|
|
90
|
+
? { text: resolved.resolvedText }
|
|
91
|
+
: {}),
|
|
92
|
+
rawText: resolved.rawText,
|
|
93
|
+
resolvedText: resolved.resolvedText,
|
|
94
|
+
specialString: {
|
|
95
|
+
parameterNames: resolved.parameterNames,
|
|
96
|
+
expressionParts: resolved.expressionParts
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Splits a supported special-string expression into literal and parameter
|
|
104
|
+
* parts.
|
|
105
|
+
* @param {string} text
|
|
106
|
+
* @returns {{ type: string, name?: string, value?: string }[]}
|
|
107
|
+
*/
|
|
108
|
+
static #parseExpressionParts(text) {
|
|
109
|
+
const segments = PcbSpecialStringResolver.#splitConcatenation(text)
|
|
110
|
+
const parts = []
|
|
111
|
+
|
|
112
|
+
for (const segment of segments) {
|
|
113
|
+
const trimmed = segment.trim()
|
|
114
|
+
if (!trimmed) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (PcbSpecialStringResolver.#isQuoted(trimmed)) {
|
|
119
|
+
parts.push({
|
|
120
|
+
type: 'literal',
|
|
121
|
+
value: PcbSpecialStringResolver.#unquote(trimmed)
|
|
122
|
+
})
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const parameterMatch = trimmed.match(/^\.([A-Za-z_][\w.-]*)$/u)
|
|
127
|
+
if (parameterMatch) {
|
|
128
|
+
parts.push({
|
|
129
|
+
type: 'parameter',
|
|
130
|
+
name: parameterMatch[1]
|
|
131
|
+
})
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (trimmed.startsWith('.')) {
|
|
136
|
+
parts.push({
|
|
137
|
+
type: 'literal',
|
|
138
|
+
value: trimmed
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return parts
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Splits an expression on plus operators outside quoted strings.
|
|
148
|
+
* @param {string} text
|
|
149
|
+
* @returns {string[]}
|
|
150
|
+
*/
|
|
151
|
+
static #splitConcatenation(text) {
|
|
152
|
+
const segments = []
|
|
153
|
+
let quote = ''
|
|
154
|
+
let current = ''
|
|
155
|
+
|
|
156
|
+
for (const character of String(text || '')) {
|
|
157
|
+
if ((character === '"' || character === "'") && !quote) {
|
|
158
|
+
quote = character
|
|
159
|
+
current += character
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (character === quote) {
|
|
164
|
+
quote = ''
|
|
165
|
+
current += character
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (character === '+' && !quote) {
|
|
170
|
+
segments.push(current)
|
|
171
|
+
current = ''
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
current += character
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
segments.push(current)
|
|
179
|
+
return segments
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Returns true when one segment is a quoted literal.
|
|
184
|
+
* @param {string} value
|
|
185
|
+
* @returns {boolean}
|
|
186
|
+
*/
|
|
187
|
+
static #isQuoted(value) {
|
|
188
|
+
return (
|
|
189
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
190
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Removes simple surrounding quotes from one literal.
|
|
196
|
+
* @param {string} value
|
|
197
|
+
* @returns {string}
|
|
198
|
+
*/
|
|
199
|
+
static #unquote(value) {
|
|
200
|
+
return value.slice(1, -1).replace(/\\(["'\\])/gu, '$1')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Builds a case-insensitive project parameter map.
|
|
205
|
+
* @param {Record<string, string | number | boolean>} parameters
|
|
206
|
+
* @returns {Map<string, { name: string, value: string | number | boolean }>}
|
|
207
|
+
*/
|
|
208
|
+
static #normalizeParameters(parameters) {
|
|
209
|
+
const normalized = new Map()
|
|
210
|
+
|
|
211
|
+
for (const [name, value] of Object.entries(parameters || {})) {
|
|
212
|
+
normalized.set(String(name).toLowerCase(), {
|
|
213
|
+
name,
|
|
214
|
+
value
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return normalized
|
|
219
|
+
}
|
|
220
|
+
}
|