@pixodesk/svg-animator-web 1.0.18 → 1.0.20

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
@@ -83,8 +83,8 @@ __export(index_exports, {
83
83
  PxRetimeEffectSchema: () => PxRetimeEffectSchema,
84
84
  PxStrokeGradientEffectSchema: () => PxStrokeGradientEffectSchema,
85
85
  PxSvgNodeExtra: () => PxSvgNodeExtra,
86
- PxTextAlongPathEffectSchema: () => PxTextAlongPathEffectSchema,
87
86
  PxTextEffectSchema: () => PxTextEffectSchema,
87
+ PxTextPathEffectSchema: () => PxTextPathEffectSchema,
88
88
  PxTransformPartsSchema: () => PxTransformPartsSchema,
89
89
  PxTransformValueSchema: () => PxTransformValueSchema,
90
90
  PxTransformationEffectSchema: () => PxTransformationEffectSchema,
@@ -100,10 +100,12 @@ __export(index_exports, {
100
100
  createAnimatorImpl: () => createAnimatorImpl,
101
101
  createBasicFrameLoopAnimator: () => createBasicFrameLoopAnimator,
102
102
  createFrameLoopAnimator: () => createFrameLoopAnimator,
103
+ createPathSampler: () => createPathSampler,
103
104
  createWebApiAnimator: () => createWebApiAnimator,
104
105
  describeSchema: () => describeSchema,
105
106
  diffInEffect: () => diffInEffect,
106
107
  evaluateMotionPathSegment: () => evaluateMotionPathSegment,
108
+ extendedPathForBrowser: () => extendedPathForBrowser,
107
109
  generateNewIds: () => generateNewIds,
108
110
  getAnimatorConfig: () => getAnimatorConfig,
109
111
  getBindings: () => getBindings,
@@ -113,6 +115,7 @@ __export(index_exports, {
113
115
  isPxElementFileFormat: () => isPxElementFileFormat,
114
116
  isPxElementFileFormatDeep: () => isPxElementFileFormatDeep,
115
117
  jsonElementFactory: () => jsonElementFactory,
118
+ layoutGlyphTextChars: () => layoutGlyphTextChars,
116
119
  loadTagAnimators: () => loadTagAnimators,
117
120
  materialiseAllInTree: () => materialiseAllInTree,
118
121
  materialiseAnimatedUseInstances: () => materialiseAnimatedUseInstances,
@@ -129,6 +132,7 @@ __export(index_exports, {
129
132
  renderNode: () => renderNode,
130
133
  schemaKeys: () => schemaKeys,
131
134
  setupAnimationTriggers: () => setupAnimationTriggers,
135
+ shiftAnimatable: () => shiftAnimatable,
132
136
  toRGBA: () => toRGBA,
133
137
  validateNodeEffects: () => validateNodeEffects,
134
138
  visualModelAt: () => visualModelAt
@@ -1088,6 +1092,10 @@ var PxMaskedByEffectSchema = implementsInterface()(px.object({
1088
1092
  maskUnits: px.string().optional(),
1089
1093
  maskContentUnits: px.string().optional()
1090
1094
  }));
1095
+ var PxClipPathEffectSchema = implementsInterface()(px.object({
1096
+ d: px.string().optional(),
1097
+ animate: PxPropertyAnimationSchema.optional()
1098
+ }));
1091
1099
  var PxTrimPathEffectSchema = implementsInterface()(px.object({
1092
1100
  offset: PxAnimatableNumberSchema.optional(),
1093
1101
  range: PxAnimatableVec2Schema.optional(),
@@ -1139,8 +1147,9 @@ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1139
1147
  gradientTransform: px.string().optional()
1140
1148
  }));
1141
1149
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1142
- var PxTextAlongPathEffectSchema = implementsInterface()(px.object({
1143
- href: px.string(),
1150
+ var PxTextPathEffectSchema = implementsInterface()(px.object({
1151
+ path: px.string(),
1152
+ pathOverflow: px.string().optional(),
1144
1153
  lengthAdjust: px.string().optional(),
1145
1154
  method: px.string().optional(),
1146
1155
  spacing: px.string().optional(),
@@ -1154,12 +1163,13 @@ var PxEffectsSchema = implementsInterface()(px.object({
1154
1163
  transformation: PxTransformationEffectSchema.optional(),
1155
1164
  repeater: PxRepeaterEffectSchema.optional(),
1156
1165
  maskedBy: PxMaskedByEffectSchema.optional(),
1166
+ clipPath: PxClipPathEffectSchema.optional(),
1157
1167
  trimPath: PxTrimPathEffectSchema.optional(),
1158
1168
  clone: PxCloneEffectSchema.optional(),
1159
1169
  isCombinedShape: px.boolean().optional(),
1160
1170
  fillGradient: PxFillGradientEffectSchema.optional(),
1161
1171
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
1162
- textAlongPath: PxTextAlongPathEffectSchema.optional(),
1172
+ textPath: PxTextPathEffectSchema.optional(),
1163
1173
  text: PxTextEffectSchema.optional()
1164
1174
  }));
1165
1175
  function validateNodeEffects(root, opts) {
@@ -1460,6 +1470,17 @@ function formatOffset(o) {
1460
1470
  return pct + "%";
1461
1471
  }
1462
1472
 
1473
+ // src/effects/clipPathEffect.ts
1474
+ function applyClipPathEffect(node, fx, ctx) {
1475
+ if (!fx || !fx.d && !fx.animate) return node;
1476
+ const clipId = genId(ctx, "clip");
1477
+ const pathChild = { type: "path", d: fx.d };
1478
+ if (fx.animate) pathChild.animate = { d: fx.animate };
1479
+ ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
1480
+ node.clipPath = "url(#" + clipId + ")";
1481
+ return node;
1482
+ }
1483
+
1463
1484
  // src/effects/maskedByEffect.ts
1464
1485
  function applyMaskedByEffect(node, fx, transformation, ctx) {
1465
1486
  if (!fx) return node;
@@ -1902,6 +1923,9 @@ function synthesiseScale(raw, i) {
1902
1923
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
1903
1924
  function asRetime(r) {
1904
1925
  var _a, _b;
1926
+ if (r.timeCrop) {
1927
+ console.warn("retime: `timeCrop` is not supported yet and will be ignored");
1928
+ }
1905
1929
  return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
1906
1930
  }
1907
1931
  function concatRetime(child, parent) {
@@ -2032,103 +2056,6 @@ function remapKeyframeTimesOnly(node, start, stretch) {
2032
2056
  }
2033
2057
  }
2034
2058
 
2035
- // src/effects/textAlongPathEffect.ts
2036
- function applyTextAlongPathEffect(node, fx, _ctx) {
2037
- var _a;
2038
- if (!fx) return node;
2039
- const href = typeof fx.href === "string" && !fx.href.startsWith("#") ? "#" + fx.href : fx.href;
2040
- const textPath = {
2041
- type: "textPath",
2042
- href,
2043
- children: (_a = node.children) != null ? _a : []
2044
- };
2045
- if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
2046
- if (fx.method !== void 0) textPath.method = fx.method;
2047
- if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
2048
- applyAnimatableNumber(textPath, "startOffset", fx.startOffset);
2049
- applyAnimatableNumber(textPath, "textLength", fx.textLength);
2050
- node.children = [textPath];
2051
- return node;
2052
- }
2053
- function applyAnimatableNumber(node, attrName, raw) {
2054
- if (raw === void 0 || raw === null) return;
2055
- if (typeof raw === "number") {
2056
- node[attrName] = String(raw);
2057
- return;
2058
- }
2059
- if (typeof raw === "object" && raw !== null) {
2060
- const obj = raw;
2061
- if (Array.isArray(obj.keyframes)) {
2062
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
2063
- const animate = __spreadValues({}, prevAnimate || {});
2064
- const block = { keyframes: obj.keyframes };
2065
- if (obj.loop !== void 0) block.loop = obj.loop;
2066
- animate[attrName] = block;
2067
- node.animate = animate;
2068
- return;
2069
- }
2070
- if (typeof obj.value === "number") {
2071
- node[attrName] = String(obj.value);
2072
- return;
2073
- }
2074
- }
2075
- }
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
-
2132
2059
  // src/PxAnimatorUtil.ts
2133
2060
  function bezierToSvgPath(path) {
2134
2061
  var _a, _b, _c, _d;
@@ -2417,6 +2344,8 @@ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
2417
2344
  // Filter
2418
2345
  "filterUnits",
2419
2346
  "primitiveUnits",
2347
+ "tableValues",
2348
+ // feFuncR/G/B/A transfer table (type="table")
2420
2349
  "stdDeviation",
2421
2350
  "baseFrequency",
2422
2351
  "numOctaves",
@@ -2673,7 +2602,11 @@ function createPathSampler(d) {
2673
2602
  };
2674
2603
  return {
2675
2604
  totalLength,
2605
+ closed,
2676
2606
  sampleAtDistance(dist) {
2607
+ if (closed && totalLength > 0 && (dist < 0 || dist > totalLength)) {
2608
+ return sampleOn((dist % totalLength + totalLength) % totalLength);
2609
+ }
2677
2610
  if (!closed && (dist < 0 || dist > totalLength)) {
2678
2611
  const edge = dist < 0 ? 0 : totalLength;
2679
2612
  const p = sampleOn(edge);
@@ -2685,6 +2618,173 @@ function createPathSampler(d) {
2685
2618
  };
2686
2619
  }
2687
2620
 
2621
+ // src/effects/textPathEffect.ts
2622
+ var EXTEND_MARGIN_FRAC = 0.15;
2623
+ function numRange(v) {
2624
+ if (typeof v === "number") return { min: v, max: v };
2625
+ if (v && typeof v === "object") {
2626
+ const o = v;
2627
+ if (typeof o.value === "number") return { min: o.value, max: o.value };
2628
+ if (Array.isArray(o.keyframes) && o.keyframes.length) {
2629
+ const vals = o.keyframes.map((k) => Number(k.value) || 0);
2630
+ return { min: Math.min(...vals), max: Math.max(...vals) };
2631
+ }
2632
+ }
2633
+ return { min: 0, max: 0 };
2634
+ }
2635
+ function estimateTextAdvance(node) {
2636
+ let chars = 0, maxFont = 16;
2637
+ const walk = (el) => {
2638
+ var _a, _b;
2639
+ const fs = parseFloat(String((_a = el.fontSize) != null ? _a : "")) || 0;
2640
+ if (fs) maxFont = Math.max(maxFont, fs);
2641
+ const t = (_b = el.text) != null ? _b : el.textContent;
2642
+ if (typeof t === "string") chars += t.length;
2643
+ if (el.children) for (const c of el.children) walk(c);
2644
+ };
2645
+ walk(node);
2646
+ return chars * maxFont;
2647
+ }
2648
+ var r3 = (n) => Math.round(n * 1e3) / 1e3;
2649
+ function shiftAnimatable(v, by) {
2650
+ if (!by || v === void 0 || v === null) return v;
2651
+ if (typeof v === "number") return v + by;
2652
+ const o = v;
2653
+ if (typeof o.value === "number") return __spreadProps(__spreadValues({}, o), { value: o.value + by });
2654
+ if (Array.isArray(o.keyframes)) return __spreadProps(__spreadValues({}, o), { keyframes: o.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by })) });
2655
+ return v;
2656
+ }
2657
+ function extendedPathForBrowser(pathD, opts) {
2658
+ var _a;
2659
+ if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
2660
+ const sampler = createPathSampler(pathD);
2661
+ if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };
2662
+ const L = sampler.totalLength;
2663
+ const margin = EXTEND_MARGIN_FRAC * L;
2664
+ const so = numRange(opts.startOffset);
2665
+ const runWidth = numRange(opts.textLength).max || ((_a = opts.advance) != null ? _a : 0);
2666
+ const startOverflow = Math.max(0, -so.min);
2667
+ const endOverflow = Math.max(0, so.max + runWidth - L);
2668
+ const startExt = startOverflow > 0 ? startOverflow + margin : 0;
2669
+ const endExt = endOverflow > 0 ? endOverflow + margin : 0;
2670
+ if (endExt <= 0 && startExt <= 0) return { d: pathD, startShift: 0 };
2671
+ const s = sampler.sampleAtDistance(0);
2672
+ const e = sampler.sampleAtDistance(L);
2673
+ let d = pathD;
2674
+ if (startExt > 0) {
2675
+ const sx = s.x - Math.cos(s.angle) * startExt, sy = s.y - Math.sin(s.angle) * startExt;
2676
+ const rest = pathD.replace(/^\s*[Mm]\s*-?[\d.]+[\s,]+-?[\d.]+/, "");
2677
+ d = "M" + r3(sx) + "," + r3(sy) + "L" + r3(s.x) + "," + r3(s.y) + rest;
2678
+ }
2679
+ if (endExt > 0) {
2680
+ const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;
2681
+ d += "L" + r3(ex) + "," + r3(ey);
2682
+ }
2683
+ return { d, startShift: startExt };
2684
+ }
2685
+ function applyTextPathEffect(node, fx, ctx) {
2686
+ var _a;
2687
+ if (!fx || typeof fx.path !== "string" || !fx.path) return node;
2688
+ const pathId = genId(ctx, "tpath");
2689
+ const { d, startShift } = extendedPathForBrowser(fx.path, {
2690
+ pathOverflow: fx.pathOverflow,
2691
+ startOffset: fx.startOffset,
2692
+ textLength: fx.textLength,
2693
+ advance: estimateTextAdvance(node)
2694
+ });
2695
+ ctx.defs.push({ type: "path", id: pathId, d });
2696
+ const textPath = {
2697
+ type: "textPath",
2698
+ href: "#" + pathId,
2699
+ children: (_a = node.children) != null ? _a : []
2700
+ };
2701
+ if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
2702
+ if (fx.method !== void 0) textPath.method = fx.method;
2703
+ if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
2704
+ applyAnimatableNumber(textPath, "startOffset", shiftAnimatable(fx.startOffset, startShift));
2705
+ applyAnimatableNumber(textPath, "textLength", fx.textLength);
2706
+ node.children = [textPath];
2707
+ return node;
2708
+ }
2709
+ function applyAnimatableNumber(node, attrName, raw) {
2710
+ if (raw === void 0 || raw === null) return;
2711
+ if (typeof raw === "number") {
2712
+ node[attrName] = String(raw);
2713
+ return;
2714
+ }
2715
+ if (typeof raw === "object" && raw !== null) {
2716
+ const obj = raw;
2717
+ if (Array.isArray(obj.keyframes)) {
2718
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
2719
+ const animate = __spreadValues({}, prevAnimate || {});
2720
+ const block = { keyframes: obj.keyframes };
2721
+ if (obj.loop !== void 0) block.loop = obj.loop;
2722
+ animate[attrName] = block;
2723
+ node.animate = animate;
2724
+ return;
2725
+ }
2726
+ if (typeof obj.value === "number") {
2727
+ node[attrName] = String(obj.value);
2728
+ return;
2729
+ }
2730
+ }
2731
+ }
2732
+
2733
+ // src/effects/elementFactory.ts
2734
+ var jsonElementFactory = (type, props, children) => {
2735
+ const node = { type };
2736
+ for (const k in props) if (props[k] !== void 0) node[k] = props[k];
2737
+ const arr = Array.isArray(children) ? children.filter((c) => c != null) : children != null ? [children] : [];
2738
+ if (arr.length) node.children = arr;
2739
+ return node;
2740
+ };
2741
+
2742
+ // src/effects/glyphPathBake.ts
2743
+ function fmt(v, decimals) {
2744
+ return Math.round(v) === v ? "" + Math.round(v) : v.toFixed(decimals);
2745
+ }
2746
+ function pack(nums, decimals) {
2747
+ let s = "";
2748
+ for (let i = 0; i < nums.length; i++) {
2749
+ const str2 = fmt(nums[i], decimals);
2750
+ if (i > 0 && str2.charCodeAt(0) !== 45) s += " ";
2751
+ s += str2;
2752
+ }
2753
+ return s;
2754
+ }
2755
+ function apply(m, x, y) {
2756
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
2757
+ }
2758
+ var TOKEN_RE = /([MLCQZ])|(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi;
2759
+ function transformPathData(d, m, decimals = 2) {
2760
+ const tokens = [];
2761
+ let match;
2762
+ TOKEN_RE.lastIndex = 0;
2763
+ while ((match = TOKEN_RE.exec(d)) !== null) tokens.push(match[0]);
2764
+ let out = "";
2765
+ let i = 0;
2766
+ const num2 = () => parseFloat(tokens[i++]);
2767
+ while (i < tokens.length) {
2768
+ const cmd = tokens[i++];
2769
+ if (cmd === "M" || cmd === "L") {
2770
+ const [x, y] = apply(m, num2(), num2());
2771
+ out += cmd + pack([x, y], decimals);
2772
+ } else if (cmd === "C") {
2773
+ const [x1, y1] = apply(m, num2(), num2());
2774
+ const [x2, y2] = apply(m, num2(), num2());
2775
+ const [x, y] = apply(m, num2(), num2());
2776
+ out += "C" + pack([x1, y1, x2, y2, x, y], decimals);
2777
+ } else if (cmd === "Q") {
2778
+ const [x1, y1] = apply(m, num2(), num2());
2779
+ const [x, y] = apply(m, num2(), num2());
2780
+ out += "Q" + pack([x1, y1, x, y], decimals);
2781
+ } else if (cmd === "Z" || cmd === "z") {
2782
+ out += "Z";
2783
+ }
2784
+ }
2785
+ return out;
2786
+ }
2787
+
2688
2788
  // src/effects/textGlyphsEffect.ts
2689
2789
  var DEFAULT_FONT_SIZE = 16;
2690
2790
  var TEXT_ATTR_KEYS = [
@@ -2763,6 +2863,19 @@ function soleFontOf(glyphs) {
2763
2863
  const names = Object.keys(glyphs);
2764
2864
  return names.length === 1 ? glyphs[names[0]] : void 0;
2765
2865
  }
2866
+ var MISSING_GLYPH_ADVANCE_EM = 0.6;
2867
+ function missingGlyphBoxEm(advanceEm, ascentEm) {
2868
+ if (advanceEm <= 0 || ascentEm <= 0) return "";
2869
+ const inset = 0.08;
2870
+ const x0 = advanceEm * inset, x1 = advanceEm * (1 - inset);
2871
+ const y1 = -ascentEm * (1 - inset);
2872
+ const bw = x1 - x0, bh = -y1;
2873
+ const t = Math.max(1, Math.min(bw, bh) * 0.12);
2874
+ const outer = "M" + x0 + " 0L" + x1 + " 0L" + x1 + " " + y1 + "L" + x0 + " " + y1 + "Z";
2875
+ if (bw <= 2 * t || bh <= 2 * t) return outer;
2876
+ const ix0 = x0 + t, ix1 = x1 - t, iyb = -t, iyt = y1 + t;
2877
+ return outer + "M" + ix0 + " " + iyb + "L" + ix0 + " " + iyt + "L" + ix1 + " " + iyt + "L" + ix1 + " " + iyb + "Z";
2878
+ }
2766
2879
  function materialiseGlyphTextHorizontal(node, opts) {
2767
2880
  var _a, _b, _c, _d;
2768
2881
  const { glyphs, create = jsonElementFactory, warnings } = opts;
@@ -2772,15 +2885,26 @@ function materialiseGlyphTextHorizontal(node, opts) {
2772
2885
  const lines = [{ start: pen.x, end: pen.x }];
2773
2886
  let line = 0;
2774
2887
  const renderChars = (content, s) => {
2888
+ var _a2;
2775
2889
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2776
- if (!gf) return;
2777
- const scale = s.fontSize / gf.unitsPerEm;
2890
+ const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
2891
+ const scale = s.fontSize / upm;
2892
+ const ascentEm = (_a2 = gf == null ? void 0 : gf.ascent) != null ? _a2 : 0.9 * upm;
2778
2893
  const paint = paintOf(s);
2779
2894
  for (let i = 0; i < content.length; i++) {
2780
2895
  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);
2896
+ const g = gf == null ? void 0 : gf.glyphs[ch];
2897
+ if (g && g.d) {
2898
+ placements.push({ glyphD: g.d, m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });
2899
+ pen.x += g.width * scale;
2900
+ } else if (/\S/.test(ch)) {
2901
+ const advEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * upm;
2902
+ placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });
2903
+ pen.x += advEm * scale;
2904
+ } else {
2905
+ pen.x += (g ? g.width : 0) * scale;
2906
+ }
2907
+ pen.x += s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2784
2908
  lines[line].end = pen.x;
2785
2909
  }
2786
2910
  };
@@ -2815,24 +2939,152 @@ function materialiseGlyphTextHorizontal(node, opts) {
2815
2939
  }
2816
2940
  return toGroup(node, buildPaths(placements, create, warnings), create);
2817
2941
  }
2942
+ function layoutGlyphTextChars(node, opts) {
2943
+ var _a, _b, _c, _d, _e;
2944
+ if ((_a = opts.alongPath) == null ? void 0 : _a.pathD) return layoutGlyphTextCharsAlongPath(node, opts.alongPath.pathD, opts);
2945
+ const { glyphs, warnings } = opts;
2946
+ const soleFont = soleFontOf(glyphs);
2947
+ const pen = { x: (_b = parseLen(node.x)) != null ? _b : 0, y: (_c = parseLen(node.y)) != null ? _c : 0 };
2948
+ const boxes = [];
2949
+ const lines = [{ start: pen.x, end: pen.x }];
2950
+ let line = 0;
2951
+ const renderChars = (content, s) => {
2952
+ var _a2;
2953
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2954
+ const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
2955
+ const scale = s.fontSize / upm;
2956
+ const ascent = ((_a2 = gf == null ? void 0 : gf.ascent) != null ? _a2 : 0.9 * upm) * scale;
2957
+ for (let i = 0; i < content.length; i++) {
2958
+ const ch = content.charAt(i);
2959
+ const g = gf == null ? void 0 : gf.glyphs[ch];
2960
+ const advance = (g ? g.width : 0) * scale + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2961
+ boxes.push({ x: pen.x, y: pen.y, width: advance, ascent, fontSize: s.fontSize, line });
2962
+ pen.x += advance;
2963
+ lines[line].end = pen.x;
2964
+ }
2965
+ };
2966
+ const walk = (el, parentStyle) => {
2967
+ var _a2, _b2, _c2, _d2;
2968
+ const s = resolveStyle(el, parentStyle);
2969
+ const x = parseLen(el.x);
2970
+ const y = parseLen(el.y);
2971
+ if (x !== void 0) {
2972
+ pen.x = x;
2973
+ line = lines.length;
2974
+ lines.push({ start: pen.x, end: pen.x });
2975
+ }
2976
+ if (y !== void 0) pen.y = y;
2977
+ pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
2978
+ pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
2979
+ const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
2980
+ if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
2981
+ if (el.children) for (const ch of el.children) walk(ch, s);
2982
+ };
2983
+ const rootStyle = rootStyleOf(node);
2984
+ if (node.children) for (const ch of node.children) walk(ch, rootStyle);
2985
+ const rootContent = (_d = str(node[TEXT_ATTR])) != null ? _d : str(node[TEXT_CONTENT_ATTR]);
2986
+ if (rootContent && !((_e = node.children) == null ? void 0 : _e.length)) renderChars(rootContent, rootStyle);
2987
+ const anchor = str(node.textAnchor);
2988
+ if (anchor === "middle" || anchor === "end") {
2989
+ for (const b of boxes) {
2990
+ const w = lines[b.line].end - lines[b.line].start;
2991
+ b.x += anchor === "middle" ? -w / 2 : -w;
2992
+ }
2993
+ }
2994
+ return boxes.map((_f) => {
2995
+ var _g = _f, { line: _l } = _g, b = __objRest(_g, ["line"]);
2996
+ return b;
2997
+ });
2998
+ }
2999
+ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
3000
+ var _a;
3001
+ const { glyphs, warnings, alongPath } = opts;
3002
+ const sampler = createPathSampler(pathD);
3003
+ if (!sampler) {
3004
+ warnings == null ? void 0 : warnings.push("textGlyphs: unparsable along-path geometry (caret)");
3005
+ return [];
3006
+ }
3007
+ const soleFont = soleFontOf(glyphs);
3008
+ const chars = [];
3009
+ let adv = 0;
3010
+ const walk = (el, parentStyle) => {
3011
+ var _a2, _b, _c;
3012
+ const s = resolveStyle(el, parentStyle);
3013
+ const content = (_a2 = str(el[TEXT_ATTR])) != null ? _a2 : str(el[TEXT_CONTENT_ATTR]);
3014
+ if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
3015
+ const gf = glyphFontFor(s, glyphs, soleFont, warnings);
3016
+ const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
3017
+ const scale = s.fontSize / upm;
3018
+ const ascent = ((_c = gf == null ? void 0 : gf.ascent) != null ? _c : 0.9 * upm) * scale;
3019
+ for (let i = 0; i < content.length; i++) {
3020
+ const ch = content.charAt(i);
3021
+ const g = gf == null ? void 0 : gf.glyphs[ch];
3022
+ const glyphW = (g ? g.width : 0) * scale;
3023
+ const advance = glyphW + s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
3024
+ chars.push({ advStart: adv, advEnd: adv + advance, glyphW, ascent, fontSize: s.fontSize });
3025
+ adv += advance;
3026
+ }
3027
+ }
3028
+ if (el.children) for (const ch of el.children) walk(ch, s);
3029
+ };
3030
+ walk(node, rootStyleOf(node));
3031
+ const width = adv;
3032
+ const k = (alongPath == null ? void 0 : alongPath.textLength) && alongPath.textLength > 0 && width > 0 ? alongPath.textLength / width : 1;
3033
+ const so = readAnimatable(alongPath == null ? void 0 : alongPath.startOffset);
3034
+ const { along: alongOffset, perp } = alongPathNodeOffsets(node);
3035
+ const base = alongOffset + (so.kind === "animated" /* Animated */ ? Number((_a = so.keyframes[0]) == null ? void 0 : _a.value) || 0 : so.kind === "static" /* Static */ ? Number(so.value) || 0 : 0);
3036
+ const withPerp = (p) => ({
3037
+ x: p.x - perp * Math.sin(p.angle),
3038
+ y: p.y + perp * Math.cos(p.angle),
3039
+ angle: p.angle
3040
+ });
3041
+ return chars.map((c) => {
3042
+ const dStart = base + c.advStart * k;
3043
+ const dEnd = base + c.advEnd * k;
3044
+ const p0 = withPerp(sampler.sampleAtDistance(dStart));
3045
+ const p1 = withPerp(sampler.sampleAtDistance(dEnd));
3046
+ const glyphMid = sampler.sampleAtDistance(base + (c.advStart + c.glyphW / 2) * k);
3047
+ return {
3048
+ x: p0.x,
3049
+ y: p0.y,
3050
+ width: c.advEnd - c.advStart,
3051
+ ascent: c.ascent,
3052
+ fontSize: c.fontSize,
3053
+ endX: p1.x,
3054
+ endY: p1.y,
3055
+ rotation: glyphMid.angle * 180 / Math.PI
3056
+ };
3057
+ });
3058
+ }
2818
3059
  function collectAlongPathCells(node, glyphs, soleFont, warnings) {
2819
3060
  const cells = [];
2820
3061
  let adv = 0;
2821
3062
  const walk = (el, parentStyle) => {
2822
- var _a, _b;
3063
+ var _a, _b, _c;
2823
3064
  const s = resolveStyle(el, parentStyle);
2824
3065
  const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
2825
3066
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
2826
3067
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
2827
3068
  if (gf) {
2828
3069
  const scale = s.fontSize / gf.unitsPerEm;
3070
+ const ascentEm = (_c = gf.ascent) != null ? _c : 0.9 * gf.unitsPerEm;
2829
3071
  const paint = paintOf(s);
2830
3072
  for (let i = 0; i < content.length; i++) {
2831
3073
  const ch = content.charAt(i);
2832
3074
  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);
3075
+ if (g && g.d) {
3076
+ const glyphAdv = g.width * scale;
3077
+ cells.push({ glyphD: g.d, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });
3078
+ adv += glyphAdv;
3079
+ } else if (/\S/.test(ch)) {
3080
+ const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * gf.unitsPerEm;
3081
+ const boxAdv = wEm * scale;
3082
+ cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, midBase: adv + boxAdv / 2 });
3083
+ adv += boxAdv;
3084
+ } else {
3085
+ adv += (g ? g.width : 0) * scale;
3086
+ }
3087
+ adv += s.letterSpacing + (ch === " " ? s.wordSpacing : 0);
2836
3088
  }
2837
3089
  }
2838
3090
  }
@@ -2841,12 +3093,19 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
2841
3093
  walk(node, rootStyleOf(node));
2842
3094
  return { cells, width: adv };
2843
3095
  }
2844
- function alongAffine(sampler, dist, scale, widthEm) {
3096
+ function alongAffine(sampler, dist, scale, widthEm, perp = 0) {
2845
3097
  const { x, y, angle } = sampler.sampleAtDistance(dist);
2846
3098
  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];
3099
+ return [scale * cos, scale * sin, -scale * sin, scale * cos, x - scale * cos * hw - perp * sin, y - scale * sin * hw + perp * cos];
3100
+ }
3101
+ function alongPathNodeOffsets(node) {
3102
+ var _a, _b, _c;
3103
+ return {
3104
+ along: ((_a = parseLen(node.x)) != null ? _a : 0) + ((_b = parseLen(node.dx)) != null ? _b : 0),
3105
+ perp: (_c = parseLen(node.dy)) != null ? _c : 0
3106
+ };
2848
3107
  }
2849
- function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLength) {
3108
+ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLength, pathOverflow) {
2850
3109
  var _a;
2851
3110
  const { glyphs, create = jsonElementFactory, warnings } = opts;
2852
3111
  const sampler = pathD ? createPathSampler(pathD) : null;
@@ -2857,19 +3116,25 @@ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLengt
2857
3116
  const soleFont = soleFontOf(glyphs);
2858
3117
  const { cells, width } = collectAlongPathCells(node, glyphs, soleFont, warnings);
2859
3118
  if (!cells.length) return toGroup(node, [], create);
3119
+ const { along: alongOffset, perp } = alongPathNodeOffsets(node);
2860
3120
  if (textLength && textLength > 0 && width > 0) {
2861
3121
  const k = textLength / width;
2862
3122
  for (const c of cells) c.midBase *= k;
2863
3123
  }
3124
+ const isClip = pathOverflow === "clip" && !sampler.closed;
2864
3125
  const so = readAnimatable(startOffset);
2865
3126
  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) => ({
3127
+ return toGroup(node, buildAnimatedAlongPath(cells, sampler, so.keyframes, so.loop, create, isClip, alongOffset, perp), create);
3128
+ }
3129
+ const base = alongOffset + (so.kind === "animated" /* Animated */ ? Number((_a = so.keyframes[0]) == null ? void 0 : _a.value) || 0 : so.kind === "static" /* Static */ ? Number(so.value) || 0 : 0);
3130
+ const placeCells = isClip ? cells.filter((c) => {
3131
+ const d = base + c.midBase;
3132
+ return d >= 0 && d <= sampler.totalLength;
3133
+ }) : cells;
3134
+ const placements = placeCells.map((c) => ({
2870
3135
  glyphD: c.glyphD,
2871
3136
  paint: c.paint,
2872
- m: alongAffine(sampler, base + c.midBase, c.scale, c.widthEm)
3137
+ m: alongAffine(sampler, base + c.midBase, c.scale, c.widthEm, perp)
2873
3138
  }));
2874
3139
  return toGroup(node, buildPaths(placements, create, warnings), create);
2875
3140
  }
@@ -2879,37 +3144,51 @@ function roundN(v, n) {
2879
3144
  const f = __pow(10, n);
2880
3145
  return Math.round(v * f) / f;
2881
3146
  }
2882
- function buildAnimatedAlongPath(cells, sampler, sokfs, loop, create) {
3147
+ function buildAnimatedAlongPath(cells, sampler, sokfs, loop, create, isClip, alongOffset = 0, perp = 0) {
2883
3148
  const step = Math.max(sampler.totalLength / ALONG_PATH_MAX_STEPS, 0.5);
2884
3149
  const timeOf = (kf) => Number(kf.time) || 0;
2885
- const offOf = (kf) => Number(kf.value) || 0;
3150
+ const offOf = (kf) => alongOffset + (Number(kf.value) || 0);
3151
+ const onPath = (dist) => dist >= 0 && dist <= sampler.totalLength;
2886
3152
  const out = [];
2887
3153
  for (const c of cells) {
2888
3154
  const centred = [c.scale, 0, 0, c.scale, -c.scale * (c.widthEm / 2), 0];
2889
3155
  const d = transformPathData(c.glyphD, centred);
2890
3156
  const sampleKf = (dist, time) => {
2891
3157
  const { x, y, angle } = sampler.sampleAtDistance(dist);
3158
+ const cos = Math.cos(angle), sin = Math.sin(angle);
2892
3159
  return {
2893
3160
  time,
2894
3161
  value: {
2895
- ["translate" /* Translate */]: [roundN(x, 3), roundN(y, 3)],
3162
+ ["translate" /* Translate */]: [roundN(x - perp * sin, 3), roundN(y + perp * cos, 3)],
2896
3163
  ["rotate" /* Rotate */]: roundN(angle * 180 / Math.PI, 3)
2897
3164
  }
2898
3165
  };
2899
3166
  };
2900
- const kfs = [sampleKf(offOf(sokfs[0]) + c.midBase, timeOf(sokfs[0]))];
3167
+ const kfs = [];
3168
+ const opKfs = [];
3169
+ const pushKf = (dist, time) => {
3170
+ kfs.push(sampleKf(dist, time));
3171
+ if (isClip) opKfs.push({ time, value: onPath(dist) ? 1 : 0 });
3172
+ };
3173
+ pushKf(offOf(sokfs[0]) + c.midBase, timeOf(sokfs[0]));
2901
3174
  for (let k = 1; k < sokfs.length; k++) {
2902
3175
  const t0 = timeOf(sokfs[k - 1]), t1 = timeOf(sokfs[k]);
2903
3176
  const o0 = offOf(sokfs[k - 1]), o1 = offOf(sokfs[k]);
2904
3177
  const n = Math.min(ALONG_PATH_MAX_STEPS_PER_SEGMENT, Math.max(1, Math.ceil(Math.abs(o1 - o0) / step)));
2905
3178
  for (let s = 1; s <= n; s++) {
2906
3179
  const f = s / n;
2907
- kfs.push(sampleKf(o0 + f * (o1 - o0) + c.midBase, t0 + f * (t1 - t0)));
3180
+ pushKf(o0 + f * (o1 - o0) + c.midBase, t0 + f * (t1 - t0));
2908
3181
  }
2909
3182
  }
2910
3183
  const transform = { keyframes: kfs };
2911
3184
  if (loop !== void 0) transform.loop = loop;
2912
- out.push(create("path", __spreadProps(__spreadValues({ d }, paintProps(c.paint)), { animate: { transform } }), []));
3185
+ const animate = { transform };
3186
+ if (isClip && opKfs.some((k) => k.value === 0)) {
3187
+ const op = { keyframes: opKfs };
3188
+ if (loop !== void 0) op.loop = loop;
3189
+ animate.opacity = op;
3190
+ }
3191
+ out.push(create("path", __spreadProps(__spreadValues({ d }, paintProps(c.paint)), { animate }), []));
2913
3192
  }
2914
3193
  return out;
2915
3194
  }
@@ -2953,7 +3232,7 @@ function toGroup(node, children, create) {
2953
3232
  return create("g", gProps, children);
2954
3233
  }
2955
3234
  function materialiseGlyphText(node, opts) {
2956
- if (opts.alongPath) return materialiseGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength);
3235
+ if (opts.alongPath) return materialiseGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength, opts.alongPath.pathOverflow);
2957
3236
  return materialiseGlyphTextHorizontal(node, opts);
2958
3237
  }
2959
3238
  function applyTextGlyphsEffect(node, fx, ctx) {
@@ -2964,12 +3243,12 @@ function applyTextGlyphsEffect(node, fx, ctx) {
2964
3243
  }
2965
3244
  return materialiseGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
2966
3245
  }
2967
- function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength) {
3246
+ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pathOverflow) {
2968
3247
  if (!ctx.glyphs) {
2969
3248
  ctx.warnings.push("textGlyphs: no definitions.glyphs");
2970
3249
  return null;
2971
3250
  }
2972
- return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength);
3251
+ return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
2973
3252
  }
2974
3253
 
2975
3254
  // src/PxMotionPath.ts
@@ -3150,9 +3429,18 @@ function buildOutKfValue(prevV, nextV, p, translate, rotateDegFromAutoOrient, au
3150
3429
  if (pv === void 0 && nv === void 0) continue;
3151
3430
  value[k] = interpolatePart(pv, nv, p);
3152
3431
  }
3153
- if (rotateDegFromAutoOrient !== void 0) value.rotate = rotateDegFromAutoOrient;
3432
+ if (rotateDegFromAutoOrient !== void 0) {
3433
+ value.rotate = rotateDegFromAutoOrient + explicitRotateAt(prevV, nextV, p);
3434
+ }
3154
3435
  return value;
3155
3436
  }
3437
+ function explicitRotateAt(prevV, nextV, p) {
3438
+ const pv = typeof (prevV == null ? void 0 : prevV.rotate) === "number" ? prevV.rotate : void 0;
3439
+ const nv = typeof (nextV == null ? void 0 : nextV.rotate) === "number" ? nextV.rotate : void 0;
3440
+ if (pv === void 0 && nv === void 0) return 0;
3441
+ const r = interpolatePart(pv, nv, p);
3442
+ return typeof r === "number" ? r : 0;
3443
+ }
3156
3444
  function derivAngleForFirstKf(kf0, kf1) {
3157
3445
  const p0 = getKfTranslate(kf0);
3158
3446
  const p1 = getKfTranslate(kf1);
@@ -3173,10 +3461,12 @@ function insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos,
3173
3461
  const lastV = (_a = lastKf.v) != null ? _a : lastKf.value;
3174
3462
  const prevExit = lastV == null ? void 0 : lastV.rotate;
3175
3463
  if (typeof prevExit !== "number") return;
3464
+ const boundaryV = getKfValueParts(prevKf);
3465
+ const prevExitTangent = prevExit - explicitRotateAt(boundaryV, boundaryV, 0);
3176
3466
  const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
3177
3467
  const tanAtStart = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);
3178
3468
  const nextEntry = Math.atan2(tanAtStart[1], tanAtStart[0]) * 180 / Math.PI;
3179
- const delta = wrappedAngleDelta(nextEntry, prevExit);
3469
+ const delta = wrappedAngleDelta(nextEntry, prevExitTangent);
3180
3470
  if (Math.abs(delta) <= rotationTol) return;
3181
3471
  const dupValue = buildOutKfValue(
3182
3472
  getKfValueParts(prevKf),
@@ -4375,18 +4665,17 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
4375
4665
  const originalId = typeof node.id === "string" ? node.id : void 0;
4376
4666
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
4377
4667
  if (!fx && !innerIdForContentRef) return node;
4378
- const { transformation, repeater, maskedBy, trimPath, clone: cloneFx, fillGradient, strokeGradient, textAlongPath, text } = fx != null ? fx : {};
4668
+ const { transformation, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
4379
4669
  const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
4380
4670
  if (fx) delete node.effects;
4381
4671
  let n = node;
4382
4672
  let consumedByGlyphs = false;
4383
4673
  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;
4674
+ if (textPath) {
4675
+ const pathD = typeof textPath.path === "string" ? textPath.path : void 0;
4676
+ const rawTL = textPath.textLength;
4388
4677
  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);
4678
+ const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textLength, textPath.pathOverflow);
4390
4679
  if (glyphed) {
4391
4680
  n = glyphed;
4392
4681
  consumedByGlyphs = true;
@@ -4396,12 +4685,13 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
4396
4685
  consumedByGlyphs = true;
4397
4686
  }
4398
4687
  }
4399
- if (!consumedByGlyphs) n = applyTextAlongPathEffect(n, textAlongPath, ctx);
4688
+ if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
4400
4689
  n = applyFillGradientEffect(n, fillGradient, ctx);
4401
4690
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
4402
4691
  n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
4403
4692
  n = applyRepeaterEffect(n, repeater, ctx);
4404
4693
  n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
4694
+ n = applyClipPathEffect(n, clipPath, ctx);
4405
4695
  if (innerIdForContentRef) {
4406
4696
  applyRefHref(n, cloneFx, ctx);
4407
4697
  n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
@@ -4730,6 +5020,8 @@ function getNormalizedProps(props) {
4730
5020
  function renderNode(node, defs) {
4731
5021
  if (!node) return null;
4732
5022
  const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
5023
+ const feFuncType = props.funcType;
5024
+ if (feFuncType !== void 0) delete props.funcType;
4733
5025
  const nodeDefs = getDefs(node) || defs;
4734
5026
  const resolvedStyle = resolveStyle2(style, nodeDefs);
4735
5027
  let childElements;
@@ -4742,13 +5034,15 @@ function renderNode(node, defs) {
4742
5034
  }
4743
5035
  }
4744
5036
  }
4745
- return createElement(
5037
+ const element = createElement(
4746
5038
  type || "g",
4747
5039
  getNormalizedProps(props),
4748
5040
  resolvedStyle,
4749
5041
  childElements,
4750
5042
  props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
4751
5043
  );
5044
+ if (element && feFuncType !== void 0) element.setAttribute("type", feFuncType);
5045
+ return element;
4752
5046
  }
4753
5047
 
4754
5048
  // src/PxAnimatorTriggers.ts
@@ -4759,7 +5053,12 @@ function setupAnimationTriggers(api, config) {
4759
5053
  console.warn("setupAnimationTriggers: No root element found for animation.");
4760
5054
  return api;
4761
5055
  }
5056
+ let reversed = false;
4762
5057
  const start = () => {
5058
+ if (reversed) {
5059
+ reversed = false;
5060
+ api.setPlaybackRate(1);
5061
+ }
4763
5062
  api.play();
4764
5063
  };
4765
5064
  const handleEndAction = () => {
@@ -4771,6 +5070,8 @@ function setupAnimationTriggers(api, config) {
4771
5070
  api.cancel();
4772
5071
  break;
4773
5072
  case "reverse":
5073
+ reversed = true;
5074
+ api.setPlaybackRate(-1);
4774
5075
  api.play();
4775
5076
  break;
4776
5077
  case "continue":
@@ -4833,6 +5134,7 @@ function getSelector(id) {
4833
5134
  return "#" + id;
4834
5135
  }
4835
5136
  function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5137
+ var _a;
4836
5138
  const config = getAnimatorConfig(doc) || {};
4837
5139
  const bindings = getNormalisedBindings(doc, PxAnimatorEngine.frames);
4838
5140
  const _iterations = config.iterations;
@@ -4843,18 +5145,24 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
4843
5145
  const duration = +(config.duration || DEFAULT_DURATION_MS);
4844
5146
  const totalDuration = duration && iterations ? duration * (iterations === Infinity ? Infinity : iterations) : duration ? (iterations != null ? iterations : 1) * duration : 0;
4845
5147
  const direction = config.direction || "normal";
5148
+ const fill = (_a = config.fill) != null ? _a : "forwards";
5149
+ const fillsForwards = fill === "forwards" || fill === "both";
5150
+ const fillsBackwards = fill === "backwards" || fill === "both";
4846
5151
  let timerId = null;
4847
5152
  let playing = false;
4848
- let timeBeforeLastStartMs = config.delay || 0;
5153
+ let timeBeforeLastStartMs = -(config.delay || 0);
4849
5154
  let lastStartedTs = 0;
4850
5155
  let playbackRate = 1;
4851
5156
  let finishCalled = false;
4852
5157
  const frameRate = config.frameRate;
4853
5158
  const minFrameIntervalMs = frameRate && frameRate > 0 ? 1e3 / frameRate : 0;
4854
5159
  let lastRenderTs = 0;
4855
- const getAnimCurrentTime = () => {
5160
+ const getRawAnimTime = () => {
4856
5161
  const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;
4857
- let time = timeBeforeLastStartMs + runningElapsed;
5162
+ return timeBeforeLastStartMs + runningElapsed;
5163
+ };
5164
+ const getAnimCurrentTime = () => {
5165
+ let time = getRawAnimTime();
4858
5166
  if (Number.isFinite(totalDuration) && time > totalDuration) time = totalDuration;
4859
5167
  if (time < 0) time = 0;
4860
5168
  return time;
@@ -4899,40 +5207,59 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
4899
5207
  }
4900
5208
  }
4901
5209
  const tick = () => {
4902
- var _a;
5210
+ var _a2, _b;
4903
5211
  if (!adapter.isConnected()) {
4904
5212
  pauseAnim();
4905
5213
  return;
4906
5214
  }
4907
5215
  const currentTime = getAnimCurrentTime();
4908
- if (minFrameIntervalMs > 0) {
4909
- const now = Date.now();
4910
- if (lastRenderTs && now - lastRenderTs < minFrameIntervalMs) {
4911
- return;
4912
- }
4913
- lastRenderTs = now;
4914
- }
4915
- if (duration <= 0) {
5216
+ const rawTime = getRawAnimTime();
5217
+ if (rawTime < 0 && playbackRate > 0) {
5218
+ if (fillsBackwards) renderFrame(0);
5219
+ return;
4916
5220
  }
4917
- if (totalDuration && Number.isFinite(totalDuration) && currentTime >= totalDuration) {
4918
- renderFrame(totalDuration);
5221
+ if (playbackRate < 0 && rawTime <= 0) {
5222
+ pauseAnim();
5223
+ renderFrame(0);
4919
5224
  if (!finishCalled) {
4920
5225
  finishCalled = true;
4921
- (_a = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
5226
+ (_a2 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
4922
5227
  }
5228
+ return;
5229
+ }
5230
+ if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= totalDuration) {
4923
5231
  pauseAnim();
5232
+ renderFrame(fillsForwards ? totalDuration : 0);
5233
+ if (!finishCalled) {
5234
+ finishCalled = true;
5235
+ (_b = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _b.call(callbacks);
5236
+ }
4924
5237
  return;
4925
5238
  }
5239
+ if (minFrameIntervalMs > 0) {
5240
+ const now = Date.now();
5241
+ if (lastRenderTs && now - lastRenderTs < minFrameIntervalMs) {
5242
+ return;
5243
+ }
5244
+ lastRenderTs = now;
5245
+ }
4926
5246
  renderFrame(currentTime);
4927
5247
  };
4928
5248
  if (config.delay) {
4929
- renderFrame(getAnimCurrentTime());
5249
+ if (config.delay < 0) {
5250
+ renderFrame(getAnimCurrentTime());
5251
+ } else if (fillsBackwards) {
5252
+ renderFrame(0);
5253
+ }
4930
5254
  }
4931
5255
  const _isPlaying = () => {
4932
5256
  if (!playing) return false;
4933
5257
  if (!adapter.isConnected()) {
4934
5258
  return false;
4935
5259
  }
5260
+ if (playbackRate < 0) {
5261
+ return getRawAnimTime() > 0;
5262
+ }
4936
5263
  if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= totalDuration) return false;
4937
5264
  return true;
4938
5265
  };
@@ -4952,37 +5279,53 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
4952
5279
  };
4953
5280
  const startAnim = () => {
4954
5281
  if (playing) return;
4955
- if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= totalDuration) {
4956
- timeBeforeLastStartMs = 0;
4957
- finishCalled = false;
5282
+ if (playbackRate >= 0) {
5283
+ if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= totalDuration) {
5284
+ timeBeforeLastStartMs = 0;
5285
+ }
5286
+ } else {
5287
+ if (timeBeforeLastStartMs <= 0) {
5288
+ if (!Number.isFinite(totalDuration)) {
5289
+ console.warn("play: cannot start reverse playback of an infinite animation from time 0");
5290
+ return;
5291
+ }
5292
+ timeBeforeLastStartMs = totalDuration;
5293
+ }
4958
5294
  }
5295
+ finishCalled = false;
4959
5296
  playing = true;
4960
5297
  lastStartedTs = Date.now();
4961
5298
  loopAnim(true);
4962
5299
  };
4963
5300
  const pauseAnim = () => {
4964
5301
  if (!playing) return;
4965
- timeBeforeLastStartMs = getAnimCurrentTime();
5302
+ let raw = getRawAnimTime();
5303
+ if (Number.isFinite(totalDuration) && raw > totalDuration) raw = totalDuration;
5304
+ timeBeforeLastStartMs = raw;
4966
5305
  lastStartedTs = 0;
4967
5306
  playing = false;
4968
5307
  if (timerId !== null) {
4969
5308
  cancelAnimationFrame(timerId);
4970
5309
  timerId = null;
4971
5310
  }
4972
- renderFrame(timeBeforeLastStartMs);
5311
+ if (raw >= 0) {
5312
+ renderFrame(raw);
5313
+ } else if (fillsBackwards) {
5314
+ renderFrame(0);
5315
+ }
4973
5316
  };
4974
5317
  const cancelAnim = () => {
4975
- var _a;
5318
+ var _a2;
4976
5319
  pauseAnim();
4977
5320
  timeBeforeLastStartMs = 0;
4978
5321
  lastStartedTs = 0;
4979
5322
  playing = false;
4980
5323
  finishCalled = false;
4981
5324
  renderFrame(timeBeforeLastStartMs);
4982
- (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
5325
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
4983
5326
  };
4984
5327
  const finishAnim = (callOnFinish = true) => {
4985
- var _a;
5328
+ var _a2;
4986
5329
  if (Number.isFinite(totalDuration)) {
4987
5330
  timeBeforeLastStartMs = totalDuration;
4988
5331
  } else {
@@ -4990,10 +5333,14 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
4990
5333
  }
4991
5334
  lastStartedTs = 0;
4992
5335
  playing = false;
4993
- renderFrame(timeBeforeLastStartMs);
5336
+ if (timerId !== null) {
5337
+ cancelAnimationFrame(timerId);
5338
+ timerId = null;
5339
+ }
5340
+ renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);
4994
5341
  if (callOnFinish && !finishCalled) {
4995
5342
  finishCalled = true;
4996
- (_a = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
5343
+ (_a2 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
4997
5344
  }
4998
5345
  };
4999
5346
  const api = {
@@ -5003,14 +5350,14 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5003
5350
  return _isPlaying();
5004
5351
  },
5005
5352
  "play": () => {
5006
- var _a;
5353
+ var _a2;
5007
5354
  startAnim();
5008
- (_a = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a.call(callbacks);
5355
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
5009
5356
  },
5010
5357
  "pause": () => {
5011
- var _a;
5358
+ var _a2;
5012
5359
  pauseAnim();
5013
- (_a = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a.call(callbacks);
5360
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
5014
5361
  },
5015
5362
  "cancel": () => {
5016
5363
  cancelAnim();
@@ -5023,7 +5370,9 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5023
5370
  console.warn("setPlaybackRate: rate must be finite and non-zero");
5024
5371
  return;
5025
5372
  }
5026
- const current = getAnimCurrentTime();
5373
+ let current = getRawAnimTime();
5374
+ if (Number.isFinite(totalDuration) && current > totalDuration) current = totalDuration;
5375
+ if (rate < 0 !== playbackRate < 0) finishCalled = false;
5027
5376
  playbackRate = rate;
5028
5377
  timeBeforeLastStartMs = current;
5029
5378
  if (playing) lastStartedTs = Date.now();
@@ -5036,10 +5385,13 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5036
5385
  if (Number.isFinite(totalDuration) && newTime > totalDuration) newTime = totalDuration;
5037
5386
  timeBeforeLastStartMs = newTime;
5038
5387
  if (playing) lastStartedTs = Date.now();
5388
+ if (!Number.isFinite(totalDuration) || newTime < totalDuration) finishCalled = false;
5039
5389
  renderFrame(getAnimCurrentTime());
5040
5390
  },
5041
5391
  "destroy": () => {
5392
+ var _a2;
5042
5393
  api.cancel();
5394
+ (_a2 = callbacks == null ? void 0 : callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
5043
5395
  }
5044
5396
  };
5045
5397
  return api;
@@ -5204,6 +5556,8 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5204
5556
  if (typeof _iterations === "number") iterations = _iterations;
5205
5557
  if (_iterations === "infinite") iterations = Infinity;
5206
5558
  const unsupportedSet = /* @__PURE__ */ new Set();
5559
+ let finishNotified = false;
5560
+ let removeCalled = false;
5207
5561
  if (!(bindings == null ? void 0 : bindings.length)) {
5208
5562
  console.warn("createWebApiAnimator: No animation bindings defined");
5209
5563
  }
@@ -5220,7 +5574,11 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5220
5574
  }
5221
5575
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
5222
5576
  const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
5223
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
5577
+ let seekPosition;
5578
+ if (config.delay && config.delay < 0 && config.duration) {
5579
+ const rawSeek = -config.delay;
5580
+ seekPosition = iterations === Infinity ? rawSeek % config.duration : Math.min(rawSeek, config.duration * (iterations != null ? iterations : 1));
5581
+ }
5224
5582
  const effectOptions = {
5225
5583
  duration: config.duration,
5226
5584
  delay: positiveDelay,
@@ -5241,13 +5599,17 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5241
5599
  const anim = new Animation(effect, document.timeline);
5242
5600
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
5243
5601
  var _a2;
5244
- return (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
5602
+ if (finishNotified) return;
5603
+ finishNotified = true;
5604
+ (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
5245
5605
  };
5246
5606
  if (callbacks == null ? void 0 : callbacks.onRemove) anim.onremove = () => {
5247
5607
  var _a2;
5248
- return (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
5608
+ if (removeCalled) return;
5609
+ removeCalled = true;
5610
+ (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
5249
5611
  };
5250
- if (seekPosition) {
5612
+ if (seekPosition !== void 0) {
5251
5613
  anim.currentTime = seekPosition;
5252
5614
  }
5253
5615
  animations.push(anim);
@@ -5271,6 +5633,7 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5271
5633
  },
5272
5634
  "play": () => {
5273
5635
  var _a2;
5636
+ finishNotified = false;
5274
5637
  animations.forEach((a) => a.play());
5275
5638
  (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
5276
5639
  },
@@ -5281,6 +5644,7 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5281
5644
  },
5282
5645
  "cancel": () => {
5283
5646
  var _a2;
5647
+ finishNotified = false;
5284
5648
  animations.forEach((a) => a.cancel());
5285
5649
  (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
5286
5650
  },
@@ -5310,13 +5674,19 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5310
5674
  return res !== null ? +res : null;
5311
5675
  },
5312
5676
  "setCurrentTime": (time) => {
5677
+ finishNotified = false;
5313
5678
  animations.forEach((a) => {
5314
5679
  a.currentTime = time;
5315
5680
  });
5316
5681
  },
5317
5682
  "destroy": () => {
5683
+ var _a2;
5318
5684
  api.cancel();
5319
5685
  animations.splice(0, animations.length);
5686
+ if (!removeCalled) {
5687
+ removeCalled = true;
5688
+ (_a2 = callbacks == null ? void 0 : callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
5689
+ }
5320
5690
  }
5321
5691
  };
5322
5692
  if (config.trigger) {
@@ -5460,7 +5830,6 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
5460
5830
  const effectsWarnings = validateNodeEffects(doc);
5461
5831
  for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
5462
5832
  const animatorConfig = getAnimatorConfig(doc) || {};
5463
- animatorConfig.debug = true;
5464
5833
  const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi;
5465
5834
  doc = materialiseAllInTree(doc, engine);
5466
5835
  let rootElement = null;
@@ -5489,37 +5858,55 @@ function createAnimator(options) {
5489
5858
  return createAnimatorImpl(data, adapter, callbacks, container);
5490
5859
  }
5491
5860
  let animator = null;
5861
+ let pending = [];
5862
+ let destroyed = false;
5863
+ const enqueue = (call) => {
5864
+ if (animator) {
5865
+ call(animator);
5866
+ } else if (pending) {
5867
+ pending.push(call);
5868
+ }
5869
+ };
5492
5870
  fetch(src).then((res) => res.json()).then((json) => {
5871
+ if (destroyed) return;
5493
5872
  if (isPxElementFileFormat(json)) {
5494
5873
  animator = createAnimatorImpl(json, adapter, callbacks, container);
5874
+ const queued = pending;
5875
+ pending = null;
5876
+ queued == null ? void 0 : queued.forEach((call) => call(animator));
5495
5877
  } else {
5496
- console.error("Invalid animation document format");
5878
+ console.error('createAnimator: invalid animation document format at "' + src + '"');
5497
5879
  }
5880
+ }).catch((err) => {
5881
+ pending = null;
5882
+ console.error('createAnimator: failed to load "' + src + '"', err);
5498
5883
  });
5499
5884
  return {
5500
5885
  "isReady": () => !!animator,
5501
5886
  "getRootElement": () => animator ? animator.getRootElement() : null,
5502
5887
  "isPlaying": () => (animator == null ? void 0 : animator.isPlaying()) || false,
5503
5888
  "play": () => {
5504
- animator == null ? void 0 : animator.play();
5889
+ enqueue((api) => api.play());
5505
5890
  },
5506
5891
  "pause": () => {
5507
- animator == null ? void 0 : animator.pause();
5892
+ enqueue((api) => api.pause());
5508
5893
  },
5509
5894
  "cancel": () => {
5510
- animator == null ? void 0 : animator.cancel();
5895
+ enqueue((api) => api.cancel());
5511
5896
  },
5512
5897
  "finish": () => {
5513
- animator == null ? void 0 : animator.finish();
5898
+ enqueue((api) => api.finish());
5514
5899
  },
5515
5900
  "setPlaybackRate": (rate) => {
5516
- animator == null ? void 0 : animator.setPlaybackRate(rate);
5901
+ enqueue((api) => api.setPlaybackRate(rate));
5517
5902
  },
5518
- "getCurrentTime": () => (animator == null ? void 0 : animator.getCurrentTime()) || 0,
5903
+ "getCurrentTime": () => animator ? animator.getCurrentTime() : null,
5519
5904
  "setCurrentTime": (time) => {
5520
- animator == null ? void 0 : animator.setCurrentTime(time);
5905
+ enqueue((api) => api.setCurrentTime(time));
5521
5906
  },
5522
5907
  "destroy": () => {
5908
+ destroyed = true;
5909
+ pending = null;
5523
5910
  animator == null ? void 0 : animator.destroy();
5524
5911
  }
5525
5912
  };
@@ -5784,8 +6171,8 @@ function subtractMultiset(a, b) {
5784
6171
  PxRetimeEffectSchema,
5785
6172
  PxStrokeGradientEffectSchema,
5786
6173
  PxSvgNodeExtra,
5787
- PxTextAlongPathEffectSchema,
5788
6174
  PxTextEffectSchema,
6175
+ PxTextPathEffectSchema,
5789
6176
  PxTransformPartsSchema,
5790
6177
  PxTransformValueSchema,
5791
6178
  PxTransformationEffectSchema,
@@ -5801,10 +6188,12 @@ function subtractMultiset(a, b) {
5801
6188
  createAnimatorImpl,
5802
6189
  createBasicFrameLoopAnimator,
5803
6190
  createFrameLoopAnimator,
6191
+ createPathSampler,
5804
6192
  createWebApiAnimator,
5805
6193
  describeSchema,
5806
6194
  diffInEffect,
5807
6195
  evaluateMotionPathSegment,
6196
+ extendedPathForBrowser,
5808
6197
  generateNewIds,
5809
6198
  getAnimatorConfig,
5810
6199
  getBindings,
@@ -5814,6 +6203,7 @@ function subtractMultiset(a, b) {
5814
6203
  isPxElementFileFormat,
5815
6204
  isPxElementFileFormatDeep,
5816
6205
  jsonElementFactory,
6206
+ layoutGlyphTextChars,
5817
6207
  loadTagAnimators,
5818
6208
  materialiseAllInTree,
5819
6209
  materialiseAnimatedUseInstances,
@@ -5830,6 +6220,7 @@ function subtractMultiset(a, b) {
5830
6220
  renderNode,
5831
6221
  schemaKeys,
5832
6222
  setupAnimationTriggers,
6223
+ shiftAnimatable,
5833
6224
  toRGBA,
5834
6225
  validateNodeEffects,
5835
6226
  visualModelAt