@weasel-js/svg 1.5.2 → 1.6.1

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 CHANGED
@@ -91,6 +91,11 @@ decodes it, so an external URL round-trips as a reference and resolves only
91
91
  when something downstream loads it. `unpackSvgFiles` maps the node onto the
92
92
  kit's `kit:image` painter (`data.image.src`), which does load it.
93
93
 
94
+ A source rect and flips (`source`, `flipX`, `flipY`) are written as a nested
95
+ `<svg viewBox>` viewport and read back as the same node. `unpackSvgFiles`
96
+ carries them onto `data.image` under the same names, and `svgImageFromKit`
97
+ writes a `kit:image` leaf back as an `SvgImageNode`.
98
+
94
99
  `preserveAspectRatio` is not modeled. The box is taken literally on the way
95
100
  in (a non-`none` value warns) and written back as `none`, so a source file
96
101
  that relied on letterboxing imports stretched.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Mat3 } from '@weasel-js/geom';
2
- import { Path, FillStyle, ScreenLength, StrokeAlign, StyledRun, TextStyle, TextVerticalAlign, Stroke, MarkerEntry, TilePatternSpec, IngestCtx } from '@weasel-js/core';
2
+ import { Path, FillStyle, ScreenLength, StrokeAlign, StyledRun, TextStyle, TextVerticalAlign, Stroke, MarkerEntry, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
3
3
 
4
4
  /**
5
5
  * Public types for `@weasel-js/svg`. The package exposes a flat,
@@ -426,6 +426,9 @@ type SvgSceneDraft = {
426
426
  * `userSpaceOnUse` gradient survives the fit-clamp and the drop-point
427
427
  * placement. */
428
428
  declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftBounds): Stroke | undefined;
429
+ /** Write a `kit:image` leaf as an `SvgImageNode` — the inverse of the image
430
+ * arm of {@link svgNodesToKitDrafts}. The pose is the image's box. */
431
+ declare function svgImageFromKit(image: ImageNodeData['image'], pose: DraftPose): SvgImageNode;
429
432
  /**
430
433
  * Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
431
434
  * {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
@@ -441,4 +444,4 @@ declare function svgNodesToKitDrafts(nodes: readonly SvgNode[], nextId: () => st
441
444
  */
442
445
  declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
443
446
 
444
- 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 };
447
+ 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, svgImageFromKit, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
package/dist/index.js CHANGED
@@ -13,9 +13,9 @@ function isIdentity(m, eps = 1e-9) {
13
13
  var NUM_RE = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
14
14
  function readNumbers(s) {
15
15
  const out = [];
16
- const matches = s.match(NUM_RE);
17
- if (!matches) return out;
18
- for (const m of matches) {
16
+ const matches2 = s.match(NUM_RE);
17
+ if (!matches2) return out;
18
+ for (const m of matches2) {
19
19
  const n = Number(m);
20
20
  if (Number.isFinite(n)) out.push(n);
21
21
  }
@@ -249,6 +249,302 @@ function parsePaintAttr(raw) {
249
249
  }
250
250
  }
251
251
 
252
+ // src/cascade.ts
253
+ var EMPTY_STYLE = {};
254
+ var INHERITABLE = [
255
+ "fill",
256
+ "fill-opacity",
257
+ "fill-rule",
258
+ "stroke",
259
+ "stroke-width",
260
+ "stroke-opacity",
261
+ "stroke-linecap",
262
+ "stroke-linejoin",
263
+ "stroke-dasharray",
264
+ "stroke-miterlimit",
265
+ "marker-start",
266
+ "marker-mid",
267
+ "marker-end",
268
+ "color",
269
+ "font-size",
270
+ "font-family",
271
+ "font-weight",
272
+ "font-style",
273
+ "text-anchor",
274
+ "letter-spacing",
275
+ "text-decoration",
276
+ "direction",
277
+ "text-transform"
278
+ ];
279
+ function skipString(s, i) {
280
+ const q = s[i];
281
+ for (let j = i + 1; j < s.length; j++) {
282
+ if (s[j] === "\\") j++;
283
+ else if (s[j] === q) return j + 1;
284
+ }
285
+ return s.length;
286
+ }
287
+ function stripComments(s) {
288
+ if (!s.includes("/*")) return s;
289
+ let out = "";
290
+ let i = 0;
291
+ while (i < s.length) {
292
+ const ch = s[i];
293
+ if (ch === '"' || ch === "'") {
294
+ const end = skipString(s, i);
295
+ out += s.slice(i, end);
296
+ i = end;
297
+ } else if (ch === "/" && s[i + 1] === "*") {
298
+ const end = s.indexOf("*/", i + 2);
299
+ i = end < 0 ? s.length : end + 2;
300
+ out += " ";
301
+ } else {
302
+ out += ch;
303
+ i++;
304
+ }
305
+ }
306
+ return out;
307
+ }
308
+ function splitTopLevel(s, sep, dropBlocks = false) {
309
+ const parts = [];
310
+ let depth = 0;
311
+ let start = 0;
312
+ let i = 0;
313
+ while (i < s.length) {
314
+ const ch = s[i];
315
+ if (ch === '"' || ch === "'") {
316
+ i = skipString(s, i);
317
+ continue;
318
+ }
319
+ if (ch === "(" || ch === "[") depth++;
320
+ else if ((ch === ")" || ch === "]") && depth > 0) depth--;
321
+ else if (depth === 0 && ch === sep) {
322
+ parts.push(s.slice(start, i));
323
+ start = i + 1;
324
+ } else if (depth === 0 && dropBlocks && ch === "{") {
325
+ i = skipBlock(s, i);
326
+ start = i;
327
+ continue;
328
+ }
329
+ i++;
330
+ }
331
+ parts.push(s.slice(start));
332
+ return parts;
333
+ }
334
+ function skipBlock(s, i) {
335
+ let depth = 0;
336
+ while (i < s.length) {
337
+ const ch = s[i];
338
+ if (ch === '"' || ch === "'") {
339
+ i = skipString(s, i);
340
+ continue;
341
+ }
342
+ if (ch === "{") depth++;
343
+ else if (ch === "}" && --depth === 0) return i + 1;
344
+ i++;
345
+ }
346
+ return s.length;
347
+ }
348
+ var PROP_NAME = /^-?[a-z_][a-z0-9_-]*$/;
349
+ var IMPORTANT = /!\s*important\s*$/i;
350
+ function parseDeclarations(block) {
351
+ const out = [];
352
+ for (const part of splitTopLevel(stripComments(block), ";", true)) {
353
+ const colon = part.indexOf(":");
354
+ if (colon < 0) continue;
355
+ const prop = part.slice(0, colon).trim().toLowerCase();
356
+ if (!PROP_NAME.test(prop)) continue;
357
+ let value = part.slice(colon + 1).trim();
358
+ const important = IMPORTANT.test(value);
359
+ if (important) value = value.replace(IMPORTANT, "").trim();
360
+ if (!value) continue;
361
+ out.push({ prop, value, important });
362
+ }
363
+ return out;
364
+ }
365
+ function identEnd(s, i) {
366
+ while (i < s.length) {
367
+ const ch = s[i];
368
+ if (ch === "\\") i += 2;
369
+ else if (/[\w-]/.test(ch) || ch.charCodeAt(0) > 127) i++;
370
+ else break;
371
+ }
372
+ return i;
373
+ }
374
+ function parenEnd(s, i) {
375
+ let depth = 0;
376
+ while (i < s.length) {
377
+ const ch = s[i];
378
+ if (ch === '"' || ch === "'") {
379
+ i = skipString(s, i);
380
+ continue;
381
+ }
382
+ if (ch === "(") depth++;
383
+ else if (ch === ")" && --depth === 0) return i + 1;
384
+ i++;
385
+ }
386
+ return s.length;
387
+ }
388
+ var LEGACY_PSEUDO_ELEMENTS = /* @__PURE__ */ new Set(["before", "after", "first-line", "first-letter"]);
389
+ var MAX_ARG_PSEUDOS = /* @__PURE__ */ new Set(["not", "is", "matches", "-webkit-any", "has"]);
390
+ function compareSpecificity(a, b) {
391
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
392
+ }
393
+ function maxSpecificity(list) {
394
+ let best = [0, 0, 0];
395
+ for (const sel of splitTopLevel(list, ",")) {
396
+ const s = specificity(sel);
397
+ if (compareSpecificity(s, best) > 0) best = s;
398
+ }
399
+ return best;
400
+ }
401
+ function specificity(selector) {
402
+ let a = 0, b = 0, c = 0;
403
+ const s = selector;
404
+ let i = 0;
405
+ while (i < s.length) {
406
+ const ch = s[i];
407
+ if (ch === "#") {
408
+ a++;
409
+ i = identEnd(s, i + 1);
410
+ } else if (ch === ".") {
411
+ b++;
412
+ i = identEnd(s, i + 1);
413
+ } else if (ch === "[") {
414
+ b++;
415
+ while (i < s.length && s[i] !== "]") {
416
+ i = s[i] === '"' || s[i] === "'" ? skipString(s, i) : i + 1;
417
+ }
418
+ i++;
419
+ } else if (ch === ":") {
420
+ const element = s[i + 1] === ":";
421
+ const nameStart = i + (element ? 2 : 1);
422
+ const nameEnd = identEnd(s, nameStart);
423
+ const name = s.slice(nameStart, nameEnd).toLowerCase();
424
+ let arg = null;
425
+ i = nameEnd;
426
+ if (s[i] === "(") {
427
+ const end = parenEnd(s, i);
428
+ arg = s.slice(i + 1, end - 1);
429
+ i = end;
430
+ }
431
+ if (element || LEGACY_PSEUDO_ELEMENTS.has(name)) c++;
432
+ else if (name === "where") ; else if (MAX_ARG_PSEUDOS.has(name) && arg != null) {
433
+ const m = maxSpecificity(arg);
434
+ a += m[0];
435
+ b += m[1];
436
+ c += m[2];
437
+ } else b++;
438
+ } else if (/[a-zA-Z_\\-]/.test(ch) || ch.charCodeAt(0) > 127) {
439
+ c++;
440
+ i = identEnd(s, i);
441
+ } else {
442
+ i++;
443
+ }
444
+ }
445
+ return [a, b, c];
446
+ }
447
+ function parseStylesheet(text, firstOrder = 0) {
448
+ const src = stripComments(text).replace(/<!\[CDATA\[|\]\]>|<!--|-->/g, " ");
449
+ const rules = [];
450
+ let order = firstOrder;
451
+ let i = 0;
452
+ let preludeStart = 0;
453
+ while (i < src.length) {
454
+ const ch = src[i];
455
+ if (ch === '"' || ch === "'") {
456
+ i = skipString(src, i);
457
+ continue;
458
+ }
459
+ if (ch === ";") {
460
+ i++;
461
+ preludeStart = i;
462
+ continue;
463
+ }
464
+ if (ch !== "{") {
465
+ i++;
466
+ continue;
467
+ }
468
+ const prelude = src.slice(preludeStart, i).trim();
469
+ const end = skipBlock(src, i);
470
+ if (!prelude.startsWith("@") && prelude) {
471
+ const declarations = parseDeclarations(src.slice(i + 1, end - 1));
472
+ for (const raw of splitTopLevel(prelude, ",")) {
473
+ const selector = raw.trim();
474
+ if (selector) {
475
+ rules.push({ selector, declarations, specificity: specificity(selector), order: order++ });
476
+ }
477
+ }
478
+ }
479
+ i = end;
480
+ preludeStart = end;
481
+ }
482
+ return rules;
483
+ }
484
+ function appliesAsCss(style) {
485
+ const type = style.getAttribute("type")?.trim().toLowerCase();
486
+ if (type && type !== "text/css") return false;
487
+ const media = style.getAttribute("media")?.trim().toLowerCase();
488
+ if (!media) return true;
489
+ return media.split(",").some((m) => m.trim() === "all" || m.trim() === "screen");
490
+ }
491
+ var sheetCache = /* @__PURE__ */ new WeakMap();
492
+ function documentRules(doc) {
493
+ const cached = sheetCache.get(doc);
494
+ if (cached) return cached;
495
+ const rules = [];
496
+ const styles = doc.getElementsByTagNameNS("*", "style");
497
+ for (let i = 0; i < styles.length; i++) {
498
+ if (!appliesAsCss(styles[i])) continue;
499
+ rules.push(...parseStylesheet(styles[i].textContent ?? "", rules.length));
500
+ }
501
+ rules.sort((x, y) => compareSpecificity(x.specificity, y.specificity) || x.order - y.order);
502
+ sheetCache.set(doc, rules);
503
+ return rules;
504
+ }
505
+ function matches(el, selector) {
506
+ try {
507
+ return el.matches(selector);
508
+ } catch {
509
+ return false;
510
+ }
511
+ }
512
+ var elementCache = /* @__PURE__ */ new WeakMap();
513
+ function authorDeclarations(el) {
514
+ const cached = elementCache.get(el);
515
+ if (cached) return cached;
516
+ const rules = el.ownerDocument ? documentRules(el.ownerDocument) : [];
517
+ const matched = rules.filter((r) => matches(el, r.selector));
518
+ const inline = parseDeclarations(el.getAttribute("style") ?? "");
519
+ const out = /* @__PURE__ */ new Map();
520
+ for (const important of [false, true]) {
521
+ for (const r of matched) {
522
+ for (const d of r.declarations) if (d.important === important) out.set(d.prop, d.value);
523
+ }
524
+ for (const d of inline) if (d.important === important) out.set(d.prop, d.value);
525
+ }
526
+ elementCache.set(el, out);
527
+ return out;
528
+ }
529
+ function ownProp(el, prop) {
530
+ return authorDeclarations(el).get(prop) ?? el.getAttribute(prop);
531
+ }
532
+ function deriveStyle(parent, el) {
533
+ let next = null;
534
+ for (const prop of INHERITABLE) {
535
+ const own = ownProp(el, prop);
536
+ if (own == null || own === "inherit") continue;
537
+ if (!next) next = { ...parent };
538
+ next[prop] = own;
539
+ }
540
+ return next ?? parent;
541
+ }
542
+ function resolveCurrentColor(raw, style) {
543
+ if (raw == null) return null;
544
+ if (raw.trim().toLowerCase() === "currentcolor") return style["color"] ?? "#000000";
545
+ return raw;
546
+ }
547
+
252
548
  // src/elements.ts
253
549
  function collectElementsByTag(root, tags) {
254
550
  const out = /* @__PURE__ */ new Map();
@@ -437,7 +733,7 @@ function warnUnsupportedDefsChildren(svg, onWarn) {
437
733
  const child = root.children[i];
438
734
  const tag = child.tagName.toLowerCase();
439
735
  if (GRADIENT_TAGS.has(tag) || GRADIENT_TAGS.has(localName(tag))) continue;
440
- if (tag === "pattern" || tag === "marker" || tag === "clippath") continue;
736
+ if (tag === "pattern" || tag === "marker" || tag === "clippath" || tag === "style") continue;
441
737
  onWarn(`unsupported <defs> child: <${child.tagName}>`);
442
738
  }
443
739
  }
@@ -474,10 +770,10 @@ function readStops(el, onWarn) {
474
770
  if (c.tagName.toLowerCase() !== "stop") continue;
475
771
  const offsetRaw = parseRatio(c.getAttribute("offset") ?? "0");
476
772
  const offset = Number.isFinite(offsetRaw) ? offsetRaw : 0;
477
- const colorAttr = c.getAttribute("stop-color") ?? "#000000";
773
+ const colorAttr = ownProp(c, "stop-color") ?? "#000000";
478
774
  const parsed = parsePaintAttr(colorAttr);
479
775
  if (parsed && parsed.kind === "solid") {
480
- const opacityAttr = c.getAttribute("stop-opacity");
776
+ const opacityAttr = ownProp(c, "stop-opacity");
481
777
  const own = opacityAttr != null ? parseRatio(opacityAttr) : NaN;
482
778
  const alpha = Number.isFinite(own) ? own : parsed.alpha;
483
779
  stops.push({ offset, color: alpha < 1 ? applyAlpha(parsed.color, alpha) : parsed.color });
@@ -721,59 +1017,6 @@ function stopXml(s) {
721
1017
  return `<stop offset="${trimNumber(s.offset)}" stop-color="${s.color}"/>`;
722
1018
  }
723
1019
 
724
- // src/cascade.ts
725
- var EMPTY_STYLE = {};
726
- var INHERITABLE = [
727
- "fill",
728
- "fill-opacity",
729
- "fill-rule",
730
- "stroke",
731
- "stroke-width",
732
- "stroke-opacity",
733
- "stroke-linecap",
734
- "stroke-linejoin",
735
- "stroke-dasharray",
736
- "stroke-miterlimit",
737
- "marker-start",
738
- "marker-mid",
739
- "marker-end",
740
- "color",
741
- "font-size",
742
- "font-family",
743
- "font-weight",
744
- "font-style",
745
- "text-anchor",
746
- "letter-spacing",
747
- "text-decoration",
748
- "direction"
749
- ];
750
- function readStyleProp(el, prop) {
751
- const style = el.getAttribute("style");
752
- if (!style) return null;
753
- const re = new RegExp(`(?:^|;)\\s*${prop}\\s*:\\s*([^;]+)`, "i");
754
- const m = re.exec(style);
755
- if (!m) return null;
756
- return m[1].trim();
757
- }
758
- function ownProp(el, prop) {
759
- return readStyleProp(el, prop) ?? el.getAttribute(prop);
760
- }
761
- function deriveStyle(parent, el) {
762
- let next = null;
763
- for (const prop of INHERITABLE) {
764
- const own = ownProp(el, prop);
765
- if (own == null || own === "inherit") continue;
766
- if (!next) next = { ...parent };
767
- next[prop] = own;
768
- }
769
- return next ?? parent;
770
- }
771
- function resolveCurrentColor(raw, style) {
772
- if (raw == null) return null;
773
- if (raw.trim().toLowerCase() === "currentcolor") return style["color"] ?? "#000000";
774
- return raw;
775
- }
776
-
777
1020
  // src/parse.ts
778
1021
  var SUPPORTED_LEAF_TAGS = /* @__PURE__ */ new Set([
779
1022
  "rect",
@@ -794,7 +1037,8 @@ var IGNORED_TAGS = /* @__PURE__ */ new Set([
794
1037
  "clippath",
795
1038
  "title",
796
1039
  "desc",
797
- "metadata"
1040
+ "metadata",
1041
+ "style"
798
1042
  ]);
799
1043
  function parseSvg(svg, opts = {}) {
800
1044
  const warnings = [];
@@ -1546,6 +1790,11 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
1546
1790
  }
1547
1791
  return node;
1548
1792
  }
1793
+ var TEXT_TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
1794
+ function parseTextTransform(raw) {
1795
+ const v = raw?.trim().toLowerCase();
1796
+ return v !== void 0 && TEXT_TRANSFORMS.has(v) ? v : void 0;
1797
+ }
1549
1798
  function readTspanRun(el, gradients, style, onWarn) {
1550
1799
  const text = el.textContent ?? "";
1551
1800
  const run = { text };
@@ -1576,6 +1825,8 @@ function readTspanRun(el, gradients, style, onWarn) {
1576
1825
  if (decoration.underline) run.underline = true;
1577
1826
  if (decoration.strikethrough) run.strikethrough = true;
1578
1827
  if (decoration.overline) run.overline = true;
1828
+ const transform = parseTextTransform(ownProp(el, "text-transform"));
1829
+ if (transform) run.textTransform = transform;
1579
1830
  const tspanStyle = deriveStyle(style, el);
1580
1831
  const fillAttr = resolveCurrentColor(ownProp(el, "fill"), tspanStyle);
1581
1832
  if (fillAttr) {
@@ -1623,6 +1874,8 @@ function readTextStyle(style, onWarn) {
1623
1874
  if (decoration.underline) out.underline = true;
1624
1875
  if (decoration.strikethrough) out.strikethrough = true;
1625
1876
  if (decoration.overline) out.overline = true;
1877
+ const transform = parseTextTransform(style["text-transform"] ?? null);
1878
+ if (transform && transform !== "none") out.textTransform = transform;
1626
1879
  return out;
1627
1880
  }
1628
1881
  function readTextPaint(style, gradients, onWarn, nonScalingStroke = false) {
@@ -2188,6 +2441,9 @@ function textXml(node, registry, namespaces, warn) {
2188
2441
  }
2189
2442
  const decoration = textDecorationValue(style?.underline, style?.strikethrough, style?.overline);
2190
2443
  if (decoration) attrs.push(`text-decoration="${decoration}"`);
2444
+ if (style?.textTransform && style.textTransform !== "none") {
2445
+ attrs.push(`style="text-transform:${style.textTransform}"`);
2446
+ }
2191
2447
  if (style?.wrap) {
2192
2448
  attrs.push('data-weasel-wrap="true"');
2193
2449
  warn("text that wraps at its box width is written unwrapped: SVG text does not wrap, so other readers draw one line per hard break (weasel reads the wrap back)");
@@ -2250,6 +2506,7 @@ function runXml(run, registry, warn) {
2250
2506
  }
2251
2507
  }
2252
2508
  for (const a of coreStrokeAttrs(run.stroke, registry, warn)) attrs.push(a);
2509
+ if (run.textTransform) attrs.push(`style="text-transform:${run.textTransform}"`);
2253
2510
  const head = attrs.length > 0 ? `<tspan ${attrs.join(" ")}>` : "<tspan>";
2254
2511
  return `${head}${escapeText(run.text)}</tspan>`;
2255
2512
  }
@@ -2316,6 +2573,22 @@ function strokeDataFromSvg(stroke, box) {
2316
2573
  ...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {}
2317
2574
  };
2318
2575
  }
2576
+ function svgImageFromKit(image, pose) {
2577
+ const node = {
2578
+ kind: "image",
2579
+ href: image.src,
2580
+ x: pose.x,
2581
+ y: pose.y,
2582
+ width: pose.width,
2583
+ height: pose.height
2584
+ };
2585
+ if (image.source) node.source = { ...image.source };
2586
+ if (image.flipX) node.flipX = true;
2587
+ if (image.flipY) node.flipY = true;
2588
+ if (image.opacity != null) node.opacity = image.opacity;
2589
+ if (pose.rotation) node.rotation = pose.rotation;
2590
+ return node;
2591
+ }
2319
2592
  function fillInBoxFrame(fill, box) {
2320
2593
  const units = "units" in fill ? fill.units : void 0;
2321
2594
  return units === "world" ? fillToBoundsFrame(fill, box) : fill;
@@ -2397,7 +2670,10 @@ function svgNodesToKitDrafts(nodes, nextId) {
2397
2670
  data: {
2398
2671
  image: {
2399
2672
  src: n.href,
2400
- ...n.opacity != null ? { opacity: n.opacity } : {}
2673
+ ...n.opacity != null ? { opacity: n.opacity } : {},
2674
+ ...n.source ? { source: { ...n.source } } : {},
2675
+ ...n.flipX ? { flipX: true } : {},
2676
+ ...n.flipY ? { flipY: true } : {}
2401
2677
  }
2402
2678
  }
2403
2679
  });
@@ -2509,6 +2785,6 @@ async function unpackSvgFiles(files, ctx) {
2509
2785
  }
2510
2786
  }
2511
2787
 
2512
- export { IDENTITY_MATRIX, parseSvg, serializeSvg, strokeDataFromSvg, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
2788
+ export { IDENTITY_MATRIX, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
2513
2789
  //# sourceMappingURL=index.js.map
2514
2790
  //# sourceMappingURL=index.js.map