@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.js CHANGED
@@ -4,6 +4,7 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __pow = Math.pow;
7
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
9
  var __spreadValues = (a, b) => {
9
10
  for (var prop in b || (b = {}))
@@ -45,7 +46,7 @@ function readAnimatable(raw) {
45
46
  if (typeof raw === "object") {
46
47
  const obj = raw;
47
48
  if (obj.keyframes) {
48
- return { kind: "animated" /* Animated */, keyframes: obj.keyframes, autoOrient: obj.autoOrient };
49
+ return { kind: "animated" /* Animated */, keyframes: obj.keyframes, autoOrient: obj.autoOrient, loop: obj.loop };
49
50
  }
50
51
  if (obj.value !== void 0) return { kind: "static" /* Static */, value: obj.value };
51
52
  }
@@ -135,6 +136,7 @@ function wrapTransformPart(inner, part, raw, ctx) {
135
136
  if (v.kind === "animated" /* Animated */) {
136
137
  const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
137
138
  if (v.autoOrient) animTr.autoOrient = true;
139
+ if (v.loop !== void 0) animTr.loop = v.loop;
138
140
  return {
139
141
  type: "g",
140
142
  animate: { transform: animTr },
@@ -153,9 +155,11 @@ function wrapOrigin(inner, raw, invert) {
153
155
  return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
154
156
  }
155
157
  if (v.kind === "animated" /* Animated */) {
158
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) };
159
+ if (v.loop !== void 0) animTr.loop = v.loop;
156
160
  return {
157
161
  type: "g",
158
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
162
+ animate: { transform: animTr },
159
163
  children: [inner]
160
164
  };
161
165
  }
@@ -165,8 +169,8 @@ function wrapOrigin(inner, raw, invert) {
165
169
  // src/effects/contentRefSplit.ts
166
170
  function identifyContentRefTargets(node, ctx, allocator) {
167
171
  var _a, _b, _c;
168
- if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
169
- const baseId = node.effects.ref.baseId;
172
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
173
+ const baseId = node.effects.clone.baseId;
170
174
  if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
171
175
  ctx.contentRefInnerIds.set(baseId, allocator(baseId));
172
176
  }
@@ -214,6 +218,8 @@ function liftBodyTranslate(node, transformation) {
214
218
  });
215
219
  const outerAnimTr = { keyframes: outerKfs };
216
220
  if (animTr.autoOrient) outerAnimTr.autoOrient = true;
221
+ const srcLoop = animTr.loop;
222
+ if (srcLoop !== void 0) outerAnimTr.loop = srcLoop;
217
223
  out.animate = { transform: outerAnimTr };
218
224
  const innerHasPivotedPart = kfs.some((kf) => {
219
225
  const v = kf.value || {};
@@ -235,7 +241,9 @@ function liftBodyTranslate(node, transformation) {
235
241
  delete node.animate.transform;
236
242
  if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
237
243
  } else {
238
- node.animate.transform = { keyframes: innerKfs };
244
+ const innerAnimTr = { keyframes: innerKfs };
245
+ if (srcLoop !== void 0) innerAnimTr.loop = srcLoop;
246
+ node.animate.transform = innerAnimTr;
239
247
  }
240
248
  didLiftAnimate = true;
241
249
  }
@@ -899,10 +907,22 @@ var PxTriggerSchema = implementsInterface()(px.object({
899
907
  outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
900
908
  scrollIntoViewThreshold: px.number().optional()
901
909
  }));
910
+ var PxGlyphSchema = implementsInterface()(px.object({
911
+ width: px.number(),
912
+ d: px.string()
913
+ }));
914
+ var PxGlyphFontSchema = implementsInterface()(px.object({
915
+ fFamily: px.string(),
916
+ style: px.string(),
917
+ ascent: px.number(),
918
+ unitsPerEm: px.number(),
919
+ glyphs: px.record(PxGlyphSchema)
920
+ }));
902
921
  var PxDefsSchema = implementsInterface()(px.object({
903
922
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
904
923
  animations: px.record(PxAnimationDefinitionSchema).optional(),
905
- styles: px.record(px.any()).optional()
924
+ styles: px.record(px.any()).optional(),
925
+ glyphs: px.record(PxGlyphFontSchema).optional()
906
926
  }));
907
927
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
908
928
  mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
@@ -975,9 +995,10 @@ var PxRetimeEffectSchema = implementsInterface()(px.object({
975
995
  stretch: px.number().optional(),
976
996
  timeCrop: px.tuple([px.number(), px.number()]).optional()
977
997
  }));
978
- var PxRefEffectSchema = implementsInterface()(px.object({
998
+ var PxCloneEffectSchema = implementsInterface()(px.object({
999
+ type: px.string().optional(),
979
1000
  baseId: px.string().optional(),
980
- type: px.string().optional()
1001
+ retime: PxRetimeEffectSchema.optional()
981
1002
  }));
982
1003
  var PxGradientUnits = {
983
1004
  userSpaceOnUse: "userSpaceOnUse",
@@ -1022,17 +1043,20 @@ var PxTextAlongPathEffectSchema = implementsInterface()(px.object({
1022
1043
  startOffset: PxAnimatableNumberSchema.optional(),
1023
1044
  textLength: PxAnimatableNumberSchema.optional()
1024
1045
  }));
1046
+ var PxTextEffectSchema = implementsInterface()(px.object({
1047
+ useGlyphs: px.boolean().optional()
1048
+ }));
1025
1049
  var PxEffectsSchema = implementsInterface()(px.object({
1026
1050
  transformation: PxTransformationEffectSchema.optional(),
1027
1051
  repeater: PxRepeaterEffectSchema.optional(),
1028
1052
  maskedBy: PxMaskedByEffectSchema.optional(),
1029
1053
  trimPath: PxTrimPathEffectSchema.optional(),
1030
- retime: PxRetimeEffectSchema.optional(),
1054
+ clone: PxCloneEffectSchema.optional(),
1031
1055
  isCombinedShape: px.boolean().optional(),
1032
- ref: PxRefEffectSchema.optional(),
1033
1056
  fillGradient: PxFillGradientEffectSchema.optional(),
1034
1057
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
1035
- textAlongPath: PxTextAlongPathEffectSchema.optional()
1058
+ textAlongPath: PxTextAlongPathEffectSchema.optional(),
1059
+ text: PxTextEffectSchema.optional()
1036
1060
  }));
1037
1061
  function validateNodeEffects(root, opts) {
1038
1062
  const warnings = [];
@@ -1257,6 +1281,7 @@ function buildStopChildren(stops, ctx) {
1257
1281
  const animBlock = stops;
1258
1282
  const kfs = animBlock.keyframes;
1259
1283
  if (!Array.isArray(kfs) || !kfs.length) return [];
1284
+ const loopFromSource = animBlock.loop;
1260
1285
  let stopCount = 0;
1261
1286
  for (const kf of kfs) {
1262
1287
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -1269,7 +1294,7 @@ function buildStopChildren(stops, ctx) {
1269
1294
  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" };
1270
1295
  baselineStops.push({ offset: s.offset, color: s.color });
1271
1296
  }
1272
- return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx));
1297
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
1273
1298
  }
1274
1299
  function staticStopNode(s) {
1275
1300
  return {
@@ -1278,7 +1303,7 @@ function staticStopNode(s) {
1278
1303
  stopColor: s.color
1279
1304
  };
1280
1305
  }
1281
- function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
1306
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
1282
1307
  var _a, _b, _c, _d, _e;
1283
1308
  const colorKfs = [];
1284
1309
  const offsetKfs = [];
@@ -1303,8 +1328,14 @@ function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
1303
1328
  stopColor: baseline.color
1304
1329
  };
1305
1330
  const animate = {};
1306
- if (colorKfs.length) animate.stopColor = { keyframes: colorKfs };
1307
- if (offsetVaries && offsetKfs.length) animate.offset = { keyframes: offsetKfs };
1331
+ if (colorKfs.length) {
1332
+ animate.stopColor = { keyframes: colorKfs };
1333
+ if (loop !== void 0) animate.stopColor.loop = loop;
1334
+ }
1335
+ if (offsetVaries && offsetKfs.length) {
1336
+ animate.offset = { keyframes: offsetKfs };
1337
+ if (loop !== void 0) animate.offset.loop = loop;
1338
+ }
1308
1339
  if (Object.keys(animate).length) stop.animate = animate;
1309
1340
  return stop;
1310
1341
  }
@@ -1369,9 +1400,11 @@ function wrapInversePart(inner, part, raw, origin, ctx) {
1369
1400
  return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
1370
1401
  }
1371
1402
  if (v.kind === "animated" /* Animated */) {
1403
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) };
1404
+ if (v.loop !== void 0) animTr.loop = v.loop;
1372
1405
  return {
1373
1406
  type: "g",
1374
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
1407
+ animate: { transform: animTr },
1375
1408
  children: [inner]
1376
1409
  };
1377
1410
  }
@@ -1408,10 +1441,16 @@ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
1408
1441
  scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
1409
1442
  }
1410
1443
  }
1444
+ const srcLoop = animTr == null ? void 0 : animTr.loop;
1445
+ const withLoop = (kfs2) => {
1446
+ const block = { keyframes: kfs2 };
1447
+ if (srcLoop !== void 0) block.loop = srcLoop;
1448
+ return block;
1449
+ };
1411
1450
  let n = inner;
1412
- if (translateKfs.length) n = { type: "g", animate: { transform: { keyframes: translateKfs } }, children: [n] };
1413
- if (rotateKfs.length) n = { type: "g", animate: { transform: { keyframes: rotateKfs } }, children: [n] };
1414
- if (scaleKfs.length) n = { type: "g", animate: { transform: { keyframes: scaleKfs } }, children: [n] };
1451
+ if (translateKfs.length) n = { type: "g", animate: { transform: withLoop(translateKfs) }, children: [n] };
1452
+ if (rotateKfs.length) n = { type: "g", animate: { transform: withLoop(rotateKfs) }, children: [n] };
1453
+ if (scaleKfs.length) n = { type: "g", animate: { transform: withLoop(scaleKfs) }, children: [n] };
1415
1454
  return n;
1416
1455
  }
1417
1456
  function nodeHasBodyTransform(node) {
@@ -1645,16 +1684,18 @@ function parseTranslateOnlyFromString(s, ctx) {
1645
1684
 
1646
1685
  // src/effects/refEffect.ts
1647
1686
  var CONTENT_SUBREF = "content";
1648
- function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
1649
- if (ref) {
1650
- const baseId = ref.baseId;
1651
- if (!baseId) {
1652
- ctx.errors.push("ref: missing baseId");
1653
- } else {
1654
- const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
1655
- node.href = "#" + targetId;
1656
- }
1687
+ function applyRefHref(node, clone2, ctx) {
1688
+ if (!clone2) return;
1689
+ const baseId = clone2.baseId;
1690
+ if (!baseId) {
1691
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
1692
+ return;
1657
1693
  }
1694
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
1695
+ node.href = "#" + targetId;
1696
+ }
1697
+ function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
1698
+ applyRefHref(node, clone2, ctx);
1658
1699
  return applyTransformationEffect(node, transformation, ctx);
1659
1700
  }
1660
1701
 
@@ -1765,22 +1806,32 @@ function concatRetime(child, parent) {
1765
1806
  stretch: parent.stretch * child.stretch
1766
1807
  };
1767
1808
  }
1768
- function applyAllRetimeEffects(root, ctx) {
1809
+ function readCloneRetime(n) {
1810
+ var _a, _b;
1811
+ return (_b = (_a = n.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.retime;
1812
+ }
1813
+ function clearCloneRetime(n) {
1769
1814
  var _a;
1815
+ const clone2 = (_a = n.effects) == null ? void 0 : _a.clone;
1816
+ if (!clone2) return;
1817
+ delete clone2.retime;
1818
+ if (Object.keys(clone2).length === 0) delete n.effects.clone;
1819
+ if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;
1820
+ }
1821
+ function applyAllRetimeEffects(root, ctx) {
1770
1822
  ctx.idMap.clear();
1771
1823
  indexById(root, ctx.idMap);
1772
1824
  const sites = [];
1773
1825
  const collect = (n) => {
1774
- var _a2, _b;
1775
- if ((_a2 = n.effects) == null ? void 0 : _a2.retime) sites.push(n);
1776
- (_b = n.children) == null ? void 0 : _b.forEach(collect);
1826
+ var _a;
1827
+ if (readCloneRetime(n)) sites.push(n);
1828
+ (_a = n.children) == null ? void 0 : _a.forEach(collect);
1777
1829
  };
1778
1830
  collect(root);
1779
1831
  for (const useNode of sites) {
1780
- const retime = (_a = useNode.effects) == null ? void 0 : _a.retime;
1832
+ const retime = readCloneRetime(useNode);
1781
1833
  if (!retime) continue;
1782
- delete useNode.effects.retime;
1783
- if (Object.keys(useNode.effects).length === 0) delete useNode.effects;
1834
+ clearCloneRetime(useNode);
1784
1835
  materialiseRetime(useNode, asRetime(retime), ctx);
1785
1836
  }
1786
1837
  }
@@ -1804,7 +1855,6 @@ function materialiseRetime(useNode, retime, ctx) {
1804
1855
  }
1805
1856
  }
1806
1857
  function buildChainClone(targetId, accum, ctx, chain) {
1807
- var _a, _b;
1808
1858
  if (chain.has(targetId)) {
1809
1859
  ctx.errors.push('retime: loop via "' + targetId + '"');
1810
1860
  return void 0;
@@ -1821,25 +1871,43 @@ function buildChainClone(targetId, accum, ctx, chain) {
1821
1871
  } else {
1822
1872
  remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
1823
1873
  }
1824
- if ((_a = cloneNode.effects) == null ? void 0 : _a.retime) {
1825
- delete cloneNode.effects.retime;
1826
- if (Object.keys(cloneNode.effects).length === 0) delete cloneNode.effects;
1827
- }
1874
+ clearCloneRetime(cloneNode);
1828
1875
  if (target.type === "use" && target.href) {
1829
1876
  const subId = stripHash(target.href);
1830
1877
  if (subId) {
1831
- const innerRetime = (_b = target.effects) == null ? void 0 : _b.retime;
1878
+ const innerRetime = readCloneRetime(target);
1832
1879
  const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
1833
1880
  const subChain = new Set(chain);
1834
1881
  subChain.add(targetId);
1835
1882
  const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
1836
1883
  if (subId2) cloneNode.href = "#" + subId2;
1837
1884
  }
1885
+ } else {
1886
+ materialiseNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);
1838
1887
  }
1839
1888
  ctx.defs.push(cloneNode);
1840
1889
  if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
1841
1890
  return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
1842
1891
  }
1892
+ function materialiseNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
1893
+ const visit = (n) => {
1894
+ var _a;
1895
+ const retime = readCloneRetime(n);
1896
+ if (n.type === "use" && retime && n.href) {
1897
+ const subId = stripHash(n.href);
1898
+ if (subId) {
1899
+ const subAccum = concatRetime(asRetime(retime), accum);
1900
+ const subChain = new Set(chain);
1901
+ subChain.add(parentTargetId);
1902
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
1903
+ if (subId2) n.href = "#" + subId2;
1904
+ }
1905
+ clearCloneRetime(n);
1906
+ }
1907
+ (_a = n.children) == null ? void 0 : _a.forEach(visit);
1908
+ };
1909
+ visit(node);
1910
+ }
1843
1911
  function remapKeyframeTimes(node, start, stretch) {
1844
1912
  var _a;
1845
1913
  remapKeyframeTimesOnly(node, start, stretch);
@@ -1884,12 +1952,14 @@ function applyAnimatableNumber(node, attrName, raw) {
1884
1952
  node[attrName] = String(raw);
1885
1953
  return;
1886
1954
  }
1887
- if (typeof raw === "object") {
1955
+ if (typeof raw === "object" && raw !== null) {
1888
1956
  const obj = raw;
1889
1957
  if (Array.isArray(obj.keyframes)) {
1890
1958
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
1891
1959
  const animate = __spreadValues({}, prevAnimate || {});
1892
- animate[attrName] = { keyframes: obj.keyframes };
1960
+ const block = { keyframes: obj.keyframes };
1961
+ if (obj.loop !== void 0) block.loop = obj.loop;
1962
+ animate[attrName] = block;
1893
1963
  node.animate = animate;
1894
1964
  return;
1895
1965
  }
@@ -1900,6 +1970,61 @@ function applyAnimatableNumber(node, attrName, raw) {
1900
1970
  }
1901
1971
  }
1902
1972
 
1973
+ // src/effects/elementFactory.ts
1974
+ var jsonElementFactory = (type, props, children) => {
1975
+ const node = { type };
1976
+ for (const k in props) if (props[k] !== void 0) node[k] = props[k];
1977
+ const arr = Array.isArray(children) ? children.filter((c) => c != null) : children != null ? [children] : [];
1978
+ if (arr.length) node.children = arr;
1979
+ return node;
1980
+ };
1981
+
1982
+ // src/effects/glyphPathBake.ts
1983
+ function fmt(v, decimals) {
1984
+ return Math.round(v) === v ? "" + Math.round(v) : v.toFixed(decimals);
1985
+ }
1986
+ function pack(nums, decimals) {
1987
+ let s = "";
1988
+ for (let i = 0; i < nums.length; i++) {
1989
+ const str2 = fmt(nums[i], decimals);
1990
+ if (i > 0 && str2.charCodeAt(0) !== 45) s += " ";
1991
+ s += str2;
1992
+ }
1993
+ return s;
1994
+ }
1995
+ function apply(m, x, y) {
1996
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
1997
+ }
1998
+ var TOKEN_RE = /([MLCQZ])|(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi;
1999
+ function transformPathData(d, m, decimals = 2) {
2000
+ const tokens = [];
2001
+ let match;
2002
+ TOKEN_RE.lastIndex = 0;
2003
+ while ((match = TOKEN_RE.exec(d)) !== null) tokens.push(match[0]);
2004
+ let out = "";
2005
+ let i = 0;
2006
+ const num2 = () => parseFloat(tokens[i++]);
2007
+ while (i < tokens.length) {
2008
+ const cmd = tokens[i++];
2009
+ if (cmd === "M" || cmd === "L") {
2010
+ const [x, y] = apply(m, num2(), num2());
2011
+ out += cmd + pack([x, y], decimals);
2012
+ } else if (cmd === "C") {
2013
+ const [x1, y1] = apply(m, num2(), num2());
2014
+ const [x2, y2] = apply(m, num2(), num2());
2015
+ const [x, y] = apply(m, num2(), num2());
2016
+ out += "C" + pack([x1, y1, x2, y2, x, y], decimals);
2017
+ } else if (cmd === "Q") {
2018
+ const [x1, y1] = apply(m, num2(), num2());
2019
+ const [x, y] = apply(m, num2(), num2());
2020
+ out += "Q" + pack([x1, y1, x, y], decimals);
2021
+ } else if (cmd === "Z" || cmd === "z") {
2022
+ out += "Z";
2023
+ }
2024
+ }
2025
+ return out;
2026
+ }
2027
+
1903
2028
  // src/PxAnimatorUtil.ts
1904
2029
  function bezierToSvgPath(path) {
1905
2030
  var _a, _b, _c, _d;
@@ -2272,6 +2397,24 @@ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
2272
2397
  }
2273
2398
  return { ts, ds };
2274
2399
  }
2400
+ function bezier2D_tForDistance(lut, distance) {
2401
+ const { ts, ds } = lut;
2402
+ const last = ds.length - 1;
2403
+ if (distance <= 0) return ts[0];
2404
+ if (distance >= ds[last]) return ts[last];
2405
+ let lo = 1;
2406
+ let hi = last;
2407
+ while (lo < hi) {
2408
+ const mid = lo + hi >>> 1;
2409
+ if (ds[mid] < distance) lo = mid + 1;
2410
+ else hi = mid;
2411
+ }
2412
+ const dPrev = ds[hi - 1];
2413
+ const dCur = ds[hi];
2414
+ const span = dCur - dPrev;
2415
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
2416
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
2417
+ }
2275
2418
  function bezier2D_arcAtT(lut, t) {
2276
2419
  const { ts, ds } = lut;
2277
2420
  const last = ts.length - 1;
@@ -2294,6 +2437,437 @@ function invertEasing(easing) {
2294
2437
  return cubicBezier(flipped);
2295
2438
  }
2296
2439
 
2440
+ // src/effects/pathSampler.ts
2441
+ var LUT_STEPS = 48;
2442
+ var CMD_RE = /[MmLlHhVvCcSsQqTtAaZz]/;
2443
+ function tokenize(d) {
2444
+ const tokens = [];
2445
+ const re = /([MmLlHhVvCcSsQqTtAaZz])|(-?\d*\.?\d+(?:[eE][-+]?\d+)?)/g;
2446
+ let m;
2447
+ while ((m = re.exec(d)) !== null) tokens.push(m[0]);
2448
+ return tokens;
2449
+ }
2450
+ function quadToCubic(P0, Qc, P3) {
2451
+ return {
2452
+ P1: [P0[0] + 2 / 3 * (Qc[0] - P0[0]), P0[1] + 2 / 3 * (Qc[1] - P0[1])],
2453
+ P2: [P3[0] + 2 / 3 * (Qc[0] - P3[0]), P3[1] + 2 / 3 * (Qc[1] - P3[1])]
2454
+ };
2455
+ }
2456
+ function parseCubics(d) {
2457
+ const tokens = tokenize(d);
2458
+ const segs = [];
2459
+ let i = 0;
2460
+ let cx = 0, cy = 0;
2461
+ let sx = 0, sy = 0;
2462
+ let pcx = 0, pcy = 0;
2463
+ let pqx = 0, pqy = 0;
2464
+ let prevCmd = "";
2465
+ const num2 = () => parseFloat(tokens[i++]);
2466
+ const push = (P1, P2, P3) => {
2467
+ const P0 = [cx, cy];
2468
+ const lut = bezier2D_arcLengthLUT(P0, P1, P2, P3, LUT_STEPS);
2469
+ segs.push({ P0, P1, P2, P3, lut, len: lut.ds[lut.ds.length - 1] });
2470
+ cx = P3[0];
2471
+ cy = P3[1];
2472
+ };
2473
+ const pushLine = (x, y) => {
2474
+ push(
2475
+ [cx + (x - cx) / 3, cy + (y - cy) / 3],
2476
+ [cx + 2 * (x - cx) / 3, cy + 2 * (y - cy) / 3],
2477
+ [x, y]
2478
+ );
2479
+ };
2480
+ while (i < tokens.length) {
2481
+ let cmd = tokens[i];
2482
+ if (CMD_RE.test(cmd)) i++;
2483
+ else cmd = prevCmd === "M" ? "L" : prevCmd === "m" ? "l" : prevCmd;
2484
+ const rel = cmd >= "a";
2485
+ const U = cmd.toUpperCase();
2486
+ if (U === "Z") {
2487
+ pushLine(sx, sy);
2488
+ cx = sx;
2489
+ cy = sy;
2490
+ prevCmd = cmd;
2491
+ continue;
2492
+ }
2493
+ if (U === "M") {
2494
+ const x = num2() + (rel ? cx : 0), y = num2() + (rel ? cy : 0);
2495
+ cx = x;
2496
+ cy = y;
2497
+ sx = x;
2498
+ sy = y;
2499
+ prevCmd = cmd;
2500
+ continue;
2501
+ }
2502
+ if (U === "L") {
2503
+ pushLine(num2() + (rel ? cx : 0), num2() + (rel ? cy : 0));
2504
+ } else if (U === "H") {
2505
+ pushLine(num2() + (rel ? cx : 0), cy);
2506
+ } else if (U === "V") {
2507
+ pushLine(cx, num2() + (rel ? cy : 0));
2508
+ } else if (U === "C") {
2509
+ const p1 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2510
+ const p2 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2511
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2512
+ pcx = p2[0];
2513
+ pcy = p2[1];
2514
+ push(p1, p2, p3);
2515
+ } else if (U === "S") {
2516
+ const smooth = prevCmd.toUpperCase() === "C" || prevCmd.toUpperCase() === "S";
2517
+ const p1 = smooth ? [2 * cx - pcx, 2 * cy - pcy] : [cx, cy];
2518
+ const p2 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2519
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2520
+ pcx = p2[0];
2521
+ pcy = p2[1];
2522
+ push(p1, p2, p3);
2523
+ } else if (U === "Q") {
2524
+ const qc = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2525
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2526
+ pqx = qc[0];
2527
+ pqy = qc[1];
2528
+ const { P1, P2 } = quadToCubic([cx, cy], qc, p3);
2529
+ push(P1, P2, p3);
2530
+ } else if (U === "T") {
2531
+ const smooth = prevCmd.toUpperCase() === "Q" || prevCmd.toUpperCase() === "T";
2532
+ const qc = smooth ? [2 * cx - pqx, 2 * cy - pqy] : [cx, cy];
2533
+ const p3 = [num2() + (rel ? cx : 0), num2() + (rel ? cy : 0)];
2534
+ pqx = qc[0];
2535
+ pqy = qc[1];
2536
+ const { P1, P2 } = quadToCubic([cx, cy], qc, p3);
2537
+ push(P1, P2, p3);
2538
+ } else if (U === "A") {
2539
+ i += 5;
2540
+ pushLine(num2() + (rel ? cx : 0), num2() + (rel ? cy : 0));
2541
+ } else {
2542
+ i++;
2543
+ continue;
2544
+ }
2545
+ prevCmd = cmd;
2546
+ }
2547
+ return segs.length ? segs : null;
2548
+ }
2549
+ function clamp2(v, lo, hi) {
2550
+ return v < lo ? lo : v > hi ? hi : v;
2551
+ }
2552
+ function createPathSampler(d) {
2553
+ const segs = parseCubics(d);
2554
+ if (!segs) return null;
2555
+ const cum = new Float64Array(segs.length + 1);
2556
+ for (let k = 0; k < segs.length; k++) cum[k + 1] = cum[k] + segs[k].len;
2557
+ const totalLength = cum[segs.length];
2558
+ const start = segs[0].P0, end = segs[segs.length - 1].P3;
2559
+ const closed = Math.hypot(end[0] - start[0], end[1] - start[1]) < 1e-3;
2560
+ const sampleOn = (dist) => {
2561
+ let k = 0;
2562
+ while (k < segs.length - 1 && dist > cum[k + 1]) k++;
2563
+ const seg = segs[k];
2564
+ const local = dist - cum[k];
2565
+ const t = seg.len > 0 ? bezier2D_tForDistance(seg.lut, local) : 0;
2566
+ const [x, y] = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2567
+ const [dx, dy] = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2568
+ return { x, y, angle: Math.atan2(dy, dx) };
2569
+ };
2570
+ return {
2571
+ totalLength,
2572
+ sampleAtDistance(dist) {
2573
+ if (!closed && (dist < 0 || dist > totalLength)) {
2574
+ const edge = dist < 0 ? 0 : totalLength;
2575
+ const p = sampleOn(edge);
2576
+ const over = dist - edge;
2577
+ return { x: p.x + Math.cos(p.angle) * over, y: p.y + Math.sin(p.angle) * over, angle: p.angle };
2578
+ }
2579
+ return sampleOn(clamp2(dist, 0, totalLength));
2580
+ }
2581
+ };
2582
+ }
2583
+
2584
+ // src/effects/textGlyphsEffect.ts
2585
+ var DEFAULT_FONT_SIZE = 16;
2586
+ var TEXT_ATTR_KEYS = [
2587
+ "fontFamily",
2588
+ "fontSize",
2589
+ "fontWeight",
2590
+ "fontStyle",
2591
+ "textAnchor",
2592
+ "letterSpacing",
2593
+ "wordSpacing",
2594
+ "textDecoration",
2595
+ "textTransform",
2596
+ "whiteSpace",
2597
+ "x",
2598
+ "y",
2599
+ "dx",
2600
+ "dy",
2601
+ "lengthAdjust",
2602
+ "fill",
2603
+ "stroke",
2604
+ "strokeWidth",
2605
+ "effects",
2606
+ TEXT_ATTR,
2607
+ TEXT_CONTENT_ATTR,
2608
+ "xml:space"
2609
+ ];
2610
+ function parseLen(v) {
2611
+ if (typeof v === "number") return v;
2612
+ if (typeof v === "string") {
2613
+ const n = parseFloat(v);
2614
+ return isNaN(n) ? void 0 : n;
2615
+ }
2616
+ return void 0;
2617
+ }
2618
+ function str(v) {
2619
+ return typeof v === "string" ? v : void 0;
2620
+ }
2621
+ function resolveStyle(node, parent) {
2622
+ var _a, _b, _c, _d, _e, _f, _g;
2623
+ return {
2624
+ fontFamily: (_a = str(node.fontFamily)) != null ? _a : parent.fontFamily,
2625
+ fontSize: (_b = parseLen(node.fontSize)) != null ? _b : parent.fontSize,
2626
+ fill: (_c = node.fill) != null ? _c : parent.fill,
2627
+ stroke: (_d = node.stroke) != null ? _d : parent.stroke,
2628
+ strokeWidth: (_e = node.strokeWidth) != null ? _e : parent.strokeWidth,
2629
+ letterSpacing: (_f = parseLen(node.letterSpacing)) != null ? _f : parent.letterSpacing,
2630
+ wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing
2631
+ };
2632
+ }
2633
+ function rootStyleOf(node) {
2634
+ var _a, _b, _c;
2635
+ return {
2636
+ fontFamily: str(node.fontFamily),
2637
+ fontSize: (_a = parseLen(node.fontSize)) != null ? _a : DEFAULT_FONT_SIZE,
2638
+ fill: node.fill,
2639
+ stroke: node.stroke,
2640
+ strokeWidth: node.strokeWidth,
2641
+ letterSpacing: (_b = parseLen(node.letterSpacing)) != null ? _b : 0,
2642
+ wordSpacing: (_c = parseLen(node.wordSpacing)) != null ? _c : 0
2643
+ };
2644
+ }
2645
+ function paintOf(s) {
2646
+ const p = {};
2647
+ if (s.fill !== void 0) p.fill = s.fill;
2648
+ if (s.stroke !== void 0) p.stroke = s.stroke;
2649
+ if (s.strokeWidth !== void 0) p.strokeWidth = s.strokeWidth;
2650
+ return p;
2651
+ }
2652
+ function glyphFontFor(s, glyphs, soleFont, warnings) {
2653
+ var _a;
2654
+ const gf = s.fontFamily ? glyphs[s.fontFamily] : soleFont;
2655
+ if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a = s.fontFamily) != null ? _a : "") + '"');
2656
+ return gf;
2657
+ }
2658
+ function soleFontOf(glyphs) {
2659
+ const names = Object.keys(glyphs);
2660
+ return names.length === 1 ? glyphs[names[0]] : void 0;
2661
+ }
2662
+ function materialiseGlyphTextHorizontal(node, opts) {
2663
+ var _a, _b, _c, _d;
2664
+ const { glyphs, create = jsonElementFactory, warnings } = opts;
2665
+ const soleFont = soleFontOf(glyphs);
2666
+ const pen = { x: (_a = parseLen(node.x)) != null ? _a : 0, y: (_b = parseLen(node.y)) != null ? _b : 0 };
2667
+ const placements = [];
2668
+ const lines = [{ start: pen.x, end: pen.x }];
2669
+ let line = 0;
2670
+ const renderChars = (content, s) => {
2671
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2672
+ if (!gf) return;
2673
+ const scale = s.fontSize / gf.unitsPerEm;
2674
+ const paint = paintOf(s);
2675
+ for (let i = 0; i < content.length; i++) {
2676
+ const ch = content.charAt(i);
2677
+ const g = gf.glyphs[ch];
2678
+ 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 });
2679
+ pen.x += (g ? g.width : 0) * scale + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2680
+ lines[line].end = pen.x;
2681
+ }
2682
+ };
2683
+ const walk = (el, parentStyle) => {
2684
+ var _a2, _b2, _c2, _d2;
2685
+ const s = resolveStyle(el, parentStyle);
2686
+ const x = parseLen(el.x);
2687
+ const y = parseLen(el.y);
2688
+ if (x !== void 0) {
2689
+ pen.x = x;
2690
+ line = lines.length;
2691
+ lines.push({ start: pen.x, end: pen.x });
2692
+ }
2693
+ if (y !== void 0) pen.y = y;
2694
+ pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
2695
+ pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
2696
+ const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
2697
+ if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
2698
+ if (el.children) for (const ch of el.children) walk(ch, s);
2699
+ };
2700
+ const rootStyle = rootStyleOf(node);
2701
+ if (node.children) for (const ch of node.children) walk(ch, rootStyle);
2702
+ const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
2703
+ if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
2704
+ const anchor = str(node.textAnchor);
2705
+ if (anchor === "middle" || anchor === "end") {
2706
+ for (const p of placements) {
2707
+ const w = lines[p.line].end - lines[p.line].start;
2708
+ const shift = anchor === "middle" ? -w / 2 : -w;
2709
+ p.m = [p.scale, 0, 0, p.scale, p.x + shift, p.y];
2710
+ }
2711
+ }
2712
+ return toGroup(node, buildPaths(placements, create, warnings), create);
2713
+ }
2714
+ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
2715
+ const cells = [];
2716
+ let adv = 0;
2717
+ const walk = (el, parentStyle) => {
2718
+ var _a, _b;
2719
+ const s = resolveStyle(el, parentStyle);
2720
+ const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
2721
+ if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
2722
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2723
+ if (gf) {
2724
+ const scale = s.fontSize / gf.unitsPerEm;
2725
+ const paint = paintOf(s);
2726
+ for (let i = 0; i < content.length; i++) {
2727
+ const ch = content.charAt(i);
2728
+ const g = gf.glyphs[ch];
2729
+ const glyphAdv = (g ? g.width : 0) * scale;
2730
+ if (g && g.d) cells.push({ glyphD: g.d, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });
2731
+ adv += glyphAdv + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2732
+ }
2733
+ }
2734
+ }
2735
+ if (el.children) for (const ch of el.children) walk(ch, s);
2736
+ };
2737
+ walk(node, rootStyleOf(node));
2738
+ return { cells, width: adv };
2739
+ }
2740
+ function alongAffine(sampler, dist, scale, widthEm) {
2741
+ const { x, y, angle } = sampler.sampleAtDistance(dist);
2742
+ const cos = Math.cos(angle), sin = Math.sin(angle), hw = widthEm / 2;
2743
+ return [scale * cos, scale * sin, -scale * sin, scale * cos, x - scale * cos * hw, y - scale * sin * hw];
2744
+ }
2745
+ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLength) {
2746
+ var _a;
2747
+ const { glyphs, create = jsonElementFactory, warnings } = opts;
2748
+ const sampler = pathD ? createPathSampler(pathD) : null;
2749
+ if (!sampler) {
2750
+ warnings == null ? void 0 : warnings.push("textGlyphs: unparsable along-path geometry");
2751
+ return null;
2752
+ }
2753
+ const soleFont = soleFontOf(glyphs);
2754
+ const { cells, width } = collectAlongPathCells(node, glyphs, soleFont, warnings);
2755
+ if (!cells.length) return toGroup(node, [], create);
2756
+ if (textLength && textLength > 0 && width > 0) {
2757
+ const k = textLength / width;
2758
+ for (const c of cells) c.midBase *= k;
2759
+ }
2760
+ const so = readAnimatable(startOffset);
2761
+ if (so.kind === "animated" /* Animated */ && so.keyframes.length >= 2) {
2762
+ return toGroup(node, buildAnimatedAlongPath(cells, sampler, so.keyframes, so.loop, create), create);
2763
+ }
2764
+ 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;
2765
+ const placements = cells.map((c) => ({
2766
+ glyphD: c.glyphD,
2767
+ paint: c.paint,
2768
+ m: alongAffine(sampler, base + c.midBase, c.scale, c.widthEm)
2769
+ }));
2770
+ return toGroup(node, buildPaths(placements, create, warnings), create);
2771
+ }
2772
+ var ALONG_PATH_MAX_STEPS = 48;
2773
+ var ALONG_PATH_MAX_STEPS_PER_SEGMENT = 64;
2774
+ function roundN(v, n) {
2775
+ const f = __pow(10, n);
2776
+ return Math.round(v * f) / f;
2777
+ }
2778
+ function buildAnimatedAlongPath(cells, sampler, sokfs, loop, create) {
2779
+ const step = Math.max(sampler.totalLength / ALONG_PATH_MAX_STEPS, 0.5);
2780
+ const timeOf = (kf) => Number(kf.time) || 0;
2781
+ const offOf = (kf) => Number(kf.value) || 0;
2782
+ const out = [];
2783
+ for (const c of cells) {
2784
+ const centred = [c.scale, 0, 0, c.scale, -c.scale * (c.widthEm / 2), 0];
2785
+ const d = transformPathData(c.glyphD, centred);
2786
+ const sampleKf = (dist, time) => {
2787
+ const { x, y, angle } = sampler.sampleAtDistance(dist);
2788
+ return {
2789
+ time,
2790
+ value: {
2791
+ ["translate" /* Translate */]: [roundN(x, 3), roundN(y, 3)],
2792
+ ["rotate" /* Rotate */]: roundN(angle * 180 / Math.PI, 3)
2793
+ }
2794
+ };
2795
+ };
2796
+ const kfs = [sampleKf(offOf(sokfs[0]) + c.midBase, timeOf(sokfs[0]))];
2797
+ for (let k = 1; k < sokfs.length; k++) {
2798
+ const t0 = timeOf(sokfs[k - 1]), t1 = timeOf(sokfs[k]);
2799
+ const o0 = offOf(sokfs[k - 1]), o1 = offOf(sokfs[k]);
2800
+ const n = Math.min(ALONG_PATH_MAX_STEPS_PER_SEGMENT, Math.max(1, Math.ceil(Math.abs(o1 - o0) / step)));
2801
+ for (let s = 1; s <= n; s++) {
2802
+ const f = s / n;
2803
+ kfs.push(sampleKf(o0 + f * (o1 - o0) + c.midBase, t0 + f * (t1 - t0)));
2804
+ }
2805
+ }
2806
+ const transform = { keyframes: kfs };
2807
+ if (loop !== void 0) transform.loop = loop;
2808
+ out.push(create("path", __spreadProps(__spreadValues({ d }, paintProps(c.paint)), { animate: { transform } }), []));
2809
+ }
2810
+ return out;
2811
+ }
2812
+ function paintProps(paint) {
2813
+ const p = {};
2814
+ if (paint.fill !== void 0) p.fill = paint.fill;
2815
+ if (paint.stroke !== void 0) p.stroke = paint.stroke;
2816
+ if (paint.strokeWidth !== void 0) p.strokeWidth = paint.strokeWidth;
2817
+ return p;
2818
+ }
2819
+ function buildPaths(placements, create, warnings) {
2820
+ var _a, _b, _c;
2821
+ if (!placements.length) {
2822
+ warnings == null ? void 0 : warnings.push("textGlyphs: nothing to render");
2823
+ return [];
2824
+ }
2825
+ const byPaint = /* @__PURE__ */ new Map();
2826
+ for (const p of placements) {
2827
+ 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]);
2828
+ const baked = transformPathData(p.glyphD, p.m);
2829
+ const entry = byPaint.get(key);
2830
+ if (entry) entry.d += baked;
2831
+ else byPaint.set(key, { paint: p.paint, d: baked });
2832
+ }
2833
+ const out = [];
2834
+ for (const { paint, d } of byPaint.values()) out.push(create("path", __spreadValues({ d }, paintProps(paint)), []));
2835
+ return out;
2836
+ }
2837
+ function toGroup(node, children, create) {
2838
+ const gProps = {};
2839
+ for (const k of Object.keys(node)) {
2840
+ if (k === "type" || k === "children" || TEXT_ATTR_KEYS.indexOf(k) !== -1) continue;
2841
+ gProps[k] = node[k];
2842
+ }
2843
+ if (gProps.style && typeof gProps.style === "object") {
2844
+ const style = __spreadValues({}, gProps.style);
2845
+ delete style["white-space"];
2846
+ if (Object.keys(style).length) gProps.style = style;
2847
+ else delete gProps.style;
2848
+ }
2849
+ return create("g", gProps, children);
2850
+ }
2851
+ function materialiseGlyphText(node, opts) {
2852
+ if (opts.alongPath) return materialiseGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength);
2853
+ return materialiseGlyphTextHorizontal(node, opts);
2854
+ }
2855
+ function applyTextGlyphsEffect(node, fx, ctx) {
2856
+ if (!(fx == null ? void 0 : fx.useGlyphs)) return node;
2857
+ if (!ctx.glyphs) {
2858
+ ctx.warnings.push("textGlyphs: no definitions.glyphs \u2014 left as native <text>");
2859
+ return node;
2860
+ }
2861
+ return materialiseGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
2862
+ }
2863
+ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength) {
2864
+ if (!ctx.glyphs) {
2865
+ ctx.warnings.push("textGlyphs: no definitions.glyphs");
2866
+ return null;
2867
+ }
2868
+ return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength);
2869
+ }
2870
+
2297
2871
  // src/PxMotionPath.ts
2298
2872
  function getKfTranslate(kf) {
2299
2873
  var _a;
@@ -2737,12 +3311,12 @@ function parseSvgPathToBezier(d) {
2737
3311
  }
2738
3312
  return res;
2739
3313
  }
2740
- function extractPathData(str) {
2741
- if (str.startsWith("path(") && str.endsWith(")")) {
2742
- return str.slice(5, -1);
3314
+ function extractPathData(str2) {
3315
+ if (str2.startsWith("path(") && str2.endsWith(")")) {
3316
+ return str2.slice(5, -1);
2743
3317
  }
2744
- if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str)) {
2745
- return str;
3318
+ if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str2)) {
3319
+ return str2;
2746
3320
  }
2747
3321
  return void 0;
2748
3322
  }
@@ -2952,16 +3526,67 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2952
3526
  looped.push(pushed);
2953
3527
  }
2954
3528
  }
2955
- for (let rep = 0; rep < fullReps; rep++) {
2956
- const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
2957
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2958
- const repStart = fillStart + rep * segDuration;
2959
- appendRep(repStart, isReversed);
3529
+ function appendRepTail(repStart, isReversed, tailFraction) {
3530
+ let entries;
3531
+ if (isReversed) {
3532
+ entries = [];
3533
+ for (let i = template.length - 1; i >= 0; i--) {
3534
+ entries.push({
3535
+ relT: 1 - template[i].relT,
3536
+ v: template[i].v,
3537
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
3538
+ tangentIn: template[i].tangentOut,
3539
+ tangentOut: template[i].tangentIn
3540
+ });
3541
+ }
3542
+ } else {
3543
+ entries = template;
3544
+ }
3545
+ const startRelT = 1 - tailFraction;
3546
+ for (let i = 0; i < entries.length; i++) {
3547
+ const entry = entries[i];
3548
+ if (entry.relT < startRelT - 1e-9) continue;
3549
+ const prev = entries[i - 1];
3550
+ if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {
3551
+ const intervalSpan = entry.relT - prev.relT;
3552
+ const localFrac = (startRelT - prev.relT) / intervalSpan;
3553
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
3554
+ const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
3555
+ const { right: rightEasing } = splitEasing(prev.e, localFrac);
3556
+ looped.push({ t: repStart, v: startValue, e: rightEasing });
3557
+ }
3558
+ const pushed = {
3559
+ t: repStart + (entry.relT - startRelT) * segDuration,
3560
+ v: entry.v,
3561
+ e: i < entries.length - 1 ? entry.e : void 0
3562
+ };
3563
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
3564
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
3565
+ looped.push(pushed);
3566
+ }
2960
3567
  }
2961
- if (partialFraction > 1e-9) {
2962
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2963
- const repStart = fillStart + fullReps * segDuration;
2964
- appendRep(repStart, isReversed, partialFraction);
3568
+ if (loop.before) {
3569
+ if (partialFraction > 1e-9) {
3570
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
3571
+ appendRepTail(fillStart, isReversed, partialFraction);
3572
+ }
3573
+ for (let rep = 0; rep < fullReps; rep++) {
3574
+ const distFromBoundary = fullReps - 1 - rep;
3575
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
3576
+ const repStart = fillStart + remainder + rep * segDuration;
3577
+ appendRep(repStart, isReversed);
3578
+ }
3579
+ } else {
3580
+ for (let rep = 0; rep < fullReps; rep++) {
3581
+ const isReversed = !!loop.alternate && rep % 2 === 0;
3582
+ const repStart = fillStart + rep * segDuration;
3583
+ appendRep(repStart, isReversed);
3584
+ }
3585
+ if (partialFraction > 1e-9) {
3586
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
3587
+ const repStart = fillStart + fullReps * segDuration;
3588
+ appendRep(repStart, isReversed, partialFraction);
3589
+ }
2965
3590
  }
2966
3591
  if (loop.before) {
2967
3592
  return [...looped, ...keyframes];
@@ -3280,7 +3905,6 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
3280
3905
  if (!trimPath) return node;
3281
3906
  const trimAllAsOne = !!trimPath.trimAllAsOne;
3282
3907
  const leafEntries = [];
3283
- let acc = 0;
3284
3908
  const measure = (n) => {
3285
3909
  if (Array.isArray(n.children) && n.children.length > 0) {
3286
3910
  for (const ch of n.children) measure(ch);
@@ -3292,15 +3916,22 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
3292
3916
  if (!subpaths.length) return;
3293
3917
  const entry = { leaf: n, subpaths: [] };
3294
3918
  for (const sp of subpaths) {
3295
- if (!trimAllAsOne) acc = 0;
3296
3919
  const lengthPx = pxBezierPathLength(sp);
3297
- entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: acc });
3298
- acc += lengthPx;
3920
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });
3299
3921
  }
3300
3922
  leafEntries.push(entry);
3301
3923
  };
3302
3924
  measure(node);
3303
3925
  if (!leafEntries.length) return node;
3926
+ let acc = 0;
3927
+ const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
3928
+ for (const entry of iterOrder) {
3929
+ for (const sp of entry.subpaths) {
3930
+ if (!trimAllAsOne) acc = 0;
3931
+ sp.startOffsetPx = acc;
3932
+ acc += sp.lengthPx;
3933
+ }
3934
+ }
3304
3935
  const chainLengthPx = acc;
3305
3936
  if (trimAllAsOne && chainLengthPx < 1e-3) return node;
3306
3937
  const offsetReadRaw = readAnimatable(trimPath.offset);
@@ -3427,10 +4058,12 @@ function computeAnimAttr(read, map) {
3427
4058
  value: map((_c = kf.value) != null ? _c : kf.v),
3428
4059
  easing: (_d = kf.easing) != null ? _d : kf.e
3429
4060
  };
3430
- })
4061
+ }),
4062
+ loop: read.loop
3431
4063
  };
3432
4064
  }
3433
4065
  function applyAttr(node, attrName, attr) {
4066
+ var _a, _b;
3434
4067
  if (!attr) return;
3435
4068
  if (attr.kind === "static" /* Static */) {
3436
4069
  node[attrName] = attr.value;
@@ -3438,9 +4071,16 @@ function applyAttr(node, attrName, attr) {
3438
4071
  }
3439
4072
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3440
4073
  const animate = __spreadValues({}, prevAnimate || {});
3441
- animate[attrName] = { keyframes: attr.keyframes };
4074
+ const block = { keyframes: attr.keyframes };
4075
+ if (attr.loop !== void 0) block.loop = attr.loop;
4076
+ animate[attrName] = block;
3442
4077
  node.animate = animate;
4078
+ const firstKf = attr.keyframes[0];
4079
+ if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4080
+ node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4081
+ }
3443
4082
  }
4083
+ var OPACITY_STEP_MS = 10;
3444
4084
  function computeOpacityFromRange(rangeRead) {
3445
4085
  var _a, _b, _c, _d, _e, _f;
3446
4086
  const hide = (v) => v[0] === v[1];
@@ -3466,9 +4106,9 @@ function computeOpacityFromRange(rangeRead) {
3466
4106
  const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
3467
4107
  if (prevHide && !nextHide) {
3468
4108
  out.push({ time: t, value: 0 });
3469
- out.push({ time: t + 1, value: 1 });
4109
+ out.push({ time: t + OPACITY_STEP_MS, value: 1 });
3470
4110
  } else if (!prevHide && nextHide) {
3471
- out.push({ time: t - 1, value: 1 });
4111
+ out.push({ time: t - OPACITY_STEP_MS, value: 1 });
3472
4112
  out.push({ time: t, value: 0 });
3473
4113
  }
3474
4114
  }
@@ -3601,6 +4241,7 @@ function rectToPathD(node) {
3601
4241
 
3602
4242
  // src/effects/PlayerEffectsUtil.ts
3603
4243
  function applyPlayerEffects(root) {
4244
+ var _a, _b;
3604
4245
  const ctx = {
3605
4246
  defs: [],
3606
4247
  warnings: [],
@@ -3608,7 +4249,11 @@ function applyPlayerEffects(root) {
3608
4249
  idMap: /* @__PURE__ */ new Map(),
3609
4250
  nextId: 0,
3610
4251
  contentRefInnerIds: /* @__PURE__ */ new Map(),
3611
- maskAncestorChains: /* @__PURE__ */ new Map()
4252
+ maskAncestorChains: /* @__PURE__ */ new Map(),
4253
+ // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
4254
+ // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
4255
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
4256
+ glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
3612
4257
  };
3613
4258
  const working = clone(root);
3614
4259
  indexById(working, ctx.idMap);
@@ -3620,27 +4265,46 @@ function applyPlayerEffects(root) {
3620
4265
  return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
3621
4266
  }
3622
4267
  function applyPlayerEffects_exceptRetime(node, ctx) {
4268
+ var _a;
3623
4269
  if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
3624
4270
  const fx = node.effects;
3625
4271
  const originalId = typeof node.id === "string" ? node.id : void 0;
3626
4272
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
3627
4273
  if (!fx && !innerIdForContentRef) return node;
3628
- const { transformation, repeater, maskedBy, trimPath, retime, ref, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
4274
+ const { transformation, repeater, maskedBy, trimPath, clone: cloneFx, fillGradient, strokeGradient, textAlongPath, text } = fx != null ? fx : {};
3629
4275
  const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
3630
4276
  if (fx) delete node.effects;
3631
4277
  let n = node;
3632
- n = applyTextAlongPathEffect(n, textAlongPath, ctx);
4278
+ let consumedByGlyphs = false;
4279
+ if (text == null ? void 0 : text.useGlyphs) {
4280
+ if (textAlongPath) {
4281
+ const pathNode = typeof textAlongPath.href === "string" ? ctx.idMap.get(textAlongPath.href) : void 0;
4282
+ const pathD = pathNode && typeof pathNode.d === "string" ? pathNode.d : void 0;
4283
+ const rawTL = textAlongPath.textLength;
4284
+ 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;
4285
+ const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textAlongPath.startOffset, textLength);
4286
+ if (glyphed) {
4287
+ n = glyphed;
4288
+ consumedByGlyphs = true;
4289
+ }
4290
+ } else {
4291
+ n = applyTextGlyphsEffect(n, text, ctx);
4292
+ consumedByGlyphs = true;
4293
+ }
4294
+ }
4295
+ if (!consumedByGlyphs) n = applyTextAlongPathEffect(n, textAlongPath, ctx);
3633
4296
  n = applyFillGradientEffect(n, fillGradient, ctx);
3634
4297
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
3635
4298
  n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
3636
4299
  n = applyRepeaterEffect(n, repeater, ctx);
3637
4300
  n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
3638
4301
  if (innerIdForContentRef) {
4302
+ applyRefHref(n, cloneFx, ctx);
3639
4303
  n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
3640
4304
  } else {
3641
- n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
4305
+ n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
3642
4306
  }
3643
- if (retime) node.effects = { retime };
4307
+ if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
3644
4308
  if (originalId) ctx.idMap.set(originalId, n);
3645
4309
  return n;
3646
4310
  }
@@ -3657,13 +4321,30 @@ function materialiseAnimatedUseInstances(root) {
3657
4321
  if (animatedIds.size === 0) return root;
3658
4322
  let idCounter = 0;
3659
4323
  const genId3 = () => "_lw_use_mat_" + ++idCounter;
4324
+ const rootViewport = readRootViewport(root);
3660
4325
  const defsCollector = [];
3661
- const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector);
4326
+ const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
3662
4327
  if (defsCollector.length === 0) return walked;
3663
4328
  const defsNode = { type: "defs", children: defsCollector };
3664
4329
  const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
3665
4330
  return __spreadProps(__spreadValues({}, walked), { children: newChildren });
3666
4331
  }
4332
+ function readRootViewport(root) {
4333
+ const vb = parseViewBox(root.viewBox);
4334
+ if (vb) return [vb[2], vb[3]];
4335
+ const w = numericAttr(root.width);
4336
+ const h = numericAttr(root.height);
4337
+ if (w !== void 0 && h !== void 0) return [w, h];
4338
+ return [1, 1];
4339
+ }
4340
+ function numericAttr(v) {
4341
+ if (typeof v === "number") return v;
4342
+ if (typeof v === "string") {
4343
+ const n = parseFloat(v);
4344
+ return Number.isFinite(n) ? n : void 0;
4345
+ }
4346
+ return void 0;
4347
+ }
3667
4348
  function buildIdMap(root) {
3668
4349
  const map = /* @__PURE__ */ new Map();
3669
4350
  const visit = (n) => {
@@ -3715,16 +4396,22 @@ function stripHash2(href) {
3715
4396
  if (typeof href !== "string") return void 0;
3716
4397
  return href.startsWith("#") ? href.slice(1) : href;
3717
4398
  }
3718
- function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector) {
4399
+ function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
4400
+ var _a, _b, _c, _d;
3719
4401
  const clone2 = deepClonePxNode(target);
3720
4402
  regenerateIdsAndRewriteRefs(clone2, genId3);
3721
- const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector) : clone2;
3722
- const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector);
4403
+ const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
4404
+ const useW = (_b = (_a = numericAttr(useNode.width)) != null ? _a : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
4405
+ const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
4406
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
4407
+ const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
3723
4408
  const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materialisedClone] });
3724
4409
  delete newNode.href;
4410
+ delete newNode.width;
4411
+ delete newNode.height;
3725
4412
  return applyUseOffsetToG(newNode);
3726
4413
  }
3727
- function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
4414
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector, useW, useH) {
3728
4415
  const viewBox = parseViewBox(symbolNode.viewBox);
3729
4416
  const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
3730
4417
  delete g.viewBox;
@@ -3733,9 +4420,14 @@ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
3733
4420
  delete g.height;
3734
4421
  if (!viewBox) return g;
3735
4422
  const [vbX, vbY, vbW, vbH] = viewBox;
3736
- if (vbX !== 0 || vbY !== 0) {
3737
- g.transform = "translate(" + -vbX + "," + -vbY + ")";
3738
- }
4423
+ const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;
4424
+ const xOff = (useW - vbW * scale) / 2;
4425
+ const yOff = (useH - vbH * scale) / 2;
4426
+ const parts = [];
4427
+ if (xOff !== 0 || yOff !== 0) parts.push("translate(" + xOff + "," + yOff + ")");
4428
+ if (scale !== 1) parts.push("scale(" + scale + ")");
4429
+ if (vbX !== 0 || vbY !== 0) parts.push("translate(" + -vbX + "," + -vbY + ")");
4430
+ if (parts.length) g.transform = parts.join("");
3739
4431
  const clipId = genId3();
3740
4432
  defsCollector.push({
3741
4433
  type: "clipPath",
@@ -3751,18 +4443,18 @@ function parseViewBox(v) {
3751
4443
  if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
3752
4444
  return [parts[0], parts[1], parts[2], parts[3]];
3753
4445
  }
3754
- function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector) {
4446
+ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector, rootViewport) {
3755
4447
  if (node.type === "use" && typeof node.href === "string") {
3756
4448
  const targetId = stripHash2(node.href);
3757
4449
  if (targetId && animatedIds.has(targetId)) {
3758
4450
  const target = idMap.get(targetId);
3759
- if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector);
4451
+ if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector, rootViewport);
3760
4452
  }
3761
4453
  }
3762
4454
  if (!node.children) return node;
3763
4455
  let changed = false;
3764
4456
  const newChildren = node.children.map((ch) => {
3765
- const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector);
4457
+ const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector, rootViewport);
3766
4458
  if (m !== ch) changed = true;
3767
4459
  return m;
3768
4460
  });
@@ -3778,6 +4470,36 @@ function materialiseAllInTree(doc, engine, opts) {
3778
4470
  if (engine === PxAnimatorEngine.webapi) {
3779
4471
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
3780
4472
  root = materialiseAnimatedUseInstances(root);
4473
+ root = pruneUnreferencedDefs(root);
4474
+ }
4475
+ return root;
4476
+ }
4477
+ function pruneUnreferencedDefs(root) {
4478
+ const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
4479
+ const walk = (n, fn) => {
4480
+ var _a;
4481
+ fn(n);
4482
+ (_a = n.children) == null ? void 0 : _a.forEach((c) => walk(c, fn));
4483
+ };
4484
+ let changed = true;
4485
+ while (changed) {
4486
+ changed = false;
4487
+ const referenced = /* @__PURE__ */ new Set();
4488
+ walk(root, (n) => {
4489
+ if (n.type === "use" && typeof n.href === "string") referenced.add(stripHash3(n.href));
4490
+ });
4491
+ walk(root, (n) => {
4492
+ if (!n.children) return;
4493
+ let kept = n.children;
4494
+ if (n.type === "defs") {
4495
+ kept = kept.filter((c) => !((c.type === "g" || c.type === "symbol") && typeof c.id === "string" && !referenced.has(c.id)));
4496
+ }
4497
+ kept = kept.filter((c) => !(c.type === "defs" && (!c.children || c.children.length === 0)));
4498
+ if (kept.length !== n.children.length) {
4499
+ n.children = kept;
4500
+ changed = true;
4501
+ }
4502
+ });
3781
4503
  }
3782
4504
  return root;
3783
4505
  }
@@ -3810,6 +4532,13 @@ var URL_VALUE_ATTRS_LOWER = /* @__PURE__ */ new Set([
3810
4532
  ]);
3811
4533
  var IMAGE_REF_ATTRS_LOWER = /* @__PURE__ */ new Set(["href", "xlink:href", "src"]);
3812
4534
  var DATA_RASTER_IMAGE_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,/i;
4535
+ var DATA_SVG_IMAGE_RE = /^data:image\/svg\+xml(?:;[^,]*)?,/i;
4536
+ var DATA_IMAGE_BASE64_PAYLOAD_RE = /^data:image\/[^;,]*;base64,([A-Za-z0-9+/]{8})/i;
4537
+ var BASE64_RASTER_MAGICS = ["iVBORw0K", "/9j/", "R0lGOD", "UklGR", "Qk"];
4538
+ function isContentSniffedRasterImage(str2) {
4539
+ const m = DATA_IMAGE_BASE64_PAYLOAD_RE.exec(str2);
4540
+ return !!m && BASE64_RASTER_MAGICS.some((magic) => m[1].startsWith(magic));
4541
+ }
3813
4542
  function isDangerousAttrName(nameLower) {
3814
4543
  if (nameLower.startsWith("on")) return true;
3815
4544
  return false;
@@ -3821,19 +4550,19 @@ function sanitiseAttributeValue(name, value) {
3821
4550
  return void 0;
3822
4551
  }
3823
4552
  if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopcolor") {
3824
- const str = String(value);
3825
- if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
4553
+ const str2 = String(value);
4554
+ if (str2.includes("url(") && !/^url\(#[^)]+\)$/.test(str2)) {
3826
4555
  console.warn('Attribute "' + nameLower + '" blocked: url() must be internal url(#id), got:', value);
3827
4556
  return void 0;
3828
4557
  }
3829
4558
  return value;
3830
4559
  }
3831
4560
  if (URL_VALUE_ATTRS_LOWER.has(nameLower)) {
3832
- const str = String(value);
3833
- if (str.startsWith("#")) return value;
3834
- if (/^url\(#[^)]+\)$/.test(str)) return value;
3835
- if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && DATA_RASTER_IMAGE_RE.test(str)) return value;
3836
- console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or base64 raster data: URI, got:', value);
4561
+ const str2 = String(value);
4562
+ if (str2.startsWith("#")) return value;
4563
+ if (/^url\(#[^)]+\)$/.test(str2)) return value;
4564
+ if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && (DATA_RASTER_IMAGE_RE.test(str2) || DATA_SVG_IMAGE_RE.test(str2) || isContentSniffedRasterImage(str2))) return value;
4565
+ console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or data:image/\u2026 URI, got:', value);
3837
4566
  return void 0;
3838
4567
  }
3839
4568
  return value;
@@ -3862,7 +4591,7 @@ function createElement(tagName, normalisedProps, style, children, textContent) {
3862
4591
  if (textContent) element.textContent = textContent;
3863
4592
  return element;
3864
4593
  }
3865
- function resolveStyle(style, defs) {
4594
+ function resolveStyle2(style, defs) {
3866
4595
  var _a;
3867
4596
  if (!style) return void 0;
3868
4597
  if (typeof style === "string") {
@@ -3898,7 +4627,7 @@ function renderNode(node, defs) {
3898
4627
  if (!node) return null;
3899
4628
  const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
3900
4629
  const nodeDefs = getDefs(node) || defs;
3901
- const resolvedStyle = resolveStyle(style, nodeDefs);
4630
+ const resolvedStyle = resolveStyle2(style, nodeDefs);
3902
4631
  let childElements;
3903
4632
  if (children) {
3904
4633
  for (const ch of children) {
@@ -4930,6 +5659,7 @@ export {
4930
5659
  PxAttrValueSchema,
4931
5660
  PxBezierPathSchema,
4932
5661
  PxBindingSchema,
5662
+ PxCloneEffectSchema,
4933
5663
  PxDefsSchema,
4934
5664
  PxEasingOrRefSchema,
4935
5665
  PxEffectsSchema,
@@ -4945,12 +5675,12 @@ export {
4945
5675
  PxNodeBase,
4946
5676
  PxNodeSchema,
4947
5677
  PxPropertyAnimationSchema,
4948
- PxRefEffectSchema,
4949
5678
  PxRepeaterEffectSchema,
4950
5679
  PxRetimeEffectSchema,
4951
5680
  PxStrokeGradientEffectSchema,
4952
5681
  PxSvgNodeExtra,
4953
5682
  PxTextAlongPathEffectSchema,
5683
+ PxTextEffectSchema,
4954
5684
  PxTransformPartsSchema,
4955
5685
  PxTransformValueSchema,
4956
5686
  PxTransformationEffectSchema,
@@ -4978,9 +5708,13 @@ export {
4978
5708
  getNormalizedProps,
4979
5709
  isPxElementFileFormat,
4980
5710
  isPxElementFileFormatDeep,
5711
+ jsonElementFactory,
4981
5712
  loadTagAnimators,
4982
5713
  materialiseAllInTree,
4983
5714
  materialiseAnimatedUseInstances,
5715
+ materialiseGlyphText,
5716
+ materialiseGlyphTextAlongPath,
5717
+ materialiseGlyphTextHorizontal,
4984
5718
  materialiseInternalLoopsInPropAnim,
4985
5719
  materialiseInternalLoopsInTree,
4986
5720
  materialiseMotionPathInPropAnim,