@weasel-js/svg 1.4.3 → 1.5.0
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/dist/index.d.ts +12 -14
- package/dist/index.js +95 -43
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,12 @@ type SvgPaint = {
|
|
|
72
72
|
/** Stroke description: a paint plus structural line parameters. */
|
|
73
73
|
interface SvgStroke {
|
|
74
74
|
paint: SvgPaint;
|
|
75
|
-
width
|
|
75
|
+
/** World units, or `{ px }` for a width that holds its rendered thickness
|
|
76
|
+
* however the document is scaled — SVG's `vector-effect="non-scaling-stroke"`,
|
|
77
|
+
* and the same unit system as the kit's `Stroke.width`. */
|
|
78
|
+
width: number | {
|
|
79
|
+
px: number;
|
|
80
|
+
};
|
|
76
81
|
opacity?: number;
|
|
77
82
|
/** `stroke-linecap`. Default per SVG spec is `'butt'`. */
|
|
78
83
|
cap?: 'butt' | 'round' | 'square';
|
|
@@ -125,16 +130,6 @@ interface SvgGroupNode {
|
|
|
125
130
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
126
131
|
meta?: NamespaceMeta;
|
|
127
132
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Width given to an external `<text>` that carries no `data-weasel-width`:
|
|
130
|
-
* large enough that line wrapping never fires, since SVG text flows from a
|
|
131
|
-
* baseline and the source says nothing about a box.
|
|
132
|
-
*
|
|
133
|
-
* It is a wrap sentinel, not a measurement. Anything that treats a text
|
|
134
|
-
* node's width as geometry — a union AABB, a fit-clamp — has to recognize it
|
|
135
|
-
* and substitute an estimate, or one text node swamps the whole file.
|
|
136
|
-
*/
|
|
137
|
-
declare const UNBOUNDED_TEXT_WIDTH = 99999;
|
|
138
133
|
/**
|
|
139
134
|
* Text leaf node. Mirrors the kit's `TextPose` shape — a bounding box plus
|
|
140
135
|
* text content, optional rich-text `runs` (per-range styling), and an
|
|
@@ -145,9 +140,12 @@ declare const UNBOUNDED_TEXT_WIDTH = 99999;
|
|
|
145
140
|
* `dominant-baseline="text-before-edge"` so `y` is the top of the text
|
|
146
141
|
* box, plus `data-weasel-width` / `data-weasel-height` so weasel's
|
|
147
142
|
* explicit box dimensions round-trip losslessly. External SVG text
|
|
148
|
-
* (lacking the data-* attrs) imports with
|
|
149
|
-
*
|
|
143
|
+
* (lacking the data-* attrs) imports with the width the kit's text layout
|
|
144
|
+
* measures — an em-based estimate when no registered font can — and
|
|
150
145
|
* `height = fontSize * lineHeight * (lineCount || 1)`.
|
|
146
|
+
*
|
|
147
|
+
* `x` here is always the box's left edge. In the SVG it is the
|
|
148
|
+
* `text-anchor` point, and the parser and serializer convert between them.
|
|
151
149
|
*/
|
|
152
150
|
interface SvgTextNode {
|
|
153
151
|
kind: 'text';
|
|
@@ -396,4 +394,4 @@ declare function svgNodesToKitDrafts(nodes: readonly SvgNode[], nextId: () => st
|
|
|
396
394
|
*/
|
|
397
395
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
398
396
|
|
|
399
|
-
export { IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SvgDraftBounds, type SvgGroupNode, type SvgImageNode, type SvgNode, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode,
|
|
397
|
+
export { IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SvgDraftBounds, type SvgGroupNode, type SvgImageNode, type SvgNode, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, getMarker, SCRIPT_METRICS, dwarn, createInsertOp, PathBuilder, parseColor, rgbaToHex, PATH_C, PATH_Q,
|
|
1
|
+
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, getMarker, SCRIPT_METRICS, dwarn, createInsertOp, PathBuilder, parseColor, rgbaToHex, resolveTextStyle, layoutRuns, resolveRuns, resolveAlign, PATH_C, PATH_Q, solid, fillToBoundsFrame, getPaintKind } from '@weasel-js/core';
|
|
2
|
+
import { invert } from '@weasel-js/geom';
|
|
2
3
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
3
4
|
|
|
4
5
|
// src/parse.ts
|
|
5
6
|
|
|
6
7
|
// src/types.ts
|
|
7
8
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|
8
|
-
var UNBOUNDED_TEXT_WIDTH = 99999;
|
|
9
|
-
|
|
10
|
-
// src/transform.ts
|
|
11
9
|
function multiply(a, b) {
|
|
12
10
|
return [
|
|
13
11
|
a[0] * b[0] + a[2] * b[1],
|
|
@@ -78,15 +76,11 @@ function parseTransform(attr, onWarn) {
|
|
|
78
76
|
}
|
|
79
77
|
return result;
|
|
80
78
|
}
|
|
81
|
-
function invertMatrix(m) {
|
|
82
|
-
const det = m[0] * m[3] - m[1] * m[2];
|
|
83
|
-
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
|
|
84
|
-
const a = m[3] / det, b = -m[1] / det, c = -m[2] / det, d = m[0] / det;
|
|
85
|
-
return [a, b, c, d, -(a * m[4] + c * m[5]), -(b * m[4] + d * m[5])];
|
|
86
|
-
}
|
|
87
79
|
function rebaseTransform(ctm, local) {
|
|
88
|
-
const inv =
|
|
89
|
-
|
|
80
|
+
const inv = invert([...ctm]);
|
|
81
|
+
if (!inv) return null;
|
|
82
|
+
const [a, b, c, d, e, f] = inv;
|
|
83
|
+
return multiply(multiply(ctm, local), [a, b, c, d, e, f]);
|
|
90
84
|
}
|
|
91
85
|
function decomposeRotation(m, cx, cy, eps = 1e-4) {
|
|
92
86
|
const [a, b, c, d, e, f] = m;
|
|
@@ -236,6 +230,10 @@ function polygonToPath(points, m = IDENTITY_MATRIX) {
|
|
|
236
230
|
b.close();
|
|
237
231
|
return transformPath(b.build(), m);
|
|
238
232
|
}
|
|
233
|
+
function anchorOffset(style, width) {
|
|
234
|
+
const edge = resolveAlign(style?.align ?? "left", style?.direction ?? "ltr");
|
|
235
|
+
return edge === "center" ? width / 2 : edge === "right" ? width : 0;
|
|
236
|
+
}
|
|
239
237
|
function parsePaintAttr(raw) {
|
|
240
238
|
if (raw == null) return null;
|
|
241
239
|
const s = raw.trim();
|
|
@@ -866,8 +864,10 @@ function parseElement(el, ctm, style, gradients, onWarn, uriToPrefix) {
|
|
|
866
864
|
const bounds = pathAabb(path);
|
|
867
865
|
const cx = bounds.x + bounds.width / 2;
|
|
868
866
|
const cy = bounds.y + bounds.height / 2;
|
|
869
|
-
const angle = decomposeRotation(worldLocal, cx, cy);
|
|
870
|
-
if (
|
|
867
|
+
const angle = worldLocal && decomposeRotation(worldLocal, cx, cy);
|
|
868
|
+
if (!worldLocal) {
|
|
869
|
+
onWarn("leaf transform sits under a singular parent transform; dropped");
|
|
870
|
+
} else if (angle != null) {
|
|
871
871
|
rotation = angle;
|
|
872
872
|
} else {
|
|
873
873
|
path = transformPath(path, worldLocal);
|
|
@@ -879,7 +879,7 @@ function parseElement(el, ctm, style, gradients, onWarn, uriToPrefix) {
|
|
|
879
879
|
}
|
|
880
880
|
const leafStyle = deriveStyle(style, el);
|
|
881
881
|
const fill = readPaint(leafStyle, "fill", "#000000", gradients, onWarn);
|
|
882
|
-
const stroke = readStroke(leafStyle, gradients, onWarn);
|
|
882
|
+
const stroke = readStroke(leafStyle, gradients, onWarn, isNonScalingStroke(el));
|
|
883
883
|
const opacity = readOpacityAttr(el, "opacity");
|
|
884
884
|
const fillRuleRaw = leafStyle["fill-rule"] ?? null;
|
|
885
885
|
if (fillRuleRaw === "evenodd" && path.kind === "polygon") {
|
|
@@ -988,14 +988,14 @@ function readPaint(style, attr, defaultColor, gradients, onWarn) {
|
|
|
988
988
|
if (a != null) out.opacity = a;
|
|
989
989
|
return out;
|
|
990
990
|
}
|
|
991
|
-
function readStroke(style, gradients, onWarn) {
|
|
991
|
+
function readStroke(style, gradients, onWarn, nonScaling = false) {
|
|
992
992
|
const inheritedStroke = style["stroke"] ?? null;
|
|
993
993
|
const inheritedWidth = style["stroke-width"] ?? null;
|
|
994
994
|
if (inheritedStroke == null && inheritedWidth == null) return void 0;
|
|
995
995
|
const paint = readPaint(style, "stroke", "#000000", gradients, onWarn);
|
|
996
996
|
if (paint.kind === "none") return void 0;
|
|
997
|
-
const
|
|
998
|
-
const stroke = { paint, width };
|
|
997
|
+
const n = inheritedWidth != null ? parseFloat(inheritedWidth) : 1;
|
|
998
|
+
const stroke = { paint, width: nonScaling ? { px: n } : n };
|
|
999
999
|
const opacityRaw = style["stroke-opacity"] ?? null;
|
|
1000
1000
|
if (opacityRaw != null) {
|
|
1001
1001
|
const a = clamp01(parseFloat(opacityRaw));
|
|
@@ -1062,6 +1062,9 @@ var STROKE_KEYS = [
|
|
|
1062
1062
|
"marker-mid",
|
|
1063
1063
|
"marker-end"
|
|
1064
1064
|
];
|
|
1065
|
+
function isNonScalingStroke(el) {
|
|
1066
|
+
return ownProp(el, "vector-effect") === "non-scaling-stroke";
|
|
1067
|
+
}
|
|
1065
1068
|
function ownStrokeStyle(el) {
|
|
1066
1069
|
const out = {};
|
|
1067
1070
|
for (const k of STROKE_KEYS) {
|
|
@@ -1184,8 +1187,10 @@ function parseImageElement(el, ctm, onWarn) {
|
|
|
1184
1187
|
const worldLocal = rebaseTransform(ctm, localTransform);
|
|
1185
1188
|
const cx = node.x + node.width / 2;
|
|
1186
1189
|
const cy = node.y + node.height / 2;
|
|
1187
|
-
const angle = decomposeRotation(worldLocal, cx, cy);
|
|
1188
|
-
if (
|
|
1190
|
+
const angle = worldLocal && decomposeRotation(worldLocal, cx, cy);
|
|
1191
|
+
if (!worldLocal) {
|
|
1192
|
+
onWarn("<image> transform sits under a singular parent transform; dropped");
|
|
1193
|
+
} else if (angle != null) {
|
|
1189
1194
|
node.rotation = angle;
|
|
1190
1195
|
} else {
|
|
1191
1196
|
const rotComp = rotationComponent(worldLocal);
|
|
@@ -1235,6 +1240,19 @@ function collapseRunWhitespace(runs) {
|
|
|
1235
1240
|
if (runs[i].text === "") runs.splice(i, 1);
|
|
1236
1241
|
}
|
|
1237
1242
|
}
|
|
1243
|
+
var ESTIMATED_GLYPH_ADVANCE_EM = 0.6;
|
|
1244
|
+
function laidOutWidth(runs, style, plain) {
|
|
1245
|
+
const resolved = resolveTextStyle(style);
|
|
1246
|
+
const { bounds } = layoutRuns(resolveRuns(runs.length > 0 ? runs : [{ text: plain }], resolved), {
|
|
1247
|
+
maxWidth: Infinity,
|
|
1248
|
+
lineHeight: resolved.lineHeight,
|
|
1249
|
+
align: resolved.align,
|
|
1250
|
+
direction: resolved.direction
|
|
1251
|
+
});
|
|
1252
|
+
if (bounds.width > 0) return bounds.width;
|
|
1253
|
+
const longest = plain.split("\n").reduce((max, line) => Math.max(max, line.length), 0);
|
|
1254
|
+
return longest * resolved.fontSize * ESTIMATED_GLYPH_ADVANCE_EM;
|
|
1255
|
+
}
|
|
1238
1256
|
function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
1239
1257
|
const num2 = (raw, fallback) => {
|
|
1240
1258
|
if (raw == null) return fallback;
|
|
@@ -1249,7 +1267,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1249
1267
|
const ay = m[1] * rawX + m[3] * rawY + m[5];
|
|
1250
1268
|
const leafStyle = deriveStyle(style, el);
|
|
1251
1269
|
const textStyle = readTextStyle(leafStyle, onWarn);
|
|
1252
|
-
const textPaint = readTextPaint(leafStyle, gradients, onWarn);
|
|
1270
|
+
const textPaint = readTextPaint(leafStyle, gradients, onWarn, isNonScalingStroke(el));
|
|
1253
1271
|
const fontSize = textStyle.fontSize ?? 16;
|
|
1254
1272
|
const lineHeight = textStyle.lineHeight ?? 1.2;
|
|
1255
1273
|
const dominantBaseline = el.getAttribute("dominant-baseline");
|
|
@@ -1280,13 +1298,13 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1280
1298
|
const plain = runs.map((r) => r.text).join("");
|
|
1281
1299
|
const dataW = num2(el.getAttribute("data-weasel-width"), NaN);
|
|
1282
1300
|
const dataH = num2(el.getAttribute("data-weasel-height"), NaN);
|
|
1283
|
-
const width = Number.isFinite(dataW) ? dataW :
|
|
1301
|
+
const width = Number.isFinite(dataW) ? dataW : laidOutWidth(runs, textStyle, plain);
|
|
1284
1302
|
const lines = (plain.match(/\n/g)?.length ?? 0) + 1;
|
|
1285
1303
|
const height = Number.isFinite(dataH) ? dataH : fontSize * lineHeight * lines;
|
|
1286
1304
|
const opacity = readOpacityAttr(el, "opacity");
|
|
1287
1305
|
const node = {
|
|
1288
1306
|
kind: "text",
|
|
1289
|
-
x: ax,
|
|
1307
|
+
x: ax - anchorOffset(textStyle, width),
|
|
1290
1308
|
y: topY,
|
|
1291
1309
|
width,
|
|
1292
1310
|
height,
|
|
@@ -1296,6 +1314,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1296
1314
|
(r) => Object.entries(r).some(([k, v]) => k !== "text" && v !== void 0)
|
|
1297
1315
|
);
|
|
1298
1316
|
if (hasStyling) node.runs = runs;
|
|
1317
|
+
if (el.getAttribute("data-weasel-wrap") === "true") textStyle.wrap = true;
|
|
1299
1318
|
if (Object.keys(textStyle).length > 0) node.style = textStyle;
|
|
1300
1319
|
if (textPaint.fill !== void 0) node.fill = textPaint.fill;
|
|
1301
1320
|
if (textPaint.stroke != null) node.stroke = textPaint.stroke;
|
|
@@ -1304,8 +1323,10 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1304
1323
|
const worldLocal = rebaseTransform(ctm, localTransform);
|
|
1305
1324
|
const cx = node.x + node.width / 2;
|
|
1306
1325
|
const cy = node.y + node.height / 2;
|
|
1307
|
-
const angle = decomposeRotation(worldLocal, cx, cy);
|
|
1308
|
-
if (
|
|
1326
|
+
const angle = worldLocal && decomposeRotation(worldLocal, cx, cy);
|
|
1327
|
+
if (!worldLocal) {
|
|
1328
|
+
onWarn("<text> transform sits under a singular parent transform; dropped");
|
|
1329
|
+
} else if (angle != null) {
|
|
1309
1330
|
node.rotation = angle;
|
|
1310
1331
|
} else {
|
|
1311
1332
|
const rotComp = rotationComponent(worldLocal);
|
|
@@ -1357,7 +1378,7 @@ function readTspanRun(el, gradients, style, onWarn) {
|
|
|
1357
1378
|
if (paint) run.fill = paint;
|
|
1358
1379
|
}
|
|
1359
1380
|
}
|
|
1360
|
-
const stroke = coreStroke(readStroke(ownStrokeStyle(el), gradients, onWarn));
|
|
1381
|
+
const stroke = coreStroke(readStroke(ownStrokeStyle(el), gradients, onWarn, isNonScalingStroke(el)));
|
|
1361
1382
|
if (stroke) run.stroke = stroke;
|
|
1362
1383
|
return run;
|
|
1363
1384
|
}
|
|
@@ -1394,7 +1415,7 @@ function readTextStyle(style, onWarn) {
|
|
|
1394
1415
|
if (decoration.overline) out.overline = true;
|
|
1395
1416
|
return out;
|
|
1396
1417
|
}
|
|
1397
|
-
function readTextPaint(style, gradients, onWarn) {
|
|
1418
|
+
function readTextPaint(style, gradients, onWarn, nonScalingStroke = false) {
|
|
1398
1419
|
const out = {};
|
|
1399
1420
|
const fillRaw = resolveCurrentColor(style["fill"] ?? null, style);
|
|
1400
1421
|
if (fillRaw) {
|
|
@@ -1408,7 +1429,7 @@ function readTextPaint(style, gradients, onWarn) {
|
|
|
1408
1429
|
out.fill = null;
|
|
1409
1430
|
}
|
|
1410
1431
|
}
|
|
1411
|
-
const stroke = coreStroke(readStroke(style, gradients, onWarn));
|
|
1432
|
+
const stroke = coreStroke(readStroke(style, gradients, onWarn, nonScalingStroke));
|
|
1412
1433
|
if (stroke) out.stroke = stroke;
|
|
1413
1434
|
return out;
|
|
1414
1435
|
}
|
|
@@ -1608,11 +1629,15 @@ function paintAttrs(paint, name, registry, includeOpacity = true) {
|
|
|
1608
1629
|
const id = registry.register(paint.paint);
|
|
1609
1630
|
return [`${name}="url(#${id})"`];
|
|
1610
1631
|
}
|
|
1632
|
+
function strokeWidthAttrs(width) {
|
|
1633
|
+
if (typeof width === "number") return [`stroke-width="${trimNumber(width)}"`];
|
|
1634
|
+
return [`stroke-width="${trimNumber(width.px)}"`, 'vector-effect="non-scaling-stroke"'];
|
|
1635
|
+
}
|
|
1611
1636
|
function coreStrokeAttrs(stroke, registry) {
|
|
1612
1637
|
const paint = stroke?.paint;
|
|
1613
1638
|
if (!stroke || !paint) return [];
|
|
1614
|
-
const width =
|
|
1615
|
-
if (!(width > 0)) return [];
|
|
1639
|
+
const width = stroke.width ?? 1;
|
|
1640
|
+
if (!((typeof width === "object" ? width.px : width) > 0)) return [];
|
|
1616
1641
|
const attrs = [];
|
|
1617
1642
|
if ("color" in paint) {
|
|
1618
1643
|
attrs.push(`stroke="${paint.color}"`);
|
|
@@ -1622,7 +1647,7 @@ function coreStrokeAttrs(stroke, registry) {
|
|
|
1622
1647
|
} else {
|
|
1623
1648
|
attrs.push(`stroke="url(#${registry.register(paint)})"`);
|
|
1624
1649
|
}
|
|
1625
|
-
attrs.push(
|
|
1650
|
+
attrs.push(...strokeWidthAttrs(width));
|
|
1626
1651
|
if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
1627
1652
|
if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
1628
1653
|
if (stroke.dash && stroke.dash.length > 0) {
|
|
@@ -1645,7 +1670,7 @@ function coreStrokeAttrs(stroke, registry) {
|
|
|
1645
1670
|
}
|
|
1646
1671
|
function strokeAttrsFor(stroke, registry) {
|
|
1647
1672
|
const attrs = paintAttrs(stroke.paint, "stroke", registry, false);
|
|
1648
|
-
attrs.push(
|
|
1673
|
+
attrs.push(...strokeWidthAttrs(stroke.width));
|
|
1649
1674
|
const opacity = stroke.opacity ?? (stroke.paint.kind === "solid" ? stroke.paint.opacity : void 0);
|
|
1650
1675
|
if (opacity != null && opacity !== 1) {
|
|
1651
1676
|
attrs.push(`stroke-opacity="${trimNumber(opacity)}"`);
|
|
@@ -1712,7 +1737,7 @@ function rotateAboutCenter(ctm, b, angle) {
|
|
|
1712
1737
|
}
|
|
1713
1738
|
function textXml(node, registry, namespaces) {
|
|
1714
1739
|
const attrs = [
|
|
1715
|
-
`x="${trimNumber(node.x)}"`,
|
|
1740
|
+
`x="${trimNumber(node.x + anchorOffset(node.style, node.width))}"`,
|
|
1716
1741
|
`y="${trimNumber(node.y)}"`,
|
|
1717
1742
|
`dominant-baseline="text-before-edge"`,
|
|
1718
1743
|
// The runs model stores real line breaks; SVG's default whitespace
|
|
@@ -1738,6 +1763,7 @@ function textXml(node, registry, namespaces) {
|
|
|
1738
1763
|
}
|
|
1739
1764
|
const decoration = textDecorationValue(style?.underline, style?.strikethrough, style?.overline);
|
|
1740
1765
|
if (decoration) attrs.push(`text-decoration="${decoration}"`);
|
|
1766
|
+
if (style?.wrap) attrs.push('data-weasel-wrap="true"');
|
|
1741
1767
|
if (node.fill === null) {
|
|
1742
1768
|
attrs.push('fill="none"');
|
|
1743
1769
|
} else if (node.fill) {
|
|
@@ -1860,6 +1886,21 @@ function strokeDataFromSvg(stroke, box) {
|
|
|
1860
1886
|
...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {}
|
|
1861
1887
|
};
|
|
1862
1888
|
}
|
|
1889
|
+
function fillInBoxFrame(fill, box) {
|
|
1890
|
+
const units = "units" in fill ? fill.units : void 0;
|
|
1891
|
+
return units === "world" ? fillToBoundsFrame(fill, box) : fill;
|
|
1892
|
+
}
|
|
1893
|
+
function runInBoxFrame(run, box) {
|
|
1894
|
+
const fill = run.fill ? fillInBoxFrame(run.fill, box) : run.fill;
|
|
1895
|
+
const stroke = run.stroke ? strokeInBoxFrame(run.stroke, box) : run.stroke;
|
|
1896
|
+
if (fill === run.fill && stroke === run.stroke) return run;
|
|
1897
|
+
return { ...run, ...fill ? { fill } : {}, ...stroke ? { stroke } : {} };
|
|
1898
|
+
}
|
|
1899
|
+
function strokeInBoxFrame(stroke, box) {
|
|
1900
|
+
if (!stroke.paint) return stroke;
|
|
1901
|
+
const paint = fillInBoxFrame(stroke.paint, box);
|
|
1902
|
+
return paint === stroke.paint ? stroke : { ...stroke, paint };
|
|
1903
|
+
}
|
|
1863
1904
|
function svgNodesToKitDrafts(nodes, nextId) {
|
|
1864
1905
|
const drafts = [];
|
|
1865
1906
|
const visit = (n, parentId) => {
|
|
@@ -1887,16 +1928,30 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
1887
1928
|
const pose2 = {
|
|
1888
1929
|
x: n.x,
|
|
1889
1930
|
y: n.y,
|
|
1890
|
-
width:
|
|
1931
|
+
width: n.width,
|
|
1891
1932
|
height: n.height
|
|
1892
1933
|
};
|
|
1893
1934
|
if (n.rotation) pose2.rotation = n.rotation;
|
|
1935
|
+
const box = {
|
|
1936
|
+
x: pose2.x,
|
|
1937
|
+
y: pose2.y,
|
|
1938
|
+
width: pose2.width,
|
|
1939
|
+
height: pose2.height
|
|
1940
|
+
};
|
|
1894
1941
|
drafts.push({
|
|
1895
1942
|
kind: "leaf",
|
|
1896
1943
|
id: nextId(),
|
|
1897
1944
|
parentId,
|
|
1898
1945
|
pose: pose2,
|
|
1899
|
-
data: {
|
|
1946
|
+
data: {
|
|
1947
|
+
text: n.text,
|
|
1948
|
+
...n.style ? { style: n.style } : {},
|
|
1949
|
+
...n.runs ? { runs: n.runs.map((r) => runInBoxFrame(r, box)) } : {},
|
|
1950
|
+
// `!== undefined`, not a truthiness test: `null` is the document
|
|
1951
|
+
// saying `fill="none"`, and absent takes the painter's default.
|
|
1952
|
+
...n.fill !== void 0 ? { fill: n.fill === null ? null : fillInBoxFrame(n.fill, box) } : {},
|
|
1953
|
+
...n.stroke !== void 0 ? { stroke: strokeInBoxFrame(n.stroke, box) } : {}
|
|
1954
|
+
}
|
|
1900
1955
|
});
|
|
1901
1956
|
return pose2;
|
|
1902
1957
|
}
|
|
@@ -1938,18 +1993,15 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
1938
1993
|
for (const n of nodes) visit(n, null);
|
|
1939
1994
|
return drafts;
|
|
1940
1995
|
}
|
|
1941
|
-
var ESTIMATED_GLYPH_ADVANCE_EM = 0.6;
|
|
1942
|
-
function textBoxWidth(n) {
|
|
1943
|
-
if (n.width !== UNBOUNDED_TEXT_WIDTH) return n.width;
|
|
1944
|
-
const fontSize = resolveTextStyle(n.style).fontSize;
|
|
1945
|
-
const longest = n.text.split("\n").reduce((max, line) => Math.max(max, line.length), 0);
|
|
1946
|
-
return longest * fontSize * ESTIMATED_GLYPH_ADVANCE_EM;
|
|
1947
|
-
}
|
|
1948
1996
|
function scaleTextData(data, scale) {
|
|
1949
1997
|
if (scale === 1 || typeof data.text !== "string") return data;
|
|
1950
1998
|
const style = data.style ?? {};
|
|
1999
|
+
const runs = data.runs;
|
|
1951
2000
|
return {
|
|
1952
2001
|
...data,
|
|
2002
|
+
...runs ? {
|
|
2003
|
+
runs: runs.map((r) => r.fontSize !== void 0 ? { ...r, fontSize: r.fontSize * scale } : r)
|
|
2004
|
+
} : {},
|
|
1953
2005
|
style: { ...style, fontSize: resolveTextStyle(style).fontSize * scale }
|
|
1954
2006
|
};
|
|
1955
2007
|
}
|
|
@@ -2024,6 +2076,6 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
2024
2076
|
}
|
|
2025
2077
|
}
|
|
2026
2078
|
|
|
2027
|
-
export { IDENTITY_MATRIX,
|
|
2079
|
+
export { IDENTITY_MATRIX, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
|
2028
2080
|
//# sourceMappingURL=index.js.map
|
|
2029
2081
|
//# sourceMappingURL=index.js.map
|