@pixldocs/canvas-renderer 0.5.205 → 0.5.207

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.
@@ -3631,17 +3631,14 @@ async function normalizeSvgImageDimensions(fabricImage, imageUrl, sourceFormat)
3631
3631
  const el = ((_a = fabricImage.getElement) == null ? void 0 : _a.call(fabricImage)) ?? fabricImage._element;
3632
3632
  let w = 0;
3633
3633
  let h = 0;
3634
- if (el && typeof el.naturalWidth === "number" && typeof el.naturalHeight === "number" && el.naturalWidth > 0 && el.naturalHeight > 0) {
3634
+ const dims = await loadSvgDimensions(imageUrl);
3635
+ if (dims && dims.width > 0 && dims.height > 0) {
3636
+ w = dims.width;
3637
+ h = dims.height;
3638
+ } else if (el && typeof el.naturalWidth === "number" && typeof el.naturalHeight === "number" && el.naturalWidth > 0 && el.naturalHeight > 0) {
3635
3639
  w = el.naturalWidth;
3636
3640
  h = el.naturalHeight;
3637
3641
  }
3638
- if (w <= 0 || h <= 0) {
3639
- const dims = await loadSvgDimensions(imageUrl);
3640
- if (dims && dims.width > 0 && dims.height > 0) {
3641
- w = dims.width;
3642
- h = dims.height;
3643
- }
3644
- }
3645
3642
  if (w > 0 && h > 0) {
3646
3643
  fabricImage.set({ width: w, height: h });
3647
3644
  fabricImage.setCoords();
@@ -5695,6 +5692,134 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5695
5692
  return true;
5696
5693
  });
5697
5694
  }
5695
+ const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
5696
+ function arcPath(w, sag, up) {
5697
+ if (sag <= 0.5) return `M 0 0 L ${w} 0`;
5698
+ const r = (sag * sag + w / 2 * (w / 2)) / (2 * sag);
5699
+ const sweep = 1;
5700
+ const y0 = sag;
5701
+ const y1 = sag;
5702
+ return `M 0 ${y0} A ${r.toFixed(2)} ${r.toFixed(2)} 0 0 ${sweep} ${w} ${y1}`;
5703
+ }
5704
+ function sampledPath(fn, steps = 60) {
5705
+ const parts = [];
5706
+ for (let i = 0; i <= steps; i++) {
5707
+ const [x, y] = fn(i / steps);
5708
+ parts.push(i === 0 ? `M ${x.toFixed(2)} ${y.toFixed(2)}` : `L ${x.toFixed(2)} ${y.toFixed(2)}`);
5709
+ }
5710
+ return parts.join(" ");
5711
+ }
5712
+ function resolveTextPath(cfg, width, fontSize) {
5713
+ if (!cfg || !cfg.preset || cfg.preset === "none") return null;
5714
+ const w = Math.max(1, width);
5715
+ const fs = Math.max(4, fontSize);
5716
+ const t = clamp01$1(cfg.intensity ?? 0.5);
5717
+ if (cfg.bezier && cfg.preset !== "circle" && cfg.preset !== "rise" && cfg.preset !== "angle") {
5718
+ const { p0, c0, c1, p1 } = cfg.bezier;
5719
+ const minX = Math.min(p0[0], c0[0], c1[0], p1[0]);
5720
+ const maxX = Math.max(p0[0], c0[0], c1[0], p1[0]);
5721
+ const minY = Math.min(p0[1], c0[1], c1[1], p1[1]);
5722
+ const maxY = Math.max(p0[1], c0[1], c1[1], p1[1]);
5723
+ return {
5724
+ d: `M ${p0[0]} ${p0[1]} C ${c0[0]} ${c0[1]}, ${c1[0]} ${c1[1]}, ${p1[0]} ${p1[1]}`,
5725
+ bbox: { width: maxX - minX, height: maxY - minY + fs * 1.4 }
5726
+ };
5727
+ }
5728
+ if (cfg.preset === "custom") {
5729
+ if (!cfg.path) return null;
5730
+ return { d: cfg.path, bbox: cfg.bbox ?? { width: w, height: fs * 2 } };
5731
+ }
5732
+ switch (cfg.preset) {
5733
+ case "arch": {
5734
+ const sag = t * w * 0.45;
5735
+ return { d: arcPath(w, sag), bbox: { width: w, height: sag + fs * 1.4 } };
5736
+ }
5737
+ case "rise":
5738
+ case "angle": {
5739
+ const ep = cfg.endpoints;
5740
+ const defaultDy = t * fs * 3;
5741
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultDy;
5742
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : 0;
5743
+ const lineMid = (leftY + rightY) / 2;
5744
+ const hasCenterHandle = ep && Number.isFinite(ep.centerY);
5745
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : 0;
5746
+ const curveAmt = Math.max(-1, Math.min(1, rawCurve));
5747
+ const curveAmp = Math.min(w * 0.18, fs * 4);
5748
+ const centerY = hasCenterHandle ? ep.centerY : lineMid - curveAmt * curveAmp;
5749
+ if (Math.abs(centerY - lineMid) < 0.5) {
5750
+ const minY2 = Math.min(leftY, rightY);
5751
+ const maxY2 = Math.max(leftY, rightY);
5752
+ return {
5753
+ d: `M 0 ${leftY.toFixed(2)} L ${w} ${rightY.toFixed(2)}`,
5754
+ bbox: { width: w, height: maxY2 - minY2 + fs * 1.4 }
5755
+ };
5756
+ }
5757
+ const controlY = 2 * centerY - lineMid;
5758
+ const minY = Math.min(leftY, rightY, centerY);
5759
+ const maxY = Math.max(leftY, rightY, centerY);
5760
+ return {
5761
+ d: `M 0 ${leftY.toFixed(2)} Q ${(w / 2).toFixed(2)} ${controlY.toFixed(2)} ${w} ${rightY.toFixed(2)}`,
5762
+ bbox: { width: w, height: maxY - minY + fs * 1.4 }
5763
+ };
5764
+ }
5765
+ case "wave": {
5766
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : t;
5767
+ const c = Math.max(-1, Math.min(1, rawCurve));
5768
+ const amp = c * fs * 3;
5769
+ const absAmp = Math.abs(amp);
5770
+ const d = sampledPath((u) => [u * w, absAmp - amp * Math.sin(u * Math.PI)]);
5771
+ return { d, bbox: { width: w, height: absAmp * 2 + fs * 1.4 } };
5772
+ }
5773
+ case "flag": {
5774
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : t;
5775
+ const c = Math.max(-1, Math.min(1, rawCurve));
5776
+ const amp = c * fs * 2.5;
5777
+ const absAmp = Math.abs(amp);
5778
+ const d = sampledPath((u) => [u * w, absAmp + amp * Math.sin(u * Math.PI * 2)]);
5779
+ return { d, bbox: { width: w, height: absAmp * 2 + fs * 1.4 } };
5780
+ }
5781
+ case "circle": {
5782
+ const auto = Math.max(fs * 1.5, w / Math.PI);
5783
+ const r = Math.max(fs * 0.75, Number.isFinite(cfg.radius) && cfg.radius > 0 ? cfg.radius : auto);
5784
+ return {
5785
+ d: `M ${r} ${2 * r} A ${r} ${r} 0 1 1 ${r} 0 A ${r} ${r} 0 1 1 ${r} ${2 * r}`,
5786
+ bbox: { width: 2 * r, height: 2 * r + fs * 1.4 }
5787
+ };
5788
+ }
5789
+ default:
5790
+ return null;
5791
+ }
5792
+ }
5793
+ function measurePath(d) {
5794
+ if (typeof document === "undefined") return null;
5795
+ const svgNS = "http://www.w3.org/2000/svg";
5796
+ const svg = document.createElementNS(svgNS, "svg");
5797
+ const path = document.createElementNS(svgNS, "path");
5798
+ path.setAttribute("d", d);
5799
+ svg.appendChild(path);
5800
+ return path;
5801
+ }
5802
+ function resolveTextPathAlphabeticBaselineDy(ctx, sampleText, fontSize) {
5803
+ const prevBaseline = ctx.textBaseline;
5804
+ try {
5805
+ ctx.textBaseline = "alphabetic";
5806
+ const metrics = ctx.measureText(sampleText || "M");
5807
+ const fontAscent = Number(metrics.fontBoundingBoxAscent);
5808
+ const fontDescent = Number(metrics.fontBoundingBoxDescent);
5809
+ if (Number.isFinite(fontAscent) && Number.isFinite(fontDescent) && fontAscent + fontDescent > 0) {
5810
+ return (fontAscent - fontDescent) / 2;
5811
+ }
5812
+ const actualAscent = Number(metrics.actualBoundingBoxAscent);
5813
+ const actualDescent = Number(metrics.actualBoundingBoxDescent);
5814
+ if (Number.isFinite(actualAscent) && Number.isFinite(actualDescent) && actualAscent + actualDescent > 0) {
5815
+ return (actualAscent - actualDescent) / 2;
5816
+ }
5817
+ } catch {
5818
+ } finally {
5819
+ ctx.textBaseline = prevBaseline;
5820
+ }
5821
+ return fontSize / 2;
5822
+ }
5698
5823
  const TextboxProto = fabric.Textbox.prototype;
5699
5824
  if (!TextboxProto.__pixldocsOrigCalcTextHeight) {
5700
5825
  TextboxProto.__pixldocsOrigCalcTextHeight = TextboxProto.calcTextHeight;
@@ -5736,23 +5861,988 @@ const stateProps = fabric.Textbox.prototype.stateProperties;
5736
5861
  if (Array.isArray(stateProps)) {
5737
5862
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
5738
5863
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
5864
+ if (!stateProps.includes("textPath")) stateProps.push("textPath");
5739
5865
  }
5740
5866
  const cacheProps = fabric.Textbox.prototype.cacheProperties;
5741
5867
  if (Array.isArray(cacheProps)) {
5742
5868
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
5743
5869
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
5870
+ if (!cacheProps.includes("textPath")) cacheProps.push("textPath");
5871
+ }
5872
+ const hasActiveTextPath = (obj) => {
5873
+ const tp = obj.textPath;
5874
+ return !!tp && !!tp.preset && tp.preset !== "none";
5875
+ };
5876
+ function applyWarpFillStyle(ctx, obj) {
5877
+ const filler = obj.fill;
5878
+ if (filler && typeof filler === "object" && Array.isArray(filler.colorStops) && filler.coords) {
5879
+ try {
5880
+ const halfW = (obj.width || 0) / 2;
5881
+ const halfH = (obj.height || 0) / 2;
5882
+ const c = filler.coords || {};
5883
+ let live = null;
5884
+ if (filler.type === "radial") {
5885
+ live = ctx.createRadialGradient(
5886
+ (Number(c.x1) || 0) - halfW,
5887
+ (Number(c.y1) || 0) - halfH,
5888
+ Math.max(0, Number(c.r1) || 0),
5889
+ (Number(c.x2 ?? c.x1) || 0) - halfW,
5890
+ (Number(c.y2 ?? c.y1) || 0) - halfH,
5891
+ Math.max(0.1, Number(c.r2 ?? c.r) || Math.max(obj.width || 1, obj.height || 1) / 2)
5892
+ );
5893
+ } else {
5894
+ live = ctx.createLinearGradient(
5895
+ (Number(c.x1) || 0) - halfW,
5896
+ (Number(c.y1) || 0) - halfH,
5897
+ (Number(c.x2 ?? obj.width) || 0) - halfW,
5898
+ (Number(c.y2) || 0) - halfH
5899
+ );
5900
+ }
5901
+ for (const stop of filler.colorStops) {
5902
+ live.addColorStop(Math.max(0, Math.min(1, Number(stop.offset) || 0)), stop.color || "#000");
5903
+ }
5904
+ ctx.fillStyle = live;
5905
+ return { offsetX: 0, offsetY: 0 };
5906
+ } catch {
5907
+ }
5908
+ }
5909
+ if (typeof obj.handleFiller === "function") {
5910
+ try {
5911
+ return obj.handleFiller(ctx, "fillStyle", filler || "#000") || { offsetX: 0, offsetY: 0 };
5912
+ } catch {
5913
+ }
5914
+ }
5915
+ if (filler && typeof filler.toLive === "function") {
5916
+ try {
5917
+ ctx.fillStyle = filler.toLive(ctx) || "#000";
5918
+ return { offsetX: 0, offsetY: 0 };
5919
+ } catch {
5920
+ }
5921
+ }
5922
+ ctx.fillStyle = typeof obj.fill === "string" ? obj.fill : "#000";
5923
+ return { offsetX: 0, offsetY: 0 };
5924
+ }
5925
+ function parseCssColor(input) {
5926
+ const fallback = [0, 0, 0, 1];
5927
+ if (!input) return fallback;
5928
+ const s = String(input).trim();
5929
+ if (s.startsWith("#")) {
5930
+ let hex = s.slice(1);
5931
+ if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
5932
+ if (hex.length === 4) hex = hex.split("").map((c) => c + c).join("");
5933
+ if (hex.length === 6) {
5934
+ const n = parseInt(hex, 16);
5935
+ return [n >> 16 & 255, n >> 8 & 255, n & 255, 1];
5936
+ }
5937
+ if (hex.length === 8) {
5938
+ const n = parseInt(hex, 16);
5939
+ return [n >> 24 & 255, n >> 16 & 255, n >> 8 & 255, (n & 255) / 255];
5940
+ }
5941
+ }
5942
+ const m = s.match(/^rgba?\s*\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+%?))?\s*\)$/i);
5943
+ if (m) {
5944
+ let a = 1;
5945
+ if (m[4] != null) a = m[4].endsWith("%") ? parseFloat(m[4]) / 100 : parseFloat(m[4]);
5946
+ return [parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3]), Number.isFinite(a) ? a : 1];
5947
+ }
5948
+ try {
5949
+ const c = document.createElement("canvas").getContext("2d");
5950
+ if (c) {
5951
+ c.fillStyle = "#000";
5952
+ c.fillStyle = s;
5953
+ const computed = c.fillStyle;
5954
+ return parseCssColor(computed.startsWith("#") || computed.startsWith("rgb") ? computed : "#000");
5955
+ }
5956
+ } catch {
5957
+ }
5958
+ return fallback;
5959
+ }
5960
+ function sampleGradientColor(stops, t) {
5961
+ if (!stops || !stops.length) return "#000";
5962
+ const sorted = [...stops].sort((a, b) => (a.offset || 0) - (b.offset || 0));
5963
+ const u = Math.max(0, Math.min(1, t));
5964
+ if (u <= (sorted[0].offset || 0)) return sorted[0].color || "#000";
5965
+ if (u >= (sorted[sorted.length - 1].offset || 1)) return sorted[sorted.length - 1].color || "#000";
5966
+ for (let i = 0; i < sorted.length - 1; i++) {
5967
+ const a = sorted[i];
5968
+ const b = sorted[i + 1];
5969
+ const ao = a.offset || 0;
5970
+ const bo = b.offset || 0;
5971
+ if (u >= ao && u <= bo) {
5972
+ const span = bo - ao || 1;
5973
+ const k = (u - ao) / span;
5974
+ const ca = parseCssColor(a.color);
5975
+ const cb = parseCssColor(b.color);
5976
+ const r = Math.round(ca[0] + (cb[0] - ca[0]) * k);
5977
+ const g = Math.round(ca[1] + (cb[1] - ca[1]) * k);
5978
+ const bl = Math.round(ca[2] + (cb[2] - ca[2]) * k);
5979
+ const al = ca[3] + (cb[3] - ca[3]) * k;
5980
+ return `rgba(${r}, ${g}, ${bl}, ${al.toFixed(3)})`;
5981
+ }
5982
+ }
5983
+ return sorted[sorted.length - 1].color || "#000";
5984
+ }
5985
+ function getGradientStopsForFlow(obj) {
5986
+ const f = obj.fill;
5987
+ if (f && typeof f === "object" && Array.isArray(f.colorStops) && f.colorStops.length) {
5988
+ return f.colorStops.map((s) => ({ offset: Number(s.offset) || 0, color: s.color || "#000" }));
5989
+ }
5990
+ return null;
5991
+ }
5992
+ function localPointFromCanvas(target, x, y) {
5993
+ const point = new fabric.Point(x, y);
5994
+ try {
5995
+ return target.toLocalPoint(point, "left", "top");
5996
+ } catch {
5997
+ const inv = fabric.util.invertTransform(target.calcTransformMatrix());
5998
+ const local = fabric.util.transformPoint(point, inv);
5999
+ return new fabric.Point(local.x + (target.width || 0) / 2, local.y + (target.height || 0) / 2);
6000
+ }
6001
+ }
6002
+ function localPointFromFrozenMatrix(target, x, y, matrix) {
6003
+ const local = fabric.util.transformPoint(new fabric.Point(x, y), fabric.util.invertTransform(matrix));
6004
+ return new fabric.Point(local.x + (target.width || 0) / 2, local.y + (target.height || 0) / 2);
6005
+ }
6006
+ function vectorFromFrozenMatrix(matrix, dx, dy) {
6007
+ return new fabric.Point(matrix[0] * dx + matrix[2] * dy, matrix[1] * dx + matrix[3] * dy);
6008
+ }
6009
+ function scaleLocalToScreen(target, p) {
6010
+ var _a;
6011
+ const vpt = ((_a = target == null ? void 0 : target.canvas) == null ? void 0 : _a.viewportTransform) || [1, 0, 0, 1, 0, 0];
6012
+ const zx = Math.abs(vpt[0] || 1);
6013
+ const zy = Math.abs(vpt[3] || 1);
6014
+ const sx = Math.abs((target == null ? void 0 : target.scaleX) || 1);
6015
+ const sy = Math.abs((target == null ? void 0 : target.scaleY) || 1);
6016
+ return new fabric.Point(p.x * sx * zx, p.y * sy * zy);
6017
+ }
6018
+ function applyTextPathControls(textbox) {
6019
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
6020
+ const obj = textbox;
6021
+ if (!hasActiveTextPath(obj)) {
6022
+ obj.__pdTextPathHovered = false;
6023
+ if (obj.__pdTextPathControls) {
6024
+ try {
6025
+ const cu2 = fabric.controlsUtils;
6026
+ const defaults = ((_a = cu2 == null ? void 0 : cu2.createTextboxDefaultControls) == null ? void 0 : _a.call(cu2)) ?? ((_b = cu2 == null ? void 0 : cu2.createObjectDefaultControls) == null ? void 0 : _b.call(cu2));
6027
+ if (defaults) obj.controls = defaults;
6028
+ } catch {
6029
+ }
6030
+ delete obj.controls.bzP0;
6031
+ delete obj.controls.bzC0;
6032
+ delete obj.controls.bzC1;
6033
+ delete obj.controls.bzP1;
6034
+ delete obj.controls.rsL;
6035
+ delete obj.controls.rsR;
6036
+ delete obj.controls.rsC;
6037
+ delete obj.controls.bzMid;
6038
+ delete obj.controls.bzMidT0;
6039
+ delete obj.controls.bzMidT1;
6040
+ delete obj.controls.tpRot;
6041
+ delete obj.controls.tpPivot;
6042
+ (_c = obj.setControlsVisibility) == null ? void 0 : _c.call(obj, { tl: true, tr: true, bl: true, br: true, mt: true, mb: true, ml: true, mr: true, mtr: true });
6043
+ obj.hasBorders = true;
6044
+ obj.__pdTextPathControls = false;
6045
+ obj.setCoords();
6046
+ }
6047
+ return;
6048
+ }
6049
+ if (!obj.__pdTextPathHoverWired) {
6050
+ obj.on("mouseover", () => {
6051
+ var _a2;
6052
+ if (!hasActiveTextPath(obj)) return;
6053
+ obj.__pdTextPathHovered = true;
6054
+ (_a2 = obj.canvas) == null ? void 0 : _a2.requestRenderAll();
6055
+ });
6056
+ obj.on("mouseout", () => {
6057
+ var _a2;
6058
+ obj.__pdTextPathHovered = false;
6059
+ (_a2 = obj.canvas) == null ? void 0 : _a2.requestRenderAll();
6060
+ });
6061
+ obj.__pdTextPathHoverWired = true;
6062
+ }
6063
+ obj.controls = {};
6064
+ const halfWOf = (t) => (t.width || 0) / 2;
6065
+ const halfHOf = (t) => (t.height || 0) / 2;
6066
+ const cu = fabric.controlsUtils;
6067
+ const renderRotation = (ctx, left, top) => {
6068
+ ctx.save();
6069
+ ctx.translate(left, top);
6070
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6071
+ ctx.shadowBlur = 2;
6072
+ ctx.shadowOffsetY = 0.5;
6073
+ ctx.fillStyle = "#ffffff";
6074
+ ctx.strokeStyle = "#1d9bf0";
6075
+ ctx.lineWidth = 1.5;
6076
+ ctx.beginPath();
6077
+ ctx.arc(0, 0, 6, 0, Math.PI * 2);
6078
+ ctx.fill();
6079
+ ctx.shadowColor = "transparent";
6080
+ ctx.stroke();
6081
+ ctx.strokeStyle = "#1d9bf0";
6082
+ ctx.lineWidth = 1.25;
6083
+ ctx.beginPath();
6084
+ ctx.arc(0, 0, 3, -Math.PI * 0.85, Math.PI * 0.75);
6085
+ ctx.stroke();
6086
+ ctx.restore();
6087
+ };
6088
+ const renderPivot = (ctx, left, top) => {
6089
+ ctx.save();
6090
+ ctx.translate(left, top);
6091
+ ctx.strokeStyle = "#1d9bf0";
6092
+ ctx.fillStyle = "#ffffff";
6093
+ ctx.lineWidth = 1.25;
6094
+ ctx.beginPath();
6095
+ ctx.arc(0, 0, 5, 0, Math.PI * 2);
6096
+ ctx.fill();
6097
+ ctx.stroke();
6098
+ ctx.beginPath();
6099
+ ctx.moveTo(-3.5, 0);
6100
+ ctx.lineTo(3.5, 0);
6101
+ ctx.moveTo(0, -3.5);
6102
+ ctx.lineTo(0, 3.5);
6103
+ ctx.stroke();
6104
+ ctx.fillStyle = "#1d9bf0";
6105
+ ctx.beginPath();
6106
+ ctx.arc(0, 0, 1.25, 0, Math.PI * 2);
6107
+ ctx.fill();
6108
+ ctx.restore();
6109
+ };
6110
+ const addRotationAndPivot = () => {
6111
+ var _a2, _b2;
6112
+ obj.controls.tpRot = new fabric.Control({
6113
+ x: 0,
6114
+ y: -0.5,
6115
+ offsetY: -28,
6116
+ cursorStyleHandler: cu == null ? void 0 : cu.rotationStyleHandler,
6117
+ actionHandler: cu == null ? void 0 : cu.rotationWithSnapping,
6118
+ actionName: "rotate",
6119
+ render: renderRotation,
6120
+ withConnection: false
6121
+ });
6122
+ obj.controls.tpPivot = new fabric.Control({
6123
+ x: 0,
6124
+ y: 0,
6125
+ cursorStyle: "default",
6126
+ actionName: "tpPivot",
6127
+ actionHandler: () => false,
6128
+ render: renderPivot
6129
+ });
6130
+ (_a2 = obj.setControlVisible) == null ? void 0 : _a2.call(obj, "tpRot", true);
6131
+ (_b2 = obj.setControlVisible) == null ? void 0 : _b2.call(obj, "tpPivot", true);
6132
+ };
6133
+ if (((_d = obj.textPath) == null ? void 0 : _d.preset) === "circle") {
6134
+ const getRadius = (target) => {
6135
+ const tp = target.textPath || {};
6136
+ const fs = target.fontSize || 16;
6137
+ const w = target.width || 0;
6138
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6139
+ const r = tp.radius;
6140
+ return Math.max(fs * 0.75, Number.isFinite(r) && r > 0 ? r : auto);
6141
+ };
6142
+ const setRadius = (target, r) => {
6143
+ var _a2;
6144
+ const fs = target.fontSize || 16;
6145
+ const clamped = Math.max(fs * 0.75, r);
6146
+ if (!target.textPath) target.textPath = { preset: "circle" };
6147
+ target.textPath.preset = "circle";
6148
+ target.textPath.radius = clamped;
6149
+ target.dirty = true;
6150
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6151
+ };
6152
+ const ringPoint = (target, dir) => {
6153
+ const r = getRadius(target);
6154
+ const cx = r - halfWOf(target);
6155
+ const cy = r - halfHOf(target);
6156
+ switch (dir) {
6157
+ case "N":
6158
+ return new fabric.Point(cx, cy - r);
6159
+ case "E":
6160
+ return new fabric.Point(cx + r, cy);
6161
+ case "S":
6162
+ return new fabric.Point(cx, cy + r);
6163
+ case "W":
6164
+ return new fabric.Point(cx - r, cy);
6165
+ }
6166
+ };
6167
+ const positionAtRing = (dir) => (_d2, finalMatrix, target) => scaleLocalToScreen(target, ringPoint(target, dir)).transform(finalMatrix);
6168
+ const dragRadius = (dir) => (_e2, transform, x, y) => {
6169
+ const target = transform.target;
6170
+ const state = transform.__pdCircleRadiusDrag || (transform.__pdCircleRadiusDrag = {
6171
+ dir,
6172
+ startRadius: getRadius(target),
6173
+ startLeft: target.left || 0,
6174
+ startTop: target.top || 0,
6175
+ startMatrix: target.calcTransformMatrix(),
6176
+ startLocal: null
6177
+ });
6178
+ if (!state.startLocal) state.startLocal = localPointFromFrozenMatrix(target, x, y, state.startMatrix);
6179
+ const local = localPointFromFrozenMatrix(target, x, y, state.startMatrix);
6180
+ const dx = local.x - state.startLocal.x;
6181
+ const dy = local.y - state.startLocal.y;
6182
+ let next = state.startRadius;
6183
+ if (dir === "E") next = state.startRadius + dx / 2;
6184
+ else if (dir === "W") next = state.startRadius - dx / 2;
6185
+ else if (dir === "S") next = state.startRadius + dy / 2;
6186
+ else next = state.startRadius - dy / 2;
6187
+ const fs = target.fontSize || 16;
6188
+ next = Math.max(fs * 0.75, next);
6189
+ if (dir === "W") {
6190
+ const appliedDx = (state.startRadius - next) * 2;
6191
+ const move = vectorFromFrozenMatrix(state.startMatrix, appliedDx, 0);
6192
+ target.set({ left: state.startLeft + move.x, top: state.startTop + move.y });
6193
+ } else if (dir === "N") {
6194
+ const appliedDy = (state.startRadius - next) * 2;
6195
+ const move = vectorFromFrozenMatrix(state.startMatrix, 0, appliedDy);
6196
+ target.set({ left: state.startLeft + move.x, top: state.startTop + move.y });
6197
+ }
6198
+ setRadius(target, next);
6199
+ return true;
6200
+ };
6201
+ const renderRingHandle = (ctx, left, top) => {
6202
+ ctx.save();
6203
+ ctx.translate(left, top);
6204
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6205
+ ctx.shadowBlur = 2;
6206
+ ctx.shadowOffsetY = 0.5;
6207
+ ctx.fillStyle = "#ffffff";
6208
+ ctx.strokeStyle = "#1d9bf0";
6209
+ ctx.lineWidth = 1.5;
6210
+ ctx.beginPath();
6211
+ ctx.arc(0, 0, 5.5, 0, Math.PI * 2);
6212
+ ctx.fill();
6213
+ ctx.shadowColor = "transparent";
6214
+ ctx.stroke();
6215
+ ctx.restore();
6216
+ };
6217
+ obj.controls.crN = new fabric.Control({ x: 0, y: -0.5, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragRadius("N"), positionHandler: positionAtRing("N"), render: renderRingHandle });
6218
+ obj.controls.crE = new fabric.Control({ x: 0.5, y: 0, cursorStyle: "ew-resize", actionName: "textPath", actionHandler: dragRadius("E"), positionHandler: positionAtRing("E"), render: renderRingHandle });
6219
+ obj.controls.crS = new fabric.Control({ x: 0, y: 0.5, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragRadius("S"), positionHandler: positionAtRing("S"), render: renderRingHandle });
6220
+ obj.controls.crW = new fabric.Control({ x: -0.5, y: 0, cursorStyle: "ew-resize", actionName: "textPath", actionHandler: dragRadius("W"), positionHandler: positionAtRing("W"), render: renderRingHandle });
6221
+ (_e = obj.setControlVisible) == null ? void 0 : _e.call(obj, "crN", true);
6222
+ (_f = obj.setControlVisible) == null ? void 0 : _f.call(obj, "crE", true);
6223
+ (_g = obj.setControlVisible) == null ? void 0 : _g.call(obj, "crS", true);
6224
+ (_h = obj.setControlVisible) == null ? void 0 : _h.call(obj, "crW", true);
6225
+ addRotationAndPivot();
6226
+ obj.hasBorders = false;
6227
+ obj.hasControls = true;
6228
+ obj.objectCaching = false;
6229
+ obj.__pdTextPathControls = true;
6230
+ obj.setCoords();
6231
+ return;
6232
+ }
6233
+ if (((_i = obj.textPath) == null ? void 0 : _i.preset) === "rise") {
6234
+ const getEndpoints = (target) => {
6235
+ const w = target.width || 0;
6236
+ const fs = target.fontSize || 16;
6237
+ const tp = target.textPath || {};
6238
+ const ep = tp.endpoints;
6239
+ const defaultLeftY = fs * 1.5;
6240
+ const defaultRightY = fs * 0.5;
6241
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
6242
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
6243
+ const lineMid = (leftY + rightY) / 2;
6244
+ const rawCurve = Number.isFinite(tp.curve) ? tp.curve : 0;
6245
+ const curveAmp = Math.min(w * 0.18, fs * 4);
6246
+ const centerY = ep && Number.isFinite(ep.centerY) ? ep.centerY : lineMid - Math.max(-1, Math.min(1, rawCurve)) * curveAmp;
6247
+ return { leftY, centerY, rightY };
6248
+ };
6249
+ const setEndpoints = (target, next) => {
6250
+ var _a2;
6251
+ target.width || 1;
6252
+ target.fontSize || 16;
6253
+ const lineMid = (next.leftY + next.rightY) / 2;
6254
+ const straight = { leftY: next.leftY, centerY: lineMid, rightY: next.rightY };
6255
+ if (!target.textPath) target.textPath = { preset: "rise" };
6256
+ target.textPath = {
6257
+ ...target.textPath,
6258
+ preset: "rise",
6259
+ curve: 0,
6260
+ endpoints: straight
6261
+ };
6262
+ if (target.skewY) target.set("skewY", 0);
6263
+ target.dirty = true;
6264
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6265
+ };
6266
+ const positionAtAngleHandle = (key) => (_d2, finalMatrix, target) => {
6267
+ const ep = getEndpoints(target);
6268
+ const x = key === "leftY" ? 0 : key === "rightY" ? target.width || 0 : (target.width || 0) / 2;
6269
+ return scaleLocalToScreen(target, new fabric.Point(x - halfWOf(target), ep[key] - halfHOf(target))).transform(finalMatrix);
6270
+ };
6271
+ const dragAngleHandle = (key) => (_e2, transform, x, y) => {
6272
+ const target = transform.target;
6273
+ const local = localPointFromCanvas(target, x, y);
6274
+ const current = getEndpoints(target);
6275
+ const next = { ...current, [key]: local.y };
6276
+ setEndpoints(target, next);
6277
+ return true;
6278
+ };
6279
+ const renderAngleHandle = (ctx, left, top) => {
6280
+ ctx.save();
6281
+ ctx.translate(left, top);
6282
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6283
+ ctx.shadowBlur = 2;
6284
+ ctx.shadowOffsetY = 0.5;
6285
+ ctx.fillStyle = "#ffffff";
6286
+ ctx.strokeStyle = "#1d9bf0";
6287
+ ctx.lineWidth = 1.5;
6288
+ ctx.beginPath();
6289
+ ctx.arc(0, 0, 5.5, 0, Math.PI * 2);
6290
+ ctx.fill();
6291
+ ctx.shadowColor = "transparent";
6292
+ ctx.stroke();
6293
+ ctx.restore();
6294
+ };
6295
+ if (obj.skewY) obj.set("skewY", 0);
6296
+ obj.controls.rsL = new fabric.Control({ x: -0.5, y: 0, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragAngleHandle("leftY"), positionHandler: positionAtAngleHandle("leftY"), render: renderAngleHandle });
6297
+ obj.controls.rsR = new fabric.Control({ x: 0.5, y: 0, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragAngleHandle("rightY"), positionHandler: positionAtAngleHandle("rightY"), render: renderAngleHandle });
6298
+ (_j = obj.setControlVisible) == null ? void 0 : _j.call(obj, "rsL", true);
6299
+ (_k = obj.setControlVisible) == null ? void 0 : _k.call(obj, "rsR", true);
6300
+ addRotationAndPivot();
6301
+ obj.hasBorders = false;
6302
+ obj.hasControls = true;
6303
+ obj.objectCaching = false;
6304
+ obj.__pdTextPathControls = true;
6305
+ obj.setCoords();
6306
+ return;
6307
+ }
6308
+ const ensureBezier = (target) => {
6309
+ const tp = target.textPath;
6310
+ if (tp == null ? void 0 : tp.bezier) return tp.bezier;
6311
+ const resolved = resolveTextPath(tp, target.width || 0, target.fontSize || 16);
6312
+ const path = resolved ? measurePath(resolved.d) : null;
6313
+ if (!path) {
6314
+ const w = target.width || 0;
6315
+ const fs = target.fontSize || 16;
6316
+ const h = target.height || fs * 1.4;
6317
+ const cy = h / 2;
6318
+ const handleLen = w * 0.25;
6319
+ const fallback = {
6320
+ p0: [0, cy],
6321
+ c0: [handleLen, cy],
6322
+ c1: [w - handleLen, cy],
6323
+ p1: [w, cy]
6324
+ };
6325
+ target.textPath = { ...tp || { preset: "custom" }, bezier: fallback };
6326
+ return fallback;
6327
+ }
6328
+ const len = path.getTotalLength();
6329
+ const sample = (u) => {
6330
+ const p = path.getPointAtLength(u * len);
6331
+ return [p.x, p.y];
6332
+ };
6333
+ const p0 = sample(0);
6334
+ const a = sample(1 / 3);
6335
+ const b = sample(2 / 3);
6336
+ const p1 = sample(1);
6337
+ const c0 = [
6338
+ (-5 * p0[0] + 18 * a[0] - 9 * b[0] + 2 * p1[0]) / 6,
6339
+ (-5 * p0[1] + 18 * a[1] - 9 * b[1] + 2 * p1[1]) / 6
6340
+ ];
6341
+ const c1 = [
6342
+ (2 * p0[0] - 9 * a[0] + 18 * b[0] - 5 * p1[0]) / 6,
6343
+ (2 * p0[1] - 9 * a[1] + 18 * b[1] - 5 * p1[1]) / 6
6344
+ ];
6345
+ const solved = { p0, c0, c1, p1 };
6346
+ target.textPath = { ...tp || { preset: "custom" }, bezier: solved };
6347
+ return solved;
6348
+ };
6349
+ ensureBezier(obj);
6350
+ const positionAtBezier = (key) => (_d2, finalMatrix, target) => {
6351
+ const bz = ensureBezier(target);
6352
+ const [bx, by] = bz[key];
6353
+ return scaleLocalToScreen(target, new fabric.Point(bx - halfWOf(target), by - halfHOf(target))).transform(finalMatrix);
6354
+ };
6355
+ const bezierMidpoint = (bz) => [
6356
+ 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0],
6357
+ 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1]
6358
+ ];
6359
+ const positionAtMid = (_d2, finalMatrix, target) => {
6360
+ const bz = ensureBezier(target);
6361
+ const [mx, my] = bezierMidpoint(bz);
6362
+ return scaleLocalToScreen(target, new fabric.Point(mx - halfWOf(target), my - halfHOf(target))).transform(finalMatrix);
6363
+ };
6364
+ const midTangentVector = (bz) => {
6365
+ let dx = bz.p1[0] + 3 * bz.c1[0] - 3 * bz.c0[0] - bz.p0[0];
6366
+ let dy = bz.p1[1] + 3 * bz.c1[1] - 3 * bz.c0[1] - bz.p0[1];
6367
+ const dlen = Math.hypot(dx, dy) || 1;
6368
+ dx /= dlen;
6369
+ dy /= dlen;
6370
+ const mx = 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0];
6371
+ const my = 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1];
6372
+ const projC1 = (bz.c1[0] - mx) * dx + (bz.c1[1] - my) * dy;
6373
+ const projC0 = (mx - bz.c0[0]) * dx + (my - bz.c0[1]) * dy;
6374
+ const reach = Math.max(8, (Math.abs(projC1) + Math.abs(projC0)) / 2);
6375
+ return { vx: dx * reach, vy: dy * reach };
6376
+ };
6377
+ const positionAtMidTangent = (side) => (_d2, finalMatrix, target) => {
6378
+ const bz = ensureBezier(target);
6379
+ const [mx, my] = bezierMidpoint(bz);
6380
+ const { vx, vy } = midTangentVector(bz);
6381
+ const px = mx + side * vx;
6382
+ const py = my + side * vy;
6383
+ return scaleLocalToScreen(target, new fabric.Point(px - halfWOf(target), py - halfHOf(target))).transform(finalMatrix);
6384
+ };
6385
+ const makeMidTangentDrag = (side) => (_e2, transform, x, y) => {
6386
+ var _a2;
6387
+ const target = transform.target;
6388
+ const local = localPointFromCanvas(target, x, y);
6389
+ const current = ensureBezier(target);
6390
+ const bz = {
6391
+ p0: [...current.p0],
6392
+ c0: [...current.c0],
6393
+ c1: [...current.c1],
6394
+ p1: [...current.p1]
6395
+ };
6396
+ const [mx, my] = bezierMidpoint(bz);
6397
+ const oldV = midTangentVector(bz);
6398
+ const nvx = (local.x - mx) * side;
6399
+ const nvy = (local.y - my) * side;
6400
+ const oldLen2 = oldV.vx * oldV.vx + oldV.vy * oldV.vy;
6401
+ if (oldLen2 < 1e-6) return true;
6402
+ const a = (oldV.vx * nvx + oldV.vy * nvy) / oldLen2;
6403
+ const b = (oldV.vx * nvy - oldV.vy * nvx) / oldLen2;
6404
+ const apply = (px, py) => {
6405
+ const rx = px - mx;
6406
+ const ry = py - my;
6407
+ return [mx + a * rx - b * ry, my + b * rx + a * ry];
6408
+ };
6409
+ bz.c0 = apply(bz.c0[0], bz.c0[1]);
6410
+ bz.c1 = apply(bz.c1[0], bz.c1[1]);
6411
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6412
+ target.dirty = true;
6413
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6414
+ return true;
6415
+ };
6416
+ const makeDrag = (key) => (_e2, transform, x, y) => {
6417
+ var _a2;
6418
+ const target = transform.target;
6419
+ const local = localPointFromCanvas(target, x, y);
6420
+ const current = ensureBezier(target);
6421
+ const bz = { p0: [...current.p0], c0: [...current.c0], c1: [...current.c1], p1: [...current.p1] };
6422
+ if (key === "p0" || key === "p1") {
6423
+ const ctrlKey = key === "p0" ? "c0" : "c1";
6424
+ const oldAnchor = bz[key];
6425
+ const oldCtrl = bz[ctrlKey];
6426
+ const dx = local.x - oldAnchor[0];
6427
+ const dy = local.y - oldAnchor[1];
6428
+ bz[key] = [local.x, local.y];
6429
+ bz[ctrlKey] = [oldCtrl[0] + dx, oldCtrl[1] + dy];
6430
+ } else {
6431
+ bz[key] = [local.x, local.y];
6432
+ }
6433
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6434
+ target.dirty = true;
6435
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6436
+ return true;
6437
+ };
6438
+ const makeMidDrag = () => (_e2, transform, x, y) => {
6439
+ var _a2;
6440
+ const target = transform.target;
6441
+ const local = localPointFromCanvas(target, x, y);
6442
+ const current = ensureBezier(target);
6443
+ const bz = {
6444
+ p0: [...current.p0],
6445
+ c0: [...current.c0],
6446
+ c1: [...current.c1],
6447
+ p1: [...current.p1]
6448
+ };
6449
+ const [mx, my] = bezierMidpoint(bz);
6450
+ const dx = (local.x - mx) / 0.75;
6451
+ const dy = (local.y - my) / 0.75;
6452
+ bz.c0 = [bz.c0[0] + dx, bz.c0[1] + dy];
6453
+ bz.c1 = [bz.c1[0] + dx, bz.c1[1] + dy];
6454
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6455
+ target.dirty = true;
6456
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6457
+ return true;
6458
+ };
6459
+ const renderAnchor = (ctx, left, top) => {
6460
+ ctx.save();
6461
+ ctx.translate(left, top);
6462
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6463
+ ctx.shadowBlur = 2;
6464
+ ctx.shadowOffsetY = 0.5;
6465
+ ctx.fillStyle = "#ffffff";
6466
+ ctx.strokeStyle = "#1d9bf0";
6467
+ ctx.lineWidth = 1.5;
6468
+ ctx.beginPath();
6469
+ ctx.arc(0, 0, 5, 0, Math.PI * 2);
6470
+ ctx.fill();
6471
+ ctx.shadowColor = "transparent";
6472
+ ctx.stroke();
6473
+ ctx.restore();
6474
+ };
6475
+ const renderControlHandle = (ctx, left, top) => {
6476
+ ctx.save();
6477
+ ctx.translate(left, top);
6478
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6479
+ ctx.shadowBlur = 2;
6480
+ ctx.shadowOffsetY = 0.5;
6481
+ ctx.fillStyle = "#1d9bf0";
6482
+ ctx.strokeStyle = "#ffffff";
6483
+ ctx.lineWidth = 1.5;
6484
+ ctx.beginPath();
6485
+ ctx.arc(0, 0, 4, 0, Math.PI * 2);
6486
+ ctx.fill();
6487
+ ctx.shadowColor = "transparent";
6488
+ ctx.stroke();
6489
+ ctx.restore();
6490
+ };
6491
+ obj.controls.bzP0 = new fabric.Control({ x: -0.5, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("p0"), positionHandler: positionAtBezier("p0"), render: renderAnchor });
6492
+ obj.controls.bzP1 = new fabric.Control({ x: 0.5, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("p1"), positionHandler: positionAtBezier("p1"), render: renderAnchor });
6493
+ obj.controls.bzC0 = new fabric.Control({ x: -0.25, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("c0"), positionHandler: positionAtBezier("c0"), render: renderControlHandle });
6494
+ obj.controls.bzC1 = new fabric.Control({ x: 0.25, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("c1"), positionHandler: positionAtBezier("c1"), render: renderControlHandle });
6495
+ obj.controls.bzMid = new fabric.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidDrag(), positionHandler: positionAtMid, render: renderAnchor });
6496
+ obj.controls.bzMidT0 = new fabric.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidTangentDrag(-1), positionHandler: positionAtMidTangent(-1), render: renderControlHandle });
6497
+ obj.controls.bzMidT1 = new fabric.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidTangentDrag(1), positionHandler: positionAtMidTangent(1), render: renderControlHandle });
6498
+ (_l = obj.setControlVisible) == null ? void 0 : _l.call(obj, "bzP0", true);
6499
+ (_m = obj.setControlVisible) == null ? void 0 : _m.call(obj, "bzC0", true);
6500
+ (_n = obj.setControlVisible) == null ? void 0 : _n.call(obj, "bzC1", true);
6501
+ (_o = obj.setControlVisible) == null ? void 0 : _o.call(obj, "bzP1", true);
6502
+ (_p = obj.setControlVisible) == null ? void 0 : _p.call(obj, "bzMid", true);
6503
+ (_q = obj.setControlVisible) == null ? void 0 : _q.call(obj, "bzMidT0", true);
6504
+ (_r = obj.setControlVisible) == null ? void 0 : _r.call(obj, "bzMidT1", true);
6505
+ addRotationAndPivot();
6506
+ obj.hasBorders = false;
6507
+ obj.hasControls = true;
6508
+ obj.__pdTextPathControls = true;
6509
+ obj.setCoords();
6510
+ }
6511
+ function computeWarpBoundsLocal(obj) {
6512
+ const resolved = resolveTextPath(obj.textPath, obj.width || 0, obj.fontSize || 16);
6513
+ const path = resolved ? measurePath(resolved.d) : null;
6514
+ if (!path) return null;
6515
+ const len = path.getTotalLength();
6516
+ if (!Number.isFinite(len) || len <= 0) return null;
6517
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
6518
+ const steps = 96;
6519
+ for (let i = 0; i <= steps; i++) {
6520
+ const p = path.getPointAtLength(len * i / steps);
6521
+ if (p.x < minX) minX = p.x;
6522
+ if (p.x > maxX) maxX = p.x;
6523
+ if (p.y < minY) minY = p.y;
6524
+ if (p.y > maxY) maxY = p.y;
6525
+ }
6526
+ const fs = obj.fontSize || 16;
6527
+ const halfW = (obj.width || 0) / 2;
6528
+ const halfH = (obj.height || 0) / 2;
6529
+ return {
6530
+ minX: minX - fs * 0.2 - halfW,
6531
+ maxX: maxX + fs * 0.2 - halfW,
6532
+ minY: minY - fs * 1.05 - halfH,
6533
+ maxY: maxY + fs * 0.35 - halfH
6534
+ };
6535
+ }
6536
+ function computeCircleWarpBoundsLocal(obj) {
6537
+ var _a;
6538
+ if (((_a = obj.textPath) == null ? void 0 : _a.preset) !== "circle") return null;
6539
+ const fs = obj.fontSize || 16;
6540
+ const w = obj.width || 0;
6541
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6542
+ const rRaw = obj.textPath.radius;
6543
+ const r = Math.max(fs * 0.75, Number.isFinite(rRaw) && rRaw > 0 ? rRaw : auto);
6544
+ const halfW = w / 2;
6545
+ const halfH = (obj.height || 0) / 2;
6546
+ const cx = r - halfW;
6547
+ const cy = r - halfH;
6548
+ return { minX: cx - r - fs * 0.2, maxX: cx + r + fs * 0.2, minY: cy - r - fs * 0.6, maxY: cy + r + fs * 0.6 };
6549
+ }
6550
+ function getTextPathHitBounds(obj) {
6551
+ var _a;
6552
+ return ((_a = obj.textPath) == null ? void 0 : _a.preset) === "circle" ? computeCircleWarpBoundsLocal(obj) : computeWarpBoundsLocal(obj);
6553
+ }
6554
+ function textPathBoundsContainScenePoint(obj, point) {
6555
+ const bounds = getTextPathHitBounds(obj);
6556
+ if (!bounds) return false;
6557
+ try {
6558
+ const inv = fabric.util.invertTransform(obj.calcTransformMatrix());
6559
+ const local = fabric.util.transformPoint(point, inv);
6560
+ return local.x >= bounds.minX && local.x <= bounds.maxX && local.y >= bounds.minY && local.y <= bounds.maxY;
6561
+ } catch {
6562
+ return false;
6563
+ }
6564
+ }
6565
+ function drawTextPathBounds(ctx, obj, bounds) {
6566
+ var _a;
6567
+ if (!obj.canvas) return;
6568
+ const retina = ((_a = obj.getCanvasRetinaScaling) == null ? void 0 : _a.call(obj)) || 1;
6569
+ const matrix = fabric.util.multiplyTransformMatrices(obj.canvas.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
6570
+ const corners = [
6571
+ new fabric.Point(bounds.minX, bounds.minY),
6572
+ new fabric.Point(bounds.maxX, bounds.minY),
6573
+ new fabric.Point(bounds.maxX, bounds.maxY),
6574
+ new fabric.Point(bounds.minX, bounds.maxY)
6575
+ ].map((p) => fabric.util.transformPoint(p, matrix));
6576
+ ctx.save();
6577
+ ctx.setTransform(retina, 0, 0, retina, 0, 0);
6578
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.9)";
6579
+ ctx.lineWidth = 1.25;
6580
+ ctx.setLineDash([5, 4]);
6581
+ ctx.beginPath();
6582
+ ctx.moveTo(corners[0].x, corners[0].y);
6583
+ for (let i = 1; i < corners.length; i++) ctx.lineTo(corners[i].x, corners[i].y);
6584
+ ctx.closePath();
6585
+ ctx.stroke();
6586
+ ctx.restore();
6587
+ }
6588
+ if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
6589
+ let drawWarpGuides = function(ctx) {
6590
+ var _a, _b, _c, _d, _e, _f, _g;
6591
+ if (!this.canvas) return;
6592
+ const hoverBounds = getTextPathHitBounds(this);
6593
+ if (hoverBounds && (this.__pdTextPathHovered || ((_b = (_a = this.canvas).getActiveObject) == null ? void 0 : _b.call(_a)) === this)) {
6594
+ drawTextPathBounds(ctx, this, hoverBounds);
6595
+ }
6596
+ const resolved = resolveTextPath(this.textPath, this.width || 0, this.fontSize || 16);
6597
+ const path = resolved ? measurePath(resolved.d) : null;
6598
+ if (!path) return;
6599
+ const len = path.getTotalLength();
6600
+ if (!Number.isFinite(len) || len <= 0) return;
6601
+ const retina = ((_c = this.getCanvasRetinaScaling) == null ? void 0 : _c.call(this)) || 1;
6602
+ const matrix = fabric.util.multiplyTransformMatrices(
6603
+ this.canvas.viewportTransform || [1, 0, 0, 1, 0, 0],
6604
+ this.calcTransformMatrix()
6605
+ );
6606
+ const halfW = (this.width || 0) / 2;
6607
+ const halfH = (this.height || 0) / 2;
6608
+ const toScreen = (t) => {
6609
+ const p = path.getPointAtLength(Math.max(0, Math.min(len, len * t)));
6610
+ return fabric.util.transformPoint(new fabric.Point(p.x - halfW, p.y - halfH), matrix);
6611
+ };
6612
+ const toScreenXY = (x, y) => fabric.util.transformPoint(new fabric.Point(x, y), matrix);
6613
+ ctx.save();
6614
+ ctx.setTransform(retina, 0, 0, retina, 0, 0);
6615
+ const isCircle = ((_d = this.textPath) == null ? void 0 : _d.preset) === "circle";
6616
+ const isAngle = ((_e = this.textPath) == null ? void 0 : _e.preset) === "rise" || ((_f = this.textPath) == null ? void 0 : _f.preset) === "angle";
6617
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.95)";
6618
+ ctx.lineWidth = 1.25;
6619
+ ctx.setLineDash([]);
6620
+ if (isAngle) {
6621
+ const tp = this.textPath;
6622
+ const w = this.width || 0;
6623
+ const fs = this.fontSize || 16;
6624
+ const ep = tp.endpoints;
6625
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : fs * 1.5;
6626
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : fs * 0.5;
6627
+ const a = toScreenXY(0 - halfW, leftY - halfH);
6628
+ const b = toScreenXY(w - halfW, rightY - halfH);
6629
+ ctx.beginPath();
6630
+ ctx.moveTo(a.x, a.y);
6631
+ ctx.lineTo(b.x, b.y);
6632
+ ctx.stroke();
6633
+ } else {
6634
+ ctx.beginPath();
6635
+ for (let i = 0; i <= 96; i++) {
6636
+ const p = toScreen(i / 96);
6637
+ if (i === 0) ctx.moveTo(p.x, p.y);
6638
+ else ctx.lineTo(p.x, p.y);
6639
+ }
6640
+ ctx.stroke();
6641
+ }
6642
+ if (isCircle) {
6643
+ const tp = this.textPath || {};
6644
+ const fs = this.fontSize || 16;
6645
+ const w = this.width || 0;
6646
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6647
+ const rRaw = tp.radius;
6648
+ const r = Math.max(fs * 0.75, Number.isFinite(rRaw) && rRaw > 0 ? rRaw : auto);
6649
+ const cx = r - halfW;
6650
+ const cy = r - halfH;
6651
+ const steps = 96;
6652
+ ctx.beginPath();
6653
+ for (let i = 0; i <= steps; i++) {
6654
+ const a = i / steps * Math.PI * 2;
6655
+ const p = toScreenXY(cx + Math.cos(a) * r, cy + Math.sin(a) * r);
6656
+ if (i === 0) ctx.moveTo(p.x, p.y);
6657
+ else ctx.lineTo(p.x, p.y);
6658
+ }
6659
+ ctx.closePath();
6660
+ ctx.stroke();
6661
+ }
6662
+ const bz = isCircle ? null : (_g = this.textPath) == null ? void 0 : _g.bezier;
6663
+ if (bz) {
6664
+ const a = toScreenXY(bz.p0[0] - halfW, bz.p0[1] - halfH);
6665
+ const ah = toScreenXY(bz.c0[0] - halfW, bz.c0[1] - halfH);
6666
+ const b = toScreenXY(bz.p1[0] - halfW, bz.p1[1] - halfH);
6667
+ const bh = toScreenXY(bz.c1[0] - halfW, bz.c1[1] - halfH);
6668
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.7)";
6669
+ ctx.lineWidth = 1;
6670
+ ctx.setLineDash([]);
6671
+ ctx.beginPath();
6672
+ ctx.moveTo(a.x, a.y);
6673
+ ctx.lineTo(ah.x, ah.y);
6674
+ ctx.moveTo(b.x, b.y);
6675
+ ctx.lineTo(bh.x, bh.y);
6676
+ ctx.stroke();
6677
+ const midX = 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0];
6678
+ const midY = 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1];
6679
+ let tdx = bz.p1[0] + 3 * bz.c1[0] - 3 * bz.c0[0] - bz.p0[0];
6680
+ let tdy = bz.p1[1] + 3 * bz.c1[1] - 3 * bz.c0[1] - bz.p0[1];
6681
+ const tlen = Math.hypot(tdx, tdy) || 1;
6682
+ tdx /= tlen;
6683
+ tdy /= tlen;
6684
+ const pC1 = (bz.c1[0] - midX) * tdx + (bz.c1[1] - midY) * tdy;
6685
+ const pC0 = (midX - bz.c0[0]) * tdx + (midY - bz.c0[1]) * tdy;
6686
+ const LEG = Math.max(8, (Math.abs(pC1) + Math.abs(pC0)) / 2);
6687
+ const m = toScreenXY(midX - halfW, midY - halfH);
6688
+ const t0 = toScreenXY(midX - tdx * LEG - halfW, midY - tdy * LEG - halfH);
6689
+ const t1 = toScreenXY(midX + tdx * LEG - halfW, midY + tdy * LEG - halfH);
6690
+ ctx.beginPath();
6691
+ ctx.moveTo(m.x, m.y);
6692
+ ctx.lineTo(t0.x, t0.y);
6693
+ ctx.moveTo(m.x, m.y);
6694
+ ctx.lineTo(t1.x, t1.y);
6695
+ ctx.stroke();
6696
+ }
6697
+ ctx.restore();
6698
+ };
6699
+ TextboxProto.__pixldocsOrigRenderControls = TextboxProto._renderControls;
6700
+ TextboxProto._renderControls = function(ctx, styleOverride) {
6701
+ if (hasActiveTextPath(this)) {
6702
+ const prevBorders = this.hasBorders;
6703
+ this.hasBorders = false;
6704
+ try {
6705
+ drawWarpGuides.call(this, ctx);
6706
+ TextboxProto.__pixldocsOrigRenderControls.call(this, ctx, styleOverride);
6707
+ } finally {
6708
+ this.hasBorders = prevBorders;
6709
+ }
6710
+ return;
6711
+ }
6712
+ TextboxProto.__pixldocsOrigRenderControls.call(this, ctx, styleOverride);
6713
+ if (!this.canvas) return;
6714
+ };
6715
+ }
6716
+ if (!TextboxProto.__pixldocsOrigRender && typeof TextboxProto._render === "function") {
6717
+ TextboxProto.__pixldocsOrigRender = TextboxProto._render;
6718
+ TextboxProto._render = function(ctx) {
6719
+ const tp = this.textPath;
6720
+ const orig = TextboxProto.__pixldocsOrigRender;
6721
+ if (!hasActiveTextPath(this)) {
6722
+ return orig.call(this, ctx);
6723
+ }
6724
+ if (tp && (tp.preset === "rise" || tp.preset === "angle")) {
6725
+ const w = this.width || 0;
6726
+ const h = this.height || 0;
6727
+ const fs2 = this.fontSize || 16;
6728
+ const ep = tp.endpoints;
6729
+ const defaultLeftY = fs2 * 1.5;
6730
+ const defaultRightY = fs2 * 0.5;
6731
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
6732
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
6733
+ const slope = w > 0 ? (rightY - leftY) / w : 0;
6734
+ const dy = (leftY + rightY) / 2 - h / 2;
6735
+ ctx.save();
6736
+ ctx.transform(1, slope, 0, 1, 0, dy);
6737
+ orig.call(this, ctx);
6738
+ ctx.restore();
6739
+ return;
6740
+ }
6741
+ const resolved = resolveTextPath(tp, this.width || 0, this.fontSize || 16);
6742
+ if (!resolved) return orig.call(this, ctx);
6743
+ const pathEl = measurePath(resolved.d);
6744
+ if (!pathEl) return orig.call(this, ctx);
6745
+ const totalLen = pathEl.getTotalLength();
6746
+ if (!Number.isFinite(totalLen) || totalLen <= 0) return orig.call(this, ctx);
6747
+ const plain = String(this.text || "").replace(/\n/g, " ");
6748
+ if (!plain) return;
6749
+ const weight = this.fontWeight || 400;
6750
+ const style = this.fontStyle || "normal";
6751
+ const family = this.fontFamily || "Open Sans";
6752
+ const fs = this.fontSize || 16;
6753
+ ctx.save();
6754
+ ctx.font = `${style} ${weight} ${fs}px "${family}"`;
6755
+ ctx.textAlign = "center";
6756
+ ctx.textBaseline = "alphabetic";
6757
+ const chars = Array.from(plain);
6758
+ const spacing = (this.charSpacing || 0) * fs / 1e3;
6759
+ const widths = chars.map((c) => ctx.measureText(c).width + spacing);
6760
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
6761
+ const baselineDy = resolveTextPathAlphabeticBaselineDy(ctx, plain, fs);
6762
+ let cursor;
6763
+ if (this.textAlign === "center") cursor = Math.max(0, (totalLen - totalWidth) / 2);
6764
+ else if (this.textAlign === "right") cursor = Math.max(0, totalLen - totalWidth);
6765
+ else cursor = 0;
6766
+ const flowStops = getGradientStopsForFlow(this);
6767
+ const fillOffsets = flowStops ? { offsetX: 0, offsetY: 0 } : applyWarpFillStyle(ctx, this);
6768
+ const halfW = (this.width || 0) / 2;
6769
+ const halfH = (this.height || 0) / 2;
6770
+ const strokeColor = this.stroke;
6771
+ const strokeWidth = Number(this.strokeWidth) || 0;
6772
+ const hasStroke = !!strokeColor && strokeColor !== "transparent" && strokeWidth > 0;
6773
+ if (hasStroke) {
6774
+ ctx.strokeStyle = String(strokeColor);
6775
+ ctx.lineWidth = strokeWidth;
6776
+ ctx.lineJoin = "round";
6777
+ ctx.miterLimit = 4;
6778
+ }
6779
+ for (let i = 0; i < chars.length; i++) {
6780
+ const ch = chars[i];
6781
+ const cw = widths[i];
6782
+ const mid = cursor + cw / 2;
6783
+ cursor += cw;
6784
+ if (mid > totalLen) break;
6785
+ const p = pathEl.getPointAtLength(Math.max(0, mid));
6786
+ const ahead = pathEl.getPointAtLength(Math.min(totalLen, mid + 0.5));
6787
+ const angle = Math.atan2(ahead.y - p.y, ahead.x - p.x);
6788
+ if (flowStops) {
6789
+ const tt = chars.length > 1 ? i / (chars.length - 1) : 0;
6790
+ ctx.fillStyle = sampleGradientColor(flowStops, tt);
6791
+ }
6792
+ ctx.save();
6793
+ ctx.translate(p.x - halfW - fillOffsets.offsetX, p.y - halfH - fillOffsets.offsetY);
6794
+ ctx.rotate(angle);
6795
+ ctx.fillText(ch, 0, baselineDy);
6796
+ if (hasStroke) ctx.strokeText(ch, 0, baselineDy);
6797
+ ctx.restore();
6798
+ }
6799
+ ctx.restore();
6800
+ };
5744
6801
  }
5745
6802
  TextboxProto.__pixldocsTextboxExtended = true;
6803
+ const TextboxProtoHit = fabric.Textbox.prototype;
6804
+ if (!TextboxProtoHit.__pixldocsOrigGetCoords && typeof TextboxProtoHit.getCoords === "function") {
6805
+ TextboxProtoHit.__pixldocsOrigGetCoords = TextboxProtoHit.getCoords;
6806
+ TextboxProtoHit.getCoords = function() {
6807
+ if (!hasActiveTextPath(this)) return TextboxProtoHit.__pixldocsOrigGetCoords.call(this);
6808
+ const bounds = getTextPathHitBounds(this);
6809
+ if (!bounds) return TextboxProtoHit.__pixldocsOrigGetCoords.call(this);
6810
+ const matrix = this.calcTransformMatrix();
6811
+ const coords = [
6812
+ new fabric.Point(bounds.minX, bounds.minY),
6813
+ new fabric.Point(bounds.maxX, bounds.minY),
6814
+ new fabric.Point(bounds.maxX, bounds.maxY),
6815
+ new fabric.Point(bounds.minX, bounds.maxY)
6816
+ ].map((p) => fabric.util.transformPoint(p, matrix));
6817
+ if (this.group) {
6818
+ const groupMatrix = this.group.calcTransformMatrix();
6819
+ return coords.map((p) => fabric.util.transformPoint(p, groupMatrix));
6820
+ }
6821
+ return coords;
6822
+ };
6823
+ }
6824
+ if (!TextboxProtoHit.__pixldocsOrigContainsPoint && typeof TextboxProtoHit.containsPoint === "function") {
6825
+ TextboxProtoHit.__pixldocsOrigContainsPoint = TextboxProtoHit.containsPoint;
6826
+ TextboxProtoHit.containsPoint = function(point) {
6827
+ const origHit = TextboxProtoHit.__pixldocsOrigContainsPoint.call(this, point);
6828
+ if (origHit) return true;
6829
+ if (!hasActiveTextPath(this)) return false;
6830
+ return textPathBoundsContainScenePoint(this, point);
6831
+ };
6832
+ }
5746
6833
  const PD_BG_KEY = "__pdBg";
5747
6834
  const PATCHED_KEY = "__pdBgPatched";
6835
+ const LINE_SHADOW_STROKE_WIDTH = 0.5;
5748
6836
  function extractTextBgConfig(element) {
5749
6837
  const legacy = Math.max(0, Number(element.textBgPadding ?? 0)) || 0;
5750
6838
  const pick = (v) => {
5751
6839
  const n = Number(v);
5752
6840
  return Number.isFinite(n) && n >= 0 ? n : void 0;
5753
6841
  };
6842
+ const grad = element.textBgGradient;
5754
6843
  return {
5755
6844
  color: element.textBgColor,
6845
+ gradient: isGradientConfig(grad) && grad.stops && grad.stops.length >= 2 ? grad : void 0,
5756
6846
  padTop: pick(element.textBgPaddingTop) ?? legacy,
5757
6847
  padRight: pick(element.textBgPaddingRight) ?? legacy,
5758
6848
  padBottom: pick(element.textBgPaddingBottom) ?? legacy,
@@ -5768,11 +6858,16 @@ function extractTextBgConfig(element) {
5768
6858
  })(),
5769
6859
  shadowAffectsBg: element.textShadowAffectsBg !== false,
5770
6860
  shadowAffectsText: element.textShadowAffectsText !== false,
5771
- fitToText: element.textBgFitToText === true
6861
+ fitToText: element.textBgFitToText === true,
6862
+ shadowType: element.textShadowType,
6863
+ shadowColor: element.textShadowColor,
6864
+ shadowOffsetX: Number(element.textShadowOffsetX ?? 0) || 0,
6865
+ shadowOffsetY: Number(element.textShadowOffsetY ?? 0) || 0
5772
6866
  };
5773
6867
  }
5774
6868
  function hasTextBackground(cfg) {
5775
6869
  if (!cfg) return false;
6870
+ if (cfg.gradient && cfg.gradient.stops && cfg.gradient.stops.length >= 2) return true;
5776
6871
  const c = (cfg.color || "").toString().trim().toLowerCase();
5777
6872
  return !!c && c !== "transparent" && c !== "none" && c !== "rgba(0,0,0,0)";
5778
6873
  }
@@ -5782,6 +6877,8 @@ function buildTextShadow(element) {
5782
6877
  const ox = Number(element.textShadowOffsetX ?? 0);
5783
6878
  const oy = Number(element.textShadowOffsetY ?? 0);
5784
6879
  if (!color || color === "transparent") return null;
6880
+ const type = element.textShadowType;
6881
+ if (type && type !== "drop") return null;
5785
6882
  return new fabric.Shadow({
5786
6883
  color: String(color),
5787
6884
  blur: blur || 0,
@@ -5812,11 +6909,35 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
5812
6909
  function applyTextBackground(obj, cfg) {
5813
6910
  var _a;
5814
6911
  obj[PD_BG_KEY] = { ...cfg };
6912
+ try {
6913
+ const hasOffset = (Number(cfg == null ? void 0 : cfg.shadowOffsetX) || 0) !== 0 || (Number(cfg == null ? void 0 : cfg.shadowOffsetY) || 0) !== 0;
6914
+ const isLineOrBlock = (cfg == null ? void 0 : cfg.shadowType) === "line" || (cfg == null ? void 0 : cfg.shadowType) === "block";
6915
+ if (isLineOrBlock && hasOffset && !!(cfg == null ? void 0 : cfg.shadowColor) && cfg.shadowColor !== "transparent") {
6916
+ obj.objectCaching = false;
6917
+ obj.dirty = true;
6918
+ }
6919
+ } catch {
6920
+ }
5815
6921
  if (obj[PATCHED_KEY]) return;
5816
6922
  obj[PATCHED_KEY] = true;
5817
6923
  const originalRender = obj._render.bind(obj);
5818
6924
  obj._render = function(ctx) {
5819
6925
  const bg = this[PD_BG_KEY];
6926
+ const extOX = Number((bg == null ? void 0 : bg.shadowOffsetX) ?? 0) || 0;
6927
+ const extOY = Number((bg == null ? void 0 : bg.shadowOffsetY) ?? 0) || 0;
6928
+ const extDist = Math.hypot(extOX, extOY);
6929
+ const hasShadowColor = !!(bg == null ? void 0 : bg.shadowColor) && bg.shadowColor !== "transparent";
6930
+ const blockShadowActive = !!bg && bg.shadowType === "block" && hasShadowColor && extDist > 0;
6931
+ const lineShadowActive = !!bg && bg.shadowType === "line" && hasShadowColor && extDist > 0;
6932
+ let blockSteps = 0;
6933
+ let blockStepX = 0;
6934
+ let blockStepY = 0;
6935
+ if (blockShadowActive) {
6936
+ const STEP = 0.5;
6937
+ blockSteps = Math.min(600, Math.max(1, Math.ceil(extDist / STEP)));
6938
+ blockStepX = extOX / blockSteps;
6939
+ blockStepY = extOY / blockSteps;
6940
+ }
5820
6941
  if (hasTextBackground(bg)) {
5821
6942
  const w = this.width ?? 0;
5822
6943
  const h = this.height ?? 0;
@@ -5834,24 +6955,182 @@ function applyTextBackground(obj, cfg) {
5834
6955
  }
5835
6956
  const op = typeof bg.opacity === "number" ? Math.max(0, Math.min(1, bg.opacity)) : 1;
5836
6957
  if (op < 1) ctx.globalAlpha = (ctx.globalAlpha ?? 1) * op;
5837
- ctx.fillStyle = bg.color;
5838
- const rects = computeBgRects(this, w, h, pT, pR, pB, pL, !!bg.fitToText);
5839
- for (const r of rects) {
5840
- buildRoundedRectPath2D(
5841
- ctx,
5842
- r.x,
5843
- r.y,
5844
- r.w,
5845
- r.h,
5846
- bg.rxTL ?? 0,
5847
- bg.rxTR ?? 0,
5848
- bg.rxBR ?? 0,
5849
- bg.rxBL ?? 0
5850
- );
5851
- ctx.fill();
6958
+ const tpAny = this.textPath;
6959
+ const isShear = tpAny && (tpAny.preset === "rise" || tpAny.preset === "angle");
6960
+ const ribbonD = !isShear ? buildWarpRibbonD(
6961
+ this,
6962
+ pT,
6963
+ pR,
6964
+ pB,
6965
+ pL,
6966
+ bg.rxTL ?? 0,
6967
+ bg.rxTR ?? 0,
6968
+ bg.rxBR ?? 0,
6969
+ bg.rxBL ?? 0
6970
+ ) : null;
6971
+ let shearTransform = null;
6972
+ if (isShear) {
6973
+ const fs = this.fontSize || 16;
6974
+ const ep = tpAny.endpoints;
6975
+ const dL = fs * 1.5;
6976
+ const dR = fs * 0.5;
6977
+ const lY = ep && Number.isFinite(ep.leftY) ? ep.leftY : dL;
6978
+ const rY = ep && Number.isFinite(ep.rightY) ? ep.rightY : dR;
6979
+ const slope = w > 0 ? (rY - lY) / w : 0;
6980
+ const dy = (lY + rY) / 2 - h / 2;
6981
+ shearTransform = { slope, dy };
6982
+ }
6983
+ let bgBounds;
6984
+ let bgRectsForFill = null;
6985
+ if (ribbonD) {
6986
+ bgBounds = computeRibbonBoundsFor(this, pT, pR, pB, pL);
6987
+ } else {
6988
+ bgRectsForFill = computeBgRects(this, w, h, pT, pR, pB, pL, !!bg.fitToText);
6989
+ bgBounds = unionBounds(bgRectsForFill);
6990
+ }
6991
+ if (blockShadowActive && bg.shadowAffectsBg !== false) {
6992
+ ctx.save();
6993
+ if (shearTransform) {
6994
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
6995
+ }
6996
+ ctx.fillStyle = bg.shadowColor;
6997
+ for (let i = blockSteps; i >= 1; i--) {
6998
+ ctx.save();
6999
+ ctx.translate(blockStepX * i, blockStepY * i);
7000
+ if (ribbonD) {
7001
+ try {
7002
+ ctx.fill(new Path2D(ribbonD));
7003
+ } catch {
7004
+ }
7005
+ } else {
7006
+ const rects = bgRectsForFill;
7007
+ for (const r of rects) {
7008
+ buildRoundedRectPath2D(
7009
+ ctx,
7010
+ r.x,
7011
+ r.y,
7012
+ r.w,
7013
+ r.h,
7014
+ bg.rxTL ?? 0,
7015
+ bg.rxTR ?? 0,
7016
+ bg.rxBR ?? 0,
7017
+ bg.rxBL ?? 0
7018
+ );
7019
+ ctx.fill();
7020
+ }
7021
+ }
7022
+ ctx.restore();
7023
+ }
7024
+ ctx.restore();
7025
+ }
7026
+ if (lineShadowActive && bg.shadowAffectsBg !== false) {
7027
+ ctx.save();
7028
+ if (shearTransform) {
7029
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
7030
+ }
7031
+ ctx.translate(extOX, extOY);
7032
+ ctx.strokeStyle = bg.shadowColor;
7033
+ ctx.lineWidth = 1;
7034
+ ctx.lineJoin = "round";
7035
+ if (ribbonD) {
7036
+ try {
7037
+ ctx.stroke(new Path2D(ribbonD));
7038
+ } catch {
7039
+ }
7040
+ } else {
7041
+ const rects = bgRectsForFill;
7042
+ for (const r of rects) {
7043
+ buildRoundedRectPath2D(
7044
+ ctx,
7045
+ r.x,
7046
+ r.y,
7047
+ r.w,
7048
+ r.h,
7049
+ bg.rxTL ?? 0,
7050
+ bg.rxTR ?? 0,
7051
+ bg.rxBR ?? 0,
7052
+ bg.rxBL ?? 0
7053
+ );
7054
+ ctx.stroke();
7055
+ }
7056
+ }
7057
+ ctx.restore();
7058
+ }
7059
+ ctx.fillStyle = bg.gradient ? buildCanvasGradient(ctx, bg.gradient, bgBounds.x, bgBounds.y, bgBounds.w, bgBounds.h) : bg.color || "#000";
7060
+ if (ribbonD) {
7061
+ try {
7062
+ const p2 = new Path2D(ribbonD);
7063
+ ctx.fill(p2);
7064
+ } catch {
7065
+ }
7066
+ } else {
7067
+ if (shearTransform) {
7068
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
7069
+ }
7070
+ const rects = bgRectsForFill;
7071
+ for (const r of rects) {
7072
+ buildRoundedRectPath2D(
7073
+ ctx,
7074
+ r.x,
7075
+ r.y,
7076
+ r.w,
7077
+ r.h,
7078
+ bg.rxTL ?? 0,
7079
+ bg.rxTR ?? 0,
7080
+ bg.rxBR ?? 0,
7081
+ bg.rxBL ?? 0
7082
+ );
7083
+ ctx.fill();
7084
+ }
5852
7085
  }
5853
7086
  ctx.restore();
5854
7087
  }
7088
+ if (blockShadowActive && bg.shadowAffectsText !== false) {
7089
+ const self = this;
7090
+ const origFill = self.fill;
7091
+ const origStyles = self.styles;
7092
+ const origShadow = self.shadow;
7093
+ try {
7094
+ self.fill = bg.shadowColor;
7095
+ self.styles = {};
7096
+ self.shadow = null;
7097
+ for (let i = blockSteps; i >= 1; i--) {
7098
+ ctx.save();
7099
+ ctx.translate(blockStepX * i, blockStepY * i);
7100
+ originalRender(ctx);
7101
+ ctx.restore();
7102
+ }
7103
+ } finally {
7104
+ self.fill = origFill;
7105
+ self.styles = origStyles;
7106
+ self.shadow = origShadow;
7107
+ }
7108
+ }
7109
+ if (lineShadowActive && bg.shadowAffectsText !== false) {
7110
+ const self = this;
7111
+ const origFill = self.fill;
7112
+ const origStroke = self.stroke;
7113
+ const origStrokeWidth = self.strokeWidth;
7114
+ const origStyles = self.styles;
7115
+ const origShadow = self.shadow;
7116
+ try {
7117
+ self.fill = "transparent";
7118
+ self.stroke = bg.shadowColor;
7119
+ self.strokeWidth = LINE_SHADOW_STROKE_WIDTH;
7120
+ self.styles = {};
7121
+ self.shadow = null;
7122
+ ctx.save();
7123
+ ctx.translate(extOX, extOY);
7124
+ originalRender(ctx);
7125
+ ctx.restore();
7126
+ } finally {
7127
+ self.fill = origFill;
7128
+ self.stroke = origStroke;
7129
+ self.strokeWidth = origStrokeWidth;
7130
+ self.styles = origStyles;
7131
+ self.shadow = origShadow;
7132
+ }
7133
+ }
5855
7134
  const suppressShadowOnText = bg && bg.shadowAffectsText === false;
5856
7135
  if (suppressShadowOnText) {
5857
7136
  ctx.save();
@@ -5870,19 +7149,27 @@ function applyTextBackground(obj, cfg) {
5870
7149
  const out = originalToObject(propertiesToInclude);
5871
7150
  const bg = this[PD_BG_KEY];
5872
7151
  if (hasTextBackground(bg)) {
5873
- out.__pdBg = { ...bg };
7152
+ out.__pdBg = { ...bg, gradient: bg.gradient ? { ...bg.gradient, stops: bg.gradient.stops.map((s) => ({ ...s })) } : void 0 };
5874
7153
  }
5875
7154
  return out;
5876
7155
  };
5877
7156
  const originalToSVG = (_a = obj.toSVG) == null ? void 0 : _a.bind(obj);
5878
7157
  if (typeof originalToSVG === "function") {
5879
7158
  obj.toSVG = function(reviver) {
7159
+ var _a2, _b;
5880
7160
  let svg = originalToSVG(reviver);
5881
7161
  const bg = this[PD_BG_KEY];
5882
7162
  const shadow = this.shadow;
5883
7163
  const hasBg = hasTextBackground(bg);
5884
7164
  const hasShadow = !!shadow && !!shadow.color && shadow.color !== "transparent";
5885
- if (!hasBg && !hasShadow) return svg;
7165
+ const extOX = Number((bg == null ? void 0 : bg.shadowOffsetX) ?? 0) || 0;
7166
+ const extOY = Number((bg == null ? void 0 : bg.shadowOffsetY) ?? 0) || 0;
7167
+ const extDist = Math.hypot(extOX, extOY);
7168
+ const hasExtShadowColor = !!(bg == null ? void 0 : bg.shadowColor) && bg.shadowColor !== "transparent";
7169
+ const hasBlockShadow = !!bg && bg.shadowType === "block" && hasExtShadowColor && extDist > 0;
7170
+ const hasLineShadow = !!bg && bg.shadowType === "line" && hasExtShadowColor && extDist > 0;
7171
+ if (!hasBg && !hasShadow && !hasBlockShadow && !hasLineShadow) return svg;
7172
+ const hasActiveTextPath2 = !!(((_a2 = this.textPath) == null ? void 0 : _a2.preset) && this.textPath.preset !== "none");
5886
7173
  const w = this.width ?? 0;
5887
7174
  const h = this.height ?? 0;
5888
7175
  const pT = Math.max(0, Number((bg == null ? void 0 : bg.padTop) ?? 0));
@@ -5891,7 +7178,18 @@ function applyTextBackground(obj, cfg) {
5891
7178
  const pL = Math.max(0, Number((bg == null ? void 0 : bg.padLeft) ?? 0));
5892
7179
  const fit = !!(bg == null ? void 0 : bg.fitToText);
5893
7180
  const rects = computeBgRects(this, w, h, pT, pR, pB, pL, fit);
5894
- const bgD = rects.map((r) => buildRoundedRectPathD(
7181
+ const ribbonD = buildWarpRibbonD(
7182
+ this,
7183
+ pT,
7184
+ pR,
7185
+ pB,
7186
+ pL,
7187
+ (bg == null ? void 0 : bg.rxTL) ?? 0,
7188
+ (bg == null ? void 0 : bg.rxTR) ?? 0,
7189
+ (bg == null ? void 0 : bg.rxBR) ?? 0,
7190
+ (bg == null ? void 0 : bg.rxBL) ?? 0
7191
+ );
7192
+ const bgD = ribbonD ?? rects.map((r) => buildRoundedRectPathD(
5895
7193
  r.x,
5896
7194
  r.y,
5897
7195
  r.w,
@@ -5904,7 +7202,16 @@ function applyTextBackground(obj, cfg) {
5904
7202
  const bgFill = (bg == null ? void 0 : bg.color) || "";
5905
7203
  const bgOpacity = typeof (bg == null ? void 0 : bg.opacity) === "number" ? Math.max(0, Math.min(1, bg.opacity)) : 1;
5906
7204
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
5907
- const bgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(bgFill)}"${bgOpacityAttr} />` : "";
7205
+ let bgGradDefs = "";
7206
+ let bgFillAttr = escapeXmlAttr(bgFill);
7207
+ if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b = bg.gradient.stops) == null ? void 0 : _b.length) >= 2) {
7208
+ const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7209
+ const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7210
+ const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
7211
+ bgGradDefs = `<defs>${def}</defs>`;
7212
+ bgFillAttr = `url(#${gid})`;
7213
+ }
7214
+ const bgPath = hasBg ? `${bgGradDefs}<path class="__pdTextBgShape" d="${bgD}" fill="${bgFillAttr}"${bgOpacityAttr} />` : "";
5908
7215
  svg = svg.replace(/style="[^"]*filter:\s*url\([^)]+\)[^"]*"/i, "");
5909
7216
  svg = svg.replace(/<filter[\s\S]*?<\/filter>/gi, "");
5910
7217
  let bgShadowMarker = "";
@@ -5930,14 +7237,63 @@ function applyTextBackground(obj, cfg) {
5930
7237
  const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
5931
7238
  bgShadowMarker = wrapShadow(shadowBgPath);
5932
7239
  }
5933
- if ((bg == null ? void 0 : bg.shadowAffectsText) !== false) {
7240
+ if ((bg == null ? void 0 : bg.shadowAffectsText) !== false && !hasActiveTextPath2) {
5934
7241
  const inner = extractGInnerMarkup(svg);
5935
7242
  const recoloredText = recolorSvgFills(inner, shadowColor);
5936
7243
  if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
5937
7244
  }
5938
7245
  }
7246
+ let bgBlockShadowMarker = "";
7247
+ let textBlockShadowMarker = "";
7248
+ if (hasBlockShadow) {
7249
+ const STEP = 0.5;
7250
+ const steps = Math.min(600, Math.max(1, Math.ceil(extDist / STEP)));
7251
+ const stepX = extOX / steps;
7252
+ const stepY = extOY / steps;
7253
+ const shadowColor = String(bg.shadowColor);
7254
+ const fillObj = this.fill;
7255
+ const parentTextIsGradient = !!fillObj && typeof fillObj === "object" && (Array.isArray(fillObj.colorStops) || fillObj.type === "linear" || fillObj.type === "radial");
7256
+ const cloneOutlineAttr = parentTextIsGradient ? ' data-pd-outline-text="1"' : "";
7257
+ const buildCopies = (markup, cls) => {
7258
+ const parts = [];
7259
+ for (let i = steps; i >= 1; i--) {
7260
+ parts.push(`<g class="${cls}"${cloneOutlineAttr} transform="translate(${(stepX * i).toFixed(3)} ${(stepY * i).toFixed(3)})">${markup}</g>`);
7261
+ }
7262
+ return parts.join("");
7263
+ };
7264
+ if (hasBg && bg.shadowAffectsBg !== false) {
7265
+ const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7266
+ const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
7267
+ bgBlockShadowMarker = buildCopies(shadowBgPath, "__pdBgShadowClone");
7268
+ }
7269
+ if (bg.shadowAffectsText !== false) {
7270
+ const inner = extractGInnerMarkup(svg);
7271
+ const recoloredText = recolorSvgFills(inner, shadowColor);
7272
+ if (recoloredText) textBlockShadowMarker = buildCopies(recoloredText, "__pdTextShadowClone");
7273
+ }
7274
+ }
7275
+ let bgLineShadowMarker = "";
7276
+ let textLineShadowMarker = "";
7277
+ if (hasLineShadow) {
7278
+ const shadowColor = String(bg.shadowColor);
7279
+ const tx = extOX.toFixed(3);
7280
+ const ty = extOY.toFixed(3);
7281
+ const fillObjLine = this.fill;
7282
+ const parentTextIsGradientLine = !!fillObjLine && typeof fillObjLine === "object" && (Array.isArray(fillObjLine.colorStops) || fillObjLine.type === "linear" || fillObjLine.type === "radial");
7283
+ const lineOutlineAttr = parentTextIsGradientLine ? ' data-pd-outline-text="1"' : "";
7284
+ if (hasBg && bg.shadowAffectsBg !== false) {
7285
+ const outlineBg = `<path d="${bgD}" fill="none" stroke="${escapeXmlAttr(shadowColor)}" stroke-width="1" stroke-linejoin="round" />`;
7286
+ bgLineShadowMarker = `<g class="__pdBgShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlineBg}</g>`;
7287
+ }
7288
+ if (bg.shadowAffectsText !== false) {
7289
+ const inner = extractGInnerMarkup(svg);
7290
+ const strokeW = LINE_SHADOW_STROKE_WIDTH;
7291
+ const outlined = outlineTextSvgForLineShadow(inner, shadowColor, strokeW);
7292
+ if (outlined) textLineShadowMarker = `<g class="__pdTextShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlined}</g>`;
7293
+ }
7294
+ }
5939
7295
  const openTagMatch = svg.match(/^\s*<g\b[^>]*>/);
5940
- const inserted = bgShadowMarker + bgPath + textShadowMarker;
7296
+ const inserted = bgBlockShadowMarker + bgLineShadowMarker + bgShadowMarker + bgPath + textBlockShadowMarker + textLineShadowMarker + textShadowMarker;
5941
7297
  const shadowBlur = hasShadow ? Math.max(0, Number(shadow.blur ?? 0)) : 0;
5942
7298
  const decorationTags = [
5943
7299
  shadowBlur > 0 ? 'data-pd-shadow-blur="1"' : "",
@@ -5957,7 +7313,38 @@ function recolorSvgFills(svg, color) {
5957
7313
  return _recolorSvgFills(svg, color);
5958
7314
  }
5959
7315
  function _recolorSvgFills(svg, color) {
7316
+ var _a;
5960
7317
  const safe = escapeXmlAttr(color);
7318
+ try {
7319
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svg}</svg>`;
7320
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7321
+ const root = doc.documentElement;
7322
+ if (root && root.nodeName !== "parsererror") {
7323
+ for (const g of Array.from(root.querySelectorAll("linearGradient, radialGradient, pattern"))) {
7324
+ try {
7325
+ (_a = g.parentNode) == null ? void 0 : _a.removeChild(g);
7326
+ } catch {
7327
+ }
7328
+ }
7329
+ for (const el of Array.from(root.querySelectorAll("text, tspan, path, rect"))) {
7330
+ const cur = el.getAttribute("fill");
7331
+ if (cur && (cur.trim().toLowerCase() === "none" || cur.trim().toLowerCase() === "transparent")) continue;
7332
+ const style = el.getAttribute("style");
7333
+ if (style && /fill\s*:/i.test(style)) {
7334
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7335
+ if (cleaned) el.setAttribute("style", cleaned);
7336
+ else el.removeAttribute("style");
7337
+ }
7338
+ el.setAttribute("fill", color);
7339
+ }
7340
+ let out2 = "";
7341
+ for (const child of Array.from(root.childNodes)) {
7342
+ out2 += new XMLSerializer().serializeToString(child);
7343
+ }
7344
+ return out2;
7345
+ }
7346
+ } catch {
7347
+ }
5961
7348
  let out = svg.replace(
5962
7349
  /(<(?:text|tspan|path|rect)\b[^>]*?\sfill=")([^"]*)("[^>]*>)/gi,
5963
7350
  (_m, pre, val, post) => {
@@ -5973,6 +7360,11 @@ function _recolorSvgFills(svg, color) {
5973
7360
  return pre + replaced + post;
5974
7361
  }
5975
7362
  );
7363
+ out = out.replace(/<(text|tspan|path)\b([^>]*)>/gi, (m, tag, attrs) => {
7364
+ if (/\sfill=/i.test(attrs)) return m;
7365
+ if (/style="[^"]*\bfill\s*:/i.test(attrs)) return m;
7366
+ return `<${tag}${attrs} fill="${safe}">`;
7367
+ });
5976
7368
  return out;
5977
7369
  }
5978
7370
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
@@ -6111,6 +7503,239 @@ function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
6111
7503
  function escapeXmlAttr(s) {
6112
7504
  return String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6113
7505
  }
7506
+ function buildWarpRibbonD(obj, pT, pR, pB, pL, rxTL, rxTR, rxBR, rxBL) {
7507
+ const tp = obj == null ? void 0 : obj.textPath;
7508
+ if (!tp || !tp.preset || tp.preset === "none") return null;
7509
+ if (tp.preset === "rise" || tp.preset === "angle") return null;
7510
+ const w = Number(obj.width) || 0;
7511
+ const h = Number(obj.height) || 0;
7512
+ const fs = Number(obj.fontSize) || 16;
7513
+ const resolved = resolveTextPath(tp, w, fs);
7514
+ if (!resolved) return null;
7515
+ const pathEl = measurePath(resolved.d);
7516
+ if (!pathEl) return null;
7517
+ const len = pathEl.getTotalLength();
7518
+ if (!Number.isFinite(len) || len <= 0) return null;
7519
+ const halfW = w / 2;
7520
+ const halfH = h / 2;
7521
+ const topT = halfH + Math.max(0, pT);
7522
+ const botT = halfH + Math.max(0, pB);
7523
+ const padL = Math.max(0, pL);
7524
+ const padR = Math.max(0, pR);
7525
+ const sample = (s) => {
7526
+ const ss = Math.max(0, Math.min(len, s));
7527
+ const p = pathEl.getPointAtLength(ss);
7528
+ const a = pathEl.getPointAtLength(Math.min(len, ss + 0.5));
7529
+ const b = pathEl.getPointAtLength(Math.max(0, ss - 0.5));
7530
+ let tx = a.x - b.x;
7531
+ let ty = a.y - b.y;
7532
+ const tl = Math.hypot(tx, ty) || 1;
7533
+ tx /= tl;
7534
+ ty /= tl;
7535
+ return { px: p.x - halfW, py: p.y - halfH, tx, ty, nx: ty, ny: -tx };
7536
+ };
7537
+ const SAMPLES = 96;
7538
+ const topPts = [];
7539
+ const botPts = [];
7540
+ for (let i = 0; i <= SAMPLES; i++) {
7541
+ const s = len * i / SAMPLES;
7542
+ const { px, py, nx, ny } = sample(s);
7543
+ topPts.push([px + nx * topT, py + ny * topT]);
7544
+ botPts.push([px - nx * botT, py - ny * botT]);
7545
+ }
7546
+ const s0 = sample(0);
7547
+ const s1 = sample(len);
7548
+ const TL = [s0.px - s0.tx * padL + s0.nx * topT, s0.py - s0.ty * padL + s0.ny * topT];
7549
+ const BL = [s0.px - s0.tx * padL - s0.nx * botT, s0.py - s0.ty * padL - s0.ny * botT];
7550
+ const TR = [s1.px + s1.tx * padR + s1.nx * topT, s1.py + s1.ty * padR + s1.ny * topT];
7551
+ const BR = [s1.px + s1.tx * padR - s1.nx * botT, s1.py + s1.ty * padR - s1.ny * botT];
7552
+ topPts[0] = TL;
7553
+ topPts[topPts.length - 1] = TR;
7554
+ botPts[0] = BL;
7555
+ botPts[botPts.length - 1] = BR;
7556
+ const capH = topT + botT;
7557
+ const maxLeftR = Math.min(capH * 0.5, len * 0.5 + padL);
7558
+ const maxRightR = Math.min(capH * 0.5, len * 0.5 + padR);
7559
+ const clamp2 = (r, m) => Math.max(0, Math.min(Number(r) || 0, m));
7560
+ const cTL = clamp2(rxTL, maxLeftR);
7561
+ const cBL = clamp2(rxBL, maxLeftR);
7562
+ const cTR = clamp2(rxTR, maxRightR);
7563
+ const cBR = clamp2(rxBR, maxRightR);
7564
+ const dist = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
7565
+ const inset = (from, toward, d) => {
7566
+ const dx = toward[0] - from[0];
7567
+ const dy = toward[1] - from[1];
7568
+ const L = Math.hypot(dx, dy) || 1;
7569
+ const k = Math.min(d, L) / L;
7570
+ return [from[0] + dx * k, from[1] + dy * k];
7571
+ };
7572
+ const walkEdge = (pts, target, fromStart) => {
7573
+ if (target <= 0) {
7574
+ const idx = fromStart ? 0 : pts.length - 1;
7575
+ return { pt: [pts[idx][0], pts[idx][1]], cutIdx: idx };
7576
+ }
7577
+ let acc = 0;
7578
+ if (fromStart) {
7579
+ for (let i = 1; i < pts.length; i++) {
7580
+ const seg = dist(pts[i - 1], pts[i]);
7581
+ if (acc + seg >= target) {
7582
+ const k = (target - acc) / (seg || 1);
7583
+ const pt = [
7584
+ pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k,
7585
+ pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k
7586
+ ];
7587
+ return { pt, cutIdx: i - 1 };
7588
+ }
7589
+ acc += seg;
7590
+ }
7591
+ return { pt: pts[pts.length - 1], cutIdx: pts.length - 1 };
7592
+ }
7593
+ for (let i = pts.length - 2; i >= 0; i--) {
7594
+ const seg = dist(pts[i + 1], pts[i]);
7595
+ if (acc + seg >= target) {
7596
+ const k = (target - acc) / (seg || 1);
7597
+ const pt = [
7598
+ pts[i + 1][0] + (pts[i][0] - pts[i + 1][0]) * k,
7599
+ pts[i + 1][1] + (pts[i][1] - pts[i + 1][1]) * k
7600
+ ];
7601
+ return { pt, cutIdx: i + 1 };
7602
+ }
7603
+ acc += seg;
7604
+ }
7605
+ return { pt: pts[0], cutIdx: 0 };
7606
+ };
7607
+ const topStart = walkEdge(topPts, cTL, true);
7608
+ const topEnd = walkEdge(topPts, cTR, false);
7609
+ const botStart = walkEdge(botPts, cBL, true);
7610
+ const botEnd = walkEdge(botPts, cBR, false);
7611
+ const insTL_top = topStart.pt;
7612
+ const insTR_top = topEnd.pt;
7613
+ const insBL_bot = botStart.pt;
7614
+ const insBR_bot = botEnd.pt;
7615
+ const insTL_cap = inset(TL, BL, cTL);
7616
+ const insBL_cap = inset(BL, TL, cBL);
7617
+ const insBR_cap = inset(BR, TR, cBR);
7618
+ const insTR_cap = inset(TR, BR, cTR);
7619
+ const fmt = (p) => `${p[0].toFixed(2)} ${p[1].toFixed(2)}`;
7620
+ const parts = [];
7621
+ parts.push(`M ${fmt(insTL_top)}`);
7622
+ for (let i = topStart.cutIdx + 1; i <= topEnd.cutIdx; i++) parts.push(`L ${fmt(topPts[i])}`);
7623
+ parts.push(`L ${fmt(insTR_top)}`);
7624
+ parts.push(`Q ${fmt(TR)} ${fmt(insTR_cap)}`);
7625
+ parts.push(`L ${fmt(insBR_cap)}`);
7626
+ parts.push(`Q ${fmt(BR)} ${fmt(insBR_bot)}`);
7627
+ for (let i = botEnd.cutIdx - 1; i >= botStart.cutIdx + 1; i--) parts.push(`L ${fmt(botPts[i])}`);
7628
+ parts.push(`L ${fmt(insBL_bot)}`);
7629
+ parts.push(`Q ${fmt(BL)} ${fmt(insBL_cap)}`);
7630
+ parts.push(`L ${fmt(insTL_cap)}`);
7631
+ parts.push(`Q ${fmt(TL)} ${fmt(insTL_top)}`);
7632
+ parts.push("Z");
7633
+ return parts.join(" ");
7634
+ }
7635
+ function computeRibbonBoundsFor(obj, pT, pR, pB, pL) {
7636
+ const tp = obj == null ? void 0 : obj.textPath;
7637
+ if (!tp || !tp.preset || tp.preset === "none") {
7638
+ const w2 = Number(obj.width) || 0;
7639
+ const h2 = Number(obj.height) || 0;
7640
+ return { x: -w2 / 2 - pL, y: -h2 / 2 - pT, w: w2 + pL + pR, h: h2 + pT + pB };
7641
+ }
7642
+ const w = Number(obj.width) || 0;
7643
+ const h = Number(obj.height) || 0;
7644
+ const fs = Number(obj.fontSize) || 16;
7645
+ const resolved = resolveTextPath(tp, w, fs);
7646
+ const pathEl = resolved ? measurePath(resolved.d) : null;
7647
+ const len = pathEl ? pathEl.getTotalLength() : 0;
7648
+ if (!pathEl || !Number.isFinite(len) || len <= 0) {
7649
+ return { x: -w / 2 - pL, y: -h / 2 - pT, w: w + pL + pR, h: h + pT + pB };
7650
+ }
7651
+ const halfW = w / 2;
7652
+ const halfH = h / 2;
7653
+ const topT = halfH + Math.max(0, pT);
7654
+ const botT = halfH + Math.max(0, pB);
7655
+ const STEPS = 48;
7656
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
7657
+ const ext = (x, y) => {
7658
+ if (x < minX) minX = x;
7659
+ if (y < minY) minY = y;
7660
+ if (x > maxX) maxX = x;
7661
+ if (y > maxY) maxY = y;
7662
+ };
7663
+ for (let i = 0; i <= STEPS; i++) {
7664
+ const s = len * i / STEPS;
7665
+ const p = pathEl.getPointAtLength(s);
7666
+ const a = pathEl.getPointAtLength(Math.min(len, s + 0.5));
7667
+ const b = pathEl.getPointAtLength(Math.max(0, s - 0.5));
7668
+ let tx = a.x - b.x;
7669
+ let ty = a.y - b.y;
7670
+ const tl = Math.hypot(tx, ty) || 1;
7671
+ tx /= tl;
7672
+ ty /= tl;
7673
+ const nx = ty, ny = -tx;
7674
+ const px = p.x - halfW, py = p.y - halfH;
7675
+ ext(px + nx * topT, py + ny * topT);
7676
+ ext(px - nx * botT, py - ny * botT);
7677
+ }
7678
+ if (pL > 0 || pR > 0) {
7679
+ minX -= pL;
7680
+ maxX += pR;
7681
+ }
7682
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
7683
+ }
7684
+ function normalizeStops(stops) {
7685
+ const out = stops.map((s) => ({ color: s.color, offset: Math.max(0, Math.min(1, Number(s.offset) || 0)) })).sort((a, b) => a.offset - b.offset);
7686
+ if (out.length === 0) return out;
7687
+ if (out[0].offset > 0) out.unshift({ color: out[0].color, offset: 0 });
7688
+ if (out[out.length - 1].offset < 1) out.push({ color: out[out.length - 1].color, offset: 1 });
7689
+ return out;
7690
+ }
7691
+ function buildCanvasGradient(ctx, g, bx, by, bw, bh) {
7692
+ const stops = normalizeStops(g.stops);
7693
+ if (stops.length === 0) return "#000";
7694
+ if (g.type === "radial") {
7695
+ const cx = bx + (g.cx ?? 0.5) * bw;
7696
+ const cy = by + (g.cy ?? 0.5) * bh;
7697
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7698
+ const cg2 = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);
7699
+ stops.forEach((s) => {
7700
+ try {
7701
+ cg2.addColorStop(s.offset, s.color);
7702
+ } catch {
7703
+ }
7704
+ });
7705
+ return cg2;
7706
+ }
7707
+ const angle = g.angle ?? 90;
7708
+ const rad = angle * Math.PI / 180;
7709
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7710
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7711
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7712
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7713
+ const cg = ctx.createLinearGradient(x1, y1, x2, y2);
7714
+ stops.forEach((s) => {
7715
+ try {
7716
+ cg.addColorStop(s.offset, s.color);
7717
+ } catch {
7718
+ }
7719
+ });
7720
+ return cg;
7721
+ }
7722
+ function buildSvgGradientDef(g, id, bx, by, bw, bh) {
7723
+ const stops = normalizeStops(g.stops);
7724
+ const stopsXml = stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeXmlAttr(s.color)}" />`).join("");
7725
+ if (g.type === "radial") {
7726
+ const cx = bx + (g.cx ?? 0.5) * bw;
7727
+ const cy = by + (g.cy ?? 0.5) * bh;
7728
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7729
+ return `<radialGradient id="${id}" gradientUnits="userSpaceOnUse" cx="${cx.toFixed(3)}" cy="${cy.toFixed(3)}" r="${r.toFixed(3)}" fx="${cx.toFixed(3)}" fy="${cy.toFixed(3)}">${stopsXml}</radialGradient>`;
7730
+ }
7731
+ const angle = g.angle ?? 90;
7732
+ const rad = angle * Math.PI / 180;
7733
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7734
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7735
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7736
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7737
+ return `<linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${x1.toFixed(3)}" y1="${y1.toFixed(3)}" x2="${x2.toFixed(3)}" y2="${y2.toFixed(3)}">${stopsXml}</linearGradient>`;
7738
+ }
6114
7739
  function extractGInnerMarkup(markup) {
6115
7740
  const openMatch = markup.match(/^\s*<g\b[^>]*>/);
6116
7741
  if (!openMatch) return markup;
@@ -6118,6 +7743,37 @@ function extractGInnerMarkup(markup) {
6118
7743
  if (closeIdx <= openMatch[0].length) return markup;
6119
7744
  return markup.slice(openMatch[0].length, closeIdx);
6120
7745
  }
7746
+ function outlineTextSvgForLineShadow(inner, strokeColor, strokeWidth) {
7747
+ if (!inner) return "";
7748
+ try {
7749
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${inner}</svg>`;
7750
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7751
+ const root = doc.documentElement;
7752
+ if (!root || root.nodeName === "parsererror") return "";
7753
+ const targets = Array.from(root.querySelectorAll("text, tspan"));
7754
+ if (!targets.length) return "";
7755
+ for (const el of targets) {
7756
+ const style = el.getAttribute("style");
7757
+ if (style) {
7758
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/(?:^|;)\s*stroke(?:-[a-z]+)?\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7759
+ if (cleaned) el.setAttribute("style", cleaned);
7760
+ else el.removeAttribute("style");
7761
+ }
7762
+ el.setAttribute("fill", "none");
7763
+ el.setAttribute("stroke", strokeColor);
7764
+ el.setAttribute("stroke-width", strokeWidth.toFixed(3));
7765
+ el.setAttribute("stroke-linejoin", "round");
7766
+ el.setAttribute("paint-order", "stroke");
7767
+ }
7768
+ let out = "";
7769
+ for (const child of Array.from(root.childNodes)) {
7770
+ out += new XMLSerializer().serializeToString(child);
7771
+ }
7772
+ return out;
7773
+ } catch {
7774
+ return "";
7775
+ }
7776
+ }
6121
7777
  const TRIANGLE_STROKE_MITER_LIMIT = 1e6;
6122
7778
  const toSafeNumber = (value, fallback = 0) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
6123
7779
  function normalizeShapeType(shapeType) {
@@ -6746,7 +8402,8 @@ function createText(element) {
6746
8402
  // same value as a fit-target, so the rendered box and the shrink target
6747
8403
  // stay in sync (parity with the Use page / EC2 renderer).
6748
8404
  ...(element.minBoxHeight ?? 0) > 0 ? { minBoxHeight: element.minBoxHeight } : {},
6749
- verticalAlign: element.verticalAlign || "top"
8405
+ verticalAlign: element.verticalAlign || "top",
8406
+ ...element.textPath && element.textPath.preset && element.textPath.preset !== "none" ? { textPath: element.textPath } : {}
6750
8407
  });
6751
8408
  textbox.__formattingEnabled = formattingEnabled;
6752
8409
  textbox.initDimensions();
@@ -6853,6 +8510,9 @@ function createFabricObject(element) {
6853
8510
  if (element.type === "shape" || element.type === "line" || element.type === "text") {
6854
8511
  applyInitialGradients(obj, element);
6855
8512
  }
8513
+ if (element.type === "text" && obj instanceof fabric.Textbox) {
8514
+ applyTextPathControls(obj);
8515
+ }
6856
8516
  }
6857
8517
  return obj;
6858
8518
  }
@@ -7039,7 +8699,7 @@ const progressDefinition = {
7039
8699
  ],
7040
8700
  render: renderProgressSvg
7041
8701
  };
7042
- function escapeXml(str) {
8702
+ function escapeXml$1(str) {
7043
8703
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7044
8704
  }
7045
8705
  function estimateTextWidth(text, fontSize, fontWeight) {
@@ -7086,7 +8746,7 @@ function renderTableSvg(props, width, height) {
7086
8746
  if (text) {
7087
8747
  const maxChars = Math.max(3, Math.floor(cellW / (fontSize * 0.55)));
7088
8748
  const displayText = text.length > maxChars ? text.slice(0, maxChars - 1) + "…" : text;
7089
- svg += `<text x="${x + cellW / 2}" y="${y + cellH / 2}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml(fontFamily)}" font-weight="${fontWeight}" fill="${textColor}">${escapeXml(displayText)}</text>`;
8749
+ svg += `<text x="${x + cellW / 2}" y="${y + cellH / 2}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml$1(fontFamily)}" font-weight="${fontWeight}" fill="${textColor}">${escapeXml$1(displayText)}</text>`;
7090
8750
  }
7091
8751
  }
7092
8752
  }
@@ -7153,7 +8813,7 @@ function renderAvatarSvg(props, width, height) {
7153
8813
  }
7154
8814
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
7155
8815
  ${shapeSvg}
7156
- <text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml(fontFamily)}" font-weight="600" fill="${textColor}">${initials}</text>
8816
+ <text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml$1(fontFamily)}" font-weight="600" fill="${textColor}">${initials}</text>
7157
8817
  </svg>`;
7158
8818
  }
7159
8819
  const avatarDefinition = {
@@ -7244,7 +8904,7 @@ function renderBadgeInternal(props, width, height) {
7244
8904
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${finalWidth}" height="${height}" viewBox="0 0 ${finalWidth} ${height}">
7245
8905
  <rect x="${inset}" y="${inset}" width="${finalWidth - borderWidth}" height="${height - borderWidth}" rx="${rx}" fill="${bgColor}" stroke="${borderColor}" stroke-width="${borderWidth}"/>
7246
8906
  ${iconSvg}
7247
- <text x="${textX}" y="${height / 2}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml(fontFamily)}" font-weight="${fontWeight}" fill="${textColor}">${escapeXml(displayText)}</text>
8907
+ <text x="${textX}" y="${height / 2}" text-anchor="middle" dominant-baseline="central" font-size="${fontSize}" font-family="${escapeXml$1(fontFamily)}" font-weight="${fontWeight}" fill="${textColor}">${escapeXml$1(displayText)}</text>
7248
8908
  </svg>`;
7249
8909
  return { svg, computedWidth, anchor };
7250
8910
  }
@@ -8918,6 +10578,9 @@ const PageCanvas = forwardRef(
8918
10578
  if (typeof baked === "number" && baked > 0) {
8919
10579
  elementUpdate.minBoxHeight = baked;
8920
10580
  }
10581
+ if (obj.textPath) {
10582
+ elementUpdate.textPath = obj.textPath;
10583
+ }
8921
10584
  }
8922
10585
  if (sourceElement && sourceElement.opacity !== void 0) {
8923
10586
  elementUpdate.opacity = sourceElement.opacity;
@@ -9652,6 +11315,7 @@ const PageCanvas = forwardRef(
9652
11315
  (existingObj.verticalAlign ?? "top") !== (element.verticalAlign ?? "top") || Math.abs((existingObj.minBoxHeight ?? 0) - (element.minBoxHeight ?? 0)) > 0.1 || // Detect text background + shadow changes so panel edits flow into Fabric.
9653
11316
  JSON.stringify({
9654
11317
  c: element.textBgColor ?? null,
11318
+ g: element.textBgGradient ?? null,
9655
11319
  p: element.textBgPadding ?? 0,
9656
11320
  pt: element.textBgPaddingTop ?? null,
9657
11321
  pr: element.textBgPaddingRight ?? null,
@@ -9670,7 +11334,7 @@ const PageCanvas = forwardRef(
9670
11334
  st: element.textShadowAffectsText !== false,
9671
11335
  sa: element.textShadowAffectsBg !== false
9672
11336
  }) !== (existingObj.__lastTextBgShadowJson ?? "") || // CRITICAL: Detect gradient fill/stroke changes — serialise to JSON for deep comparison
9673
- JSON.stringify(element.fillGradient || null) !== (existingObj.__lastFillGradientJson ?? "null") || JSON.stringify(element.strokeGradient || null) !== (existingObj.__lastStrokeGradientJson ?? "null");
11337
+ JSON.stringify(element.fillGradient || null) !== (existingObj.__lastFillGradientJson ?? "null") || JSON.stringify(element.strokeGradient || null) !== (existingObj.__lastStrokeGradientJson ?? "null") || JSON.stringify(element.textPath || null) !== JSON.stringify(existingObj.textPath || null);
9674
11338
  const forceApplyFromPanel = syncTriggeredByPanelRef.current;
9675
11339
  const noPropsOrPositionChanged = !positionChanged && !otherPropsChanged;
9676
11340
  if (noPropsOrPositionChanged && !forceApplyFromPanel || visibilityUpdateInProgressRef.current) {
@@ -10104,7 +11768,7 @@ const PageCanvas = forwardRef(
10104
11768
  });
10105
11769
  }, [selectedIds, isActive, ready, elements]);
10106
11770
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
10107
- var _a, _b;
11771
+ var _a, _b, _c;
10108
11772
  const fc = fabricRef.current;
10109
11773
  if (fc && isTransforming(fc)) {
10110
11774
  return;
@@ -10381,6 +12045,8 @@ const PageCanvas = forwardRef(
10381
12045
  obj.setCoords();
10382
12046
  }
10383
12047
  if (!isLine) {
12048
+ const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
12049
+ const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
10384
12050
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
10385
12051
  if (!skipPositionUpdate && obj instanceof fabric.FabricImage && obj.originX === "center") {
10386
12052
  const vW = rW * effectiveScaleX;
@@ -10396,7 +12062,7 @@ const PageCanvas = forwardRef(
10396
12062
  scaleY: effectiveScaleY,
10397
12063
  angle: element.angle ?? 0,
10398
12064
  skewX: element.skewX ?? 0,
10399
- skewY: element.skewY ?? 0
12065
+ skewY: appliedSkewY
10400
12066
  });
10401
12067
  } else {
10402
12068
  obj.set({
@@ -10416,7 +12082,7 @@ const PageCanvas = forwardRef(
10416
12082
  width: rW,
10417
12083
  angle: element.angle ?? 0,
10418
12084
  skewX: element.skewX ?? 0,
10419
- skewY: element.skewY ?? 0,
12085
+ skewY: appliedSkewY,
10420
12086
  scaleX: effectiveScaleX * baseScaleX,
10421
12087
  scaleY: effectiveScaleY * baseScaleY
10422
12088
  });
@@ -10548,7 +12214,9 @@ const PageCanvas = forwardRef(
10548
12214
  dynamicMinWidth: 0,
10549
12215
  fontSize,
10550
12216
  fontFamily: element.fontFamily || "Open Sans",
10551
- fill: element.fill || "#1a1a1a",
12217
+ // If a gradient fill is active, do not briefly overwrite it with the
12218
+ // stored solid fallback while applying textPath/panel updates.
12219
+ fill: element.fillGradient && isGradientConfig(element.fillGradient) ? obj.fill : element.fill || "#1a1a1a",
10552
12220
  fontWeight: element.fontWeight || 400,
10553
12221
  textAlign: element.textAlign || "left",
10554
12222
  fontStyle: element.fontStyle || "normal",
@@ -10565,6 +12233,13 @@ const PageCanvas = forwardRef(
10565
12233
  const minBoxH = Math.max(0, Number(element.minBoxHeight) || 0);
10566
12234
  obj.verticalAlign = valign;
10567
12235
  obj.minBoxHeight = minBoxH;
12236
+ const nextTextPath = element.textPath;
12237
+ if (nextTextPath && nextTextPath.preset && nextTextPath.preset !== "none") {
12238
+ obj.textPath = nextTextPath;
12239
+ } else {
12240
+ obj.textPath = void 0;
12241
+ }
12242
+ applyTextPathControls(obj);
10568
12243
  if (element.formattingEnabled === true) {
10569
12244
  obj.styles = parsedStyles || {};
10570
12245
  } else {
@@ -10587,9 +12262,10 @@ const PageCanvas = forwardRef(
10587
12262
  } catch {
10588
12263
  }
10589
12264
  obj.dirty = true;
10590
- (_b = obj.setCoords) == null ? void 0 : _b.call(obj);
12265
+ (_c = obj.setCoords) == null ? void 0 : _c.call(obj);
10591
12266
  obj.__lastTextBgShadowJson = JSON.stringify({
10592
12267
  c: element.textBgColor ?? null,
12268
+ g: element.textBgGradient ?? null,
10593
12269
  p: element.textBgPadding ?? 0,
10594
12270
  pt: element.textBgPaddingTop ?? null,
10595
12271
  pr: element.textBgPaddingRight ?? null,
@@ -10606,7 +12282,8 @@ const PageCanvas = forwardRef(
10606
12282
  sx: element.textShadowOffsetX ?? 0,
10607
12283
  sy: element.textShadowOffsetY ?? 0,
10608
12284
  st: element.textShadowAffectsText !== false,
10609
- sa: element.textShadowAffectsBg !== false
12285
+ sa: element.textShadowAffectsBg !== false,
12286
+ sty: element.textShadowType ?? null
10610
12287
  });
10611
12288
  obj.dirty = true;
10612
12289
  } catch (err) {
@@ -11019,9 +12696,7 @@ const PageCanvas = forwardRef(
11019
12696
  }
11020
12697
  try {
11021
12698
  let url = getProxiedImageUrl(imageUrl);
11022
- const hasColorOverrides = Boolean(element.svgColorMap && Object.keys(element.svgColorMap).length > 0);
11023
- const isInlineSvgDataUrl = imageUrl.startsWith("data:image/svg+xml");
11024
- if (isSvgImage(imageUrl, element.sourceFormat) && (!isInlineSvgDataUrl || hasColorOverrides)) {
12699
+ if (isSvgImage(imageUrl, element.sourceFormat)) {
11025
12700
  const normalized = await getNormalizedSvgUrl(imageUrl, element.svgColorMap, element.sourceFormat);
11026
12701
  if (!isLatestRequest()) return;
11027
12702
  if (normalized) url = normalized;
@@ -15718,7 +17393,7 @@ async function resolveTemplateData(options) {
15718
17393
  }
15719
17394
  }
15720
17395
  if (repeatableSectionsInput.length > 0) {
15721
- paintRepeatableSections(config, repeatableSectionsInput, inlineFormSchema == null ? void 0 : inlineFormSchema.entryFilters);
17396
+ paintRepeatableSections(config, repeatableSectionsInput);
15722
17397
  }
15723
17398
  }
15724
17399
  const mergedFormData = {
@@ -15789,7 +17464,7 @@ async function resolveFromForm(options) {
15789
17464
  normalizeLayoutModes(templateConfig);
15790
17465
  const repeatableFromSchema = templateFormSchema == null ? void 0 : templateFormSchema.repeatableSections;
15791
17466
  if ((repeatableFromSchema == null ? void 0 : repeatableFromSchema.length) && templateConfig.pages) {
15792
- paintRepeatableSections(templateConfig, repeatableFromSchema, templateFormSchema == null ? void 0 : templateFormSchema.entryFilters);
17467
+ paintRepeatableSections(templateConfig, repeatableFromSchema);
15793
17468
  }
15794
17469
  const schemaSections = getRenderableFormSections(formSchema);
15795
17470
  const repeatableNodeMap = /* @__PURE__ */ new Map();
@@ -16788,6 +18463,312 @@ function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
16788
18463
  function isTextboxLike(obj) {
16789
18464
  return !!obj && (obj instanceof fabric.Textbox || obj.type === "textbox" || Array.isArray(obj == null ? void 0 : obj._textLines) && typeof obj.getLineWidth === "function");
16790
18465
  }
18466
+ function escapeXml(s) {
18467
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
18468
+ }
18469
+ function ensureFabricGradientDef(doc, obj, width, height) {
18470
+ const grad = obj == null ? void 0 : obj.fill;
18471
+ if (!grad || typeof grad !== "object" || !Array.isArray(grad.colorStops) || !grad.coords) return "";
18472
+ const id = `pixldocs_text_grad_${String(obj.__docuforgeId || grad.id || Math.random()).replace(/[^a-zA-Z0-9_-]/g, "_")}`;
18473
+ if (doc.getElementById(id)) return `url(#${id})`;
18474
+ const ns = "http://www.w3.org/2000/svg";
18475
+ let defs = doc.querySelector("defs");
18476
+ if (!defs) {
18477
+ defs = doc.createElementNS(ns, "defs");
18478
+ doc.documentElement.insertBefore(defs, doc.documentElement.firstChild);
18479
+ }
18480
+ const c = grad.coords || {};
18481
+ const el = doc.createElementNS(ns, grad.type === "radial" ? "radialGradient" : "linearGradient");
18482
+ el.setAttribute("id", id);
18483
+ el.setAttribute("gradientUnits", "userSpaceOnUse");
18484
+ if (grad.type === "radial") {
18485
+ el.setAttribute("cx", (Number(c.x2 ?? c.x1 ?? width / 2) - width / 2).toFixed(3));
18486
+ el.setAttribute("cy", (Number(c.y2 ?? c.y1 ?? height / 2) - height / 2).toFixed(3));
18487
+ el.setAttribute("fx", (Number(c.x1 ?? c.x2 ?? width / 2) - width / 2).toFixed(3));
18488
+ el.setAttribute("fy", (Number(c.y1 ?? c.y2 ?? height / 2) - height / 2).toFixed(3));
18489
+ el.setAttribute("r", Math.max(0.1, Number(c.r2 ?? c.r ?? Math.max(width, height) / 2)).toFixed(3));
18490
+ } else {
18491
+ el.setAttribute("x1", (Number(c.x1) - width / 2).toFixed(3));
18492
+ el.setAttribute("y1", (Number(c.y1) - height / 2).toFixed(3));
18493
+ el.setAttribute("x2", (Number(c.x2 ?? width) - width / 2).toFixed(3));
18494
+ el.setAttribute("y2", (Number(c.y2) - height / 2).toFixed(3));
18495
+ }
18496
+ for (const stop of grad.colorStops) {
18497
+ const stopEl = doc.createElementNS(ns, "stop");
18498
+ stopEl.setAttribute("offset", String(Math.max(0, Math.min(1, Number(stop.offset) || 0))));
18499
+ stopEl.setAttribute("stop-color", stop.color || "#000000");
18500
+ el.appendChild(stopEl);
18501
+ }
18502
+ defs.appendChild(el);
18503
+ return `url(#${id})`;
18504
+ }
18505
+ function warpTextboxSvgAlongPath(svg, obj) {
18506
+ var _a, _b, _c, _d, _e;
18507
+ const tp = obj == null ? void 0 : obj.textPath;
18508
+ if (!tp || !tp.preset || tp.preset === "none") return svg;
18509
+ if (tp.preset === "rise" || tp.preset === "angle") {
18510
+ const w2 = Number(obj.width) || 0;
18511
+ const h2 = Number(obj.height) || 0;
18512
+ const fs2 = Number(obj.fontSize) || 16;
18513
+ const ep = tp.endpoints;
18514
+ const defaultLeftY = fs2 * 1.5;
18515
+ const defaultRightY = fs2 * 0.5;
18516
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
18517
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
18518
+ const slope = w2 > 0 ? (rightY - leftY) / w2 : 0;
18519
+ const dy = (leftY + rightY) / 2 - h2 / 2;
18520
+ if (Math.abs(slope) < 1e-6 && Math.abs(dy) < 1e-6) return svg;
18521
+ let doc2;
18522
+ try {
18523
+ doc2 = new DOMParser().parseFromString(svg, "image/svg+xml");
18524
+ } catch {
18525
+ return svg;
18526
+ }
18527
+ const textEls2 = Array.from(doc2.querySelectorAll("text"));
18528
+ if (!textEls2.length) return svg;
18529
+ const matrix = `matrix(1 ${slope.toFixed(6)} 0 1 0 ${dy.toFixed(3)})`;
18530
+ for (const textEl2 of textEls2) {
18531
+ const existing = textEl2.getAttribute("transform");
18532
+ textEl2.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18533
+ }
18534
+ const bgShapes = Array.from(doc2.querySelectorAll("path.__pdTextBgShape"));
18535
+ for (const bgEl of bgShapes) {
18536
+ const existing = bgEl.getAttribute("transform");
18537
+ bgEl.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18538
+ }
18539
+ return new XMLSerializer().serializeToString(doc2.documentElement);
18540
+ }
18541
+ const resolved = resolveTextPath(tp, Number(obj.width) || 0, Number(obj.fontSize) || 16);
18542
+ if (!resolved) return svg;
18543
+ const pathEl = measurePath(resolved.d);
18544
+ if (!pathEl) return svg;
18545
+ const totalLen = pathEl.getTotalLength();
18546
+ if (!Number.isFinite(totalLen) || totalLen <= 0) return svg;
18547
+ const plain = String(obj.text || "").replace(/\n/g, " ");
18548
+ if (!plain) return svg;
18549
+ let doc;
18550
+ try {
18551
+ doc = new DOMParser().parseFromString(svg, "image/svg+xml");
18552
+ } catch {
18553
+ return svg;
18554
+ }
18555
+ for (const marker of Array.from(doc.querySelectorAll("g.__pdShadowRaster"))) {
18556
+ try {
18557
+ (_a = marker.parentNode) == null ? void 0 : _a.removeChild(marker);
18558
+ } catch {
18559
+ }
18560
+ }
18561
+ for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
18562
+ try {
18563
+ (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
18564
+ } catch {
18565
+ }
18566
+ }
18567
+ const textEls = Array.from(doc.querySelectorAll("text"));
18568
+ if (!textEls.length) return svg;
18569
+ const hasFilterInChain = (el) => {
18570
+ let cur = el;
18571
+ while (cur && cur !== doc.documentElement) {
18572
+ if (cur.getAttribute("filter") || /filter\s*:/i.test(cur.getAttribute("style") || "")) return true;
18573
+ cur = cur.parentElement;
18574
+ }
18575
+ return false;
18576
+ };
18577
+ const textEl = textEls.find((el) => !hasFilterInChain(el)) || textEls[textEls.length - 1] || textEls[0];
18578
+ const ff = obj.fontFamily || "Open Sans";
18579
+ const fs = Number(obj.fontSize) || 16;
18580
+ const fw = obj.fontWeight ?? 400;
18581
+ const fst = obj.fontStyle || "normal";
18582
+ const readFill = (el) => {
18583
+ var _a2, _b2;
18584
+ if (!el) return "";
18585
+ return el.getAttribute("fill") || ((_b2 = (_a2 = (el.getAttribute("style") || "").match(/(?:^|;)\s*fill\s*:\s*([^;]+)/i)) == null ? void 0 : _a2[1]) == null ? void 0 : _b2.trim()) || "";
18586
+ };
18587
+ const w = Number(obj.width) || 0;
18588
+ const h = Number(obj.height) || 0;
18589
+ const synthesizedGradientFill = ensureFabricGradientDef(doc, obj, w, h);
18590
+ const fabricGradientFill = obj.fill && typeof obj.fill === "object" && obj.fill.id != null ? `url(#SVGID_${obj.fill.id})` : "";
18591
+ const fillAttr = synthesizedGradientFill || readFill(textEl) || readFill(textEl.querySelector("tspan")) || fabricGradientFill || (typeof obj.fill === "string" ? obj.fill : "#000");
18592
+ const stripFilter = (el) => {
18593
+ if (!el) return;
18594
+ el.removeAttribute("filter");
18595
+ const style = el.getAttribute("style");
18596
+ if (style && /filter\s*:/i.test(style)) {
18597
+ el.setAttribute("style", style.replace(/(?:^|;)\s*filter\s*:[^;]*/gi, "").replace(/^;+/, ""));
18598
+ }
18599
+ };
18600
+ for (const el of Array.from(doc.querySelectorAll("*"))) stripFilter(el);
18601
+ for (const filter of Array.from(doc.querySelectorAll("filter"))) {
18602
+ try {
18603
+ (_c = filter.parentNode) == null ? void 0 : _c.removeChild(filter);
18604
+ } catch {
18605
+ }
18606
+ }
18607
+ let shadowConfig = null;
18608
+ if (obj.shadow) {
18609
+ const sh = obj.shadow;
18610
+ const color = sh.color || "rgba(0,0,0,0.5)";
18611
+ const blur = Math.max(0, Number(sh.blur) || 0);
18612
+ const dx = Number(sh.offsetX) || 0;
18613
+ const dy = Number(sh.offsetY) || 0;
18614
+ shadowConfig = { color, dx, dy, blur };
18615
+ }
18616
+ const bgCfg = obj.__pdBg;
18617
+ const extOX = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetX) ?? 0) || 0;
18618
+ const extOY = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetY) ?? 0) || 0;
18619
+ const extDist = Math.hypot(extOX, extOY);
18620
+ const extColor = bgCfg == null ? void 0 : bgCfg.shadowColor;
18621
+ const hasExtShadowColor = !!extColor && extColor !== "transparent";
18622
+ const affectsText = !bgCfg || bgCfg.shadowAffectsText !== false;
18623
+ const blockShadowActive = !!bgCfg && bgCfg.shadowType === "block" && hasExtShadowColor && extDist > 0 && affectsText;
18624
+ const lineShadowActive = !!bgCfg && bgCfg.shadowType === "line" && hasExtShadowColor && extDist > 0 && affectsText;
18625
+ let blockSteps = 0;
18626
+ let blockStepX = 0;
18627
+ let blockStepY = 0;
18628
+ if (blockShadowActive) {
18629
+ const STEP = 1;
18630
+ blockSteps = Math.min(200, Math.max(1, Math.ceil(extDist / STEP)));
18631
+ blockStepX = extOX / blockSteps;
18632
+ blockStepY = extOY / blockSteps;
18633
+ }
18634
+ const flowStops = (obj == null ? void 0 : obj.fill) && typeof obj.fill === "object" && Array.isArray(obj.fill.colorStops) && obj.fill.colorStops.length ? obj.fill.colorStops.map((s) => ({ offset: Number(s.offset) || 0, color: s.color || "#000" })) : null;
18635
+ const gradId = (fillAttr.match(/url\(#([^)]+)\)/) || [])[1];
18636
+ if (gradId && w > 0 && h > 0) {
18637
+ const grad = doc.getElementById(gradId);
18638
+ if (grad && grad.getAttribute("gradientUnits") !== "userSpaceOnUse") {
18639
+ const tag = grad.tagName.toLowerCase();
18640
+ const num = (v, fallback) => {
18641
+ if (v == null) return fallback;
18642
+ const n = parseFloat(v);
18643
+ return Number.isFinite(n) ? n : fallback;
18644
+ };
18645
+ const mapX = (u) => -w / 2 + u * w;
18646
+ const mapY = (u) => -h / 2 + u * h;
18647
+ grad.setAttribute("gradientUnits", "userSpaceOnUse");
18648
+ if (tag === "lineargradient") {
18649
+ const x1 = num(grad.getAttribute("x1"), 0);
18650
+ const y1 = num(grad.getAttribute("y1"), 0);
18651
+ const x2 = num(grad.getAttribute("x2"), 1);
18652
+ const y2 = num(grad.getAttribute("y2"), 0);
18653
+ grad.setAttribute("x1", mapX(x1).toFixed(3));
18654
+ grad.setAttribute("y1", mapY(y1).toFixed(3));
18655
+ grad.setAttribute("x2", mapX(x2).toFixed(3));
18656
+ grad.setAttribute("y2", mapY(y2).toFixed(3));
18657
+ } else if (tag === "radialgradient") {
18658
+ const cx = num(grad.getAttribute("cx"), 0.5);
18659
+ const cy = num(grad.getAttribute("cy"), 0.5);
18660
+ const r = num(grad.getAttribute("r"), 0.5);
18661
+ const fx = num(grad.getAttribute("fx"), cx);
18662
+ const fy = num(grad.getAttribute("fy"), cy);
18663
+ grad.setAttribute("cx", mapX(cx).toFixed(3));
18664
+ grad.setAttribute("cy", mapY(cy).toFixed(3));
18665
+ grad.setAttribute("fx", mapX(fx).toFixed(3));
18666
+ grad.setAttribute("fy", mapY(fy).toFixed(3));
18667
+ grad.setAttribute("r", (r * Math.max(w, h)).toFixed(3));
18668
+ }
18669
+ }
18670
+ }
18671
+ const measure = document.createElement("canvas").getContext("2d");
18672
+ if (!measure) return svg;
18673
+ measure.font = `${fst} ${fw} ${fs}px "${ff}"`;
18674
+ const chars = Array.from(plain);
18675
+ const spacing = (Number(obj.charSpacing) || 0) * fs / 1e3;
18676
+ const widths = chars.map((c) => measure.measureText(c).width + spacing);
18677
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
18678
+ const baselineDy = resolveTextPathAlphabeticBaselineDy(measure, plain, fs);
18679
+ let cursor = 0;
18680
+ if (obj.textAlign === "center") cursor = Math.max(0, (totalLen - totalWidth) / 2);
18681
+ else if (obj.textAlign === "right") cursor = Math.max(0, totalLen - totalWidth);
18682
+ const halfW = (Number(obj.width) || 0) / 2;
18683
+ const halfH = (Number(obj.height) || 0) / 2;
18684
+ const glyphSvg = [];
18685
+ const shadowSvg = [];
18686
+ const blockShadowSvg = [];
18687
+ const lineShadowSvg = [];
18688
+ let minX = Number.POSITIVE_INFINITY;
18689
+ let minY = Number.POSITIVE_INFINITY;
18690
+ let maxX = Number.NEGATIVE_INFINITY;
18691
+ let maxY = Number.NEGATIVE_INFINITY;
18692
+ for (let i = 0; i < chars.length; i++) {
18693
+ const cw = widths[i];
18694
+ const mid = cursor + cw / 2;
18695
+ cursor += cw;
18696
+ if (mid > totalLen) break;
18697
+ const p = pathEl.getPointAtLength(Math.max(0, mid));
18698
+ const ahead = pathEl.getPointAtLength(Math.min(totalLen, mid + 0.5));
18699
+ const deg = Math.atan2(ahead.y - p.y, ahead.x - p.x) * 180 / Math.PI;
18700
+ const x = p.x - halfW;
18701
+ const y = p.y - halfH;
18702
+ const glyphPad = Math.max(fs, cw) * 0.75;
18703
+ minX = Math.min(minX, x - glyphPad);
18704
+ minY = Math.min(minY, y - glyphPad - fs);
18705
+ maxX = Math.max(maxX, x + glyphPad);
18706
+ maxY = Math.max(maxY, y + glyphPad);
18707
+ const glyphFill = flowStops ? sampleGradientColor(flowStops, chars.length > 1 ? i / (chars.length - 1) : 0) : fillAttr;
18708
+ if (blockShadowActive) {
18709
+ for (let s = blockSteps; s >= 1; s--) {
18710
+ const bx = x + blockStepX * s;
18711
+ const by = y + blockStepY * s;
18712
+ blockShadowSvg.push(
18713
+ `<text font-family="${escapeXml(String(ff))}" font-size="${fs}" font-weight="${fw}" font-style="${fst}" data-source-font-family="${escapeXml(String(ff))}" data-source-font-weight="${escapeXml(String(fw))}" data-source-font-style="${escapeXml(String(fst))}" fill="${escapeXml(String(extColor))}" text-anchor="middle" y="${baselineDy.toFixed(3)}" data-pixldocs-warp-block-shadow="true" transform="translate(${bx.toFixed(3)} ${by.toFixed(3)}) rotate(${deg.toFixed(3)})">${escapeXml(chars[i])}</text>`
18714
+ );
18715
+ }
18716
+ }
18717
+ if (lineShadowActive) {
18718
+ const lx = x + extOX;
18719
+ const ly = y + extOY;
18720
+ const lineStrokeW = LINE_SHADOW_STROKE_WIDTH;
18721
+ lineShadowSvg.push(
18722
+ `<text font-family="${escapeXml(String(ff))}" font-size="${fs}" font-weight="${fw}" font-style="${fst}" data-source-font-family="${escapeXml(String(ff))}" data-source-font-weight="${escapeXml(String(fw))}" data-source-font-style="${escapeXml(String(fst))}" fill="none" stroke="${escapeXml(String(extColor))}" stroke-width="${lineStrokeW.toFixed(3)}" stroke-linejoin="round" paint-order="stroke" text-anchor="middle" y="${baselineDy.toFixed(3)}" data-pixldocs-warp-line-shadow="true" transform="translate(${lx.toFixed(3)} ${ly.toFixed(3)}) rotate(${deg.toFixed(3)})">${escapeXml(chars[i])}</text>`
18723
+ );
18724
+ }
18725
+ if (shadowConfig) {
18726
+ const sx = x + shadowConfig.dx;
18727
+ const sy = y + shadowConfig.dy;
18728
+ shadowSvg.push(
18729
+ `<text font-family="${escapeXml(String(ff))}" font-size="${fs}" font-weight="${fw}" font-style="${fst}" data-source-font-family="${escapeXml(String(ff))}" data-source-font-weight="${escapeXml(String(fw))}" data-source-font-style="${escapeXml(String(fst))}" fill="${escapeXml(shadowConfig.color)}" text-anchor="middle" y="${baselineDy.toFixed(3)}" data-pixldocs-warp-shadow="true" transform="translate(${sx.toFixed(3)} ${sy.toFixed(3)}) rotate(${deg.toFixed(3)})">${escapeXml(chars[i])}</text>`
18730
+ );
18731
+ }
18732
+ glyphSvg.push(
18733
+ `<text font-family="${escapeXml(String(ff))}" font-size="${fs}" font-weight="${fw}" font-style="${fst}" data-source-font-family="${escapeXml(String(ff))}" data-source-font-weight="${escapeXml(String(fw))}" data-source-font-style="${escapeXml(String(fst))}" fill="${escapeXml(glyphFill)}" text-anchor="middle" y="${baselineDy.toFixed(3)}" data-pixldocs-warp-glyph="true" transform="translate(${x.toFixed(3)} ${y.toFixed(3)}) rotate(${deg.toFixed(3)})">${escapeXml(chars[i])}</text>`
18734
+ );
18735
+ }
18736
+ const replacement = doc.createElementNS("http://www.w3.org/2000/svg", "g");
18737
+ replacement.setAttribute("data-pixldocs-warped", "true");
18738
+ let innerHtml = "";
18739
+ if (shadowConfig && shadowSvg.length) {
18740
+ const blur = Math.max(0, Number(shadowConfig.blur) || 0);
18741
+ if (blur > 0 && Number.isFinite(minX) && Number.isFinite(maxX)) {
18742
+ const pad = Math.max(8, blur * 3);
18743
+ const sMinX = minX + shadowConfig.dx - pad;
18744
+ const sMinY = minY + shadowConfig.dy - pad;
18745
+ const sMaxX = maxX + shadowConfig.dx + pad;
18746
+ const sMaxY = maxY + shadowConfig.dy + pad;
18747
+ const bw = Math.max(1, sMaxX - sMinX);
18748
+ const bh = Math.max(1, sMaxY - sMinY);
18749
+ innerHtml += `<g class="__pdShadowRaster" data-pixldocs-warp-shadow-group="true" data-blur="${blur}" data-ox="0" data-oy="0" data-bx="${sMinX.toFixed(3)}" data-by="${sMinY.toFixed(3)}" data-bw="${bw.toFixed(3)}" data-bh="${bh.toFixed(3)}">${shadowSvg.join("")}</g>`;
18750
+ } else {
18751
+ innerHtml += `<g data-pixldocs-warp-shadow-group="true">${shadowSvg.join("")}</g>`;
18752
+ }
18753
+ }
18754
+ if (lineShadowSvg.length) {
18755
+ innerHtml = `<g data-pixldocs-warp-line-shadow-group="true">${lineShadowSvg.join("")}</g>` + innerHtml;
18756
+ }
18757
+ if (blockShadowSvg.length) {
18758
+ innerHtml = `<g data-pixldocs-warp-block-shadow-group="true">${blockShadowSvg.join("")}</g>` + innerHtml;
18759
+ }
18760
+ innerHtml += glyphSvg.join("");
18761
+ replacement.innerHTML = innerHtml;
18762
+ (_d = textEl.parentNode) == null ? void 0 : _d.replaceChild(replacement, textEl);
18763
+ for (let i = 1; i < textEls.length; i++) {
18764
+ try {
18765
+ (_e = textEls[i].parentNode) == null ? void 0 : _e.removeChild(textEls[i]);
18766
+ } catch {
18767
+ }
18768
+ }
18769
+ const serialized = new XMLSerializer().serializeToString(doc.documentElement);
18770
+ return serialized;
18771
+ }
16791
18772
  function stampFabricLineMetricsOnTextSvg(svg, obj) {
16792
18773
  const lines = Array.isArray(obj == null ? void 0 : obj._textLines) ? obj._textLines : [];
16793
18774
  if (!lines.length || typeof (obj == null ? void 0 : obj.getLineWidth) !== "function") return svg;
@@ -16858,6 +18839,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16858
18839
  obj.toSVG = (reviver) => {
16859
18840
  let svg = originalToSVG(reviver);
16860
18841
  if (isTextboxLike(obj)) svg = stampFabricLineMetricsOnTextSvg(svg, obj);
18842
+ if (isTextboxLike(obj)) svg = warpTextboxSvgAlongPath(svg, obj);
16861
18843
  if (imageId) svg = stampPixldocsImageIdOnSvg(svg, imageId);
16862
18844
  return svg;
16863
18845
  };
@@ -16952,9 +18934,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16952
18934
  }
16953
18935
  return svgString;
16954
18936
  }
16955
- const resolvedPackageVersion = "0.5.205";
18937
+ const resolvedPackageVersion = "0.5.207";
16956
18938
  const PACKAGE_VERSION = resolvedPackageVersion;
16957
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.205";
18939
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.207";
16958
18940
  const roundParityValue = (value) => {
16959
18941
  if (typeof value !== "number") return value;
16960
18942
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -17633,7 +19615,7 @@ class PixldocsRenderer {
17633
19615
  await this.waitForCanvasScene(container, cloned, i);
17634
19616
  }
17635
19617
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
17636
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CZyBCnFF.js");
19618
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-HZVoeZf3.js");
17637
19619
  const prepared = preparePagesForExport(
17638
19620
  cloned.pages,
17639
19621
  canvasWidth,
@@ -19817,7 +21799,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19817
21799
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19818
21800
  sanitizeSvgTreeForPdf(svgToDraw);
19819
21801
  try {
19820
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CZyBCnFF.js");
21802
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-HZVoeZf3.js");
19821
21803
  try {
19822
21804
  await logTextMeasurementDiagnostic(svgToDraw);
19823
21805
  } catch {
@@ -19949,7 +21931,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
19949
21931
  );
19950
21932
  }
19951
21933
  try {
19952
- const { prepareSvgTextForPdfMode } = await import("./svgTextToPath-BoT6H7Lz.js");
21934
+ const { prepareSvgTextForPdfMode } = await import("./svgTextToPath-Bw974R9h.js");
19953
21935
  pageSvg = await prepareSvgTextForPdfMode(pageSvg, textMode, fontBaseUrl);
19954
21936
  try {
19955
21937
  dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-shared-text-prep");
@@ -20217,4 +22199,4 @@ export {
20217
22199
  buildTeaserBlurFlatKeys as y,
20218
22200
  collectFontDescriptorsFromConfig as z
20219
22201
  };
20220
- //# sourceMappingURL=index-DBuMPh9u.js.map
22202
+ //# sourceMappingURL=index-D4Dis-kD.js.map