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
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Splits native Altium footer/template primitives from scalable schematic
|
|
7
|
+
* content when a standard template page is larger than the stored custom size.
|
|
8
|
+
*/
|
|
9
|
+
export class SchematicNativeFooterPartitioner {
|
|
10
|
+
/**
|
|
11
|
+
* Partitions render primitives into scalable content and fixed footer
|
|
12
|
+
* chrome.
|
|
13
|
+
* @param {{ lines?: object[], polygons?: object[], rectangles?: object[], roundedRectangles?: object[], ellipses?: object[], arcs?: object[], beziers?: object[], pies?: object[], ieeeSymbols?: object[], texts?: object[], images?: object[] }} primitives Primitive families.
|
|
14
|
+
* @param {{ width?: number, height?: number, sourceWidth?: number, sourceHeight?: number, marginWidth?: number, paperSize?: string, borderOn?: boolean }} sheet Sheet metadata.
|
|
15
|
+
* @returns {{ content: Record<string, object[]>, footer: Record<string, object[]>, footerBounds: { minX: number, minY: number, maxX: number, maxY: number } | null }}
|
|
16
|
+
*/
|
|
17
|
+
static partitionPrimitives(primitives, sheet) {
|
|
18
|
+
const families = [
|
|
19
|
+
'lines',
|
|
20
|
+
'polygons',
|
|
21
|
+
'rectangles',
|
|
22
|
+
'roundedRectangles',
|
|
23
|
+
'ellipses',
|
|
24
|
+
'arcs',
|
|
25
|
+
'beziers',
|
|
26
|
+
'pies',
|
|
27
|
+
'ieeeSymbols',
|
|
28
|
+
'texts',
|
|
29
|
+
'images'
|
|
30
|
+
]
|
|
31
|
+
const content = {}
|
|
32
|
+
const footer = {}
|
|
33
|
+
|
|
34
|
+
for (const family of families) {
|
|
35
|
+
const values = primitives?.[family] || []
|
|
36
|
+
content[family] = values.filter(
|
|
37
|
+
(primitive) =>
|
|
38
|
+
!SchematicNativeFooterPartitioner.#isNativeFooterPrimitive(
|
|
39
|
+
primitive,
|
|
40
|
+
sheet
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
footer[family] = values.filter((primitive) =>
|
|
44
|
+
SchematicNativeFooterPartitioner.#isNativeFooterPrimitive(
|
|
45
|
+
primitive,
|
|
46
|
+
sheet
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
content,
|
|
53
|
+
footer,
|
|
54
|
+
footerBounds:
|
|
55
|
+
SchematicNativeFooterPartitioner.#resolvePrimitiveSetBounds(
|
|
56
|
+
Object.values(footer).flat()
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns true when one primitive is native lower-page footer chrome.
|
|
63
|
+
* @param {object} primitive Primitive candidate.
|
|
64
|
+
* @param {{ width?: number, height?: number, sourceWidth?: number, sourceHeight?: number, marginWidth?: number, paperSize?: string, borderOn?: boolean }} sheet Sheet metadata.
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
static #isNativeFooterPrimitive(primitive, sheet) {
|
|
68
|
+
if (!SchematicNativeFooterPartitioner.#shouldSplitNativeFooter(sheet)) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
if (!String(primitive?.ownerIndex || '').trim()) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bounds =
|
|
76
|
+
SchematicNativeFooterPartitioner.#resolvePrimitiveBounds(primitive)
|
|
77
|
+
if (!bounds) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const margin = Math.max(Number(sheet?.marginWidth || 20), 10)
|
|
82
|
+
const footerLimit = Math.max(margin * 6, 120)
|
|
83
|
+
const footerStartX = Math.max(Number(sheet?.width || 0), 0) * 0.5
|
|
84
|
+
|
|
85
|
+
return bounds.maxY <= footerLimit && bounds.maxX >= footerStartX
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns true when promoted standard-sheet content needs fixed native
|
|
90
|
+
* footer handling.
|
|
91
|
+
* @param {{ width?: number, height?: number, sourceWidth?: number, sourceHeight?: number, paperSize?: string, borderOn?: boolean }} sheet Sheet metadata.
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
static #shouldSplitNativeFooter(sheet) {
|
|
95
|
+
const width = Number(sheet?.width || 0)
|
|
96
|
+
const height = Number(sheet?.height || 0)
|
|
97
|
+
const sourceWidth = Number(sheet?.sourceWidth || 0)
|
|
98
|
+
const sourceHeight = Number(sheet?.sourceHeight || 0)
|
|
99
|
+
|
|
100
|
+
return Boolean(
|
|
101
|
+
sheet?.borderOn &&
|
|
102
|
+
sheet?.paperSize &&
|
|
103
|
+
sourceWidth > 0 &&
|
|
104
|
+
sourceHeight > 0 &&
|
|
105
|
+
(width > sourceWidth || height > sourceHeight)
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolves one primitive's document-space bounds.
|
|
111
|
+
* @param {object} primitive Primitive candidate.
|
|
112
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number } | null}
|
|
113
|
+
*/
|
|
114
|
+
static #resolvePrimitiveBounds(primitive) {
|
|
115
|
+
const points =
|
|
116
|
+
SchematicNativeFooterPartitioner.#collectPrimitivePoints(primitive)
|
|
117
|
+
if (!points.length) {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
minX: Math.min(...points.map((point) => point.x)),
|
|
123
|
+
minY: Math.min(...points.map((point) => point.y)),
|
|
124
|
+
maxX: Math.max(...points.map((point) => point.x)),
|
|
125
|
+
maxY: Math.max(...points.map((point) => point.y))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resolves combined bounds for a set of primitives.
|
|
131
|
+
* @param {object[]} primitives Primitive candidates.
|
|
132
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number } | null}
|
|
133
|
+
*/
|
|
134
|
+
static #resolvePrimitiveSetBounds(primitives) {
|
|
135
|
+
const bounds = primitives
|
|
136
|
+
.map((primitive) =>
|
|
137
|
+
SchematicNativeFooterPartitioner.#resolvePrimitiveBounds(
|
|
138
|
+
primitive
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
|
|
143
|
+
if (!bounds.length) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
minX: Math.min(...bounds.map((bound) => bound.minX)),
|
|
149
|
+
minY: Math.min(...bounds.map((bound) => bound.minY)),
|
|
150
|
+
maxX: Math.max(...bounds.map((bound) => bound.maxX)),
|
|
151
|
+
maxY: Math.max(...bounds.map((bound) => bound.maxY))
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Collects point coordinates from one normalized primitive.
|
|
157
|
+
* @param {object} primitive Primitive candidate.
|
|
158
|
+
* @returns {{ x: number, y: number }[]}
|
|
159
|
+
*/
|
|
160
|
+
static #collectPrimitivePoints(primitive) {
|
|
161
|
+
if (Array.isArray(primitive?.points)) {
|
|
162
|
+
return primitive.points
|
|
163
|
+
.map((point) =>
|
|
164
|
+
SchematicNativeFooterPartitioner.#point(point?.x, point?.y)
|
|
165
|
+
)
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
}
|
|
168
|
+
if (Array.isArray(primitive?.segments)) {
|
|
169
|
+
return primitive.segments.flatMap((segment) =>
|
|
170
|
+
[
|
|
171
|
+
SchematicNativeFooterPartitioner.#point(
|
|
172
|
+
segment?.x1,
|
|
173
|
+
segment?.y1
|
|
174
|
+
),
|
|
175
|
+
SchematicNativeFooterPartitioner.#point(
|
|
176
|
+
segment?.x2,
|
|
177
|
+
segment?.y2
|
|
178
|
+
)
|
|
179
|
+
].filter(Boolean)
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
Number.isFinite(Number(primitive?.x1)) &&
|
|
184
|
+
Number.isFinite(Number(primitive?.y1)) &&
|
|
185
|
+
Number.isFinite(Number(primitive?.x2)) &&
|
|
186
|
+
Number.isFinite(Number(primitive?.y2))
|
|
187
|
+
) {
|
|
188
|
+
return [
|
|
189
|
+
SchematicNativeFooterPartitioner.#point(
|
|
190
|
+
primitive.x1,
|
|
191
|
+
primitive.y1
|
|
192
|
+
),
|
|
193
|
+
SchematicNativeFooterPartitioner.#point(
|
|
194
|
+
primitive.x2,
|
|
195
|
+
primitive.y2
|
|
196
|
+
)
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
if (
|
|
200
|
+
Number.isFinite(Number(primitive?.cornerX)) &&
|
|
201
|
+
Number.isFinite(Number(primitive?.cornerY))
|
|
202
|
+
) {
|
|
203
|
+
return [
|
|
204
|
+
SchematicNativeFooterPartitioner.#point(
|
|
205
|
+
primitive.x,
|
|
206
|
+
primitive.y
|
|
207
|
+
),
|
|
208
|
+
SchematicNativeFooterPartitioner.#point(
|
|
209
|
+
primitive.cornerX,
|
|
210
|
+
primitive.cornerY
|
|
211
|
+
)
|
|
212
|
+
].filter(Boolean)
|
|
213
|
+
}
|
|
214
|
+
if (
|
|
215
|
+
Number.isFinite(Number(primitive?.width)) &&
|
|
216
|
+
Number.isFinite(Number(primitive?.height))
|
|
217
|
+
) {
|
|
218
|
+
return [
|
|
219
|
+
SchematicNativeFooterPartitioner.#point(
|
|
220
|
+
primitive.x,
|
|
221
|
+
primitive.y
|
|
222
|
+
),
|
|
223
|
+
SchematicNativeFooterPartitioner.#point(
|
|
224
|
+
Number(primitive.x || 0) + Number(primitive.width || 0),
|
|
225
|
+
Number(primitive.y || 0) + Number(primitive.height || 0)
|
|
226
|
+
)
|
|
227
|
+
].filter(Boolean)
|
|
228
|
+
}
|
|
229
|
+
if (
|
|
230
|
+
Number.isFinite(Number(primitive?.radius)) ||
|
|
231
|
+
Number.isFinite(Number(primitive?.radiusX))
|
|
232
|
+
) {
|
|
233
|
+
const radiusX = Math.max(
|
|
234
|
+
Number(primitive?.radiusX || primitive?.radius || 0),
|
|
235
|
+
0
|
|
236
|
+
)
|
|
237
|
+
const radiusY = Math.max(
|
|
238
|
+
Number(primitive?.radiusY || primitive?.radius || 0),
|
|
239
|
+
0
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
return [
|
|
243
|
+
SchematicNativeFooterPartitioner.#point(
|
|
244
|
+
Number(primitive.x || 0) - radiusX,
|
|
245
|
+
Number(primitive.y || 0) - radiusY
|
|
246
|
+
),
|
|
247
|
+
SchematicNativeFooterPartitioner.#point(
|
|
248
|
+
Number(primitive.x || 0) + radiusX,
|
|
249
|
+
Number(primitive.y || 0) + radiusY
|
|
250
|
+
)
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return [
|
|
255
|
+
SchematicNativeFooterPartitioner.#point(primitive?.x, primitive?.y)
|
|
256
|
+
].filter(Boolean)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Builds one finite point.
|
|
261
|
+
* @param {number | string | undefined} x Raw x coordinate.
|
|
262
|
+
* @param {number | string | undefined} y Raw y coordinate.
|
|
263
|
+
* @returns {{ x: number, y: number } | null}
|
|
264
|
+
*/
|
|
265
|
+
static #point(x, y) {
|
|
266
|
+
const normalizedX = Number(x)
|
|
267
|
+
const normalizedY = Number(y)
|
|
268
|
+
|
|
269
|
+
if (!Number.isFinite(normalizedX) || !Number.isFinite(normalizedY)) {
|
|
270
|
+
return null
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { x: normalizedX, y: normalizedY }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -15,7 +15,7 @@ const MINIMUM_NOTE_TEXT_SIZE = 4
|
|
|
15
15
|
export class SchematicNoteRenderer {
|
|
16
16
|
/**
|
|
17
17
|
* Builds one boxed schematic note/callout with wrapped text rows.
|
|
18
|
-
* @param {{ x: number, y: number, color: string, fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string, cornerX?: number, cornerY?: number, fill?: string, borderColor?: string, lineWidth?: number, isSolid?: boolean, showBorder?: boolean, textMargin?: number, noteLines?: string[] }} text
|
|
18
|
+
* @param {{ x: number, y: number, color: string, fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string, anchor?: 'start' | 'middle' | 'end', cornerX?: number, cornerY?: number, fill?: string, borderColor?: string, lineWidth?: number, isSolid?: boolean, showBorder?: boolean, textMargin?: number, noteLines?: string[] }} text
|
|
19
19
|
* @param {number} sheetHeight
|
|
20
20
|
* @returns {string}
|
|
21
21
|
*/
|
|
@@ -59,6 +59,10 @@ export class SchematicNoteRenderer {
|
|
|
59
59
|
height,
|
|
60
60
|
requestedTextSize
|
|
61
61
|
)
|
|
62
|
+
const compactMarkerNote = SchematicNoteRenderer.#isCompactMarkerNote(
|
|
63
|
+
noteSourceLines,
|
|
64
|
+
compactSingleLineNote
|
|
65
|
+
)
|
|
62
66
|
const horizontalTextMargin =
|
|
63
67
|
SchematicNoteRenderer.#resolveHorizontalTextMargin(
|
|
64
68
|
textMargin,
|
|
@@ -94,6 +98,7 @@ export class SchematicNoteRenderer {
|
|
|
94
98
|
lineHeight,
|
|
95
99
|
textSize,
|
|
96
100
|
compactSingleLineNote,
|
|
101
|
+
compactMarkerNote,
|
|
97
102
|
text
|
|
98
103
|
)
|
|
99
104
|
)
|
|
@@ -462,7 +467,8 @@ export class SchematicNoteRenderer {
|
|
|
462
467
|
* @param {number} lineHeight
|
|
463
468
|
* @param {number} textSize
|
|
464
469
|
* @param {boolean} compactSingleLineNote
|
|
465
|
-
* @param {
|
|
470
|
+
* @param {boolean} compactMarkerNote
|
|
471
|
+
* @param {{ color: string, fontFamily?: string, fontWeight?: number, fontStyle?: string, anchor?: 'start' | 'middle' | 'end' }} text
|
|
466
472
|
* @returns {string}
|
|
467
473
|
*/
|
|
468
474
|
static #buildNoteLineMarkup(
|
|
@@ -476,9 +482,21 @@ export class SchematicNoteRenderer {
|
|
|
476
482
|
lineHeight,
|
|
477
483
|
textSize,
|
|
478
484
|
compactSingleLineNote,
|
|
485
|
+
compactMarkerNote,
|
|
479
486
|
text
|
|
480
487
|
) {
|
|
481
|
-
const
|
|
488
|
+
const anchor = SchematicNoteRenderer.#resolveTextAnchor(
|
|
489
|
+
text,
|
|
490
|
+
compactMarkerNote
|
|
491
|
+
)
|
|
492
|
+
const x = SchematicNoteRenderer.#resolveLineX(
|
|
493
|
+
left,
|
|
494
|
+
right,
|
|
495
|
+
horizontalTextMargin,
|
|
496
|
+
anchor
|
|
497
|
+
)
|
|
498
|
+
const ruleLeft = left + horizontalTextMargin
|
|
499
|
+
const ruleRight = right - horizontalTextMargin
|
|
482
500
|
const y =
|
|
483
501
|
top +
|
|
484
502
|
verticalTextMargin +
|
|
@@ -491,11 +509,11 @@ export class SchematicNoteRenderer {
|
|
|
491
509
|
if (/^_+$/.test(String(line || '').trim())) {
|
|
492
510
|
return (
|
|
493
511
|
'<line class="schematic-note-rule" x1="' +
|
|
494
|
-
formatNumber(
|
|
512
|
+
formatNumber(ruleLeft) +
|
|
495
513
|
'" y1="' +
|
|
496
514
|
formatNumber(y - textSize * 0.35) +
|
|
497
515
|
'" x2="' +
|
|
498
|
-
formatNumber(
|
|
516
|
+
formatNumber(ruleRight) +
|
|
499
517
|
'" y2="' +
|
|
500
518
|
formatNumber(y - textSize * 0.35) +
|
|
501
519
|
'" stroke="' +
|
|
@@ -521,7 +539,9 @@ export class SchematicNoteRenderer {
|
|
|
521
539
|
'--schematic-text-color'
|
|
522
540
|
)
|
|
523
541
|
) +
|
|
524
|
-
'" text-anchor="
|
|
542
|
+
'" text-anchor="' +
|
|
543
|
+
escapeHtml(anchor) +
|
|
544
|
+
'" font-size="' +
|
|
525
545
|
formatNumber(textSize) +
|
|
526
546
|
'" font-family="' +
|
|
527
547
|
escapeHtml(text.fontFamily || 'Times New Roman') +
|
|
@@ -534,6 +554,62 @@ export class SchematicNoteRenderer {
|
|
|
534
554
|
)
|
|
535
555
|
}
|
|
536
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Resolves the x coordinate for a note line from its text anchor.
|
|
559
|
+
* @param {number} left Note box left edge.
|
|
560
|
+
* @param {number} right Note box right edge.
|
|
561
|
+
* @param {number} horizontalTextMargin Text margin.
|
|
562
|
+
* @param {'start' | 'middle' | 'end'} anchor Text anchor.
|
|
563
|
+
* @returns {number}
|
|
564
|
+
*/
|
|
565
|
+
static #resolveLineX(left, right, horizontalTextMargin, anchor) {
|
|
566
|
+
if (anchor === 'middle') return (left + right) / 2
|
|
567
|
+
if (anchor === 'end') return right - horizontalTextMargin
|
|
568
|
+
|
|
569
|
+
return left + horizontalTextMargin
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Returns true for tight single-token marker boxes that Altium centers
|
|
574
|
+
* inside the authored note rectangle.
|
|
575
|
+
* @param {string[]} noteLines Source note rows.
|
|
576
|
+
* @param {boolean} compactSingleLineNote True for tight one-line note boxes.
|
|
577
|
+
* @returns {boolean}
|
|
578
|
+
*/
|
|
579
|
+
static #isCompactMarkerNote(noteLines, compactSingleLineNote) {
|
|
580
|
+
if (!compactSingleLineNote) {
|
|
581
|
+
return false
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const visibleLines = noteLines
|
|
585
|
+
.map((line) => String(line || '').trim())
|
|
586
|
+
.filter(Boolean)
|
|
587
|
+
|
|
588
|
+
if (visibleLines.length !== 1) {
|
|
589
|
+
return false
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return /^[A-Z0-9._/-]{1,4}$/u.test(visibleLines[0])
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Normalizes note text anchors to SVG text-anchor values.
|
|
597
|
+
* @param {{ anchor?: string }} text Note text.
|
|
598
|
+
* @param {boolean} compactMarkerNote True for centered marker notes.
|
|
599
|
+
* @returns {'start' | 'middle' | 'end'}
|
|
600
|
+
*/
|
|
601
|
+
static #resolveTextAnchor(text, compactMarkerNote) {
|
|
602
|
+
if (compactMarkerNote) {
|
|
603
|
+
return 'middle'
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (text.anchor === 'middle' || text.anchor === 'end') {
|
|
607
|
+
return text.anchor
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return 'start'
|
|
611
|
+
}
|
|
612
|
+
|
|
537
613
|
/**
|
|
538
614
|
* Builds an optional note font-style attribute.
|
|
539
615
|
* @param {string | undefined} fontStyle
|