@pixodesk/svg-animator-web 1.0.16 → 1.0.18

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.cjs CHANGED
@@ -7,6 +7,7 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __pow = Math.pow;
10
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
12
  var __spreadValues = (a, b) => {
12
13
  for (var prop in b || (b = {}))
@@ -62,6 +63,7 @@ __export(index_exports, {
62
63
  PxAttrValueSchema: () => PxAttrValueSchema,
63
64
  PxBezierPathSchema: () => PxBezierPathSchema,
64
65
  PxBindingSchema: () => PxBindingSchema,
66
+ PxCloneEffectSchema: () => PxCloneEffectSchema,
65
67
  PxDefsSchema: () => PxDefsSchema,
66
68
  PxEasingOrRefSchema: () => PxEasingOrRefSchema,
67
69
  PxEffectsSchema: () => PxEffectsSchema,
@@ -77,12 +79,12 @@ __export(index_exports, {
77
79
  PxNodeBase: () => PxNodeBase,
78
80
  PxNodeSchema: () => PxNodeSchema,
79
81
  PxPropertyAnimationSchema: () => PxPropertyAnimationSchema,
80
- PxRefEffectSchema: () => PxRefEffectSchema,
81
82
  PxRepeaterEffectSchema: () => PxRepeaterEffectSchema,
82
83
  PxRetimeEffectSchema: () => PxRetimeEffectSchema,
83
84
  PxStrokeGradientEffectSchema: () => PxStrokeGradientEffectSchema,
84
85
  PxSvgNodeExtra: () => PxSvgNodeExtra,
85
86
  PxTextAlongPathEffectSchema: () => PxTextAlongPathEffectSchema,
87
+ PxTextEffectSchema: () => PxTextEffectSchema,
86
88
  PxTransformPartsSchema: () => PxTransformPartsSchema,
87
89
  PxTransformValueSchema: () => PxTransformValueSchema,
88
90
  PxTransformationEffectSchema: () => PxTransformationEffectSchema,
@@ -110,9 +112,13 @@ __export(index_exports, {
110
112
  getNormalizedProps: () => getNormalizedProps,
111
113
  isPxElementFileFormat: () => isPxElementFileFormat,
112
114
  isPxElementFileFormatDeep: () => isPxElementFileFormatDeep,
115
+ jsonElementFactory: () => jsonElementFactory,
113
116
  loadTagAnimators: () => loadTagAnimators,
114
117
  materialiseAllInTree: () => materialiseAllInTree,
115
118
  materialiseAnimatedUseInstances: () => materialiseAnimatedUseInstances,
119
+ materialiseGlyphText: () => materialiseGlyphText,
120
+ materialiseGlyphTextAlongPath: () => materialiseGlyphTextAlongPath,
121
+ materialiseGlyphTextHorizontal: () => materialiseGlyphTextHorizontal,
116
122
  materialiseInternalLoopsInPropAnim: () => materialiseInternalLoopsInPropAnim,
117
123
  materialiseInternalLoopsInTree: () => materialiseInternalLoopsInTree,
118
124
  materialiseMotionPathInPropAnim: () => materialiseMotionPathInPropAnim,
@@ -144,7 +150,7 @@ function readAnimatable(raw) {
144
150
  if (typeof raw === "object") {
145
151
  const obj = raw;
146
152
  if (obj.keyframes) {
147
- return { kind: "animated" /* Animated */, keyframes: obj.keyframes, autoOrient: obj.autoOrient };
153
+ return { kind: "animated" /* Animated */, keyframes: obj.keyframes, autoOrient: obj.autoOrient, loop: obj.loop };
148
154
  }
149
155
  if (obj.value !== void 0) return { kind: "static" /* Static */, value: obj.value };
150
156
  }
@@ -234,6 +240,7 @@ function wrapTransformPart(inner, part, raw, ctx) {
234
240
  if (v.kind === "animated" /* Animated */) {
235
241
  const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
236
242
  if (v.autoOrient) animTr.autoOrient = true;
243
+ if (v.loop !== void 0) animTr.loop = v.loop;
237
244
  return {
238
245
  type: "g",
239
246
  animate: { transform: animTr },
@@ -252,9 +259,11 @@ function wrapOrigin(inner, raw, invert) {
252
259
  return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
253
260
  }
254
261
  if (v.kind === "animated" /* Animated */) {
262
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) };
263
+ if (v.loop !== void 0) animTr.loop = v.loop;
255
264
  return {
256
265
  type: "g",
257
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
266
+ animate: { transform: animTr },
258
267
  children: [inner]
259
268
  };
260
269
  }
@@ -264,8 +273,8 @@ function wrapOrigin(inner, raw, invert) {
264
273
  // src/effects/contentRefSplit.ts
265
274
  function identifyContentRefTargets(node, ctx, allocator) {
266
275
  var _a, _b, _c;
267
- if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
268
- const baseId = node.effects.ref.baseId;
276
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
277
+ const baseId = node.effects.clone.baseId;
269
278
  if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
270
279
  ctx.contentRefInnerIds.set(baseId, allocator(baseId));
271
280
  }
@@ -313,6 +322,8 @@ function liftBodyTranslate(node, transformation) {
313
322
  });
314
323
  const outerAnimTr = { keyframes: outerKfs };
315
324
  if (animTr.autoOrient) outerAnimTr.autoOrient = true;
325
+ const srcLoop = animTr.loop;
326
+ if (srcLoop !== void 0) outerAnimTr.loop = srcLoop;
316
327
  out.animate = { transform: outerAnimTr };
317
328
  const innerHasPivotedPart = kfs.some((kf) => {
318
329
  const v = kf.value || {};
@@ -334,7 +345,9 @@ function liftBodyTranslate(node, transformation) {
334
345
  delete node.animate.transform;
335
346
  if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
336
347
  } else {
337
- node.animate.transform = { keyframes: innerKfs };
348
+ const innerAnimTr = { keyframes: innerKfs };
349
+ if (srcLoop !== void 0) innerAnimTr.loop = srcLoop;
350
+ node.animate.transform = innerAnimTr;
338
351
  }
339
352
  didLiftAnimate = true;
340
353
  }
@@ -998,10 +1011,22 @@ var PxTriggerSchema = implementsInterface()(px.object({
998
1011
  outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
999
1012
  scrollIntoViewThreshold: px.number().optional()
1000
1013
  }));
1014
+ var PxGlyphSchema = implementsInterface()(px.object({
1015
+ width: px.number(),
1016
+ d: px.string()
1017
+ }));
1018
+ var PxGlyphFontSchema = implementsInterface()(px.object({
1019
+ fFamily: px.string(),
1020
+ style: px.string(),
1021
+ ascent: px.number(),
1022
+ unitsPerEm: px.number(),
1023
+ glyphs: px.record(PxGlyphSchema)
1024
+ }));
1001
1025
  var PxDefsSchema = implementsInterface()(px.object({
1002
1026
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1003
1027
  animations: px.record(PxAnimationDefinitionSchema).optional(),
1004
- styles: px.record(px.any()).optional()
1028
+ styles: px.record(px.any()).optional(),
1029
+ glyphs: px.record(PxGlyphFontSchema).optional()
1005
1030
  }));
1006
1031
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
1007
1032
  mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
@@ -1074,9 +1099,10 @@ var PxRetimeEffectSchema = implementsInterface()(px.object({
1074
1099
  stretch: px.number().optional(),
1075
1100
  timeCrop: px.tuple([px.number(), px.number()]).optional()
1076
1101
  }));
1077
- var PxRefEffectSchema = implementsInterface()(px.object({
1102
+ var PxCloneEffectSchema = implementsInterface()(px.object({
1103
+ type: px.string().optional(),
1078
1104
  baseId: px.string().optional(),
1079
- type: px.string().optional()
1105
+ retime: PxRetimeEffectSchema.optional()
1080
1106
  }));
1081
1107
  var PxGradientUnits = {
1082
1108
  userSpaceOnUse: "userSpaceOnUse",
@@ -1121,17 +1147,20 @@ var PxTextAlongPathEffectSchema = implementsInterface()(px.object({
1121
1147
  startOffset: PxAnimatableNumberSchema.optional(),
1122
1148
  textLength: PxAnimatableNumberSchema.optional()
1123
1149
  }));
1150
+ var PxTextEffectSchema = implementsInterface()(px.object({
1151
+ useGlyphs: px.boolean().optional()
1152
+ }));
1124
1153
  var PxEffectsSchema = implementsInterface()(px.object({
1125
1154
  transformation: PxTransformationEffectSchema.optional(),
1126
1155
  repeater: PxRepeaterEffectSchema.optional(),
1127
1156
  maskedBy: PxMaskedByEffectSchema.optional(),
1128
1157
  trimPath: PxTrimPathEffectSchema.optional(),
1129
- retime: PxRetimeEffectSchema.optional(),
1158
+ clone: PxCloneEffectSchema.optional(),
1130
1159
  isCombinedShape: px.boolean().optional(),
1131
- ref: PxRefEffectSchema.optional(),
1132
1160
  fillGradient: PxFillGradientEffectSchema.optional(),
1133
1161
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
1134
- textAlongPath: PxTextAlongPathEffectSchema.optional()
1162
+ textAlongPath: PxTextAlongPathEffectSchema.optional(),
1163
+ text: PxTextEffectSchema.optional()
1135
1164
  }));
1136
1165
  function validateNodeEffects(root, opts) {
1137
1166
  const warnings = [];
@@ -1356,6 +1385,7 @@ function buildStopChildren(stops, ctx) {
1356
1385
  const animBlock = stops;
1357
1386
  const kfs = animBlock.keyframes;
1358
1387
  if (!Array.isArray(kfs) || !kfs.length) return [];
1388
+ const loopFromSource = animBlock.loop;
1359
1389
  let stopCount = 0;
1360
1390
  for (const kf of kfs) {
1361
1391
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -1368,7 +1398,7 @@ function buildStopChildren(stops, ctx) {
1368
1398
  const s = (_d = (_c = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _c : prevDefinedStop(kfs, 0, i)) != null ? _d : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
1369
1399
  baselineStops.push({ offset: s.offset, color: s.color });
1370
1400
  }
1371
- return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx));
1401
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
1372
1402
  }
1373
1403
  function staticStopNode(s) {
1374
1404
  return {
@@ -1377,7 +1407,7 @@ function staticStopNode(s) {
1377
1407
  stopColor: s.color
1378
1408
  };
1379
1409
  }
1380
- function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
1410
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
1381
1411
  var _a, _b, _c, _d, _e;
1382
1412
  const colorKfs = [];
1383
1413
  const offsetKfs = [];
@@ -1402,8 +1432,14 @@ function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
1402
1432
  stopColor: baseline.color
1403
1433
  };
1404
1434
  const animate = {};
1405
- if (colorKfs.length) animate.stopColor = { keyframes: colorKfs };
1406
- if (offsetVaries && offsetKfs.length) animate.offset = { keyframes: offsetKfs };
1435
+ if (colorKfs.length) {
1436
+ animate.stopColor = { keyframes: colorKfs };
1437
+ if (loop !== void 0) animate.stopColor.loop = loop;
1438
+ }
1439
+ if (offsetVaries && offsetKfs.length) {
1440
+ animate.offset = { keyframes: offsetKfs };
1441
+ if (loop !== void 0) animate.offset.loop = loop;
1442
+ }
1407
1443
  if (Object.keys(animate).length) stop.animate = animate;
1408
1444
  return stop;
1409
1445
  }
@@ -1468,9 +1504,11 @@ function wrapInversePart(inner, part, raw, origin, ctx) {
1468
1504
  return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
1469
1505
  }
1470
1506
  if (v.kind === "animated" /* Animated */) {
1507
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) };
1508
+ if (v.loop !== void 0) animTr.loop = v.loop;
1471
1509
  return {
1472
1510
  type: "g",
1473
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
1511
+ animate: { transform: animTr },
1474
1512
  children: [inner]
1475
1513
  };
1476
1514
  }
@@ -1507,10 +1545,16 @@ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
1507
1545
  scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
1508
1546
  }
1509
1547
  }
1548
+ const srcLoop = animTr == null ? void 0 : animTr.loop;
1549
+ const withLoop = (kfs2) => {
1550
+ const block = { keyframes: kfs2 };
1551
+ if (srcLoop !== void 0) block.loop = srcLoop;
1552
+ return block;
1553
+ };
1510
1554
  let n = inner;
1511
- if (translateKfs.length) n = { type: "g", animate: { transform: { keyframes: translateKfs } }, children: [n] };
1512
- if (rotateKfs.length) n = { type: "g", animate: { transform: { keyframes: rotateKfs } }, children: [n] };
1513
- if (scaleKfs.length) n = { type: "g", animate: { transform: { keyframes: scaleKfs } }, children: [n] };
1555
+ if (translateKfs.length) n = { type: "g", animate: { transform: withLoop(translateKfs) }, children: [n] };
1556
+ if (rotateKfs.length) n = { type: "g", animate: { transform: withLoop(rotateKfs) }, children: [n] };
1557
+ if (scaleKfs.length) n = { type: "g", animate: { transform: withLoop(scaleKfs) }, children: [n] };
1514
1558
  return n;
1515
1559
  }
1516
1560
  function nodeHasBodyTransform(node) {
@@ -1744,16 +1788,18 @@ function parseTranslateOnlyFromString(s, ctx) {
1744
1788
 
1745
1789
  // src/effects/refEffect.ts
1746
1790
  var CONTENT_SUBREF = "content";
1747
- function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
1748
- if (ref) {
1749
- const baseId = ref.baseId;
1750
- if (!baseId) {
1751
- ctx.errors.push("ref: missing baseId");
1752
- } else {
1753
- const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
1754
- node.href = "#" + targetId;
1755
- }
1791
+ function applyRefHref(node, clone2, ctx) {
1792
+ if (!clone2) return;
1793
+ const baseId = clone2.baseId;
1794
+ if (!baseId) {
1795
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
1796
+ return;
1756
1797
  }
1798
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
1799
+ node.href = "#" + targetId;
1800
+ }
1801
+ function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
1802
+ applyRefHref(node, clone2, ctx);
1757
1803
  return applyTransformationEffect(node, transformation, ctx);
1758
1804
  }
1759
1805
 
@@ -1864,22 +1910,32 @@ function concatRetime(child, parent) {
1864
1910
  stretch: parent.stretch * child.stretch
1865
1911
  };
1866
1912
  }
1867
- function applyAllRetimeEffects(root, ctx) {
1913
+ function readCloneRetime(n) {
1914
+ var _a, _b;
1915
+ return (_b = (_a = n.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.retime;
1916
+ }
1917
+ function clearCloneRetime(n) {
1868
1918
  var _a;
1919
+ const clone2 = (_a = n.effects) == null ? void 0 : _a.clone;
1920
+ if (!clone2) return;
1921
+ delete clone2.retime;
1922
+ if (Object.keys(clone2).length === 0) delete n.effects.clone;
1923
+ if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;
1924
+ }
1925
+ function applyAllRetimeEffects(root, ctx) {
1869
1926
  ctx.idMap.clear();
1870
1927
  indexById(root, ctx.idMap);
1871
1928
  const sites = [];
1872
1929
  const collect = (n) => {
1873
- var _a2, _b;
1874
- if ((_a2 = n.effects) == null ? void 0 : _a2.retime) sites.push(n);
1875
- (_b = n.children) == null ? void 0 : _b.forEach(collect);
1930
+ var _a;
1931
+ if (readCloneRetime(n)) sites.push(n);
1932
+ (_a = n.children) == null ? void 0 : _a.forEach(collect);
1876
1933
  };
1877
1934
  collect(root);
1878
1935
  for (const useNode of sites) {
1879
- const retime = (_a = useNode.effects) == null ? void 0 : _a.retime;
1936
+ const retime = readCloneRetime(useNode);
1880
1937
  if (!retime) continue;
1881
- delete useNode.effects.retime;
1882
- if (Object.keys(useNode.effects).length === 0) delete useNode.effects;
1938
+ clearCloneRetime(useNode);
1883
1939
  materialiseRetime(useNode, asRetime(retime), ctx);
1884
1940
  }
1885
1941
  }
@@ -1903,7 +1959,6 @@ function materialiseRetime(useNode, retime, ctx) {
1903
1959
  }
1904
1960
  }
1905
1961
  function buildChainClone(targetId, accum, ctx, chain) {
1906
- var _a, _b;
1907
1962
  if (chain.has(targetId)) {
1908
1963
  ctx.errors.push('retime: loop via "' + targetId + '"');
1909
1964
  return void 0;
@@ -1920,25 +1975,43 @@ function buildChainClone(targetId, accum, ctx, chain) {
1920
1975
  } else {
1921
1976
  remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
1922
1977
  }
1923
- if ((_a = cloneNode.effects) == null ? void 0 : _a.retime) {
1924
- delete cloneNode.effects.retime;
1925
- if (Object.keys(cloneNode.effects).length === 0) delete cloneNode.effects;
1926
- }
1978
+ clearCloneRetime(cloneNode);
1927
1979
  if (target.type === "use" && target.href) {
1928
1980
  const subId = stripHash(target.href);
1929
1981
  if (subId) {
1930
- const innerRetime = (_b = target.effects) == null ? void 0 : _b.retime;
1982
+ const innerRetime = readCloneRetime(target);
1931
1983
  const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
1932
1984
  const subChain = new Set(chain);
1933
1985
  subChain.add(targetId);
1934
1986
  const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
1935
1987
  if (subId2) cloneNode.href = "#" + subId2;
1936
1988
  }
1989
+ } else {
1990
+ materialiseNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);
1937
1991
  }
1938
1992
  ctx.defs.push(cloneNode);
1939
1993
  if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
1940
1994
  return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
1941
1995
  }
1996
+ function materialiseNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
1997
+ const visit = (n) => {
1998
+ var _a;
1999
+ const retime = readCloneRetime(n);
2000
+ if (n.type === "use" && retime && n.href) {
2001
+ const subId = stripHash(n.href);
2002
+ if (subId) {
2003
+ const subAccum = concatRetime(asRetime(retime), accum);
2004
+ const subChain = new Set(chain);
2005
+ subChain.add(parentTargetId);
2006
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
2007
+ if (subId2) n.href = "#" + subId2;
2008
+ }
2009
+ clearCloneRetime(n);
2010
+ }
2011
+ (_a = n.children) == null ? void 0 : _a.forEach(visit);
2012
+ };
2013
+ visit(node);
2014
+ }
1942
2015
  function remapKeyframeTimes(node, start, stretch) {
1943
2016
  var _a;
1944
2017
  remapKeyframeTimesOnly(node, start, stretch);
@@ -1983,12 +2056,14 @@ function applyAnimatableNumber(node, attrName, raw) {
1983
2056
  node[attrName] = String(raw);
1984
2057
  return;
1985
2058
  }
1986
- if (typeof raw === "object") {
2059
+ if (typeof raw === "object" && raw !== null) {
1987
2060
  const obj = raw;
1988
2061
  if (Array.isArray(obj.keyframes)) {
1989
2062
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
1990
2063
  const animate = __spreadValues({}, prevAnimate || {});
1991
- animate[attrName] = { keyframes: obj.keyframes };
2064
+ const block = { keyframes: obj.keyframes };
2065
+ if (obj.loop !== void 0) block.loop = obj.loop;
2066
+ animate[attrName] = block;
1992
2067
  node.animate = animate;
1993
2068
  return;
1994
2069
  }
@@ -1999,6 +2074,61 @@ function applyAnimatableNumber(node, attrName, raw) {
1999
2074
  }
2000
2075
  }
2001
2076
 
2077
+ // src/effects/elementFactory.ts
2078
+ var jsonElementFactory = (type, props, children) => {
2079
+ const node = { type };
2080
+ for (const k in props) if (props[k] !== void 0) node[k] = props[k];
2081
+ const arr = Array.isArray(children) ? children.filter((c) => c != null) : children != null ? [children] : [];
2082
+ if (arr.length) node.children = arr;
2083
+ return node;
2084
+ };
2085
+
2086
+ // src/effects/glyphPathBake.ts
2087
+ function fmt(v, decimals) {
2088
+ return Math.round(v) === v ? "" + Math.round(v) : v.toFixed(decimals);
2089
+ }
2090
+ function pack(nums, decimals) {
2091
+ let s = "";
2092
+ for (let i = 0; i < nums.length; i++) {
2093
+ const str2 = fmt(nums[i], decimals);
2094
+ if (i > 0 && str2.charCodeAt(0) !== 45) s += " ";
2095
+ s += str2;
2096
+ }
2097
+ return s;
2098
+ }
2099
+ function apply(m, x, y) {
2100
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
2101
+ }
2102
+ var TOKEN_RE = /([MLCQZ])|(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi;
2103
+ function transformPathData(d, m, decimals = 2) {
2104
+ const tokens = [];
2105
+ let match;
2106
+ TOKEN_RE.lastIndex = 0;
2107
+ while ((match = TOKEN_RE.exec(d)) !== null) tokens.push(match[0]);
2108
+ let out = "";
2109
+ let i = 0;
2110
+ const num2 = () => parseFloat(tokens[i++]);
2111
+ while (i < tokens.length) {
2112
+ const cmd = tokens[i++];
2113
+ if (cmd === "M" || cmd === "L") {
2114
+ const [x, y] = apply(m, num2(), num2());
2115
+ out += cmd + pack([x, y], decimals);
2116
+ } else if (cmd === "C") {
2117
+ const [x1, y1] = apply(m, num2(), num2());
2118
+ const [x2, y2] = apply(m, num2(), num2());
2119
+ const [x, y] = apply(m, num2(), num2());
2120
+ out += "C" + pack([x1, y1, x2, y2, x, y], decimals);
2121
+ } else if (cmd === "Q") {
2122
+ const [x1, y1] = apply(m, num2(), num2());
2123
+ const [x, y] = apply(m, num2(), num2());
2124
+ out += "Q" + pack([x1, y1, x, y], decimals);
2125
+ } else if (cmd === "Z" || cmd === "z") {
2126
+ out += "Z";
2127
+ }
2128
+ }
2129
+ return out;
2130
+ }
2131
+
2002
2132
  // src/PxAnimatorUtil.ts
2003
2133
  function bezierToSvgPath(path) {
2004
2134
  var _a, _b, _c, _d;
@@ -2371,6 +2501,24 @@ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
2371
2501
  }
2372
2502
  return { ts, ds };
2373
2503
  }
2504
+ function bezier2D_tForDistance(lut, distance) {
2505
+ const { ts, ds } = lut;
2506
+ const last = ds.length - 1;
2507
+ if (distance <= 0) return ts[0];
2508
+ if (distance >= ds[last]) return ts[last];
2509
+ let lo = 1;
2510
+ let hi = last;
2511
+ while (lo < hi) {
2512
+ const mid = lo + hi >>> 1;
2513
+ if (ds[mid] < distance) lo = mid + 1;
2514
+ else hi = mid;
2515
+ }
2516
+ const dPrev = ds[hi - 1];
2517
+ const dCur = ds[hi];
2518
+ const span = dCur - dPrev;
2519
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
2520
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
2521
+ }
2374
2522
  function bezier2D_arcAtT(lut, t) {
2375
2523
  const { ts, ds } = lut;
2376
2524
  const last = ts.length - 1;
@@ -2393,6 +2541,437 @@ function invertEasing(easing) {
2393
2541
  return cubicBezier(flipped);
2394
2542
  }
2395
2543
 
2544
+ // src/effects/pathSampler.ts
2545
+ var LUT_STEPS = 48;
2546
+ var CMD_RE = /[MmLlHhVvCcSsQqTtAaZz]/;
2547
+ function tokenize(d) {
2548
+ const tokens = [];
2549
+ const re = /([MmLlHhVvCcSsQqTtAaZz])|(-?\d*\.?\d+(?:[eE][-+]?\d+)?)/g;
2550
+ let m;
2551
+ while ((m = re.exec(d)) !== null) tokens.push(m[0]);
2552
+ return tokens;
2553
+ }
2554
+ function quadToCubic(P0, Qc, P3) {
2555
+ return {
2556
+ P1: [P0[0] + 2 / 3 * (Qc[0] - P0[0]), P0[1] + 2 / 3 * (Qc[1] - P0[1])],
2557
+ P2: [P3[0] + 2 / 3 * (Qc[0] - P3[0]), P3[1] + 2 / 3 * (Qc[1] - P3[1])]
2558
+ };
2559
+ }
2560
+ function parseCubics(d) {
2561
+ const tokens = tokenize(d);
2562
+ const segs = [];
2563
+ let i = 0;
2564
+ let cx = 0, cy = 0;
2565
+ let sx = 0, sy = 0;
2566
+ let pcx = 0, pcy = 0;
2567
+ let pqx = 0, pqy = 0;
2568
+ let prevCmd = "";
2569
+ const num2 = () => parseFloat(tokens[i++]);
2570
+ const push = (P1, P2, P3) => {
2571
+ const P0 = [cx, cy];
2572
+ const lut = bezier2D_arcLengthLUT(P0, P1, P2, P3, LUT_STEPS);
2573
+ segs.push({ P0, P1, P2, P3, lut, len: lut.ds[lut.ds.length - 1] });
2574
+ cx = P3[0];
2575
+ cy = P3[1];
2576
+ };
2577
+ const pushLine = (x, y) => {
2578
+ push(
2579
+ [cx + (x - cx) / 3, cy + (y - cy) / 3],
2580
+ [cx + 2 * (x - cx) / 3, cy + 2 * (y - cy) / 3],
2581
+ [x, y]
2582
+ );
2583
+ };
2584
+ while (i < tokens.length) {
2585
+ let cmd = tokens[i];
2586
+ if (CMD_RE.test(cmd)) i++;
2587
+ else cmd = prevCmd === "M" ? "L" : prevCmd === "m" ? "l" : prevCmd;
2588
+ const rel = cmd >= "a";
2589
+ const U = cmd.toUpperCase();
2590
+ if (U === "Z") {
2591
+ pushLine(sx, sy);
2592
+ cx = sx;
2593
+ cy = sy;
2594
+ prevCmd = cmd;
2595
+ continue;
2596
+ }
2597
+ if (U === "M") {
2598
+ const x = num2() + (rel ? cx : 0), y = num2() + (rel ? cy : 0);
2599
+ cx = x;
2600
+ cy = y;
2601
+ sx = x;
2602
+ sy = y;
2603
+ prevCmd = cmd;
2604
+ continue;
2605
+ }
2606
+ if (U === "L") {
2607
+ pushLine(num2() + (rel ? cx : 0), num2() + (rel ? cy : 0));
2608
+ } else if (U === "H") {
2609
+ pushLine(num2() + (rel ? cx : 0), cy);
2610
+ } else if (U === "V") {
2611
+ pushLine(cx, num2() + (rel ? cy : 0));
2612
+ } else if (U === "C") {
2613
+ const p1 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2614
+ const p2 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2615
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2616
+ pcx = p2[0];
2617
+ pcy = p2[1];
2618
+ push(p1, p2, p3);
2619
+ } else if (U === "S") {
2620
+ const smooth = prevCmd.toUpperCase() === "C" || prevCmd.toUpperCase() === "S";
2621
+ const p1 = smooth ? [2 * cx - pcx, 2 * cy - pcy] : [cx, cy];
2622
+ const p2 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2623
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2624
+ pcx = p2[0];
2625
+ pcy = p2[1];
2626
+ push(p1, p2, p3);
2627
+ } else if (U === "Q") {
2628
+ const qc = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2629
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2630
+ pqx = qc[0];
2631
+ pqy = qc[1];
2632
+ const { P1, P2 } = quadToCubic([cx, cy], qc, p3);
2633
+ push(P1, P2, p3);
2634
+ } else if (U === "T") {
2635
+ const smooth = prevCmd.toUpperCase() === "Q" || prevCmd.toUpperCase() === "T";
2636
+ const qc = smooth ? [2 * cx - pqx, 2 * cy - pqy] : [cx, cy];
2637
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2638
+ pqx = qc[0];
2639
+ pqy = qc[1];
2640
+ const { P1, P2 } = quadToCubic([cx, cy], qc, p3);
2641
+ push(P1, P2, p3);
2642
+ } else if (U === "A") {
2643
+ i += 5;
2644
+ pushLine(num2() + (rel ? cx : 0), num2() + (rel ? cy : 0));
2645
+ } else {
2646
+ i++;
2647
+ continue;
2648
+ }
2649
+ prevCmd = cmd;
2650
+ }
2651
+ return segs.length ? segs : null;
2652
+ }
2653
+ function clamp2(v, lo, hi) {
2654
+ return v < lo ? lo : v > hi ? hi : v;
2655
+ }
2656
+ function createPathSampler(d) {
2657
+ const segs = parseCubics(d);
2658
+ if (!segs) return null;
2659
+ const cum = new Float64Array(segs.length + 1);
2660
+ for (let k = 0; k < segs.length; k++) cum[k + 1] = cum[k] + segs[k].len;
2661
+ const totalLength = cum[segs.length];
2662
+ const start = segs[0].P0, end = segs[segs.length - 1].P3;
2663
+ const closed = Math.hypot(end[0] - start[0], end[1] - start[1]) < 1e-3;
2664
+ const sampleOn = (dist) => {
2665
+ let k = 0;
2666
+ while (k < segs.length - 1 && dist > cum[k + 1]) k++;
2667
+ const seg = segs[k];
2668
+ const local = dist - cum[k];
2669
+ const t = seg.len > 0 ? bezier2D_tForDistance(seg.lut, local) : 0;
2670
+ const [x, y] = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2671
+ const [dx, dy] = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2672
+ return { x, y, angle: Math.atan2(dy, dx) };
2673
+ };
2674
+ return {
2675
+ totalLength,
2676
+ sampleAtDistance(dist) {
2677
+ if (!closed && (dist < 0 || dist > totalLength)) {
2678
+ const edge = dist < 0 ? 0 : totalLength;
2679
+ const p = sampleOn(edge);
2680
+ const over = dist - edge;
2681
+ return { x: p.x + Math.cos(p.angle) * over, y: p.y + Math.sin(p.angle) * over, angle: p.angle };
2682
+ }
2683
+ return sampleOn(clamp2(dist, 0, totalLength));
2684
+ }
2685
+ };
2686
+ }
2687
+
2688
+ // src/effects/textGlyphsEffect.ts
2689
+ var DEFAULT_FONT_SIZE = 16;
2690
+ var TEXT_ATTR_KEYS = [
2691
+ "fontFamily",
2692
+ "fontSize",
2693
+ "fontWeight",
2694
+ "fontStyle",
2695
+ "textAnchor",
2696
+ "letterSpacing",
2697
+ "wordSpacing",
2698
+ "textDecoration",
2699
+ "textTransform",
2700
+ "whiteSpace",
2701
+ "x",
2702
+ "y",
2703
+ "dx",
2704
+ "dy",
2705
+ "lengthAdjust",
2706
+ "fill",
2707
+ "stroke",
2708
+ "strokeWidth",
2709
+ "effects",
2710
+ TEXT_ATTR,
2711
+ TEXT_CONTENT_ATTR,
2712
+ "xml:space"
2713
+ ];
2714
+ function parseLen(v) {
2715
+ if (typeof v === "number") return v;
2716
+ if (typeof v === "string") {
2717
+ const n = parseFloat(v);
2718
+ return isNaN(n) ? void 0 : n;
2719
+ }
2720
+ return void 0;
2721
+ }
2722
+ function str(v) {
2723
+ return typeof v === "string" ? v : void 0;
2724
+ }
2725
+ function resolveStyle(node, parent) {
2726
+ var _a, _b, _c, _d, _e, _f, _g;
2727
+ return {
2728
+ fontFamily: (_a = str(node.fontFamily)) != null ? _a : parent.fontFamily,
2729
+ fontSize: (_b = parseLen(node.fontSize)) != null ? _b : parent.fontSize,
2730
+ fill: (_c = node.fill) != null ? _c : parent.fill,
2731
+ stroke: (_d = node.stroke) != null ? _d : parent.stroke,
2732
+ strokeWidth: (_e = node.strokeWidth) != null ? _e : parent.strokeWidth,
2733
+ letterSpacing: (_f = parseLen(node.letterSpacing)) != null ? _f : parent.letterSpacing,
2734
+ wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing
2735
+ };
2736
+ }
2737
+ function rootStyleOf(node) {
2738
+ var _a, _b, _c;
2739
+ return {
2740
+ fontFamily: str(node.fontFamily),
2741
+ fontSize: (_a = parseLen(node.fontSize)) != null ? _a : DEFAULT_FONT_SIZE,
2742
+ fill: node.fill,
2743
+ stroke: node.stroke,
2744
+ strokeWidth: node.strokeWidth,
2745
+ letterSpacing: (_b = parseLen(node.letterSpacing)) != null ? _b : 0,
2746
+ wordSpacing: (_c = parseLen(node.wordSpacing)) != null ? _c : 0
2747
+ };
2748
+ }
2749
+ function paintOf(s) {
2750
+ const p = {};
2751
+ if (s.fill !== void 0) p.fill = s.fill;
2752
+ if (s.stroke !== void 0) p.stroke = s.stroke;
2753
+ if (s.strokeWidth !== void 0) p.strokeWidth = s.strokeWidth;
2754
+ return p;
2755
+ }
2756
+ function glyphFontFor(s, glyphs, soleFont, warnings) {
2757
+ var _a;
2758
+ const gf = s.fontFamily ? glyphs[s.fontFamily] : soleFont;
2759
+ if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a = s.fontFamily) != null ? _a : "") + '"');
2760
+ return gf;
2761
+ }
2762
+ function soleFontOf(glyphs) {
2763
+ const names = Object.keys(glyphs);
2764
+ return names.length === 1 ? glyphs[names[0]] : void 0;
2765
+ }
2766
+ function materialiseGlyphTextHorizontal(node, opts) {
2767
+ var _a, _b, _c, _d;
2768
+ const { glyphs, create = jsonElementFactory, warnings } = opts;
2769
+ const soleFont = soleFontOf(glyphs);
2770
+ const pen = { x: (_a = parseLen(node.x)) != null ? _a : 0, y: (_b = parseLen(node.y)) != null ? _b : 0 };
2771
+ const placements = [];
2772
+ const lines = [{ start: pen.x, end: pen.x }];
2773
+ let line = 0;
2774
+ const renderChars = (content, s) => {
2775
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2776
+ if (!gf) return;
2777
+ const scale = s.fontSize / gf.unitsPerEm;
2778
+ const paint = paintOf(s);
2779
+ for (let i = 0; i < content.length; i++) {
2780
+ const ch = content.charAt(i);
2781
+ const g = gf.glyphs[ch];
2782
+ if (g && g.d) placements.push({ glyphD: g.d, m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });
2783
+ pen.x += (g ? g.width : 0) * scale + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2784
+ lines[line].end = pen.x;
2785
+ }
2786
+ };
2787
+ const walk = (el, parentStyle) => {
2788
+ var _a2, _b2, _c2, _d2;
2789
+ const s = resolveStyle(el, parentStyle);
2790
+ const x = parseLen(el.x);
2791
+ const y = parseLen(el.y);
2792
+ if (x !== void 0) {
2793
+ pen.x = x;
2794
+ line = lines.length;
2795
+ lines.push({ start: pen.x, end: pen.x });
2796
+ }
2797
+ if (y !== void 0) pen.y = y;
2798
+ pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
2799
+ pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
2800
+ const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
2801
+ if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
2802
+ if (el.children) for (const ch of el.children) walk(ch, s);
2803
+ };
2804
+ const rootStyle = rootStyleOf(node);
2805
+ if (node.children) for (const ch of node.children) walk(ch, rootStyle);
2806
+ const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
2807
+ if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
2808
+ const anchor = str(node.textAnchor);
2809
+ if (anchor === "middle" || anchor === "end") {
2810
+ for (const p of placements) {
2811
+ const w = lines[p.line].end - lines[p.line].start;
2812
+ const shift = anchor === "middle" ? -w / 2 : -w;
2813
+ p.m = [p.scale, 0, 0, p.scale, p.x + shift, p.y];
2814
+ }
2815
+ }
2816
+ return toGroup(node, buildPaths(placements, create, warnings), create);
2817
+ }
2818
+ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
2819
+ const cells = [];
2820
+ let adv = 0;
2821
+ const walk = (el, parentStyle) => {
2822
+ var _a, _b;
2823
+ const s = resolveStyle(el, parentStyle);
2824
+ const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
2825
+ if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
2826
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2827
+ if (gf) {
2828
+ const scale = s.fontSize / gf.unitsPerEm;
2829
+ const paint = paintOf(s);
2830
+ for (let i = 0; i < content.length; i++) {
2831
+ const ch = content.charAt(i);
2832
+ const g = gf.glyphs[ch];
2833
+ const glyphAdv = (g ? g.width : 0) * scale;
2834
+ if (g && g.d) cells.push({ glyphD: g.d, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });
2835
+ adv += glyphAdv + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2836
+ }
2837
+ }
2838
+ }
2839
+ if (el.children) for (const ch of el.children) walk(ch, s);
2840
+ };
2841
+ walk(node, rootStyleOf(node));
2842
+ return { cells, width: adv };
2843
+ }
2844
+ function alongAffine(sampler, dist, scale, widthEm) {
2845
+ const { x, y, angle } = sampler.sampleAtDistance(dist);
2846
+ const cos = Math.cos(angle), sin = Math.sin(angle), hw = widthEm / 2;
2847
+ return [scale * cos, scale * sin, -scale * sin, scale * cos, x - scale * cos * hw, y - scale * sin * hw];
2848
+ }
2849
+ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLength) {
2850
+ var _a;
2851
+ const { glyphs, create = jsonElementFactory, warnings } = opts;
2852
+ const sampler = pathD ? createPathSampler(pathD) : null;
2853
+ if (!sampler) {
2854
+ warnings == null ? void 0 : warnings.push("textGlyphs: unparsable along-path geometry");
2855
+ return null;
2856
+ }
2857
+ const soleFont = soleFontOf(glyphs);
2858
+ const { cells, width } = collectAlongPathCells(node, glyphs, soleFont, warnings);
2859
+ if (!cells.length) return toGroup(node, [], create);
2860
+ if (textLength && textLength > 0 && width > 0) {
2861
+ const k = textLength / width;
2862
+ for (const c of cells) c.midBase *= k;
2863
+ }
2864
+ const so = readAnimatable(startOffset);
2865
+ if (so.kind === "animated" /* Animated */ && so.keyframes.length >= 2) {
2866
+ return toGroup(node, buildAnimatedAlongPath(cells, sampler, so.keyframes, so.loop, create), create);
2867
+ }
2868
+ const base = so.kind === "animated" /* Animated */ ? Number((_a = so.keyframes[0]) == null ? void 0 : _a.value) || 0 : so.kind === "static" /* Static */ ? Number(so.value) || 0 : 0;
2869
+ const placements = cells.map((c) => ({
2870
+ glyphD: c.glyphD,
2871
+ paint: c.paint,
2872
+ m: alongAffine(sampler, base + c.midBase, c.scale, c.widthEm)
2873
+ }));
2874
+ return toGroup(node, buildPaths(placements, create, warnings), create);
2875
+ }
2876
+ var ALONG_PATH_MAX_STEPS = 48;
2877
+ var ALONG_PATH_MAX_STEPS_PER_SEGMENT = 64;
2878
+ function roundN(v, n) {
2879
+ const f = __pow(10, n);
2880
+ return Math.round(v * f) / f;
2881
+ }
2882
+ function buildAnimatedAlongPath(cells, sampler, sokfs, loop, create) {
2883
+ const step = Math.max(sampler.totalLength / ALONG_PATH_MAX_STEPS, 0.5);
2884
+ const timeOf = (kf) => Number(kf.time) || 0;
2885
+ const offOf = (kf) => Number(kf.value) || 0;
2886
+ const out = [];
2887
+ for (const c of cells) {
2888
+ const centred = [c.scale, 0, 0, c.scale, -c.scale * (c.widthEm / 2), 0];
2889
+ const d = transformPathData(c.glyphD, centred);
2890
+ const sampleKf = (dist, time) => {
2891
+ const { x, y, angle } = sampler.sampleAtDistance(dist);
2892
+ return {
2893
+ time,
2894
+ value: {
2895
+ ["translate" /* Translate */]: [roundN(x, 3), roundN(y, 3)],
2896
+ ["rotate" /* Rotate */]: roundN(angle * 180 / Math.PI, 3)
2897
+ }
2898
+ };
2899
+ };
2900
+ const kfs = [sampleKf(offOf(sokfs[0]) + c.midBase, timeOf(sokfs[0]))];
2901
+ for (let k = 1; k < sokfs.length; k++) {
2902
+ const t0 = timeOf(sokfs[k - 1]), t1 = timeOf(sokfs[k]);
2903
+ const o0 = offOf(sokfs[k - 1]), o1 = offOf(sokfs[k]);
2904
+ const n = Math.min(ALONG_PATH_MAX_STEPS_PER_SEGMENT, Math.max(1, Math.ceil(Math.abs(o1 - o0) / step)));
2905
+ for (let s = 1; s <= n; s++) {
2906
+ const f = s / n;
2907
+ kfs.push(sampleKf(o0 + f * (o1 - o0) + c.midBase, t0 + f * (t1 - t0)));
2908
+ }
2909
+ }
2910
+ const transform = { keyframes: kfs };
2911
+ if (loop !== void 0) transform.loop = loop;
2912
+ out.push(create("path", __spreadProps(__spreadValues({ d }, paintProps(c.paint)), { animate: { transform } }), []));
2913
+ }
2914
+ return out;
2915
+ }
2916
+ function paintProps(paint) {
2917
+ const p = {};
2918
+ if (paint.fill !== void 0) p.fill = paint.fill;
2919
+ if (paint.stroke !== void 0) p.stroke = paint.stroke;
2920
+ if (paint.strokeWidth !== void 0) p.strokeWidth = paint.strokeWidth;
2921
+ return p;
2922
+ }
2923
+ function buildPaths(placements, create, warnings) {
2924
+ var _a, _b, _c;
2925
+ if (!placements.length) {
2926
+ warnings == null ? void 0 : warnings.push("textGlyphs: nothing to render");
2927
+ return [];
2928
+ }
2929
+ const byPaint = /* @__PURE__ */ new Map();
2930
+ for (const p of placements) {
2931
+ const key = JSON.stringify([(_a = p.paint.fill) != null ? _a : null, (_b = p.paint.stroke) != null ? _b : null, (_c = p.paint.strokeWidth) != null ? _c : null]);
2932
+ const baked = transformPathData(p.glyphD, p.m);
2933
+ const entry = byPaint.get(key);
2934
+ if (entry) entry.d += baked;
2935
+ else byPaint.set(key, { paint: p.paint, d: baked });
2936
+ }
2937
+ const out = [];
2938
+ for (const { paint, d } of byPaint.values()) out.push(create("path", __spreadValues({ d }, paintProps(paint)), []));
2939
+ return out;
2940
+ }
2941
+ function toGroup(node, children, create) {
2942
+ const gProps = {};
2943
+ for (const k of Object.keys(node)) {
2944
+ if (k === "type" || k === "children" || TEXT_ATTR_KEYS.indexOf(k) !== -1) continue;
2945
+ gProps[k] = node[k];
2946
+ }
2947
+ if (gProps.style && typeof gProps.style === "object") {
2948
+ const style = __spreadValues({}, gProps.style);
2949
+ delete style["white-space"];
2950
+ if (Object.keys(style).length) gProps.style = style;
2951
+ else delete gProps.style;
2952
+ }
2953
+ return create("g", gProps, children);
2954
+ }
2955
+ function materialiseGlyphText(node, opts) {
2956
+ if (opts.alongPath) return materialiseGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength);
2957
+ return materialiseGlyphTextHorizontal(node, opts);
2958
+ }
2959
+ function applyTextGlyphsEffect(node, fx, ctx) {
2960
+ if (!(fx == null ? void 0 : fx.useGlyphs)) return node;
2961
+ if (!ctx.glyphs) {
2962
+ ctx.warnings.push("textGlyphs: no definitions.glyphs \u2014 left as native <text>");
2963
+ return node;
2964
+ }
2965
+ return materialiseGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
2966
+ }
2967
+ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength) {
2968
+ if (!ctx.glyphs) {
2969
+ ctx.warnings.push("textGlyphs: no definitions.glyphs");
2970
+ return null;
2971
+ }
2972
+ return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength);
2973
+ }
2974
+
2396
2975
  // src/PxMotionPath.ts
2397
2976
  function getKfTranslate(kf) {
2398
2977
  var _a;
@@ -2836,12 +3415,12 @@ function parseSvgPathToBezier(d) {
2836
3415
  }
2837
3416
  return res;
2838
3417
  }
2839
- function extractPathData(str) {
2840
- if (str.startsWith("path(") && str.endsWith(")")) {
2841
- return str.slice(5, -1);
3418
+ function extractPathData(str2) {
3419
+ if (str2.startsWith("path(") && str2.endsWith(")")) {
3420
+ return str2.slice(5, -1);
2842
3421
  }
2843
- if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str)) {
2844
- return str;
3422
+ if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str2)) {
3423
+ return str2;
2845
3424
  }
2846
3425
  return void 0;
2847
3426
  }
@@ -3051,16 +3630,67 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
3051
3630
  looped.push(pushed);
3052
3631
  }
3053
3632
  }
3054
- for (let rep = 0; rep < fullReps; rep++) {
3055
- const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
3056
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
3057
- const repStart = fillStart + rep * segDuration;
3058
- appendRep(repStart, isReversed);
3633
+ function appendRepTail(repStart, isReversed, tailFraction) {
3634
+ let entries;
3635
+ if (isReversed) {
3636
+ entries = [];
3637
+ for (let i = template.length - 1; i >= 0; i--) {
3638
+ entries.push({
3639
+ relT: 1 - template[i].relT,
3640
+ v: template[i].v,
3641
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
3642
+ tangentIn: template[i].tangentOut,
3643
+ tangentOut: template[i].tangentIn
3644
+ });
3645
+ }
3646
+ } else {
3647
+ entries = template;
3648
+ }
3649
+ const startRelT = 1 - tailFraction;
3650
+ for (let i = 0; i < entries.length; i++) {
3651
+ const entry = entries[i];
3652
+ if (entry.relT < startRelT - 1e-9) continue;
3653
+ const prev = entries[i - 1];
3654
+ if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {
3655
+ const intervalSpan = entry.relT - prev.relT;
3656
+ const localFrac = (startRelT - prev.relT) / intervalSpan;
3657
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
3658
+ const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
3659
+ const { right: rightEasing } = splitEasing(prev.e, localFrac);
3660
+ looped.push({ t: repStart, v: startValue, e: rightEasing });
3661
+ }
3662
+ const pushed = {
3663
+ t: repStart + (entry.relT - startRelT) * segDuration,
3664
+ v: entry.v,
3665
+ e: i < entries.length - 1 ? entry.e : void 0
3666
+ };
3667
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
3668
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
3669
+ looped.push(pushed);
3670
+ }
3059
3671
  }
3060
- if (partialFraction > 1e-9) {
3061
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
3062
- const repStart = fillStart + fullReps * segDuration;
3063
- appendRep(repStart, isReversed, partialFraction);
3672
+ if (loop.before) {
3673
+ if (partialFraction > 1e-9) {
3674
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
3675
+ appendRepTail(fillStart, isReversed, partialFraction);
3676
+ }
3677
+ for (let rep = 0; rep < fullReps; rep++) {
3678
+ const distFromBoundary = fullReps - 1 - rep;
3679
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
3680
+ const repStart = fillStart + remainder + rep * segDuration;
3681
+ appendRep(repStart, isReversed);
3682
+ }
3683
+ } else {
3684
+ for (let rep = 0; rep < fullReps; rep++) {
3685
+ const isReversed = !!loop.alternate && rep % 2 === 0;
3686
+ const repStart = fillStart + rep * segDuration;
3687
+ appendRep(repStart, isReversed);
3688
+ }
3689
+ if (partialFraction > 1e-9) {
3690
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
3691
+ const repStart = fillStart + fullReps * segDuration;
3692
+ appendRep(repStart, isReversed, partialFraction);
3693
+ }
3064
3694
  }
3065
3695
  if (loop.before) {
3066
3696
  return [...looped, ...keyframes];
@@ -3379,7 +4009,6 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
3379
4009
  if (!trimPath) return node;
3380
4010
  const trimAllAsOne = !!trimPath.trimAllAsOne;
3381
4011
  const leafEntries = [];
3382
- let acc = 0;
3383
4012
  const measure = (n) => {
3384
4013
  if (Array.isArray(n.children) && n.children.length > 0) {
3385
4014
  for (const ch of n.children) measure(ch);
@@ -3391,15 +4020,22 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
3391
4020
  if (!subpaths.length) return;
3392
4021
  const entry = { leaf: n, subpaths: [] };
3393
4022
  for (const sp of subpaths) {
3394
- if (!trimAllAsOne) acc = 0;
3395
4023
  const lengthPx = pxBezierPathLength(sp);
3396
- entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: acc });
3397
- acc += lengthPx;
4024
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });
3398
4025
  }
3399
4026
  leafEntries.push(entry);
3400
4027
  };
3401
4028
  measure(node);
3402
4029
  if (!leafEntries.length) return node;
4030
+ let acc = 0;
4031
+ const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
4032
+ for (const entry of iterOrder) {
4033
+ for (const sp of entry.subpaths) {
4034
+ if (!trimAllAsOne) acc = 0;
4035
+ sp.startOffsetPx = acc;
4036
+ acc += sp.lengthPx;
4037
+ }
4038
+ }
3403
4039
  const chainLengthPx = acc;
3404
4040
  if (trimAllAsOne && chainLengthPx < 1e-3) return node;
3405
4041
  const offsetReadRaw = readAnimatable(trimPath.offset);
@@ -3526,10 +4162,12 @@ function computeAnimAttr(read, map) {
3526
4162
  value: map((_c = kf.value) != null ? _c : kf.v),
3527
4163
  easing: (_d = kf.easing) != null ? _d : kf.e
3528
4164
  };
3529
- })
4165
+ }),
4166
+ loop: read.loop
3530
4167
  };
3531
4168
  }
3532
4169
  function applyAttr(node, attrName, attr) {
4170
+ var _a, _b;
3533
4171
  if (!attr) return;
3534
4172
  if (attr.kind === "static" /* Static */) {
3535
4173
  node[attrName] = attr.value;
@@ -3537,9 +4175,16 @@ function applyAttr(node, attrName, attr) {
3537
4175
  }
3538
4176
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3539
4177
  const animate = __spreadValues({}, prevAnimate || {});
3540
- animate[attrName] = { keyframes: attr.keyframes };
4178
+ const block = { keyframes: attr.keyframes };
4179
+ if (attr.loop !== void 0) block.loop = attr.loop;
4180
+ animate[attrName] = block;
3541
4181
  node.animate = animate;
4182
+ const firstKf = attr.keyframes[0];
4183
+ if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4184
+ node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4185
+ }
3542
4186
  }
4187
+ var OPACITY_STEP_MS = 10;
3543
4188
  function computeOpacityFromRange(rangeRead) {
3544
4189
  var _a, _b, _c, _d, _e, _f;
3545
4190
  const hide = (v) => v[0] === v[1];
@@ -3565,9 +4210,9 @@ function computeOpacityFromRange(rangeRead) {
3565
4210
  const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
3566
4211
  if (prevHide && !nextHide) {
3567
4212
  out.push({ time: t, value: 0 });
3568
- out.push({ time: t + 1, value: 1 });
4213
+ out.push({ time: t + OPACITY_STEP_MS, value: 1 });
3569
4214
  } else if (!prevHide && nextHide) {
3570
- out.push({ time: t - 1, value: 1 });
4215
+ out.push({ time: t - OPACITY_STEP_MS, value: 1 });
3571
4216
  out.push({ time: t, value: 0 });
3572
4217
  }
3573
4218
  }
@@ -3700,6 +4345,7 @@ function rectToPathD(node) {
3700
4345
 
3701
4346
  // src/effects/PlayerEffectsUtil.ts
3702
4347
  function applyPlayerEffects(root) {
4348
+ var _a, _b;
3703
4349
  const ctx = {
3704
4350
  defs: [],
3705
4351
  warnings: [],
@@ -3707,7 +4353,11 @@ function applyPlayerEffects(root) {
3707
4353
  idMap: /* @__PURE__ */ new Map(),
3708
4354
  nextId: 0,
3709
4355
  contentRefInnerIds: /* @__PURE__ */ new Map(),
3710
- maskAncestorChains: /* @__PURE__ */ new Map()
4356
+ maskAncestorChains: /* @__PURE__ */ new Map(),
4357
+ // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
4358
+ // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
4359
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
4360
+ glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
3711
4361
  };
3712
4362
  const working = clone(root);
3713
4363
  indexById(working, ctx.idMap);
@@ -3719,27 +4369,46 @@ function applyPlayerEffects(root) {
3719
4369
  return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
3720
4370
  }
3721
4371
  function applyPlayerEffects_exceptRetime(node, ctx) {
4372
+ var _a;
3722
4373
  if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
3723
4374
  const fx = node.effects;
3724
4375
  const originalId = typeof node.id === "string" ? node.id : void 0;
3725
4376
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
3726
4377
  if (!fx && !innerIdForContentRef) return node;
3727
- const { transformation, repeater, maskedBy, trimPath, retime, ref, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
4378
+ const { transformation, repeater, maskedBy, trimPath, clone: cloneFx, fillGradient, strokeGradient, textAlongPath, text } = fx != null ? fx : {};
3728
4379
  const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
3729
4380
  if (fx) delete node.effects;
3730
4381
  let n = node;
3731
- n = applyTextAlongPathEffect(n, textAlongPath, ctx);
4382
+ let consumedByGlyphs = false;
4383
+ if (text == null ? void 0 : text.useGlyphs) {
4384
+ if (textAlongPath) {
4385
+ const pathNode = typeof textAlongPath.href === "string" ? ctx.idMap.get(textAlongPath.href) : void 0;
4386
+ const pathD = pathNode && typeof pathNode.d === "string" ? pathNode.d : void 0;
4387
+ const rawTL = textAlongPath.textLength;
4388
+ const textLength = typeof rawTL === "number" ? rawTL : rawTL && typeof rawTL === "object" ? typeof rawTL.value === "number" ? rawTL.value : Array.isArray(rawTL.keyframes) && rawTL.keyframes.length ? Number((_a = rawTL.keyframes[0]) == null ? void 0 : _a.value) : void 0 : void 0;
4389
+ const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textAlongPath.startOffset, textLength);
4390
+ if (glyphed) {
4391
+ n = glyphed;
4392
+ consumedByGlyphs = true;
4393
+ }
4394
+ } else {
4395
+ n = applyTextGlyphsEffect(n, text, ctx);
4396
+ consumedByGlyphs = true;
4397
+ }
4398
+ }
4399
+ if (!consumedByGlyphs) n = applyTextAlongPathEffect(n, textAlongPath, ctx);
3732
4400
  n = applyFillGradientEffect(n, fillGradient, ctx);
3733
4401
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
3734
4402
  n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
3735
4403
  n = applyRepeaterEffect(n, repeater, ctx);
3736
4404
  n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
3737
4405
  if (innerIdForContentRef) {
4406
+ applyRefHref(n, cloneFx, ctx);
3738
4407
  n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
3739
4408
  } else {
3740
- n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
4409
+ n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
3741
4410
  }
3742
- if (retime) node.effects = { retime };
4411
+ if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
3743
4412
  if (originalId) ctx.idMap.set(originalId, n);
3744
4413
  return n;
3745
4414
  }
@@ -3756,13 +4425,30 @@ function materialiseAnimatedUseInstances(root) {
3756
4425
  if (animatedIds.size === 0) return root;
3757
4426
  let idCounter = 0;
3758
4427
  const genId3 = () => "_lw_use_mat_" + ++idCounter;
4428
+ const rootViewport = readRootViewport(root);
3759
4429
  const defsCollector = [];
3760
- const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector);
4430
+ const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
3761
4431
  if (defsCollector.length === 0) return walked;
3762
4432
  const defsNode = { type: "defs", children: defsCollector };
3763
4433
  const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
3764
4434
  return __spreadProps(__spreadValues({}, walked), { children: newChildren });
3765
4435
  }
4436
+ function readRootViewport(root) {
4437
+ const vb = parseViewBox(root.viewBox);
4438
+ if (vb) return [vb[2], vb[3]];
4439
+ const w = numericAttr(root.width);
4440
+ const h = numericAttr(root.height);
4441
+ if (w !== void 0 && h !== void 0) return [w, h];
4442
+ return [1, 1];
4443
+ }
4444
+ function numericAttr(v) {
4445
+ if (typeof v === "number") return v;
4446
+ if (typeof v === "string") {
4447
+ const n = parseFloat(v);
4448
+ return Number.isFinite(n) ? n : void 0;
4449
+ }
4450
+ return void 0;
4451
+ }
3766
4452
  function buildIdMap(root) {
3767
4453
  const map = /* @__PURE__ */ new Map();
3768
4454
  const visit = (n) => {
@@ -3814,16 +4500,22 @@ function stripHash2(href) {
3814
4500
  if (typeof href !== "string") return void 0;
3815
4501
  return href.startsWith("#") ? href.slice(1) : href;
3816
4502
  }
3817
- function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector) {
4503
+ function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
4504
+ var _a, _b, _c, _d;
3818
4505
  const clone2 = deepClonePxNode(target);
3819
4506
  regenerateIdsAndRewriteRefs(clone2, genId3);
3820
- const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector) : clone2;
3821
- const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector);
4507
+ const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
4508
+ const useW = (_b = (_a = numericAttr(useNode.width)) != null ? _a : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
4509
+ const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
4510
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
4511
+ const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
3822
4512
  const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materialisedClone] });
3823
4513
  delete newNode.href;
4514
+ delete newNode.width;
4515
+ delete newNode.height;
3824
4516
  return applyUseOffsetToG(newNode);
3825
4517
  }
3826
- function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
4518
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector, useW, useH) {
3827
4519
  const viewBox = parseViewBox(symbolNode.viewBox);
3828
4520
  const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
3829
4521
  delete g.viewBox;
@@ -3832,9 +4524,14 @@ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
3832
4524
  delete g.height;
3833
4525
  if (!viewBox) return g;
3834
4526
  const [vbX, vbY, vbW, vbH] = viewBox;
3835
- if (vbX !== 0 || vbY !== 0) {
3836
- g.transform = "translate(" + -vbX + "," + -vbY + ")";
3837
- }
4527
+ const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;
4528
+ const xOff = (useW - vbW * scale) / 2;
4529
+ const yOff = (useH - vbH * scale) / 2;
4530
+ const parts = [];
4531
+ if (xOff !== 0 || yOff !== 0) parts.push("translate(" + xOff + "," + yOff + ")");
4532
+ if (scale !== 1) parts.push("scale(" + scale + ")");
4533
+ if (vbX !== 0 || vbY !== 0) parts.push("translate(" + -vbX + "," + -vbY + ")");
4534
+ if (parts.length) g.transform = parts.join("");
3838
4535
  const clipId = genId3();
3839
4536
  defsCollector.push({
3840
4537
  type: "clipPath",
@@ -3850,18 +4547,18 @@ function parseViewBox(v) {
3850
4547
  if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
3851
4548
  return [parts[0], parts[1], parts[2], parts[3]];
3852
4549
  }
3853
- function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector) {
4550
+ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector, rootViewport) {
3854
4551
  if (node.type === "use" && typeof node.href === "string") {
3855
4552
  const targetId = stripHash2(node.href);
3856
4553
  if (targetId && animatedIds.has(targetId)) {
3857
4554
  const target = idMap.get(targetId);
3858
- if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector);
4555
+ if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector, rootViewport);
3859
4556
  }
3860
4557
  }
3861
4558
  if (!node.children) return node;
3862
4559
  let changed = false;
3863
4560
  const newChildren = node.children.map((ch) => {
3864
- const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector);
4561
+ const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector, rootViewport);
3865
4562
  if (m !== ch) changed = true;
3866
4563
  return m;
3867
4564
  });
@@ -3877,6 +4574,36 @@ function materialiseAllInTree(doc, engine, opts) {
3877
4574
  if (engine === PxAnimatorEngine.webapi) {
3878
4575
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
3879
4576
  root = materialiseAnimatedUseInstances(root);
4577
+ root = pruneUnreferencedDefs(root);
4578
+ }
4579
+ return root;
4580
+ }
4581
+ function pruneUnreferencedDefs(root) {
4582
+ const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
4583
+ const walk = (n, fn) => {
4584
+ var _a;
4585
+ fn(n);
4586
+ (_a = n.children) == null ? void 0 : _a.forEach((c) => walk(c, fn));
4587
+ };
4588
+ let changed = true;
4589
+ while (changed) {
4590
+ changed = false;
4591
+ const referenced = /* @__PURE__ */ new Set();
4592
+ walk(root, (n) => {
4593
+ if (n.type === "use" && typeof n.href === "string") referenced.add(stripHash3(n.href));
4594
+ });
4595
+ walk(root, (n) => {
4596
+ if (!n.children) return;
4597
+ let kept = n.children;
4598
+ if (n.type === "defs") {
4599
+ kept = kept.filter((c) => !((c.type === "g" || c.type === "symbol") && typeof c.id === "string" && !referenced.has(c.id)));
4600
+ }
4601
+ kept = kept.filter((c) => !(c.type === "defs" && (!c.children || c.children.length === 0)));
4602
+ if (kept.length !== n.children.length) {
4603
+ n.children = kept;
4604
+ changed = true;
4605
+ }
4606
+ });
3880
4607
  }
3881
4608
  return root;
3882
4609
  }
@@ -3909,6 +4636,13 @@ var URL_VALUE_ATTRS_LOWER = /* @__PURE__ */ new Set([
3909
4636
  ]);
3910
4637
  var IMAGE_REF_ATTRS_LOWER = /* @__PURE__ */ new Set(["href", "xlink:href", "src"]);
3911
4638
  var DATA_RASTER_IMAGE_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,/i;
4639
+ var DATA_SVG_IMAGE_RE = /^data:image\/svg\+xml(?:;[^,]*)?,/i;
4640
+ var DATA_IMAGE_BASE64_PAYLOAD_RE = /^data:image\/[^;,]*;base64,([A-Za-z0-9+/]{8})/i;
4641
+ var BASE64_RASTER_MAGICS = ["iVBORw0K", "/9j/", "R0lGOD", "UklGR", "Qk"];
4642
+ function isContentSniffedRasterImage(str2) {
4643
+ const m = DATA_IMAGE_BASE64_PAYLOAD_RE.exec(str2);
4644
+ return !!m && BASE64_RASTER_MAGICS.some((magic) => m[1].startsWith(magic));
4645
+ }
3912
4646
  function isDangerousAttrName(nameLower) {
3913
4647
  if (nameLower.startsWith("on")) return true;
3914
4648
  return false;
@@ -3920,19 +4654,19 @@ function sanitiseAttributeValue(name, value) {
3920
4654
  return void 0;
3921
4655
  }
3922
4656
  if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopcolor") {
3923
- const str = String(value);
3924
- if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
4657
+ const str2 = String(value);
4658
+ if (str2.includes("url(") && !/^url\(#[^)]+\)$/.test(str2)) {
3925
4659
  console.warn('Attribute "' + nameLower + '" blocked: url() must be internal url(#id), got:', value);
3926
4660
  return void 0;
3927
4661
  }
3928
4662
  return value;
3929
4663
  }
3930
4664
  if (URL_VALUE_ATTRS_LOWER.has(nameLower)) {
3931
- const str = String(value);
3932
- if (str.startsWith("#")) return value;
3933
- if (/^url\(#[^)]+\)$/.test(str)) return value;
3934
- if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && DATA_RASTER_IMAGE_RE.test(str)) return value;
3935
- console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or base64 raster data: URI, got:', value);
4665
+ const str2 = String(value);
4666
+ if (str2.startsWith("#")) return value;
4667
+ if (/^url\(#[^)]+\)$/.test(str2)) return value;
4668
+ if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && (DATA_RASTER_IMAGE_RE.test(str2) || DATA_SVG_IMAGE_RE.test(str2) || isContentSniffedRasterImage(str2))) return value;
4669
+ console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or data:image/\u2026 URI, got:', value);
3936
4670
  return void 0;
3937
4671
  }
3938
4672
  return value;
@@ -3961,7 +4695,7 @@ function createElement(tagName, normalisedProps, style, children, textContent) {
3961
4695
  if (textContent) element.textContent = textContent;
3962
4696
  return element;
3963
4697
  }
3964
- function resolveStyle(style, defs) {
4698
+ function resolveStyle2(style, defs) {
3965
4699
  var _a;
3966
4700
  if (!style) return void 0;
3967
4701
  if (typeof style === "string") {
@@ -3997,7 +4731,7 @@ function renderNode(node, defs) {
3997
4731
  if (!node) return null;
3998
4732
  const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
3999
4733
  const nodeDefs = getDefs(node) || defs;
4000
- const resolvedStyle = resolveStyle(style, nodeDefs);
4734
+ const resolvedStyle = resolveStyle2(style, nodeDefs);
4001
4735
  let childElements;
4002
4736
  if (children) {
4003
4737
  for (const ch of children) {
@@ -5030,6 +5764,7 @@ function subtractMultiset(a, b) {
5030
5764
  PxAttrValueSchema,
5031
5765
  PxBezierPathSchema,
5032
5766
  PxBindingSchema,
5767
+ PxCloneEffectSchema,
5033
5768
  PxDefsSchema,
5034
5769
  PxEasingOrRefSchema,
5035
5770
  PxEffectsSchema,
@@ -5045,12 +5780,12 @@ function subtractMultiset(a, b) {
5045
5780
  PxNodeBase,
5046
5781
  PxNodeSchema,
5047
5782
  PxPropertyAnimationSchema,
5048
- PxRefEffectSchema,
5049
5783
  PxRepeaterEffectSchema,
5050
5784
  PxRetimeEffectSchema,
5051
5785
  PxStrokeGradientEffectSchema,
5052
5786
  PxSvgNodeExtra,
5053
5787
  PxTextAlongPathEffectSchema,
5788
+ PxTextEffectSchema,
5054
5789
  PxTransformPartsSchema,
5055
5790
  PxTransformValueSchema,
5056
5791
  PxTransformationEffectSchema,
@@ -5078,9 +5813,13 @@ function subtractMultiset(a, b) {
5078
5813
  getNormalizedProps,
5079
5814
  isPxElementFileFormat,
5080
5815
  isPxElementFileFormatDeep,
5816
+ jsonElementFactory,
5081
5817
  loadTagAnimators,
5082
5818
  materialiseAllInTree,
5083
5819
  materialiseAnimatedUseInstances,
5820
+ materialiseGlyphText,
5821
+ materialiseGlyphTextAlongPath,
5822
+ materialiseGlyphTextHorizontal,
5084
5823
  materialiseInternalLoopsInPropAnim,
5085
5824
  materialiseInternalLoopsInTree,
5086
5825
  materialiseMotionPathInPropAnim,