altium-toolkit 1.1.2 → 1.1.3
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/package.json +1 -1
- package/src/core/altium/AltiumLayoutParser.mjs +275 -13
- package/src/core/altium/AltiumParser.mjs +240 -8
- package/src/core/altium/PcbEmbeddedFontExtractor.mjs +186 -43
- package/src/core/altium/PrintableTextDecoder.mjs +133 -13
- package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +13 -0
- package/src/core/altium/SchematicComponentTextResolver.mjs +40 -1
- package/src/core/altium/SchematicImageParser.mjs +291 -6
- package/src/core/altium/SchematicMultipartDesignatorNormalizer.mjs +164 -0
- package/src/core/altium/SchematicMultipartOwnerMatcher.mjs +2 -0
- package/src/core/altium/SchematicPinParser.mjs +175 -4
- package/src/core/altium/SchematicSheetStyleResolver.mjs +38 -0
- package/src/core/altium/SchematicTextParser.mjs +125 -11
- package/src/core/altium/SchematicTextPostProcessor.mjs +146 -102
- package/src/ui/SchematicColorResolver.mjs +78 -0
- package/src/ui/SchematicContentLayout.mjs +58 -1
- package/src/ui/SchematicImageRenderer.mjs +125 -10
- package/src/ui/SchematicJunctionRenderer.mjs +1 -1
- package/src/ui/SchematicNativeFooterPartitioner.mjs +275 -0
- package/src/ui/SchematicNoteRenderer.mjs +82 -6
- package/src/ui/SchematicOwnerPinLabelLayout.mjs +292 -3
- package/src/ui/SchematicPinSvgRenderer.mjs +197 -15
- package/src/ui/SchematicPowerDiagramImageProcessor.mjs +970 -0
- package/src/ui/SchematicPowerDiagramLineMasks.mjs +631 -0
- package/src/ui/SchematicPowerPortRenderer.mjs +1 -1
- package/src/ui/SchematicShapeRenderer.mjs +82 -23
- package/src/ui/SchematicSvgRenderer.mjs +293 -47
|
@@ -157,7 +157,7 @@ export class SchematicShapeRenderer {
|
|
|
157
157
|
pie.startAngle,
|
|
158
158
|
pie.endAngle
|
|
159
159
|
)
|
|
160
|
-
const sweep = delta
|
|
160
|
+
const sweep = SchematicShapeRenderer.#resolvePieSweep(delta)
|
|
161
161
|
const center = {
|
|
162
162
|
x: Number(pie.x) || 0,
|
|
163
163
|
y: projectSchematicY(sheetHeight, Number(pie.y) || 0)
|
|
@@ -226,11 +226,16 @@ export class SchematicShapeRenderer {
|
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
228
|
* Builds one schematic rectangle primitive.
|
|
229
|
-
* @param {{ x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number }} rectangle
|
|
229
|
+
* @param {{ x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number, ownerIndex?: string }} rectangle
|
|
230
230
|
* @param {number} sheetHeight
|
|
231
231
|
* @returns {string}
|
|
232
232
|
*/
|
|
233
233
|
static buildRectangleMarkup(rectangle, sheetHeight) {
|
|
234
|
+
const fill =
|
|
235
|
+
SchematicShapeRenderer.#resolveSchematicRectangleFill(rectangle)
|
|
236
|
+
const preserveSourceColors =
|
|
237
|
+
SchematicShapeRenderer.#isOwnerColorStrip(rectangle)
|
|
238
|
+
|
|
234
239
|
return (
|
|
235
240
|
'<rect class="schematic-rectangle" x="' +
|
|
236
241
|
formatNumber(rectangle.x) +
|
|
@@ -244,19 +249,27 @@ export class SchematicShapeRenderer {
|
|
|
244
249
|
formatNumber(rectangle.height) +
|
|
245
250
|
'" fill="' +
|
|
246
251
|
escapeHtml(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
preserveSourceColors
|
|
253
|
+
? SchematicColorResolver.resolveSourceFill(
|
|
254
|
+
fill,
|
|
255
|
+
'--schematic-fill-color'
|
|
256
|
+
)
|
|
257
|
+
: SchematicColorResolver.resolveFill(
|
|
258
|
+
fill,
|
|
259
|
+
'--schematic-fill-color'
|
|
260
|
+
)
|
|
253
261
|
) +
|
|
254
262
|
'" stroke="' +
|
|
255
263
|
escapeHtml(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
264
|
+
preserveSourceColors
|
|
265
|
+
? SchematicColorResolver.resolveSourceColor(
|
|
266
|
+
rectangle.color,
|
|
267
|
+
'--schematic-default-ink-color'
|
|
268
|
+
)
|
|
269
|
+
: SchematicColorResolver.resolveColor(
|
|
270
|
+
rectangle.color,
|
|
271
|
+
'--schematic-default-ink-color'
|
|
272
|
+
)
|
|
260
273
|
) +
|
|
261
274
|
'" stroke-width="' +
|
|
262
275
|
formatNumber(Math.max(rectangle.lineWidth || 1, 0.8)) +
|
|
@@ -271,12 +284,16 @@ export class SchematicShapeRenderer {
|
|
|
271
284
|
|
|
272
285
|
/**
|
|
273
286
|
* Builds one schematic rounded-rectangle primitive.
|
|
274
|
-
* @param {{ x: number, y: number, width: number, height: number, radius?: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number }} rectangle
|
|
287
|
+
* @param {{ x: number, y: number, width: number, height: number, radius?: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number, ownerIndex?: string }} rectangle
|
|
275
288
|
* @param {number} sheetHeight
|
|
276
289
|
* @returns {string}
|
|
277
290
|
*/
|
|
278
291
|
static buildRoundedRectangleMarkup(rectangle, sheetHeight) {
|
|
279
292
|
const radius = Math.max(Number(rectangle.radius || 0), 0)
|
|
293
|
+
const fill =
|
|
294
|
+
SchematicShapeRenderer.#resolveSchematicRectangleFill(rectangle)
|
|
295
|
+
const preserveSourceColors =
|
|
296
|
+
SchematicShapeRenderer.#isOwnerColorStrip(rectangle)
|
|
280
297
|
|
|
281
298
|
return (
|
|
282
299
|
'<rect class="schematic-rounded-rectangle" x="' +
|
|
@@ -295,19 +312,27 @@ export class SchematicShapeRenderer {
|
|
|
295
312
|
formatNumber(radius) +
|
|
296
313
|
'" fill="' +
|
|
297
314
|
escapeHtml(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
315
|
+
preserveSourceColors
|
|
316
|
+
? SchematicColorResolver.resolveSourceFill(
|
|
317
|
+
fill,
|
|
318
|
+
'--schematic-fill-color'
|
|
319
|
+
)
|
|
320
|
+
: SchematicColorResolver.resolveFill(
|
|
321
|
+
fill,
|
|
322
|
+
'--schematic-fill-color'
|
|
323
|
+
)
|
|
304
324
|
) +
|
|
305
325
|
'" stroke="' +
|
|
306
326
|
escapeHtml(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
327
|
+
preserveSourceColors
|
|
328
|
+
? SchematicColorResolver.resolveSourceColor(
|
|
329
|
+
rectangle.color,
|
|
330
|
+
'--schematic-default-ink-color'
|
|
331
|
+
)
|
|
332
|
+
: SchematicColorResolver.resolveColor(
|
|
333
|
+
rectangle.color,
|
|
334
|
+
'--schematic-default-ink-color'
|
|
335
|
+
)
|
|
311
336
|
) +
|
|
312
337
|
'" stroke-width="' +
|
|
313
338
|
formatNumber(Math.max(rectangle.lineWidth || 1, 0.8)) +
|
|
@@ -455,6 +480,25 @@ export class SchematicShapeRenderer {
|
|
|
455
480
|
return rectangle.fill || 'none'
|
|
456
481
|
}
|
|
457
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Returns true for narrow symbol-owned color swatches such as per-section
|
|
485
|
+
* strips that carry literal source palette meaning.
|
|
486
|
+
* @param {{ width: number, height: number, ownerIndex?: string }} rectangle
|
|
487
|
+
* @returns {boolean}
|
|
488
|
+
*/
|
|
489
|
+
static #isOwnerColorStrip(rectangle) {
|
|
490
|
+
if (!rectangle?.ownerIndex) {
|
|
491
|
+
return false
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const width = Math.abs(Number(rectangle.width || 0))
|
|
495
|
+
const height = Math.abs(Number(rectangle.height || 0))
|
|
496
|
+
const shortSide = Math.min(width, height)
|
|
497
|
+
const longSide = Math.max(width, height)
|
|
498
|
+
|
|
499
|
+
return shortSide > 0 && shortSide <= 8 && longSide >= 40
|
|
500
|
+
}
|
|
501
|
+
|
|
458
502
|
/**
|
|
459
503
|
* Resolves the visible fill for one schematic ellipse primitive.
|
|
460
504
|
* @param {{ fill: string, isSolid: boolean, transparent: boolean }} ellipse
|
|
@@ -766,4 +810,19 @@ export class SchematicShapeRenderer {
|
|
|
766
810
|
|
|
767
811
|
return delta
|
|
768
812
|
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Resolves SVG sweep direction for filled Altium pie primitives.
|
|
816
|
+
* @param {number} delta Normalized source angle delta.
|
|
817
|
+
* @returns {number}
|
|
818
|
+
*/
|
|
819
|
+
static #resolvePieSweep(delta) {
|
|
820
|
+
const magnitude = Math.abs(Number(delta) || 0)
|
|
821
|
+
|
|
822
|
+
if (Math.abs(magnitude - 180) <= 0.001) {
|
|
823
|
+
return 0
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return delta >= 0 ? 0 : 1
|
|
827
|
+
}
|
|
769
828
|
}
|
|
@@ -18,6 +18,7 @@ import { SchematicOwnerPinLabelLayout } from './SchematicOwnerPinLabelLayout.mjs
|
|
|
18
18
|
import { SchematicRegionRenderer } from './SchematicRegionRenderer.mjs'
|
|
19
19
|
import { SchematicSheetSymbolRenderer } from './SchematicSheetSymbolRenderer.mjs'
|
|
20
20
|
import { SchematicImageRenderer } from './SchematicImageRenderer.mjs'
|
|
21
|
+
import { SchematicNativeFooterPartitioner } from './SchematicNativeFooterPartitioner.mjs'
|
|
21
22
|
import { TextGeometrySidecarBuilder } from './TextGeometrySidecarBuilder.mjs'
|
|
22
23
|
import { SchematicRenderOpsSidecarBuilder } from './SchematicRenderOpsSidecarBuilder.mjs'
|
|
23
24
|
import { SchematicProjectParameterResolver } from '../core/altium/SchematicProjectParameterResolver.mjs'
|
|
@@ -38,7 +39,7 @@ export class SchematicSvgRenderer {
|
|
|
38
39
|
/**
|
|
39
40
|
* Renders a normalized schematic model into SVG markup.
|
|
40
41
|
* @param {{ fileName?: string, summary: { title?: string }, schematic?: { sheet: { width: number, height: number, sourceWidth?: number, sourceHeight?: number, paperSize?: string, borderOn?: boolean, titleBlockOn?: boolean, marginWidth?: number, xZones?: number, yZones?: number, titleBlock?: { title?: string, revision?: string, documentNumber?: string, sheetNumber?: string, sheetTotal?: string, date?: string, drawnBy?: string } }, lines: { x1: number, y1: number, x2: number, y2: number, color: string, width: number, lineStyle?: number, isBus?: boolean, ownerIndex?: string, renderOrder?: number, recordType?: string }[], polygons?: { points: { x: number, y: number }[], color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], rectangles?: { x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], regions?: { x: number, y: number, width: number, height: number, color: string, fill: string, renderOrder?: number }[], ellipses?: { x: number, y: number, radiusX: number, radiusY: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], arcs?: { x: number, y: number, radius: number, startAngle: number, endAngle: number, color: string, width: number, ownerIndex?: string, renderOrder?: number }[], directives?: { x: number, y: number, color: string, name: string, orientation?: number }[], texts: { x: number, y: number, text: string, color: string, recordType?: string, style?: number, fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string, rotation?: number, sourceOrientation?: number, isMirrored?: boolean, anchor?: 'start' | 'middle' | 'end', powerPortDirection?: 'up' | 'down' | 'left' | 'right', cornerX?: number, cornerY?: number, fill?: string, borderColor?: string, isSolid?: boolean, showBorder?: boolean, textMargin?: number, noteLines?: string[] }[], components: { x: number, y: number, designator: string }[], pins?: { x: number, y: number, length: number, name: string, nameSegments?: { text: string, overline: boolean }[], designator: string, orientation: 'left' | 'right' | 'top' | 'bottom', electrical?: number, symbolOuter?: number, color: string, labelColor?: string, labelMode?: 'hidden' | 'number-only' | 'name-only' | 'name-and-number', ownerIndex?: string }[], ports?: { x: number, y: number, width: number, height: number, name: string, fill: string, color: string, direction?: 'left' | 'right' | 'up' | 'down', shape?: 'single' | 'double' | 'plain' }[], crosses?: { x: number, y: number, size: number, color: string }[] } }} documentModel
|
|
41
|
-
* @param {{ projectParameters?: Record<string, string | number | boolean | null | undefined
|
|
42
|
+
* @param {{ projectParameters?: Record<string, string | number | boolean | null | undefined>, colorizeImages?: boolean, colorize_images?: boolean }} options Render options.
|
|
42
43
|
* @returns {string}
|
|
43
44
|
*/
|
|
44
45
|
static render(documentModel, options = {}) {
|
|
@@ -66,7 +67,9 @@ export class SchematicSvgRenderer {
|
|
|
66
67
|
renderedSheet.contentSheet === schematic.sheet
|
|
67
68
|
? schematic
|
|
68
69
|
: { ...schematic, sheet: renderedSheet.contentSheet }
|
|
69
|
-
const allTexts =
|
|
70
|
+
const allTexts = SchematicSvgRenderer.#filterDrawableSchematicTexts(
|
|
71
|
+
schematic.texts || []
|
|
72
|
+
)
|
|
70
73
|
const lines = (schematic.lines || []).slice(0, 2500)
|
|
71
74
|
const polygons = (schematic.polygons || []).slice(0, 1000)
|
|
72
75
|
const rectangles = (schematic.rectangles || []).slice(0, 500)
|
|
@@ -146,25 +149,6 @@ export class SchematicSvgRenderer {
|
|
|
146
149
|
renderedSchematic,
|
|
147
150
|
contentClipId
|
|
148
151
|
)
|
|
149
|
-
const ownerlessLines = lines.filter((line) => !line.ownerIndex)
|
|
150
|
-
const ownerlessPolygons = polygons.filter(
|
|
151
|
-
(polygon) => !polygon.ownerIndex
|
|
152
|
-
)
|
|
153
|
-
const ownerlessRectangles = rectangles.filter(
|
|
154
|
-
(rectangle) => !rectangle.ownerIndex
|
|
155
|
-
)
|
|
156
|
-
const ownerlessRoundedRectangles = roundedRectangles.filter(
|
|
157
|
-
(rectangle) => !rectangle.ownerIndex
|
|
158
|
-
)
|
|
159
|
-
const ownerlessEllipses = ellipses.filter(
|
|
160
|
-
(ellipse) => !ellipse.ownerIndex
|
|
161
|
-
)
|
|
162
|
-
const ownerlessArcs = arcs.filter((arc) => !arc.ownerIndex)
|
|
163
|
-
const ownerlessBeziers = beziers.filter((bezier) => !bezier.ownerIndex)
|
|
164
|
-
const ownerlessPies = pies.filter((pie) => !pie.ownerIndex)
|
|
165
|
-
const ownerlessIeeeSymbols = ieeeSymbols.filter(
|
|
166
|
-
(symbol) => !symbol.ownerIndex
|
|
167
|
-
)
|
|
168
152
|
const resolvedTexts = texts.map((text) =>
|
|
169
153
|
text.recordType === '17'
|
|
170
154
|
? {
|
|
@@ -178,6 +162,61 @@ export class SchematicSvgRenderer {
|
|
|
178
162
|
}
|
|
179
163
|
: text
|
|
180
164
|
)
|
|
165
|
+
const partitionedPrimitives =
|
|
166
|
+
SchematicNativeFooterPartitioner.partitionPrimitives(
|
|
167
|
+
{
|
|
168
|
+
lines,
|
|
169
|
+
polygons,
|
|
170
|
+
rectangles,
|
|
171
|
+
roundedRectangles,
|
|
172
|
+
ellipses,
|
|
173
|
+
arcs,
|
|
174
|
+
beziers,
|
|
175
|
+
pies,
|
|
176
|
+
ieeeSymbols,
|
|
177
|
+
texts: resolvedTexts,
|
|
178
|
+
images
|
|
179
|
+
},
|
|
180
|
+
renderedSchematic.sheet
|
|
181
|
+
)
|
|
182
|
+
const contentLines = SchematicSvgRenderer.#themeFooterChromeLines(
|
|
183
|
+
partitionedPrimitives.content.lines,
|
|
184
|
+
renderedSheet.sheet,
|
|
185
|
+
contentHeight
|
|
186
|
+
)
|
|
187
|
+
const contentPolygons = partitionedPrimitives.content.polygons
|
|
188
|
+
const contentRectangles = partitionedPrimitives.content.rectangles
|
|
189
|
+
const contentRoundedRectangles =
|
|
190
|
+
partitionedPrimitives.content.roundedRectangles
|
|
191
|
+
const contentEllipses = partitionedPrimitives.content.ellipses
|
|
192
|
+
const contentArcs = partitionedPrimitives.content.arcs
|
|
193
|
+
const contentBeziers = partitionedPrimitives.content.beziers
|
|
194
|
+
const contentPies = partitionedPrimitives.content.pies
|
|
195
|
+
const contentIeeeSymbols = partitionedPrimitives.content.ieeeSymbols
|
|
196
|
+
const contentTexts = partitionedPrimitives.content.texts
|
|
197
|
+
const contentImages = partitionedPrimitives.content.images
|
|
198
|
+
const footerPrimitives = partitionedPrimitives.footer
|
|
199
|
+
const ownerlessLines = contentLines.filter((line) => !line.ownerIndex)
|
|
200
|
+
const ownerlessPolygons = contentPolygons.filter(
|
|
201
|
+
(polygon) => !polygon.ownerIndex
|
|
202
|
+
)
|
|
203
|
+
const ownerlessRectangles = contentRectangles.filter(
|
|
204
|
+
(rectangle) => !rectangle.ownerIndex
|
|
205
|
+
)
|
|
206
|
+
const ownerlessRoundedRectangles = contentRoundedRectangles.filter(
|
|
207
|
+
(rectangle) => !rectangle.ownerIndex
|
|
208
|
+
)
|
|
209
|
+
const ownerlessEllipses = contentEllipses.filter(
|
|
210
|
+
(ellipse) => !ellipse.ownerIndex
|
|
211
|
+
)
|
|
212
|
+
const ownerlessArcs = contentArcs.filter((arc) => !arc.ownerIndex)
|
|
213
|
+
const ownerlessBeziers = contentBeziers.filter(
|
|
214
|
+
(bezier) => !bezier.ownerIndex
|
|
215
|
+
)
|
|
216
|
+
const ownerlessPies = contentPies.filter((pie) => !pie.ownerIndex)
|
|
217
|
+
const ownerlessIeeeSymbols = contentIeeeSymbols.filter(
|
|
218
|
+
(symbol) => !symbol.ownerIndex
|
|
219
|
+
)
|
|
181
220
|
const polygonMarkup = ownerlessPolygons
|
|
182
221
|
.map((polygon, index) =>
|
|
183
222
|
SchematicSvgRenderer.#appendSvgAttributes(
|
|
@@ -357,15 +396,15 @@ export class SchematicSvgRenderer {
|
|
|
357
396
|
.join('')
|
|
358
397
|
const ownerGeometryMarkup =
|
|
359
398
|
SchematicSvgRenderer.#buildOwnerGeometryMarkup(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
399
|
+
contentLines,
|
|
400
|
+
contentPolygons,
|
|
401
|
+
contentRectangles,
|
|
402
|
+
contentRoundedRectangles,
|
|
403
|
+
contentEllipses,
|
|
404
|
+
contentArcs,
|
|
405
|
+
contentBeziers,
|
|
406
|
+
contentPies,
|
|
407
|
+
contentIeeeSymbols,
|
|
369
408
|
contentHeight,
|
|
370
409
|
semanticContext
|
|
371
410
|
)
|
|
@@ -390,7 +429,7 @@ export class SchematicSvgRenderer {
|
|
|
390
429
|
const resolvedAuthoredJunctions =
|
|
391
430
|
SchematicSvgRenderer.#resolveAuthoredSchematicJunctions(
|
|
392
431
|
authoredJunctions,
|
|
393
|
-
|
|
432
|
+
contentLines
|
|
394
433
|
)
|
|
395
434
|
const authoredJunctionMarkup = resolvedAuthoredJunctions
|
|
396
435
|
.map((junction) =>
|
|
@@ -401,18 +440,74 @@ export class SchematicSvgRenderer {
|
|
|
401
440
|
)
|
|
402
441
|
.join('')
|
|
403
442
|
const imageMarkup = SchematicImageRenderer.buildMarkup(
|
|
404
|
-
|
|
405
|
-
contentHeight
|
|
443
|
+
contentImages,
|
|
444
|
+
contentHeight,
|
|
445
|
+
{
|
|
446
|
+
colorizeImages: renderOptions.colorizeImages,
|
|
447
|
+
title:
|
|
448
|
+
renderModel?.summary?.title ||
|
|
449
|
+
renderedSchematic?.sheet?.titleBlock?.title ||
|
|
450
|
+
renderModel?.fileName ||
|
|
451
|
+
''
|
|
452
|
+
}
|
|
406
453
|
)
|
|
407
454
|
const markerDefsMarkup =
|
|
408
|
-
SchematicSvgRenderer.#buildSchematicLineMarkerDefs(
|
|
455
|
+
SchematicSvgRenderer.#buildSchematicLineMarkerDefs(contentLines)
|
|
409
456
|
|
|
410
|
-
const textMarkup =
|
|
457
|
+
const textMarkup = contentTexts
|
|
411
458
|
.map((text, index) =>
|
|
412
459
|
SchematicSvgRenderer.#buildSchematicTextMarkup(
|
|
413
460
|
text,
|
|
414
461
|
contentHeight,
|
|
415
|
-
|
|
462
|
+
contentLines,
|
|
463
|
+
pins,
|
|
464
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
465
|
+
semanticContext,
|
|
466
|
+
'texts',
|
|
467
|
+
text,
|
|
468
|
+
index
|
|
469
|
+
),
|
|
470
|
+
semanticContext
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
.join('')
|
|
474
|
+
const nativeFooterLines = SchematicSvgRenderer.#themeFooterChromeLines(
|
|
475
|
+
footerPrimitives.lines,
|
|
476
|
+
renderedSheet.sheet,
|
|
477
|
+
contentHeight
|
|
478
|
+
)
|
|
479
|
+
const nativeFooterOwnerGeometryMarkup =
|
|
480
|
+
SchematicSvgRenderer.#buildOwnerGeometryMarkup(
|
|
481
|
+
nativeFooterLines,
|
|
482
|
+
footerPrimitives.polygons,
|
|
483
|
+
footerPrimitives.rectangles,
|
|
484
|
+
footerPrimitives.roundedRectangles,
|
|
485
|
+
footerPrimitives.ellipses,
|
|
486
|
+
footerPrimitives.arcs,
|
|
487
|
+
footerPrimitives.beziers,
|
|
488
|
+
footerPrimitives.pies,
|
|
489
|
+
footerPrimitives.ieeeSymbols,
|
|
490
|
+
contentHeight,
|
|
491
|
+
semanticContext
|
|
492
|
+
)
|
|
493
|
+
const nativeFooterImageMarkup = SchematicImageRenderer.buildMarkup(
|
|
494
|
+
footerPrimitives.images,
|
|
495
|
+
contentHeight,
|
|
496
|
+
{
|
|
497
|
+
colorizeImages: renderOptions.colorizeImages,
|
|
498
|
+
title:
|
|
499
|
+
renderModel?.summary?.title ||
|
|
500
|
+
renderedSchematic?.sheet?.titleBlock?.title ||
|
|
501
|
+
renderModel?.fileName ||
|
|
502
|
+
''
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
const nativeFooterTextMarkup = footerPrimitives.texts
|
|
506
|
+
.map((text, index) =>
|
|
507
|
+
SchematicSvgRenderer.#buildSchematicTextMarkup(
|
|
508
|
+
text,
|
|
509
|
+
contentHeight,
|
|
510
|
+
contentLines,
|
|
416
511
|
pins,
|
|
417
512
|
SchematicSvgRenderer.#primitiveIndex(
|
|
418
513
|
semanticContext,
|
|
@@ -424,6 +519,36 @@ export class SchematicSvgRenderer {
|
|
|
424
519
|
)
|
|
425
520
|
)
|
|
426
521
|
.join('')
|
|
522
|
+
const nativeFooterMargin = Math.max(
|
|
523
|
+
Number(renderedSchematic?.sheet?.marginWidth || 20),
|
|
524
|
+
10
|
|
525
|
+
)
|
|
526
|
+
const nativeFooterOffsetX = partitionedPrimitives.footerBounds
|
|
527
|
+
? Math.max(
|
|
528
|
+
width -
|
|
529
|
+
nativeFooterMargin -
|
|
530
|
+
partitionedPrimitives.footerBounds.maxX,
|
|
531
|
+
0
|
|
532
|
+
)
|
|
533
|
+
: 0
|
|
534
|
+
const nativeFooterTransform =
|
|
535
|
+
nativeFooterOffsetX > 0.01
|
|
536
|
+
? ' transform="translate(' +
|
|
537
|
+
formatNumber(nativeFooterOffsetX) +
|
|
538
|
+
' 0)"'
|
|
539
|
+
: ''
|
|
540
|
+
const nativeFooterMarkup =
|
|
541
|
+
nativeFooterOwnerGeometryMarkup ||
|
|
542
|
+
nativeFooterImageMarkup ||
|
|
543
|
+
nativeFooterTextMarkup
|
|
544
|
+
? '<g class="schematic-native-footer" stroke-linecap="round"' +
|
|
545
|
+
nativeFooterTransform +
|
|
546
|
+
'>' +
|
|
547
|
+
nativeFooterOwnerGeometryMarkup +
|
|
548
|
+
nativeFooterImageMarkup +
|
|
549
|
+
nativeFooterTextMarkup +
|
|
550
|
+
'</g>'
|
|
551
|
+
: ''
|
|
427
552
|
|
|
428
553
|
const componentMarkup = drawableComponents
|
|
429
554
|
.map((component, index) =>
|
|
@@ -451,6 +576,15 @@ export class SchematicSvgRenderer {
|
|
|
451
576
|
texts,
|
|
452
577
|
pins
|
|
453
578
|
)
|
|
579
|
+
const compactExternalNumberLabelSides =
|
|
580
|
+
SchematicOwnerPinLabelLayout.collectCompactExternalNumberLabelSides(
|
|
581
|
+
pins
|
|
582
|
+
)
|
|
583
|
+
const internalNumberLabelBoxes =
|
|
584
|
+
SchematicOwnerPinLabelLayout.collectInternalNumberLabelBoxes(
|
|
585
|
+
pins,
|
|
586
|
+
contentRectangles
|
|
587
|
+
)
|
|
454
588
|
const pinMarkup = pins
|
|
455
589
|
.map((pin, index) =>
|
|
456
590
|
SchematicSvgRenderer.#appendSvgAttributes(
|
|
@@ -460,7 +594,9 @@ export class SchematicSvgRenderer {
|
|
|
460
594
|
renderedSheet.contentSheet,
|
|
461
595
|
rotatedVerticalNumberOwners,
|
|
462
596
|
explicitOwnerPinNameLabels,
|
|
463
|
-
explicitOwnerPinLabelOffsets
|
|
597
|
+
explicitOwnerPinLabelOffsets,
|
|
598
|
+
compactExternalNumberLabelSides,
|
|
599
|
+
internalNumberLabelBoxes
|
|
464
600
|
),
|
|
465
601
|
SchematicSvgRenderer.#semanticAttributes(
|
|
466
602
|
'pin',
|
|
@@ -504,10 +640,10 @@ export class SchematicSvgRenderer {
|
|
|
504
640
|
)
|
|
505
641
|
.join('')
|
|
506
642
|
const junctionMarkup = SchematicJunctionRenderer.buildMarkup(
|
|
507
|
-
|
|
643
|
+
contentLines,
|
|
508
644
|
crosses,
|
|
509
645
|
ports,
|
|
510
|
-
|
|
646
|
+
contentTexts.filter((text) => text.recordType === '17'),
|
|
511
647
|
contentHeight,
|
|
512
648
|
resolvedAuthoredJunctions
|
|
513
649
|
)
|
|
@@ -626,6 +762,7 @@ export class SchematicSvgRenderer {
|
|
|
626
762
|
'</g>' +
|
|
627
763
|
'</g>' +
|
|
628
764
|
frameMarkup +
|
|
765
|
+
nativeFooterMarkup +
|
|
629
766
|
'<g class="schematic-regions">' +
|
|
630
767
|
regionMarkup +
|
|
631
768
|
'</g>' +
|
|
@@ -636,7 +773,7 @@ export class SchematicSvgRenderer {
|
|
|
636
773
|
/**
|
|
637
774
|
* Normalizes schematic SVG export options.
|
|
638
775
|
* @param {Record<string, unknown>} options Raw render options.
|
|
639
|
-
* @returns {{ includeViewBox: boolean, documentId: string, documentVersion: string, includeTextGeometrySidecar: boolean, includeRenderOperationsSidecar: boolean, renderOperationProfile: string }}
|
|
776
|
+
* @returns {{ includeViewBox: boolean, documentId: string, documentVersion: string, colorizeImages: boolean, includeTextGeometrySidecar: boolean, includeRenderOperationsSidecar: boolean, renderOperationProfile: string }}
|
|
640
777
|
*/
|
|
641
778
|
static #normalizeRenderOptions(options) {
|
|
642
779
|
const includeViewBox =
|
|
@@ -648,6 +785,9 @@ export class SchematicSvgRenderer {
|
|
|
648
785
|
documentVersion: String(
|
|
649
786
|
options?.documentVersion || options?.documentVer || ''
|
|
650
787
|
),
|
|
788
|
+
colorizeImages:
|
|
789
|
+
options?.colorizeImages === true ||
|
|
790
|
+
options?.colorize_images === true,
|
|
651
791
|
includeTextGeometrySidecar:
|
|
652
792
|
options?.includeTextGeometrySidecar === true ||
|
|
653
793
|
options?.textGeometry === 'sidecar',
|
|
@@ -660,6 +800,97 @@ export class SchematicSvgRenderer {
|
|
|
660
800
|
}
|
|
661
801
|
}
|
|
662
802
|
|
|
803
|
+
/**
|
|
804
|
+
* Removes unresolved simple Altium special-string placeholders from
|
|
805
|
+
* drawable text while preserving values resolved by project parameters.
|
|
806
|
+
* @param {object[]} texts Schematic text rows.
|
|
807
|
+
* @returns {object[]}
|
|
808
|
+
*/
|
|
809
|
+
static #filterDrawableSchematicTexts(texts) {
|
|
810
|
+
return (texts || []).filter(
|
|
811
|
+
(text) =>
|
|
812
|
+
!SchematicSvgRenderer.#isUnresolvedSimpleSpecialStringText(text)
|
|
813
|
+
)
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Uses sheet-frame chrome color for native footer linework so recovered
|
|
818
|
+
* template cells match the synthesized frame and zone separators.
|
|
819
|
+
* @param {object[]} lines Schematic line primitives.
|
|
820
|
+
* @param {{ width?: number, borderOn?: boolean, marginWidth?: number }} sheet Sheet metadata.
|
|
821
|
+
* @param {number} sheetHeight Rendered sheet height for projection.
|
|
822
|
+
* @returns {object[]}
|
|
823
|
+
*/
|
|
824
|
+
static #themeFooterChromeLines(lines, sheet, sheetHeight) {
|
|
825
|
+
return (lines || []).map((line) =>
|
|
826
|
+
SchematicSvgRenderer.#isFooterChromeLine(line, sheet, sheetHeight)
|
|
827
|
+
? {
|
|
828
|
+
...line,
|
|
829
|
+
color: 'var(--schematic-sheet-frame-stroke)'
|
|
830
|
+
}
|
|
831
|
+
: line
|
|
832
|
+
)
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Returns true for neutral owner-drawn title-block lines in the bottom
|
|
837
|
+
* right footer band.
|
|
838
|
+
* @param {{ x1?: number, y1?: number, x2?: number, y2?: number, color?: string, ownerIndex?: string }} line Schematic line candidate.
|
|
839
|
+
* @param {{ width?: number, borderOn?: boolean, marginWidth?: number }} sheet Sheet metadata.
|
|
840
|
+
* @param {number} sheetHeight Rendered sheet height for projection.
|
|
841
|
+
* @returns {boolean}
|
|
842
|
+
*/
|
|
843
|
+
static #isFooterChromeLine(line, sheet, sheetHeight) {
|
|
844
|
+
if (!sheet?.borderOn || !String(line?.ownerIndex || '').trim()) {
|
|
845
|
+
return false
|
|
846
|
+
}
|
|
847
|
+
if (
|
|
848
|
+
SchematicSvgRenderer.#resolveSchematicLineColor(line) !==
|
|
849
|
+
'var(--schematic-text-color)'
|
|
850
|
+
) {
|
|
851
|
+
return false
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const width = Number(sheet?.width || 0)
|
|
855
|
+
const height = Number(sheetHeight || 0)
|
|
856
|
+
const x1 = Number(line?.x1)
|
|
857
|
+
const x2 = Number(line?.x2)
|
|
858
|
+
const y1 = Number(line?.y1)
|
|
859
|
+
const y2 = Number(line?.y2)
|
|
860
|
+
|
|
861
|
+
if (
|
|
862
|
+
!Number.isFinite(width) ||
|
|
863
|
+
!Number.isFinite(height) ||
|
|
864
|
+
!Number.isFinite(x1) ||
|
|
865
|
+
!Number.isFinite(x2) ||
|
|
866
|
+
!Number.isFinite(y1) ||
|
|
867
|
+
!Number.isFinite(y2) ||
|
|
868
|
+
width <= 0 ||
|
|
869
|
+
height <= 0
|
|
870
|
+
) {
|
|
871
|
+
return false
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const margin = Math.max(Number(sheet?.marginWidth || 20), 10)
|
|
875
|
+
const footerBandHeight = Math.max(margin * 6, 120)
|
|
876
|
+
const footerTop = Math.max(height - footerBandHeight, margin)
|
|
877
|
+
const projectedY1 = projectSchematicY(height, y1)
|
|
878
|
+
const projectedY2 = projectSchematicY(height, y2)
|
|
879
|
+
const minProjectedY = Math.min(projectedY1, projectedY2)
|
|
880
|
+
const maxX = Math.max(x1, x2)
|
|
881
|
+
|
|
882
|
+
return minProjectedY >= footerTop && maxX >= width * 0.5
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Returns true when a text row still contains one bare special string.
|
|
887
|
+
* @param {object} text Schematic text row.
|
|
888
|
+
* @returns {boolean}
|
|
889
|
+
*/
|
|
890
|
+
static #isUnresolvedSimpleSpecialStringText(text) {
|
|
891
|
+
return /^[.=][A-Za-z_][\w.-]*$/u.test(String(text?.text || '').trim())
|
|
892
|
+
}
|
|
893
|
+
|
|
663
894
|
/**
|
|
664
895
|
* Renders root SVG viewBox attributes according to export options.
|
|
665
896
|
* @param {{ includeViewBox: boolean }} options Normalized options.
|
|
@@ -1900,12 +2131,7 @@ export class SchematicSvgRenderer {
|
|
|
1900
2131
|
'" y2="' +
|
|
1901
2132
|
formatNumber(projectSchematicY(sheetHeight, line.y2)) +
|
|
1902
2133
|
'" stroke="' +
|
|
1903
|
-
escapeHtml(
|
|
1904
|
-
SchematicColorResolver.resolveColor(
|
|
1905
|
-
line.color,
|
|
1906
|
-
'--schematic-default-ink-color'
|
|
1907
|
-
)
|
|
1908
|
-
) +
|
|
2134
|
+
escapeHtml(SchematicSvgRenderer.#resolveSchematicLineColor(line)) +
|
|
1909
2135
|
'" stroke-width="' +
|
|
1910
2136
|
formatNumber(
|
|
1911
2137
|
SchematicSvgRenderer.#resolveSchematicLineWidth(line)
|
|
@@ -1936,6 +2162,26 @@ export class SchematicSvgRenderer {
|
|
|
1936
2162
|
return Math.max(baseWidth * 3, 3)
|
|
1937
2163
|
}
|
|
1938
2164
|
|
|
2165
|
+
/**
|
|
2166
|
+
* Resolves line stroke color, treating black electrical wires as themed
|
|
2167
|
+
* schematic connectivity while leaving graphic linework source-colored.
|
|
2168
|
+
* @param {{ color: string, ownerIndex?: string, isBus?: boolean, recordType?: string }} line
|
|
2169
|
+
* @returns {string}
|
|
2170
|
+
*/
|
|
2171
|
+
static #resolveSchematicLineColor(line) {
|
|
2172
|
+
if (SchematicSvgRenderer.#isElectricalSchematicLine(line)) {
|
|
2173
|
+
return SchematicColorResolver.resolveNonTextColor(
|
|
2174
|
+
line.color,
|
|
2175
|
+
'--schematic-default-ink-color'
|
|
2176
|
+
)
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
return SchematicColorResolver.resolveColor(
|
|
2180
|
+
line.color,
|
|
2181
|
+
'--schematic-default-ink-color'
|
|
2182
|
+
)
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1939
2185
|
/**
|
|
1940
2186
|
* Returns SVG stroke attributes for one schematic line style.
|
|
1941
2187
|
* @param {{ width: number, lineStyle?: number }} line
|
|
@@ -2287,7 +2533,7 @@ export class SchematicSvgRenderer {
|
|
|
2287
2533
|
formatNumber(projectSchematicY(sheetHeight, junction.y)) +
|
|
2288
2534
|
'" r="2.4" fill="' +
|
|
2289
2535
|
escapeHtml(
|
|
2290
|
-
SchematicColorResolver.
|
|
2536
|
+
SchematicColorResolver.resolveNonTextColor(
|
|
2291
2537
|
junction.color,
|
|
2292
2538
|
'--schematic-default-ink-color'
|
|
2293
2539
|
)
|