@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 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: number;
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 estimated dimensions —
149
- * {@link UNBOUNDED_TEXT_WIDTH} and
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, UNBOUNDED_TEXT_WIDTH, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
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, resolveStrokeWidth, solid, fillToBoundsFrame, resolveTextStyle, getPaintKind } from '@weasel-js/core';
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 = invertMatrix(ctm);
89
- return inv ? multiply(multiply(ctm, local), inv) : local;
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 (angle != null) {
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 width = inheritedWidth != null ? parseFloat(inheritedWidth) : 1;
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 (angle != null) {
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 : UNBOUNDED_TEXT_WIDTH;
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 (angle != null) {
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 = resolveStrokeWidth(stroke.width ?? 1, 1);
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(`stroke-width="${trimNumber(width)}"`);
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(`stroke-width="${trimNumber(stroke.width)}"`);
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: textBoxWidth(n),
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: { text: n.text, ...n.style ? { style: n.style } : {} }
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, UNBOUNDED_TEXT_WIDTH, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
2079
+ export { IDENTITY_MATRIX, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
2028
2080
  //# sourceMappingURL=index.js.map
2029
2081
  //# sourceMappingURL=index.js.map