@weasel-js/svg 1.0.1 → 1.0.2
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 +12 -0
- package/dist/index.d.ts +37 -3
- package/dist/index.js +148 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -40,6 +40,18 @@ answer, and the alternative is unrepresentable anyway (see above). It only
|
|
|
40
40
|
shows up on foreign SVG that sets decoration on a group and cancels it on a
|
|
41
41
|
child.
|
|
42
42
|
|
|
43
|
+
## Raster images
|
|
44
|
+
|
|
45
|
+
`<image>` parses to an `SvgImageNode` holding the `href` verbatim — an
|
|
46
|
+
external URL or a `data:` URI — plus a box. The package never fetches or
|
|
47
|
+
decodes it, so an external URL round-trips as a reference and resolves only
|
|
48
|
+
when something downstream loads it. `unpackSvgFiles` maps the node onto the
|
|
49
|
+
kit's `kit:image` painter (`data.image.src`), which does load it.
|
|
50
|
+
|
|
51
|
+
`preserveAspectRatio` is not modeled. The box is taken literally on the way
|
|
52
|
+
in (a non-`none` value warns) and written back as `none`, so a source file
|
|
53
|
+
that relied on letterboxing imports stretched.
|
|
54
|
+
|
|
43
55
|
## License
|
|
44
56
|
|
|
45
57
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -121,6 +121,16 @@ interface SvgGroupNode {
|
|
|
121
121
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
122
122
|
meta?: NamespaceMeta;
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Width given to an external `<text>` that carries no `data-weasel-width`:
|
|
126
|
+
* large enough that line wrapping never fires, since SVG text flows from a
|
|
127
|
+
* baseline and the source says nothing about a box.
|
|
128
|
+
*
|
|
129
|
+
* It is a wrap sentinel, not a measurement. Anything that treats a text
|
|
130
|
+
* node's width as geometry — a union AABB, a fit-clamp — has to recognize it
|
|
131
|
+
* and substitute an estimate, or one text node swamps the whole file.
|
|
132
|
+
*/
|
|
133
|
+
declare const UNBOUNDED_TEXT_WIDTH = 99999;
|
|
124
134
|
/**
|
|
125
135
|
* Text leaf node. Mirrors the kit's `TextPose` shape — a bounding box plus
|
|
126
136
|
* text content, optional rich-text `runs` (per-range styling), and an
|
|
@@ -132,7 +142,7 @@ interface SvgGroupNode {
|
|
|
132
142
|
* box, plus `data-weasel-width` / `data-weasel-height` so weasel's
|
|
133
143
|
* explicit box dimensions round-trip losslessly. External SVG text
|
|
134
144
|
* (lacking the data-* attrs) imports with estimated dimensions —
|
|
135
|
-
*
|
|
145
|
+
* {@link UNBOUNDED_TEXT_WIDTH} and
|
|
136
146
|
* `height = fontSize * lineHeight * (lineCount || 1)`.
|
|
137
147
|
*/
|
|
138
148
|
interface SvgTextNode {
|
|
@@ -156,8 +166,32 @@ interface SvgTextNode {
|
|
|
156
166
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
157
167
|
meta?: NamespaceMeta;
|
|
158
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Raster-image leaf — an SVG `<image>`. `href` is the element's `href` (or
|
|
171
|
+
* legacy `xlink:href`) verbatim: an external URL or a `data:` URI. The
|
|
172
|
+
* package neither fetches nor decodes it, so an external URL round-trips as
|
|
173
|
+
* a reference and only resolves when something downstream loads it.
|
|
174
|
+
*
|
|
175
|
+
* SVG's `preserveAspectRatio` is not modeled; the box is taken literally.
|
|
176
|
+
*/
|
|
177
|
+
interface SvgImageNode {
|
|
178
|
+
kind: 'image';
|
|
179
|
+
href: string;
|
|
180
|
+
x: number;
|
|
181
|
+
y: number;
|
|
182
|
+
width: number;
|
|
183
|
+
height: number;
|
|
184
|
+
/** Element-level opacity (`opacity="..."`), 0..1. */
|
|
185
|
+
opacity?: number;
|
|
186
|
+
/** Element-level rotation in **radians**, pivoting around the unrotated
|
|
187
|
+
* AABB center `(x + width/2, y + height/2)`. Emitted as SVG
|
|
188
|
+
* `transform="rotate(angleDegrees cx cy)"`. */
|
|
189
|
+
rotation?: number;
|
|
190
|
+
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
191
|
+
meta?: NamespaceMeta;
|
|
192
|
+
}
|
|
159
193
|
/** Discriminated-union node — the leaf of the public tree. */
|
|
160
|
-
type SvgNode = SvgPathNode | SvgGroupNode | SvgTextNode;
|
|
194
|
+
type SvgNode = SvgPathNode | SvgGroupNode | SvgTextNode | SvgImageNode;
|
|
161
195
|
/** Options for {@link parseSvg}. */
|
|
162
196
|
interface ParseOptions {
|
|
163
197
|
/**
|
|
@@ -337,4 +371,4 @@ declare function svgNodesToKitDrafts(nodes: readonly SvgNode[], nextId: () => st
|
|
|
337
371
|
*/
|
|
338
372
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
339
373
|
|
|
340
|
-
export { IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SvgDraftBounds, type SvgGroupNode, type SvgNode, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, parseSvg, serializeSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
|
374
|
+
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, UNBOUNDED_TEXT_WIDTH, parseSvg, serializeSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, dwarn, createInsertOp, PathBuilder, parseColor, rgbaToHex, PATH_C, PATH_Q } from '@weasel-js/core';
|
|
1
|
+
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, dwarn, createInsertOp, PathBuilder, parseColor, rgbaToHex, PATH_C, PATH_Q, resolveTextStyle, fillToBoundsFrame } from '@weasel-js/core';
|
|
2
2
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
3
3
|
|
|
4
4
|
// src/parse.ts
|
|
5
5
|
|
|
6
6
|
// src/types.ts
|
|
7
7
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|
8
|
+
var UNBOUNDED_TEXT_WIDTH = 99999;
|
|
8
9
|
|
|
9
10
|
// src/transform.ts
|
|
10
11
|
function multiply(a, b) {
|
|
@@ -383,6 +384,9 @@ function applyAlpha(hex, alpha) {
|
|
|
383
384
|
const a = Math.max(0, Math.min(255, Math.round(alpha * 255)));
|
|
384
385
|
return `${hex}${a.toString(16).padStart(2, "0")}`;
|
|
385
386
|
}
|
|
387
|
+
function readGradientUnits(el) {
|
|
388
|
+
return el.getAttribute("gradientUnits") === "userSpaceOnUse" ? "world" : "bounds";
|
|
389
|
+
}
|
|
386
390
|
function readLinearGradient(el, onWarn) {
|
|
387
391
|
const x1 = parseFloat(el.getAttribute("x1") ?? "0");
|
|
388
392
|
const y1 = parseFloat(el.getAttribute("y1") ?? "0");
|
|
@@ -393,7 +397,8 @@ function readLinearGradient(el, onWarn) {
|
|
|
393
397
|
fill: "linear-gradient",
|
|
394
398
|
from: { x: x1, y: y1 },
|
|
395
399
|
to: { x: x2, y: y2 },
|
|
396
|
-
stops
|
|
400
|
+
stops,
|
|
401
|
+
units: readGradientUnits(el)
|
|
397
402
|
};
|
|
398
403
|
}
|
|
399
404
|
function readRadialGradient(el, onWarn) {
|
|
@@ -405,7 +410,8 @@ function readRadialGradient(el, onWarn) {
|
|
|
405
410
|
fill: "radial-gradient",
|
|
406
411
|
center: { x: cx, y: cy },
|
|
407
412
|
radius: r,
|
|
408
|
-
stops
|
|
413
|
+
stops,
|
|
414
|
+
units: readGradientUnits(el)
|
|
409
415
|
};
|
|
410
416
|
}
|
|
411
417
|
var PaintServerRegistry = class {
|
|
@@ -434,14 +440,17 @@ var PaintServerRegistry = class {
|
|
|
434
440
|
return parts.join("");
|
|
435
441
|
}
|
|
436
442
|
};
|
|
443
|
+
function gradientUnitsAttr(units) {
|
|
444
|
+
return units === "bounds" ? "objectBoundingBox" : "userSpaceOnUse";
|
|
445
|
+
}
|
|
437
446
|
function gradientXml(id, paint) {
|
|
438
447
|
if (paint.fill === "linear-gradient") {
|
|
439
448
|
const stops = paint.stops.map(stopXml).join("");
|
|
440
|
-
return `<linearGradient id="${id}" gradientUnits="
|
|
449
|
+
return `<linearGradient id="${id}" gradientUnits="${gradientUnitsAttr(paint.units)}" x1="${trimNumber(paint.from.x)}" y1="${trimNumber(paint.from.y)}" x2="${trimNumber(paint.to.x)}" y2="${trimNumber(paint.to.y)}">${stops}</linearGradient>`;
|
|
441
450
|
}
|
|
442
451
|
if (paint.fill === "radial-gradient") {
|
|
443
452
|
const stops = paint.stops.map(stopXml).join("");
|
|
444
|
-
return `<radialGradient id="${id}" gradientUnits="
|
|
453
|
+
return `<radialGradient id="${id}" gradientUnits="${gradientUnitsAttr(paint.units)}" cx="${trimNumber(paint.center.x)}" cy="${trimNumber(paint.center.y)}" r="${trimNumber(paint.radius)}">${stops}</radialGradient>`;
|
|
445
454
|
}
|
|
446
455
|
return "";
|
|
447
456
|
}
|
|
@@ -685,6 +694,14 @@ function parseElement(el, ctm, style, gradients, onWarn, uriToPrefix) {
|
|
|
685
694
|
}
|
|
686
695
|
return textNode;
|
|
687
696
|
}
|
|
697
|
+
if (tag === "image") {
|
|
698
|
+
const imageNode = parseImageElement(el, ctm, onWarn);
|
|
699
|
+
if (imageNode) {
|
|
700
|
+
const meta2 = collectElementMeta(el, uriToPrefix);
|
|
701
|
+
if (meta2) imageNode.meta = meta2;
|
|
702
|
+
}
|
|
703
|
+
return imageNode;
|
|
704
|
+
}
|
|
688
705
|
if (!SUPPORTED_LEAF_TAGS.has(tag)) {
|
|
689
706
|
onWarn(`unsupported element: <${el.tagName}>`);
|
|
690
707
|
return null;
|
|
@@ -933,6 +950,54 @@ function parseTextDecoration(raw) {
|
|
|
933
950
|
if (tokens.includes("line-through")) out.strikethrough = true;
|
|
934
951
|
return out;
|
|
935
952
|
}
|
|
953
|
+
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
954
|
+
function parseImageElement(el, ctm, onWarn) {
|
|
955
|
+
const href = el.getAttribute("href") ?? el.getAttributeNS(XLINK_NS, "href") ?? el.getAttribute("xlink:href");
|
|
956
|
+
if (!href) {
|
|
957
|
+
onWarn("<image> without href; dropped");
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
const num = (raw, fallback) => {
|
|
961
|
+
if (raw == null) return fallback;
|
|
962
|
+
const n = parseFloat(raw);
|
|
963
|
+
return Number.isFinite(n) ? n : fallback;
|
|
964
|
+
};
|
|
965
|
+
const rawX = num(el.getAttribute("x"), 0);
|
|
966
|
+
const rawY = num(el.getAttribute("y"), 0);
|
|
967
|
+
const rawW = num(el.getAttribute("width"), 0);
|
|
968
|
+
const rawH = num(el.getAttribute("height"), 0);
|
|
969
|
+
const px = (x, y) => [ctm[0] * x + ctm[2] * y + ctm[4], ctm[1] * x + ctm[3] * y + ctm[5]];
|
|
970
|
+
const [x0, y0] = px(rawX, rawY);
|
|
971
|
+
const [x1, y1] = px(rawX + rawW, rawY + rawH);
|
|
972
|
+
const node = {
|
|
973
|
+
kind: "image",
|
|
974
|
+
href,
|
|
975
|
+
x: Math.min(x0, x1),
|
|
976
|
+
y: Math.min(y0, y1),
|
|
977
|
+
width: Math.abs(x1 - x0),
|
|
978
|
+
height: Math.abs(y1 - y0)
|
|
979
|
+
};
|
|
980
|
+
const opacity = readOpacityAttr(el, "opacity");
|
|
981
|
+
if (opacity != null) node.opacity = opacity;
|
|
982
|
+
const localTransform = parseTransform(el.getAttribute("transform"), onWarn);
|
|
983
|
+
if (!isIdentity(localTransform)) {
|
|
984
|
+
const cx = node.x + node.width / 2;
|
|
985
|
+
const cy = node.y + node.height / 2;
|
|
986
|
+
const angle = decomposeRotation(localTransform, cx, cy);
|
|
987
|
+
if (angle != null) {
|
|
988
|
+
node.rotation = angle;
|
|
989
|
+
} else {
|
|
990
|
+
const rotComp = rotationComponent(localTransform);
|
|
991
|
+
if (Math.abs(rotComp) > 1e-4) {
|
|
992
|
+
onWarn("<image> transform has a rotational component that can't be cleanly stored as rotation; dropped (may not round-trip identically)");
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (el.hasAttribute("preserveAspectRatio") && el.getAttribute("preserveAspectRatio") !== "none") {
|
|
997
|
+
onWarn("<image> preserveAspectRatio is not modeled; the box is taken literally");
|
|
998
|
+
}
|
|
999
|
+
return node;
|
|
1000
|
+
}
|
|
936
1001
|
function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
937
1002
|
const num = (raw, fallback) => {
|
|
938
1003
|
if (raw == null) return fallback;
|
|
@@ -979,7 +1044,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
979
1044
|
}
|
|
980
1045
|
const dataW = num(el.getAttribute("data-weasel-width"), NaN);
|
|
981
1046
|
const dataH = num(el.getAttribute("data-weasel-height"), NaN);
|
|
982
|
-
const width = Number.isFinite(dataW) ? dataW :
|
|
1047
|
+
const width = Number.isFinite(dataW) ? dataW : UNBOUNDED_TEXT_WIDTH;
|
|
983
1048
|
const lines = (plain.match(/\n/g)?.length ?? 0) + 1;
|
|
984
1049
|
const height = Number.isFinite(dataH) ? dataH : fontSize * lineHeight * lines;
|
|
985
1050
|
const opacity = readOpacityAttr(el, "opacity");
|
|
@@ -1227,8 +1292,32 @@ function registerTextPaint(paint, registry) {
|
|
|
1227
1292
|
function nodeXml(node, registry, namespaces) {
|
|
1228
1293
|
if (node.kind === "group") return groupXml(node, registry, namespaces);
|
|
1229
1294
|
if (node.kind === "text") return textXml(node, registry, namespaces);
|
|
1295
|
+
if (node.kind === "image") return imageXml(node, namespaces);
|
|
1230
1296
|
return pathXml(node, registry, namespaces);
|
|
1231
1297
|
}
|
|
1298
|
+
function imageXml(node, namespaces) {
|
|
1299
|
+
const attrs = [
|
|
1300
|
+
`href="${escapeAttr2(node.href)}"`,
|
|
1301
|
+
`x="${trimNumber(node.x)}"`,
|
|
1302
|
+
`y="${trimNumber(node.y)}"`,
|
|
1303
|
+
`width="${trimNumber(node.width)}"`,
|
|
1304
|
+
`height="${trimNumber(node.height)}"`,
|
|
1305
|
+
`preserveAspectRatio="none"`
|
|
1306
|
+
];
|
|
1307
|
+
if (node.opacity != null && node.opacity !== 1) {
|
|
1308
|
+
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
1309
|
+
}
|
|
1310
|
+
if (node.rotation != null && node.rotation !== 0) {
|
|
1311
|
+
const cx = node.x + node.width / 2;
|
|
1312
|
+
const cy = node.y + node.height / 2;
|
|
1313
|
+
const deg = node.rotation * 180 / Math.PI;
|
|
1314
|
+
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
1315
|
+
}
|
|
1316
|
+
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
1317
|
+
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
1318
|
+
if (metaEls) return `<image ${attrs.join(" ")}${metaAttrs}>${metaEls}</image>`;
|
|
1319
|
+
return `<image ${attrs.join(" ")}${metaAttrs}/>`;
|
|
1320
|
+
}
|
|
1232
1321
|
function groupXml(node, registry, namespaces) {
|
|
1233
1322
|
const attrs = [];
|
|
1234
1323
|
if (node.transform) {
|
|
@@ -1334,7 +1423,7 @@ function computeBounds(nodes) {
|
|
|
1334
1423
|
n.children.forEach(visit);
|
|
1335
1424
|
return;
|
|
1336
1425
|
}
|
|
1337
|
-
const b = n.kind === "text" ? { minX: n.x, minY: n.y, maxX: n.x + n.width, maxY: n.y + n.height } : pathBounds(n.path);
|
|
1426
|
+
const b = n.kind === "text" || n.kind === "image" ? { minX: n.x, minY: n.y, maxX: n.x + n.width, maxY: n.y + n.height } : pathBounds(n.path);
|
|
1338
1427
|
if (b.minX < minX) minX = b.minX;
|
|
1339
1428
|
if (b.minY < minY) minY = b.minY;
|
|
1340
1429
|
if (b.maxX > maxX) maxX = b.maxX;
|
|
@@ -1456,11 +1545,19 @@ var svgIdCounter = 0;
|
|
|
1456
1545
|
function freshSvgNodeId() {
|
|
1457
1546
|
return `n${(svgIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1458
1547
|
}
|
|
1459
|
-
function
|
|
1548
|
+
function fillFromPaint(paint, box) {
|
|
1549
|
+
if (!paint) return void 0;
|
|
1550
|
+
if (paint.kind === "none") return "none";
|
|
1551
|
+
if (paint.kind === "solid") return paint.color;
|
|
1552
|
+
const g = paint.paint;
|
|
1553
|
+
const units = "units" in g ? g.units : void 0;
|
|
1554
|
+
return units === "world" ? fillToBoundsFrame(g, box) : g;
|
|
1555
|
+
}
|
|
1556
|
+
function strokeColorFromPaint(paint) {
|
|
1460
1557
|
if (!paint) return void 0;
|
|
1461
1558
|
if (paint.kind === "none") return "none";
|
|
1462
1559
|
if (paint.kind === "solid") return paint.color;
|
|
1463
|
-
dwarn("ingest", `svg unpack: gradient
|
|
1560
|
+
dwarn("ingest", `svg unpack: gradient stroke flattened to ${GRADIENT_FALLBACK}`);
|
|
1464
1561
|
return GRADIENT_FALLBACK;
|
|
1465
1562
|
}
|
|
1466
1563
|
function svgNodesToKitDrafts(nodes, nextId) {
|
|
@@ -1487,7 +1584,12 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
1487
1584
|
return acc;
|
|
1488
1585
|
}
|
|
1489
1586
|
if (n.kind === "text") {
|
|
1490
|
-
const pose2 = {
|
|
1587
|
+
const pose2 = {
|
|
1588
|
+
x: n.x,
|
|
1589
|
+
y: n.y,
|
|
1590
|
+
width: textBoxWidth(n),
|
|
1591
|
+
height: n.height
|
|
1592
|
+
};
|
|
1491
1593
|
if (n.rotation) pose2.rotation = n.rotation;
|
|
1492
1594
|
drafts.push({
|
|
1493
1595
|
kind: "leaf",
|
|
@@ -1498,11 +1600,28 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
1498
1600
|
});
|
|
1499
1601
|
return pose2;
|
|
1500
1602
|
}
|
|
1603
|
+
if (n.kind === "image") {
|
|
1604
|
+
const pose2 = { x: n.x, y: n.y, width: n.width, height: n.height };
|
|
1605
|
+
if (n.rotation) pose2.rotation = n.rotation;
|
|
1606
|
+
drafts.push({
|
|
1607
|
+
kind: "leaf",
|
|
1608
|
+
id: nextId(),
|
|
1609
|
+
parentId,
|
|
1610
|
+
pose: pose2,
|
|
1611
|
+
data: {
|
|
1612
|
+
image: {
|
|
1613
|
+
src: n.href,
|
|
1614
|
+
...n.opacity != null ? { opacity: n.opacity } : {}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
return pose2;
|
|
1619
|
+
}
|
|
1501
1620
|
const b = n.path.kind === "rect" ? { x: n.path.x, y: n.path.y, width: n.path.width, height: n.path.height } : boundsOfPath(n.path);
|
|
1502
1621
|
const pose = { x: b.x, y: b.y, width: b.width, height: b.height };
|
|
1503
1622
|
if (n.rotation) pose.rotation = n.rotation;
|
|
1504
|
-
const fill =
|
|
1505
|
-
const stroke = n.stroke ?
|
|
1623
|
+
const fill = fillFromPaint(n.fill, b);
|
|
1624
|
+
const stroke = n.stroke ? strokeColorFromPaint(n.stroke.paint) : void 0;
|
|
1506
1625
|
drafts.push({
|
|
1507
1626
|
kind: "leaf",
|
|
1508
1627
|
id: nextId(),
|
|
@@ -1519,6 +1638,21 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
1519
1638
|
for (const n of nodes) visit(n, null);
|
|
1520
1639
|
return drafts;
|
|
1521
1640
|
}
|
|
1641
|
+
var ESTIMATED_GLYPH_ADVANCE_EM = 0.6;
|
|
1642
|
+
function textBoxWidth(n) {
|
|
1643
|
+
if (n.width !== UNBOUNDED_TEXT_WIDTH) return n.width;
|
|
1644
|
+
const fontSize = resolveTextStyle(n.style).fontSize;
|
|
1645
|
+
const longest = n.text.split("\n").reduce((max, line) => Math.max(max, line.length), 0);
|
|
1646
|
+
return longest * fontSize * ESTIMATED_GLYPH_ADVANCE_EM;
|
|
1647
|
+
}
|
|
1648
|
+
function scaleTextData(data, scale) {
|
|
1649
|
+
if (scale === 1 || typeof data.text !== "string") return data;
|
|
1650
|
+
const style = data.style ?? {};
|
|
1651
|
+
return {
|
|
1652
|
+
...data,
|
|
1653
|
+
style: { ...style, fontSize: resolveTextStyle(style).fontSize * scale }
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1522
1656
|
function unionRect(a, b) {
|
|
1523
1657
|
const minX = Math.min(a.x, b.x);
|
|
1524
1658
|
const minY = Math.min(a.y, b.y);
|
|
@@ -1576,7 +1710,7 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
1576
1710
|
kind: d.kind,
|
|
1577
1711
|
layer,
|
|
1578
1712
|
pose: place(d.pose),
|
|
1579
|
-
data: d.kind === "leaf" ? d.data : {},
|
|
1713
|
+
data: d.kind === "leaf" ? scaleTextData(d.data, scale) : {},
|
|
1580
1714
|
parent: d.parentId
|
|
1581
1715
|
},
|
|
1582
1716
|
label: "Insert SVG"
|
|
@@ -1590,6 +1724,6 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
1590
1724
|
}
|
|
1591
1725
|
}
|
|
1592
1726
|
|
|
1593
|
-
export { IDENTITY_MATRIX, parseSvg, serializeSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
|
1727
|
+
export { IDENTITY_MATRIX, UNBOUNDED_TEXT_WIDTH, parseSvg, serializeSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
|
1594
1728
|
//# sourceMappingURL=index.js.map
|
|
1595
1729
|
//# sourceMappingURL=index.js.map
|