@pixldocs/canvas-renderer 0.5.205 → 0.5.206

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.
@@ -3649,17 +3649,14 @@ async function normalizeSvgImageDimensions(fabricImage, imageUrl, sourceFormat)
3649
3649
  const el = ((_a = fabricImage.getElement) == null ? void 0 : _a.call(fabricImage)) ?? fabricImage._element;
3650
3650
  let w = 0;
3651
3651
  let h = 0;
3652
- if (el && typeof el.naturalWidth === "number" && typeof el.naturalHeight === "number" && el.naturalWidth > 0 && el.naturalHeight > 0) {
3652
+ const dims = await loadSvgDimensions(imageUrl);
3653
+ if (dims && dims.width > 0 && dims.height > 0) {
3654
+ w = dims.width;
3655
+ h = dims.height;
3656
+ } else if (el && typeof el.naturalWidth === "number" && typeof el.naturalHeight === "number" && el.naturalWidth > 0 && el.naturalHeight > 0) {
3653
3657
  w = el.naturalWidth;
3654
3658
  h = el.naturalHeight;
3655
3659
  }
3656
- if (w <= 0 || h <= 0) {
3657
- const dims = await loadSvgDimensions(imageUrl);
3658
- if (dims && dims.width > 0 && dims.height > 0) {
3659
- w = dims.width;
3660
- h = dims.height;
3661
- }
3662
- }
3663
3660
  if (w > 0 && h > 0) {
3664
3661
  fabricImage.set({ width: w, height: h });
3665
3662
  fabricImage.setCoords();
@@ -5713,6 +5710,134 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5713
5710
  return true;
5714
5711
  });
5715
5712
  }
5713
+ const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
5714
+ function arcPath(w, sag, up) {
5715
+ if (sag <= 0.5) return `M 0 0 L ${w} 0`;
5716
+ const r = (sag * sag + w / 2 * (w / 2)) / (2 * sag);
5717
+ const sweep = 1;
5718
+ const y0 = sag;
5719
+ const y1 = sag;
5720
+ return `M 0 ${y0} A ${r.toFixed(2)} ${r.toFixed(2)} 0 0 ${sweep} ${w} ${y1}`;
5721
+ }
5722
+ function sampledPath(fn, steps = 60) {
5723
+ const parts = [];
5724
+ for (let i = 0; i <= steps; i++) {
5725
+ const [x, y] = fn(i / steps);
5726
+ parts.push(i === 0 ? `M ${x.toFixed(2)} ${y.toFixed(2)}` : `L ${x.toFixed(2)} ${y.toFixed(2)}`);
5727
+ }
5728
+ return parts.join(" ");
5729
+ }
5730
+ function resolveTextPath(cfg, width, fontSize) {
5731
+ if (!cfg || !cfg.preset || cfg.preset === "none") return null;
5732
+ const w = Math.max(1, width);
5733
+ const fs = Math.max(4, fontSize);
5734
+ const t = clamp01$1(cfg.intensity ?? 0.5);
5735
+ if (cfg.bezier && cfg.preset !== "circle" && cfg.preset !== "rise" && cfg.preset !== "angle") {
5736
+ const { p0, c0, c1, p1 } = cfg.bezier;
5737
+ const minX = Math.min(p0[0], c0[0], c1[0], p1[0]);
5738
+ const maxX = Math.max(p0[0], c0[0], c1[0], p1[0]);
5739
+ const minY = Math.min(p0[1], c0[1], c1[1], p1[1]);
5740
+ const maxY = Math.max(p0[1], c0[1], c1[1], p1[1]);
5741
+ return {
5742
+ d: `M ${p0[0]} ${p0[1]} C ${c0[0]} ${c0[1]}, ${c1[0]} ${c1[1]}, ${p1[0]} ${p1[1]}`,
5743
+ bbox: { width: maxX - minX, height: maxY - minY + fs * 1.4 }
5744
+ };
5745
+ }
5746
+ if (cfg.preset === "custom") {
5747
+ if (!cfg.path) return null;
5748
+ return { d: cfg.path, bbox: cfg.bbox ?? { width: w, height: fs * 2 } };
5749
+ }
5750
+ switch (cfg.preset) {
5751
+ case "arch": {
5752
+ const sag = t * w * 0.45;
5753
+ return { d: arcPath(w, sag), bbox: { width: w, height: sag + fs * 1.4 } };
5754
+ }
5755
+ case "rise":
5756
+ case "angle": {
5757
+ const ep = cfg.endpoints;
5758
+ const defaultDy = t * fs * 3;
5759
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultDy;
5760
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : 0;
5761
+ const lineMid = (leftY + rightY) / 2;
5762
+ const hasCenterHandle = ep && Number.isFinite(ep.centerY);
5763
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : 0;
5764
+ const curveAmt = Math.max(-1, Math.min(1, rawCurve));
5765
+ const curveAmp = Math.min(w * 0.18, fs * 4);
5766
+ const centerY = hasCenterHandle ? ep.centerY : lineMid - curveAmt * curveAmp;
5767
+ if (Math.abs(centerY - lineMid) < 0.5) {
5768
+ const minY2 = Math.min(leftY, rightY);
5769
+ const maxY2 = Math.max(leftY, rightY);
5770
+ return {
5771
+ d: `M 0 ${leftY.toFixed(2)} L ${w} ${rightY.toFixed(2)}`,
5772
+ bbox: { width: w, height: maxY2 - minY2 + fs * 1.4 }
5773
+ };
5774
+ }
5775
+ const controlY = 2 * centerY - lineMid;
5776
+ const minY = Math.min(leftY, rightY, centerY);
5777
+ const maxY = Math.max(leftY, rightY, centerY);
5778
+ return {
5779
+ d: `M 0 ${leftY.toFixed(2)} Q ${(w / 2).toFixed(2)} ${controlY.toFixed(2)} ${w} ${rightY.toFixed(2)}`,
5780
+ bbox: { width: w, height: maxY - minY + fs * 1.4 }
5781
+ };
5782
+ }
5783
+ case "wave": {
5784
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : t;
5785
+ const c = Math.max(-1, Math.min(1, rawCurve));
5786
+ const amp = c * fs * 3;
5787
+ const absAmp = Math.abs(amp);
5788
+ const d = sampledPath((u) => [u * w, absAmp - amp * Math.sin(u * Math.PI)]);
5789
+ return { d, bbox: { width: w, height: absAmp * 2 + fs * 1.4 } };
5790
+ }
5791
+ case "flag": {
5792
+ const rawCurve = Number.isFinite(cfg.curve) ? cfg.curve : t;
5793
+ const c = Math.max(-1, Math.min(1, rawCurve));
5794
+ const amp = c * fs * 2.5;
5795
+ const absAmp = Math.abs(amp);
5796
+ const d = sampledPath((u) => [u * w, absAmp + amp * Math.sin(u * Math.PI * 2)]);
5797
+ return { d, bbox: { width: w, height: absAmp * 2 + fs * 1.4 } };
5798
+ }
5799
+ case "circle": {
5800
+ const auto = Math.max(fs * 1.5, w / Math.PI);
5801
+ const r = Math.max(fs * 0.75, Number.isFinite(cfg.radius) && cfg.radius > 0 ? cfg.radius : auto);
5802
+ return {
5803
+ d: `M ${r} ${2 * r} A ${r} ${r} 0 1 1 ${r} 0 A ${r} ${r} 0 1 1 ${r} ${2 * r}`,
5804
+ bbox: { width: 2 * r, height: 2 * r + fs * 1.4 }
5805
+ };
5806
+ }
5807
+ default:
5808
+ return null;
5809
+ }
5810
+ }
5811
+ function measurePath(d) {
5812
+ if (typeof document === "undefined") return null;
5813
+ const svgNS = "http://www.w3.org/2000/svg";
5814
+ const svg = document.createElementNS(svgNS, "svg");
5815
+ const path = document.createElementNS(svgNS, "path");
5816
+ path.setAttribute("d", d);
5817
+ svg.appendChild(path);
5818
+ return path;
5819
+ }
5820
+ function resolveTextPathAlphabeticBaselineDy(ctx, sampleText, fontSize) {
5821
+ const prevBaseline = ctx.textBaseline;
5822
+ try {
5823
+ ctx.textBaseline = "alphabetic";
5824
+ const metrics = ctx.measureText(sampleText || "M");
5825
+ const fontAscent = Number(metrics.fontBoundingBoxAscent);
5826
+ const fontDescent = Number(metrics.fontBoundingBoxDescent);
5827
+ if (Number.isFinite(fontAscent) && Number.isFinite(fontDescent) && fontAscent + fontDescent > 0) {
5828
+ return (fontAscent - fontDescent) / 2;
5829
+ }
5830
+ const actualAscent = Number(metrics.actualBoundingBoxAscent);
5831
+ const actualDescent = Number(metrics.actualBoundingBoxDescent);
5832
+ if (Number.isFinite(actualAscent) && Number.isFinite(actualDescent) && actualAscent + actualDescent > 0) {
5833
+ return (actualAscent - actualDescent) / 2;
5834
+ }
5835
+ } catch {
5836
+ } finally {
5837
+ ctx.textBaseline = prevBaseline;
5838
+ }
5839
+ return fontSize / 2;
5840
+ }
5716
5841
  const TextboxProto = fabric__namespace.Textbox.prototype;
5717
5842
  if (!TextboxProto.__pixldocsOrigCalcTextHeight) {
5718
5843
  TextboxProto.__pixldocsOrigCalcTextHeight = TextboxProto.calcTextHeight;
@@ -5754,23 +5879,988 @@ const stateProps = fabric__namespace.Textbox.prototype.stateProperties;
5754
5879
  if (Array.isArray(stateProps)) {
5755
5880
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
5756
5881
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
5882
+ if (!stateProps.includes("textPath")) stateProps.push("textPath");
5757
5883
  }
5758
5884
  const cacheProps = fabric__namespace.Textbox.prototype.cacheProperties;
5759
5885
  if (Array.isArray(cacheProps)) {
5760
5886
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
5761
5887
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
5888
+ if (!cacheProps.includes("textPath")) cacheProps.push("textPath");
5889
+ }
5890
+ const hasActiveTextPath = (obj) => {
5891
+ const tp = obj.textPath;
5892
+ return !!tp && !!tp.preset && tp.preset !== "none";
5893
+ };
5894
+ function applyWarpFillStyle(ctx, obj) {
5895
+ const filler = obj.fill;
5896
+ if (filler && typeof filler === "object" && Array.isArray(filler.colorStops) && filler.coords) {
5897
+ try {
5898
+ const halfW = (obj.width || 0) / 2;
5899
+ const halfH = (obj.height || 0) / 2;
5900
+ const c = filler.coords || {};
5901
+ let live = null;
5902
+ if (filler.type === "radial") {
5903
+ live = ctx.createRadialGradient(
5904
+ (Number(c.x1) || 0) - halfW,
5905
+ (Number(c.y1) || 0) - halfH,
5906
+ Math.max(0, Number(c.r1) || 0),
5907
+ (Number(c.x2 ?? c.x1) || 0) - halfW,
5908
+ (Number(c.y2 ?? c.y1) || 0) - halfH,
5909
+ Math.max(0.1, Number(c.r2 ?? c.r) || Math.max(obj.width || 1, obj.height || 1) / 2)
5910
+ );
5911
+ } else {
5912
+ live = ctx.createLinearGradient(
5913
+ (Number(c.x1) || 0) - halfW,
5914
+ (Number(c.y1) || 0) - halfH,
5915
+ (Number(c.x2 ?? obj.width) || 0) - halfW,
5916
+ (Number(c.y2) || 0) - halfH
5917
+ );
5918
+ }
5919
+ for (const stop of filler.colorStops) {
5920
+ live.addColorStop(Math.max(0, Math.min(1, Number(stop.offset) || 0)), stop.color || "#000");
5921
+ }
5922
+ ctx.fillStyle = live;
5923
+ return { offsetX: 0, offsetY: 0 };
5924
+ } catch {
5925
+ }
5926
+ }
5927
+ if (typeof obj.handleFiller === "function") {
5928
+ try {
5929
+ return obj.handleFiller(ctx, "fillStyle", filler || "#000") || { offsetX: 0, offsetY: 0 };
5930
+ } catch {
5931
+ }
5932
+ }
5933
+ if (filler && typeof filler.toLive === "function") {
5934
+ try {
5935
+ ctx.fillStyle = filler.toLive(ctx) || "#000";
5936
+ return { offsetX: 0, offsetY: 0 };
5937
+ } catch {
5938
+ }
5939
+ }
5940
+ ctx.fillStyle = typeof obj.fill === "string" ? obj.fill : "#000";
5941
+ return { offsetX: 0, offsetY: 0 };
5942
+ }
5943
+ function parseCssColor(input) {
5944
+ const fallback = [0, 0, 0, 1];
5945
+ if (!input) return fallback;
5946
+ const s = String(input).trim();
5947
+ if (s.startsWith("#")) {
5948
+ let hex = s.slice(1);
5949
+ if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
5950
+ if (hex.length === 4) hex = hex.split("").map((c) => c + c).join("");
5951
+ if (hex.length === 6) {
5952
+ const n = parseInt(hex, 16);
5953
+ return [n >> 16 & 255, n >> 8 & 255, n & 255, 1];
5954
+ }
5955
+ if (hex.length === 8) {
5956
+ const n = parseInt(hex, 16);
5957
+ return [n >> 24 & 255, n >> 16 & 255, n >> 8 & 255, (n & 255) / 255];
5958
+ }
5959
+ }
5960
+ const m = s.match(/^rgba?\s*\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+%?))?\s*\)$/i);
5961
+ if (m) {
5962
+ let a = 1;
5963
+ if (m[4] != null) a = m[4].endsWith("%") ? parseFloat(m[4]) / 100 : parseFloat(m[4]);
5964
+ return [parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3]), Number.isFinite(a) ? a : 1];
5965
+ }
5966
+ try {
5967
+ const c = document.createElement("canvas").getContext("2d");
5968
+ if (c) {
5969
+ c.fillStyle = "#000";
5970
+ c.fillStyle = s;
5971
+ const computed = c.fillStyle;
5972
+ return parseCssColor(computed.startsWith("#") || computed.startsWith("rgb") ? computed : "#000");
5973
+ }
5974
+ } catch {
5975
+ }
5976
+ return fallback;
5977
+ }
5978
+ function sampleGradientColor(stops, t) {
5979
+ if (!stops || !stops.length) return "#000";
5980
+ const sorted = [...stops].sort((a, b) => (a.offset || 0) - (b.offset || 0));
5981
+ const u = Math.max(0, Math.min(1, t));
5982
+ if (u <= (sorted[0].offset || 0)) return sorted[0].color || "#000";
5983
+ if (u >= (sorted[sorted.length - 1].offset || 1)) return sorted[sorted.length - 1].color || "#000";
5984
+ for (let i = 0; i < sorted.length - 1; i++) {
5985
+ const a = sorted[i];
5986
+ const b = sorted[i + 1];
5987
+ const ao = a.offset || 0;
5988
+ const bo = b.offset || 0;
5989
+ if (u >= ao && u <= bo) {
5990
+ const span = bo - ao || 1;
5991
+ const k = (u - ao) / span;
5992
+ const ca = parseCssColor(a.color);
5993
+ const cb = parseCssColor(b.color);
5994
+ const r = Math.round(ca[0] + (cb[0] - ca[0]) * k);
5995
+ const g = Math.round(ca[1] + (cb[1] - ca[1]) * k);
5996
+ const bl = Math.round(ca[2] + (cb[2] - ca[2]) * k);
5997
+ const al = ca[3] + (cb[3] - ca[3]) * k;
5998
+ return `rgba(${r}, ${g}, ${bl}, ${al.toFixed(3)})`;
5999
+ }
6000
+ }
6001
+ return sorted[sorted.length - 1].color || "#000";
6002
+ }
6003
+ function getGradientStopsForFlow(obj) {
6004
+ const f = obj.fill;
6005
+ if (f && typeof f === "object" && Array.isArray(f.colorStops) && f.colorStops.length) {
6006
+ return f.colorStops.map((s) => ({ offset: Number(s.offset) || 0, color: s.color || "#000" }));
6007
+ }
6008
+ return null;
6009
+ }
6010
+ function localPointFromCanvas(target, x, y) {
6011
+ const point = new fabric__namespace.Point(x, y);
6012
+ try {
6013
+ return target.toLocalPoint(point, "left", "top");
6014
+ } catch {
6015
+ const inv = fabric__namespace.util.invertTransform(target.calcTransformMatrix());
6016
+ const local = fabric__namespace.util.transformPoint(point, inv);
6017
+ return new fabric__namespace.Point(local.x + (target.width || 0) / 2, local.y + (target.height || 0) / 2);
6018
+ }
6019
+ }
6020
+ function localPointFromFrozenMatrix(target, x, y, matrix) {
6021
+ const local = fabric__namespace.util.transformPoint(new fabric__namespace.Point(x, y), fabric__namespace.util.invertTransform(matrix));
6022
+ return new fabric__namespace.Point(local.x + (target.width || 0) / 2, local.y + (target.height || 0) / 2);
6023
+ }
6024
+ function vectorFromFrozenMatrix(matrix, dx, dy) {
6025
+ return new fabric__namespace.Point(matrix[0] * dx + matrix[2] * dy, matrix[1] * dx + matrix[3] * dy);
6026
+ }
6027
+ function scaleLocalToScreen(target, p) {
6028
+ var _a;
6029
+ const vpt = ((_a = target == null ? void 0 : target.canvas) == null ? void 0 : _a.viewportTransform) || [1, 0, 0, 1, 0, 0];
6030
+ const zx = Math.abs(vpt[0] || 1);
6031
+ const zy = Math.abs(vpt[3] || 1);
6032
+ const sx = Math.abs((target == null ? void 0 : target.scaleX) || 1);
6033
+ const sy = Math.abs((target == null ? void 0 : target.scaleY) || 1);
6034
+ return new fabric__namespace.Point(p.x * sx * zx, p.y * sy * zy);
6035
+ }
6036
+ function applyTextPathControls(textbox) {
6037
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
6038
+ const obj = textbox;
6039
+ if (!hasActiveTextPath(obj)) {
6040
+ obj.__pdTextPathHovered = false;
6041
+ if (obj.__pdTextPathControls) {
6042
+ try {
6043
+ const cu2 = fabric__namespace.controlsUtils;
6044
+ 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));
6045
+ if (defaults) obj.controls = defaults;
6046
+ } catch {
6047
+ }
6048
+ delete obj.controls.bzP0;
6049
+ delete obj.controls.bzC0;
6050
+ delete obj.controls.bzC1;
6051
+ delete obj.controls.bzP1;
6052
+ delete obj.controls.rsL;
6053
+ delete obj.controls.rsR;
6054
+ delete obj.controls.rsC;
6055
+ delete obj.controls.bzMid;
6056
+ delete obj.controls.bzMidT0;
6057
+ delete obj.controls.bzMidT1;
6058
+ delete obj.controls.tpRot;
6059
+ delete obj.controls.tpPivot;
6060
+ (_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 });
6061
+ obj.hasBorders = true;
6062
+ obj.__pdTextPathControls = false;
6063
+ obj.setCoords();
6064
+ }
6065
+ return;
6066
+ }
6067
+ if (!obj.__pdTextPathHoverWired) {
6068
+ obj.on("mouseover", () => {
6069
+ var _a2;
6070
+ if (!hasActiveTextPath(obj)) return;
6071
+ obj.__pdTextPathHovered = true;
6072
+ (_a2 = obj.canvas) == null ? void 0 : _a2.requestRenderAll();
6073
+ });
6074
+ obj.on("mouseout", () => {
6075
+ var _a2;
6076
+ obj.__pdTextPathHovered = false;
6077
+ (_a2 = obj.canvas) == null ? void 0 : _a2.requestRenderAll();
6078
+ });
6079
+ obj.__pdTextPathHoverWired = true;
6080
+ }
6081
+ obj.controls = {};
6082
+ const halfWOf = (t) => (t.width || 0) / 2;
6083
+ const halfHOf = (t) => (t.height || 0) / 2;
6084
+ const cu = fabric__namespace.controlsUtils;
6085
+ const renderRotation = (ctx, left, top) => {
6086
+ ctx.save();
6087
+ ctx.translate(left, top);
6088
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6089
+ ctx.shadowBlur = 2;
6090
+ ctx.shadowOffsetY = 0.5;
6091
+ ctx.fillStyle = "#ffffff";
6092
+ ctx.strokeStyle = "#1d9bf0";
6093
+ ctx.lineWidth = 1.5;
6094
+ ctx.beginPath();
6095
+ ctx.arc(0, 0, 6, 0, Math.PI * 2);
6096
+ ctx.fill();
6097
+ ctx.shadowColor = "transparent";
6098
+ ctx.stroke();
6099
+ ctx.strokeStyle = "#1d9bf0";
6100
+ ctx.lineWidth = 1.25;
6101
+ ctx.beginPath();
6102
+ ctx.arc(0, 0, 3, -Math.PI * 0.85, Math.PI * 0.75);
6103
+ ctx.stroke();
6104
+ ctx.restore();
6105
+ };
6106
+ const renderPivot = (ctx, left, top) => {
6107
+ ctx.save();
6108
+ ctx.translate(left, top);
6109
+ ctx.strokeStyle = "#1d9bf0";
6110
+ ctx.fillStyle = "#ffffff";
6111
+ ctx.lineWidth = 1.25;
6112
+ ctx.beginPath();
6113
+ ctx.arc(0, 0, 5, 0, Math.PI * 2);
6114
+ ctx.fill();
6115
+ ctx.stroke();
6116
+ ctx.beginPath();
6117
+ ctx.moveTo(-3.5, 0);
6118
+ ctx.lineTo(3.5, 0);
6119
+ ctx.moveTo(0, -3.5);
6120
+ ctx.lineTo(0, 3.5);
6121
+ ctx.stroke();
6122
+ ctx.fillStyle = "#1d9bf0";
6123
+ ctx.beginPath();
6124
+ ctx.arc(0, 0, 1.25, 0, Math.PI * 2);
6125
+ ctx.fill();
6126
+ ctx.restore();
6127
+ };
6128
+ const addRotationAndPivot = () => {
6129
+ var _a2, _b2;
6130
+ obj.controls.tpRot = new fabric__namespace.Control({
6131
+ x: 0,
6132
+ y: -0.5,
6133
+ offsetY: -28,
6134
+ cursorStyleHandler: cu == null ? void 0 : cu.rotationStyleHandler,
6135
+ actionHandler: cu == null ? void 0 : cu.rotationWithSnapping,
6136
+ actionName: "rotate",
6137
+ render: renderRotation,
6138
+ withConnection: false
6139
+ });
6140
+ obj.controls.tpPivot = new fabric__namespace.Control({
6141
+ x: 0,
6142
+ y: 0,
6143
+ cursorStyle: "default",
6144
+ actionName: "tpPivot",
6145
+ actionHandler: () => false,
6146
+ render: renderPivot
6147
+ });
6148
+ (_a2 = obj.setControlVisible) == null ? void 0 : _a2.call(obj, "tpRot", true);
6149
+ (_b2 = obj.setControlVisible) == null ? void 0 : _b2.call(obj, "tpPivot", true);
6150
+ };
6151
+ if (((_d = obj.textPath) == null ? void 0 : _d.preset) === "circle") {
6152
+ const getRadius = (target) => {
6153
+ const tp = target.textPath || {};
6154
+ const fs = target.fontSize || 16;
6155
+ const w = target.width || 0;
6156
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6157
+ const r = tp.radius;
6158
+ return Math.max(fs * 0.75, Number.isFinite(r) && r > 0 ? r : auto);
6159
+ };
6160
+ const setRadius = (target, r) => {
6161
+ var _a2;
6162
+ const fs = target.fontSize || 16;
6163
+ const clamped = Math.max(fs * 0.75, r);
6164
+ if (!target.textPath) target.textPath = { preset: "circle" };
6165
+ target.textPath.preset = "circle";
6166
+ target.textPath.radius = clamped;
6167
+ target.dirty = true;
6168
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6169
+ };
6170
+ const ringPoint = (target, dir) => {
6171
+ const r = getRadius(target);
6172
+ const cx = r - halfWOf(target);
6173
+ const cy = r - halfHOf(target);
6174
+ switch (dir) {
6175
+ case "N":
6176
+ return new fabric__namespace.Point(cx, cy - r);
6177
+ case "E":
6178
+ return new fabric__namespace.Point(cx + r, cy);
6179
+ case "S":
6180
+ return new fabric__namespace.Point(cx, cy + r);
6181
+ case "W":
6182
+ return new fabric__namespace.Point(cx - r, cy);
6183
+ }
6184
+ };
6185
+ const positionAtRing = (dir) => (_d2, finalMatrix, target) => scaleLocalToScreen(target, ringPoint(target, dir)).transform(finalMatrix);
6186
+ const dragRadius = (dir) => (_e2, transform, x, y) => {
6187
+ const target = transform.target;
6188
+ const state = transform.__pdCircleRadiusDrag || (transform.__pdCircleRadiusDrag = {
6189
+ dir,
6190
+ startRadius: getRadius(target),
6191
+ startLeft: target.left || 0,
6192
+ startTop: target.top || 0,
6193
+ startMatrix: target.calcTransformMatrix(),
6194
+ startLocal: null
6195
+ });
6196
+ if (!state.startLocal) state.startLocal = localPointFromFrozenMatrix(target, x, y, state.startMatrix);
6197
+ const local = localPointFromFrozenMatrix(target, x, y, state.startMatrix);
6198
+ const dx = local.x - state.startLocal.x;
6199
+ const dy = local.y - state.startLocal.y;
6200
+ let next = state.startRadius;
6201
+ if (dir === "E") next = state.startRadius + dx / 2;
6202
+ else if (dir === "W") next = state.startRadius - dx / 2;
6203
+ else if (dir === "S") next = state.startRadius + dy / 2;
6204
+ else next = state.startRadius - dy / 2;
6205
+ const fs = target.fontSize || 16;
6206
+ next = Math.max(fs * 0.75, next);
6207
+ if (dir === "W") {
6208
+ const appliedDx = (state.startRadius - next) * 2;
6209
+ const move = vectorFromFrozenMatrix(state.startMatrix, appliedDx, 0);
6210
+ target.set({ left: state.startLeft + move.x, top: state.startTop + move.y });
6211
+ } else if (dir === "N") {
6212
+ const appliedDy = (state.startRadius - next) * 2;
6213
+ const move = vectorFromFrozenMatrix(state.startMatrix, 0, appliedDy);
6214
+ target.set({ left: state.startLeft + move.x, top: state.startTop + move.y });
6215
+ }
6216
+ setRadius(target, next);
6217
+ return true;
6218
+ };
6219
+ const renderRingHandle = (ctx, left, top) => {
6220
+ ctx.save();
6221
+ ctx.translate(left, top);
6222
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6223
+ ctx.shadowBlur = 2;
6224
+ ctx.shadowOffsetY = 0.5;
6225
+ ctx.fillStyle = "#ffffff";
6226
+ ctx.strokeStyle = "#1d9bf0";
6227
+ ctx.lineWidth = 1.5;
6228
+ ctx.beginPath();
6229
+ ctx.arc(0, 0, 5.5, 0, Math.PI * 2);
6230
+ ctx.fill();
6231
+ ctx.shadowColor = "transparent";
6232
+ ctx.stroke();
6233
+ ctx.restore();
6234
+ };
6235
+ obj.controls.crN = new fabric__namespace.Control({ x: 0, y: -0.5, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragRadius("N"), positionHandler: positionAtRing("N"), render: renderRingHandle });
6236
+ obj.controls.crE = new fabric__namespace.Control({ x: 0.5, y: 0, cursorStyle: "ew-resize", actionName: "textPath", actionHandler: dragRadius("E"), positionHandler: positionAtRing("E"), render: renderRingHandle });
6237
+ obj.controls.crS = new fabric__namespace.Control({ x: 0, y: 0.5, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragRadius("S"), positionHandler: positionAtRing("S"), render: renderRingHandle });
6238
+ obj.controls.crW = new fabric__namespace.Control({ x: -0.5, y: 0, cursorStyle: "ew-resize", actionName: "textPath", actionHandler: dragRadius("W"), positionHandler: positionAtRing("W"), render: renderRingHandle });
6239
+ (_e = obj.setControlVisible) == null ? void 0 : _e.call(obj, "crN", true);
6240
+ (_f = obj.setControlVisible) == null ? void 0 : _f.call(obj, "crE", true);
6241
+ (_g = obj.setControlVisible) == null ? void 0 : _g.call(obj, "crS", true);
6242
+ (_h = obj.setControlVisible) == null ? void 0 : _h.call(obj, "crW", true);
6243
+ addRotationAndPivot();
6244
+ obj.hasBorders = false;
6245
+ obj.hasControls = true;
6246
+ obj.objectCaching = false;
6247
+ obj.__pdTextPathControls = true;
6248
+ obj.setCoords();
6249
+ return;
6250
+ }
6251
+ if (((_i = obj.textPath) == null ? void 0 : _i.preset) === "rise") {
6252
+ const getEndpoints = (target) => {
6253
+ const w = target.width || 0;
6254
+ const fs = target.fontSize || 16;
6255
+ const tp = target.textPath || {};
6256
+ const ep = tp.endpoints;
6257
+ const defaultLeftY = fs * 1.5;
6258
+ const defaultRightY = fs * 0.5;
6259
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
6260
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
6261
+ const lineMid = (leftY + rightY) / 2;
6262
+ const rawCurve = Number.isFinite(tp.curve) ? tp.curve : 0;
6263
+ const curveAmp = Math.min(w * 0.18, fs * 4);
6264
+ const centerY = ep && Number.isFinite(ep.centerY) ? ep.centerY : lineMid - Math.max(-1, Math.min(1, rawCurve)) * curveAmp;
6265
+ return { leftY, centerY, rightY };
6266
+ };
6267
+ const setEndpoints = (target, next) => {
6268
+ var _a2;
6269
+ target.width || 1;
6270
+ target.fontSize || 16;
6271
+ const lineMid = (next.leftY + next.rightY) / 2;
6272
+ const straight = { leftY: next.leftY, centerY: lineMid, rightY: next.rightY };
6273
+ if (!target.textPath) target.textPath = { preset: "rise" };
6274
+ target.textPath = {
6275
+ ...target.textPath,
6276
+ preset: "rise",
6277
+ curve: 0,
6278
+ endpoints: straight
6279
+ };
6280
+ if (target.skewY) target.set("skewY", 0);
6281
+ target.dirty = true;
6282
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6283
+ };
6284
+ const positionAtAngleHandle = (key) => (_d2, finalMatrix, target) => {
6285
+ const ep = getEndpoints(target);
6286
+ const x = key === "leftY" ? 0 : key === "rightY" ? target.width || 0 : (target.width || 0) / 2;
6287
+ return scaleLocalToScreen(target, new fabric__namespace.Point(x - halfWOf(target), ep[key] - halfHOf(target))).transform(finalMatrix);
6288
+ };
6289
+ const dragAngleHandle = (key) => (_e2, transform, x, y) => {
6290
+ const target = transform.target;
6291
+ const local = localPointFromCanvas(target, x, y);
6292
+ const current = getEndpoints(target);
6293
+ const next = { ...current, [key]: local.y };
6294
+ setEndpoints(target, next);
6295
+ return true;
6296
+ };
6297
+ const renderAngleHandle = (ctx, left, top) => {
6298
+ ctx.save();
6299
+ ctx.translate(left, top);
6300
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6301
+ ctx.shadowBlur = 2;
6302
+ ctx.shadowOffsetY = 0.5;
6303
+ ctx.fillStyle = "#ffffff";
6304
+ ctx.strokeStyle = "#1d9bf0";
6305
+ ctx.lineWidth = 1.5;
6306
+ ctx.beginPath();
6307
+ ctx.arc(0, 0, 5.5, 0, Math.PI * 2);
6308
+ ctx.fill();
6309
+ ctx.shadowColor = "transparent";
6310
+ ctx.stroke();
6311
+ ctx.restore();
6312
+ };
6313
+ if (obj.skewY) obj.set("skewY", 0);
6314
+ obj.controls.rsL = new fabric__namespace.Control({ x: -0.5, y: 0, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragAngleHandle("leftY"), positionHandler: positionAtAngleHandle("leftY"), render: renderAngleHandle });
6315
+ obj.controls.rsR = new fabric__namespace.Control({ x: 0.5, y: 0, cursorStyle: "ns-resize", actionName: "textPath", actionHandler: dragAngleHandle("rightY"), positionHandler: positionAtAngleHandle("rightY"), render: renderAngleHandle });
6316
+ (_j = obj.setControlVisible) == null ? void 0 : _j.call(obj, "rsL", true);
6317
+ (_k = obj.setControlVisible) == null ? void 0 : _k.call(obj, "rsR", true);
6318
+ addRotationAndPivot();
6319
+ obj.hasBorders = false;
6320
+ obj.hasControls = true;
6321
+ obj.objectCaching = false;
6322
+ obj.__pdTextPathControls = true;
6323
+ obj.setCoords();
6324
+ return;
6325
+ }
6326
+ const ensureBezier = (target) => {
6327
+ const tp = target.textPath;
6328
+ if (tp == null ? void 0 : tp.bezier) return tp.bezier;
6329
+ const resolved = resolveTextPath(tp, target.width || 0, target.fontSize || 16);
6330
+ const path = resolved ? measurePath(resolved.d) : null;
6331
+ if (!path) {
6332
+ const w = target.width || 0;
6333
+ const fs = target.fontSize || 16;
6334
+ const h = target.height || fs * 1.4;
6335
+ const cy = h / 2;
6336
+ const handleLen = w * 0.25;
6337
+ const fallback = {
6338
+ p0: [0, cy],
6339
+ c0: [handleLen, cy],
6340
+ c1: [w - handleLen, cy],
6341
+ p1: [w, cy]
6342
+ };
6343
+ target.textPath = { ...tp || { preset: "custom" }, bezier: fallback };
6344
+ return fallback;
6345
+ }
6346
+ const len = path.getTotalLength();
6347
+ const sample = (u) => {
6348
+ const p = path.getPointAtLength(u * len);
6349
+ return [p.x, p.y];
6350
+ };
6351
+ const p0 = sample(0);
6352
+ const a = sample(1 / 3);
6353
+ const b = sample(2 / 3);
6354
+ const p1 = sample(1);
6355
+ const c0 = [
6356
+ (-5 * p0[0] + 18 * a[0] - 9 * b[0] + 2 * p1[0]) / 6,
6357
+ (-5 * p0[1] + 18 * a[1] - 9 * b[1] + 2 * p1[1]) / 6
6358
+ ];
6359
+ const c1 = [
6360
+ (2 * p0[0] - 9 * a[0] + 18 * b[0] - 5 * p1[0]) / 6,
6361
+ (2 * p0[1] - 9 * a[1] + 18 * b[1] - 5 * p1[1]) / 6
6362
+ ];
6363
+ const solved = { p0, c0, c1, p1 };
6364
+ target.textPath = { ...tp || { preset: "custom" }, bezier: solved };
6365
+ return solved;
6366
+ };
6367
+ ensureBezier(obj);
6368
+ const positionAtBezier = (key) => (_d2, finalMatrix, target) => {
6369
+ const bz = ensureBezier(target);
6370
+ const [bx, by] = bz[key];
6371
+ return scaleLocalToScreen(target, new fabric__namespace.Point(bx - halfWOf(target), by - halfHOf(target))).transform(finalMatrix);
6372
+ };
6373
+ const bezierMidpoint = (bz) => [
6374
+ 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0],
6375
+ 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1]
6376
+ ];
6377
+ const positionAtMid = (_d2, finalMatrix, target) => {
6378
+ const bz = ensureBezier(target);
6379
+ const [mx, my] = bezierMidpoint(bz);
6380
+ return scaleLocalToScreen(target, new fabric__namespace.Point(mx - halfWOf(target), my - halfHOf(target))).transform(finalMatrix);
6381
+ };
6382
+ const midTangentVector = (bz) => {
6383
+ let dx = bz.p1[0] + 3 * bz.c1[0] - 3 * bz.c0[0] - bz.p0[0];
6384
+ let dy = bz.p1[1] + 3 * bz.c1[1] - 3 * bz.c0[1] - bz.p0[1];
6385
+ const dlen = Math.hypot(dx, dy) || 1;
6386
+ dx /= dlen;
6387
+ dy /= dlen;
6388
+ const mx = 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0];
6389
+ const my = 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1];
6390
+ const projC1 = (bz.c1[0] - mx) * dx + (bz.c1[1] - my) * dy;
6391
+ const projC0 = (mx - bz.c0[0]) * dx + (my - bz.c0[1]) * dy;
6392
+ const reach = Math.max(8, (Math.abs(projC1) + Math.abs(projC0)) / 2);
6393
+ return { vx: dx * reach, vy: dy * reach };
6394
+ };
6395
+ const positionAtMidTangent = (side) => (_d2, finalMatrix, target) => {
6396
+ const bz = ensureBezier(target);
6397
+ const [mx, my] = bezierMidpoint(bz);
6398
+ const { vx, vy } = midTangentVector(bz);
6399
+ const px = mx + side * vx;
6400
+ const py = my + side * vy;
6401
+ return scaleLocalToScreen(target, new fabric__namespace.Point(px - halfWOf(target), py - halfHOf(target))).transform(finalMatrix);
6402
+ };
6403
+ const makeMidTangentDrag = (side) => (_e2, transform, x, y) => {
6404
+ var _a2;
6405
+ const target = transform.target;
6406
+ const local = localPointFromCanvas(target, x, y);
6407
+ const current = ensureBezier(target);
6408
+ const bz = {
6409
+ p0: [...current.p0],
6410
+ c0: [...current.c0],
6411
+ c1: [...current.c1],
6412
+ p1: [...current.p1]
6413
+ };
6414
+ const [mx, my] = bezierMidpoint(bz);
6415
+ const oldV = midTangentVector(bz);
6416
+ const nvx = (local.x - mx) * side;
6417
+ const nvy = (local.y - my) * side;
6418
+ const oldLen2 = oldV.vx * oldV.vx + oldV.vy * oldV.vy;
6419
+ if (oldLen2 < 1e-6) return true;
6420
+ const a = (oldV.vx * nvx + oldV.vy * nvy) / oldLen2;
6421
+ const b = (oldV.vx * nvy - oldV.vy * nvx) / oldLen2;
6422
+ const apply = (px, py) => {
6423
+ const rx = px - mx;
6424
+ const ry = py - my;
6425
+ return [mx + a * rx - b * ry, my + b * rx + a * ry];
6426
+ };
6427
+ bz.c0 = apply(bz.c0[0], bz.c0[1]);
6428
+ bz.c1 = apply(bz.c1[0], bz.c1[1]);
6429
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6430
+ target.dirty = true;
6431
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6432
+ return true;
6433
+ };
6434
+ const makeDrag = (key) => (_e2, transform, x, y) => {
6435
+ var _a2;
6436
+ const target = transform.target;
6437
+ const local = localPointFromCanvas(target, x, y);
6438
+ const current = ensureBezier(target);
6439
+ const bz = { p0: [...current.p0], c0: [...current.c0], c1: [...current.c1], p1: [...current.p1] };
6440
+ if (key === "p0" || key === "p1") {
6441
+ const ctrlKey = key === "p0" ? "c0" : "c1";
6442
+ const oldAnchor = bz[key];
6443
+ const oldCtrl = bz[ctrlKey];
6444
+ const dx = local.x - oldAnchor[0];
6445
+ const dy = local.y - oldAnchor[1];
6446
+ bz[key] = [local.x, local.y];
6447
+ bz[ctrlKey] = [oldCtrl[0] + dx, oldCtrl[1] + dy];
6448
+ } else {
6449
+ bz[key] = [local.x, local.y];
6450
+ }
6451
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6452
+ target.dirty = true;
6453
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6454
+ return true;
6455
+ };
6456
+ const makeMidDrag = () => (_e2, transform, x, y) => {
6457
+ var _a2;
6458
+ const target = transform.target;
6459
+ const local = localPointFromCanvas(target, x, y);
6460
+ const current = ensureBezier(target);
6461
+ const bz = {
6462
+ p0: [...current.p0],
6463
+ c0: [...current.c0],
6464
+ c1: [...current.c1],
6465
+ p1: [...current.p1]
6466
+ };
6467
+ const [mx, my] = bezierMidpoint(bz);
6468
+ const dx = (local.x - mx) / 0.75;
6469
+ const dy = (local.y - my) / 0.75;
6470
+ bz.c0 = [bz.c0[0] + dx, bz.c0[1] + dy];
6471
+ bz.c1 = [bz.c1[0] + dx, bz.c1[1] + dy];
6472
+ target.textPath = { ...target.textPath || { preset: "custom" }, bezier: bz };
6473
+ target.dirty = true;
6474
+ (_a2 = target.canvas) == null ? void 0 : _a2.requestRenderAll();
6475
+ return true;
6476
+ };
6477
+ const renderAnchor = (ctx, left, top) => {
6478
+ ctx.save();
6479
+ ctx.translate(left, top);
6480
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6481
+ ctx.shadowBlur = 2;
6482
+ ctx.shadowOffsetY = 0.5;
6483
+ ctx.fillStyle = "#ffffff";
6484
+ ctx.strokeStyle = "#1d9bf0";
6485
+ ctx.lineWidth = 1.5;
6486
+ ctx.beginPath();
6487
+ ctx.arc(0, 0, 5, 0, Math.PI * 2);
6488
+ ctx.fill();
6489
+ ctx.shadowColor = "transparent";
6490
+ ctx.stroke();
6491
+ ctx.restore();
6492
+ };
6493
+ const renderControlHandle = (ctx, left, top) => {
6494
+ ctx.save();
6495
+ ctx.translate(left, top);
6496
+ ctx.shadowColor = "rgba(0,0,0,0.25)";
6497
+ ctx.shadowBlur = 2;
6498
+ ctx.shadowOffsetY = 0.5;
6499
+ ctx.fillStyle = "#1d9bf0";
6500
+ ctx.strokeStyle = "#ffffff";
6501
+ ctx.lineWidth = 1.5;
6502
+ ctx.beginPath();
6503
+ ctx.arc(0, 0, 4, 0, Math.PI * 2);
6504
+ ctx.fill();
6505
+ ctx.shadowColor = "transparent";
6506
+ ctx.stroke();
6507
+ ctx.restore();
6508
+ };
6509
+ obj.controls.bzP0 = new fabric__namespace.Control({ x: -0.5, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("p0"), positionHandler: positionAtBezier("p0"), render: renderAnchor });
6510
+ obj.controls.bzP1 = new fabric__namespace.Control({ x: 0.5, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("p1"), positionHandler: positionAtBezier("p1"), render: renderAnchor });
6511
+ obj.controls.bzC0 = new fabric__namespace.Control({ x: -0.25, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("c0"), positionHandler: positionAtBezier("c0"), render: renderControlHandle });
6512
+ obj.controls.bzC1 = new fabric__namespace.Control({ x: 0.25, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeDrag("c1"), positionHandler: positionAtBezier("c1"), render: renderControlHandle });
6513
+ obj.controls.bzMid = new fabric__namespace.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidDrag(), positionHandler: positionAtMid, render: renderAnchor });
6514
+ obj.controls.bzMidT0 = new fabric__namespace.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidTangentDrag(-1), positionHandler: positionAtMidTangent(-1), render: renderControlHandle });
6515
+ obj.controls.bzMidT1 = new fabric__namespace.Control({ x: 0, y: 0, cursorStyle: "move", actionName: "textPath", actionHandler: makeMidTangentDrag(1), positionHandler: positionAtMidTangent(1), render: renderControlHandle });
6516
+ (_l = obj.setControlVisible) == null ? void 0 : _l.call(obj, "bzP0", true);
6517
+ (_m = obj.setControlVisible) == null ? void 0 : _m.call(obj, "bzC0", true);
6518
+ (_n = obj.setControlVisible) == null ? void 0 : _n.call(obj, "bzC1", true);
6519
+ (_o = obj.setControlVisible) == null ? void 0 : _o.call(obj, "bzP1", true);
6520
+ (_p = obj.setControlVisible) == null ? void 0 : _p.call(obj, "bzMid", true);
6521
+ (_q = obj.setControlVisible) == null ? void 0 : _q.call(obj, "bzMidT0", true);
6522
+ (_r = obj.setControlVisible) == null ? void 0 : _r.call(obj, "bzMidT1", true);
6523
+ addRotationAndPivot();
6524
+ obj.hasBorders = false;
6525
+ obj.hasControls = true;
6526
+ obj.__pdTextPathControls = true;
6527
+ obj.setCoords();
6528
+ }
6529
+ function computeWarpBoundsLocal(obj) {
6530
+ const resolved = resolveTextPath(obj.textPath, obj.width || 0, obj.fontSize || 16);
6531
+ const path = resolved ? measurePath(resolved.d) : null;
6532
+ if (!path) return null;
6533
+ const len = path.getTotalLength();
6534
+ if (!Number.isFinite(len) || len <= 0) return null;
6535
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
6536
+ const steps = 96;
6537
+ for (let i = 0; i <= steps; i++) {
6538
+ const p = path.getPointAtLength(len * i / steps);
6539
+ if (p.x < minX) minX = p.x;
6540
+ if (p.x > maxX) maxX = p.x;
6541
+ if (p.y < minY) minY = p.y;
6542
+ if (p.y > maxY) maxY = p.y;
6543
+ }
6544
+ const fs = obj.fontSize || 16;
6545
+ const halfW = (obj.width || 0) / 2;
6546
+ const halfH = (obj.height || 0) / 2;
6547
+ return {
6548
+ minX: minX - fs * 0.2 - halfW,
6549
+ maxX: maxX + fs * 0.2 - halfW,
6550
+ minY: minY - fs * 1.05 - halfH,
6551
+ maxY: maxY + fs * 0.35 - halfH
6552
+ };
6553
+ }
6554
+ function computeCircleWarpBoundsLocal(obj) {
6555
+ var _a;
6556
+ if (((_a = obj.textPath) == null ? void 0 : _a.preset) !== "circle") return null;
6557
+ const fs = obj.fontSize || 16;
6558
+ const w = obj.width || 0;
6559
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6560
+ const rRaw = obj.textPath.radius;
6561
+ const r = Math.max(fs * 0.75, Number.isFinite(rRaw) && rRaw > 0 ? rRaw : auto);
6562
+ const halfW = w / 2;
6563
+ const halfH = (obj.height || 0) / 2;
6564
+ const cx = r - halfW;
6565
+ const cy = r - halfH;
6566
+ 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 };
6567
+ }
6568
+ function getTextPathHitBounds(obj) {
6569
+ var _a;
6570
+ return ((_a = obj.textPath) == null ? void 0 : _a.preset) === "circle" ? computeCircleWarpBoundsLocal(obj) : computeWarpBoundsLocal(obj);
6571
+ }
6572
+ function textPathBoundsContainScenePoint(obj, point) {
6573
+ const bounds = getTextPathHitBounds(obj);
6574
+ if (!bounds) return false;
6575
+ try {
6576
+ const inv = fabric__namespace.util.invertTransform(obj.calcTransformMatrix());
6577
+ const local = fabric__namespace.util.transformPoint(point, inv);
6578
+ return local.x >= bounds.minX && local.x <= bounds.maxX && local.y >= bounds.minY && local.y <= bounds.maxY;
6579
+ } catch {
6580
+ return false;
6581
+ }
6582
+ }
6583
+ function drawTextPathBounds(ctx, obj, bounds) {
6584
+ var _a;
6585
+ if (!obj.canvas) return;
6586
+ const retina = ((_a = obj.getCanvasRetinaScaling) == null ? void 0 : _a.call(obj)) || 1;
6587
+ const matrix = fabric__namespace.util.multiplyTransformMatrices(obj.canvas.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
6588
+ const corners = [
6589
+ new fabric__namespace.Point(bounds.minX, bounds.minY),
6590
+ new fabric__namespace.Point(bounds.maxX, bounds.minY),
6591
+ new fabric__namespace.Point(bounds.maxX, bounds.maxY),
6592
+ new fabric__namespace.Point(bounds.minX, bounds.maxY)
6593
+ ].map((p) => fabric__namespace.util.transformPoint(p, matrix));
6594
+ ctx.save();
6595
+ ctx.setTransform(retina, 0, 0, retina, 0, 0);
6596
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.9)";
6597
+ ctx.lineWidth = 1.25;
6598
+ ctx.setLineDash([5, 4]);
6599
+ ctx.beginPath();
6600
+ ctx.moveTo(corners[0].x, corners[0].y);
6601
+ for (let i = 1; i < corners.length; i++) ctx.lineTo(corners[i].x, corners[i].y);
6602
+ ctx.closePath();
6603
+ ctx.stroke();
6604
+ ctx.restore();
6605
+ }
6606
+ if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
6607
+ let drawWarpGuides = function(ctx) {
6608
+ var _a, _b, _c, _d, _e, _f, _g;
6609
+ if (!this.canvas) return;
6610
+ const hoverBounds = getTextPathHitBounds(this);
6611
+ if (hoverBounds && (this.__pdTextPathHovered || ((_b = (_a = this.canvas).getActiveObject) == null ? void 0 : _b.call(_a)) === this)) {
6612
+ drawTextPathBounds(ctx, this, hoverBounds);
6613
+ }
6614
+ const resolved = resolveTextPath(this.textPath, this.width || 0, this.fontSize || 16);
6615
+ const path = resolved ? measurePath(resolved.d) : null;
6616
+ if (!path) return;
6617
+ const len = path.getTotalLength();
6618
+ if (!Number.isFinite(len) || len <= 0) return;
6619
+ const retina = ((_c = this.getCanvasRetinaScaling) == null ? void 0 : _c.call(this)) || 1;
6620
+ const matrix = fabric__namespace.util.multiplyTransformMatrices(
6621
+ this.canvas.viewportTransform || [1, 0, 0, 1, 0, 0],
6622
+ this.calcTransformMatrix()
6623
+ );
6624
+ const halfW = (this.width || 0) / 2;
6625
+ const halfH = (this.height || 0) / 2;
6626
+ const toScreen = (t) => {
6627
+ const p = path.getPointAtLength(Math.max(0, Math.min(len, len * t)));
6628
+ return fabric__namespace.util.transformPoint(new fabric__namespace.Point(p.x - halfW, p.y - halfH), matrix);
6629
+ };
6630
+ const toScreenXY = (x, y) => fabric__namespace.util.transformPoint(new fabric__namespace.Point(x, y), matrix);
6631
+ ctx.save();
6632
+ ctx.setTransform(retina, 0, 0, retina, 0, 0);
6633
+ const isCircle = ((_d = this.textPath) == null ? void 0 : _d.preset) === "circle";
6634
+ const isAngle = ((_e = this.textPath) == null ? void 0 : _e.preset) === "rise" || ((_f = this.textPath) == null ? void 0 : _f.preset) === "angle";
6635
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.95)";
6636
+ ctx.lineWidth = 1.25;
6637
+ ctx.setLineDash([]);
6638
+ if (isAngle) {
6639
+ const tp = this.textPath;
6640
+ const w = this.width || 0;
6641
+ const fs = this.fontSize || 16;
6642
+ const ep = tp.endpoints;
6643
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : fs * 1.5;
6644
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : fs * 0.5;
6645
+ const a = toScreenXY(0 - halfW, leftY - halfH);
6646
+ const b = toScreenXY(w - halfW, rightY - halfH);
6647
+ ctx.beginPath();
6648
+ ctx.moveTo(a.x, a.y);
6649
+ ctx.lineTo(b.x, b.y);
6650
+ ctx.stroke();
6651
+ } else {
6652
+ ctx.beginPath();
6653
+ for (let i = 0; i <= 96; i++) {
6654
+ const p = toScreen(i / 96);
6655
+ if (i === 0) ctx.moveTo(p.x, p.y);
6656
+ else ctx.lineTo(p.x, p.y);
6657
+ }
6658
+ ctx.stroke();
6659
+ }
6660
+ if (isCircle) {
6661
+ const tp = this.textPath || {};
6662
+ const fs = this.fontSize || 16;
6663
+ const w = this.width || 0;
6664
+ const auto = Math.max(fs * 1.5, w / Math.PI);
6665
+ const rRaw = tp.radius;
6666
+ const r = Math.max(fs * 0.75, Number.isFinite(rRaw) && rRaw > 0 ? rRaw : auto);
6667
+ const cx = r - halfW;
6668
+ const cy = r - halfH;
6669
+ const steps = 96;
6670
+ ctx.beginPath();
6671
+ for (let i = 0; i <= steps; i++) {
6672
+ const a = i / steps * Math.PI * 2;
6673
+ const p = toScreenXY(cx + Math.cos(a) * r, cy + Math.sin(a) * r);
6674
+ if (i === 0) ctx.moveTo(p.x, p.y);
6675
+ else ctx.lineTo(p.x, p.y);
6676
+ }
6677
+ ctx.closePath();
6678
+ ctx.stroke();
6679
+ }
6680
+ const bz = isCircle ? null : (_g = this.textPath) == null ? void 0 : _g.bezier;
6681
+ if (bz) {
6682
+ const a = toScreenXY(bz.p0[0] - halfW, bz.p0[1] - halfH);
6683
+ const ah = toScreenXY(bz.c0[0] - halfW, bz.c0[1] - halfH);
6684
+ const b = toScreenXY(bz.p1[0] - halfW, bz.p1[1] - halfH);
6685
+ const bh = toScreenXY(bz.c1[0] - halfW, bz.c1[1] - halfH);
6686
+ ctx.strokeStyle = "rgba(29, 155, 240, 0.7)";
6687
+ ctx.lineWidth = 1;
6688
+ ctx.setLineDash([]);
6689
+ ctx.beginPath();
6690
+ ctx.moveTo(a.x, a.y);
6691
+ ctx.lineTo(ah.x, ah.y);
6692
+ ctx.moveTo(b.x, b.y);
6693
+ ctx.lineTo(bh.x, bh.y);
6694
+ ctx.stroke();
6695
+ const midX = 0.125 * bz.p0[0] + 0.375 * bz.c0[0] + 0.375 * bz.c1[0] + 0.125 * bz.p1[0];
6696
+ const midY = 0.125 * bz.p0[1] + 0.375 * bz.c0[1] + 0.375 * bz.c1[1] + 0.125 * bz.p1[1];
6697
+ let tdx = bz.p1[0] + 3 * bz.c1[0] - 3 * bz.c0[0] - bz.p0[0];
6698
+ let tdy = bz.p1[1] + 3 * bz.c1[1] - 3 * bz.c0[1] - bz.p0[1];
6699
+ const tlen = Math.hypot(tdx, tdy) || 1;
6700
+ tdx /= tlen;
6701
+ tdy /= tlen;
6702
+ const pC1 = (bz.c1[0] - midX) * tdx + (bz.c1[1] - midY) * tdy;
6703
+ const pC0 = (midX - bz.c0[0]) * tdx + (midY - bz.c0[1]) * tdy;
6704
+ const LEG = Math.max(8, (Math.abs(pC1) + Math.abs(pC0)) / 2);
6705
+ const m = toScreenXY(midX - halfW, midY - halfH);
6706
+ const t0 = toScreenXY(midX - tdx * LEG - halfW, midY - tdy * LEG - halfH);
6707
+ const t1 = toScreenXY(midX + tdx * LEG - halfW, midY + tdy * LEG - halfH);
6708
+ ctx.beginPath();
6709
+ ctx.moveTo(m.x, m.y);
6710
+ ctx.lineTo(t0.x, t0.y);
6711
+ ctx.moveTo(m.x, m.y);
6712
+ ctx.lineTo(t1.x, t1.y);
6713
+ ctx.stroke();
6714
+ }
6715
+ ctx.restore();
6716
+ };
6717
+ TextboxProto.__pixldocsOrigRenderControls = TextboxProto._renderControls;
6718
+ TextboxProto._renderControls = function(ctx, styleOverride) {
6719
+ if (hasActiveTextPath(this)) {
6720
+ const prevBorders = this.hasBorders;
6721
+ this.hasBorders = false;
6722
+ try {
6723
+ drawWarpGuides.call(this, ctx);
6724
+ TextboxProto.__pixldocsOrigRenderControls.call(this, ctx, styleOverride);
6725
+ } finally {
6726
+ this.hasBorders = prevBorders;
6727
+ }
6728
+ return;
6729
+ }
6730
+ TextboxProto.__pixldocsOrigRenderControls.call(this, ctx, styleOverride);
6731
+ if (!this.canvas) return;
6732
+ };
6733
+ }
6734
+ if (!TextboxProto.__pixldocsOrigRender && typeof TextboxProto._render === "function") {
6735
+ TextboxProto.__pixldocsOrigRender = TextboxProto._render;
6736
+ TextboxProto._render = function(ctx) {
6737
+ const tp = this.textPath;
6738
+ const orig = TextboxProto.__pixldocsOrigRender;
6739
+ if (!hasActiveTextPath(this)) {
6740
+ return orig.call(this, ctx);
6741
+ }
6742
+ if (tp && (tp.preset === "rise" || tp.preset === "angle")) {
6743
+ const w = this.width || 0;
6744
+ const h = this.height || 0;
6745
+ const fs2 = this.fontSize || 16;
6746
+ const ep = tp.endpoints;
6747
+ const defaultLeftY = fs2 * 1.5;
6748
+ const defaultRightY = fs2 * 0.5;
6749
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
6750
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
6751
+ const slope = w > 0 ? (rightY - leftY) / w : 0;
6752
+ const dy = (leftY + rightY) / 2 - h / 2;
6753
+ ctx.save();
6754
+ ctx.transform(1, slope, 0, 1, 0, dy);
6755
+ orig.call(this, ctx);
6756
+ ctx.restore();
6757
+ return;
6758
+ }
6759
+ const resolved = resolveTextPath(tp, this.width || 0, this.fontSize || 16);
6760
+ if (!resolved) return orig.call(this, ctx);
6761
+ const pathEl = measurePath(resolved.d);
6762
+ if (!pathEl) return orig.call(this, ctx);
6763
+ const totalLen = pathEl.getTotalLength();
6764
+ if (!Number.isFinite(totalLen) || totalLen <= 0) return orig.call(this, ctx);
6765
+ const plain = String(this.text || "").replace(/\n/g, " ");
6766
+ if (!plain) return;
6767
+ const weight = this.fontWeight || 400;
6768
+ const style = this.fontStyle || "normal";
6769
+ const family = this.fontFamily || "Open Sans";
6770
+ const fs = this.fontSize || 16;
6771
+ ctx.save();
6772
+ ctx.font = `${style} ${weight} ${fs}px "${family}"`;
6773
+ ctx.textAlign = "center";
6774
+ ctx.textBaseline = "alphabetic";
6775
+ const chars = Array.from(plain);
6776
+ const spacing = (this.charSpacing || 0) * fs / 1e3;
6777
+ const widths = chars.map((c) => ctx.measureText(c).width + spacing);
6778
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
6779
+ const baselineDy = resolveTextPathAlphabeticBaselineDy(ctx, plain, fs);
6780
+ let cursor;
6781
+ if (this.textAlign === "center") cursor = Math.max(0, (totalLen - totalWidth) / 2);
6782
+ else if (this.textAlign === "right") cursor = Math.max(0, totalLen - totalWidth);
6783
+ else cursor = 0;
6784
+ const flowStops = getGradientStopsForFlow(this);
6785
+ const fillOffsets = flowStops ? { offsetX: 0, offsetY: 0 } : applyWarpFillStyle(ctx, this);
6786
+ const halfW = (this.width || 0) / 2;
6787
+ const halfH = (this.height || 0) / 2;
6788
+ const strokeColor = this.stroke;
6789
+ const strokeWidth = Number(this.strokeWidth) || 0;
6790
+ const hasStroke = !!strokeColor && strokeColor !== "transparent" && strokeWidth > 0;
6791
+ if (hasStroke) {
6792
+ ctx.strokeStyle = String(strokeColor);
6793
+ ctx.lineWidth = strokeWidth;
6794
+ ctx.lineJoin = "round";
6795
+ ctx.miterLimit = 4;
6796
+ }
6797
+ for (let i = 0; i < chars.length; i++) {
6798
+ const ch = chars[i];
6799
+ const cw = widths[i];
6800
+ const mid = cursor + cw / 2;
6801
+ cursor += cw;
6802
+ if (mid > totalLen) break;
6803
+ const p = pathEl.getPointAtLength(Math.max(0, mid));
6804
+ const ahead = pathEl.getPointAtLength(Math.min(totalLen, mid + 0.5));
6805
+ const angle = Math.atan2(ahead.y - p.y, ahead.x - p.x);
6806
+ if (flowStops) {
6807
+ const tt = chars.length > 1 ? i / (chars.length - 1) : 0;
6808
+ ctx.fillStyle = sampleGradientColor(flowStops, tt);
6809
+ }
6810
+ ctx.save();
6811
+ ctx.translate(p.x - halfW - fillOffsets.offsetX, p.y - halfH - fillOffsets.offsetY);
6812
+ ctx.rotate(angle);
6813
+ ctx.fillText(ch, 0, baselineDy);
6814
+ if (hasStroke) ctx.strokeText(ch, 0, baselineDy);
6815
+ ctx.restore();
6816
+ }
6817
+ ctx.restore();
6818
+ };
5762
6819
  }
5763
6820
  TextboxProto.__pixldocsTextboxExtended = true;
6821
+ const TextboxProtoHit = fabric__namespace.Textbox.prototype;
6822
+ if (!TextboxProtoHit.__pixldocsOrigGetCoords && typeof TextboxProtoHit.getCoords === "function") {
6823
+ TextboxProtoHit.__pixldocsOrigGetCoords = TextboxProtoHit.getCoords;
6824
+ TextboxProtoHit.getCoords = function() {
6825
+ if (!hasActiveTextPath(this)) return TextboxProtoHit.__pixldocsOrigGetCoords.call(this);
6826
+ const bounds = getTextPathHitBounds(this);
6827
+ if (!bounds) return TextboxProtoHit.__pixldocsOrigGetCoords.call(this);
6828
+ const matrix = this.calcTransformMatrix();
6829
+ const coords = [
6830
+ new fabric__namespace.Point(bounds.minX, bounds.minY),
6831
+ new fabric__namespace.Point(bounds.maxX, bounds.minY),
6832
+ new fabric__namespace.Point(bounds.maxX, bounds.maxY),
6833
+ new fabric__namespace.Point(bounds.minX, bounds.maxY)
6834
+ ].map((p) => fabric__namespace.util.transformPoint(p, matrix));
6835
+ if (this.group) {
6836
+ const groupMatrix = this.group.calcTransformMatrix();
6837
+ return coords.map((p) => fabric__namespace.util.transformPoint(p, groupMatrix));
6838
+ }
6839
+ return coords;
6840
+ };
6841
+ }
6842
+ if (!TextboxProtoHit.__pixldocsOrigContainsPoint && typeof TextboxProtoHit.containsPoint === "function") {
6843
+ TextboxProtoHit.__pixldocsOrigContainsPoint = TextboxProtoHit.containsPoint;
6844
+ TextboxProtoHit.containsPoint = function(point) {
6845
+ const origHit = TextboxProtoHit.__pixldocsOrigContainsPoint.call(this, point);
6846
+ if (origHit) return true;
6847
+ if (!hasActiveTextPath(this)) return false;
6848
+ return textPathBoundsContainScenePoint(this, point);
6849
+ };
6850
+ }
5764
6851
  const PD_BG_KEY = "__pdBg";
5765
6852
  const PATCHED_KEY = "__pdBgPatched";
6853
+ const LINE_SHADOW_STROKE_WIDTH = 0.5;
5766
6854
  function extractTextBgConfig(element) {
5767
6855
  const legacy = Math.max(0, Number(element.textBgPadding ?? 0)) || 0;
5768
6856
  const pick = (v) => {
5769
6857
  const n = Number(v);
5770
6858
  return Number.isFinite(n) && n >= 0 ? n : void 0;
5771
6859
  };
6860
+ const grad = element.textBgGradient;
5772
6861
  return {
5773
6862
  color: element.textBgColor,
6863
+ gradient: isGradientConfig(grad) && grad.stops && grad.stops.length >= 2 ? grad : void 0,
5774
6864
  padTop: pick(element.textBgPaddingTop) ?? legacy,
5775
6865
  padRight: pick(element.textBgPaddingRight) ?? legacy,
5776
6866
  padBottom: pick(element.textBgPaddingBottom) ?? legacy,
@@ -5786,11 +6876,16 @@ function extractTextBgConfig(element) {
5786
6876
  })(),
5787
6877
  shadowAffectsBg: element.textShadowAffectsBg !== false,
5788
6878
  shadowAffectsText: element.textShadowAffectsText !== false,
5789
- fitToText: element.textBgFitToText === true
6879
+ fitToText: element.textBgFitToText === true,
6880
+ shadowType: element.textShadowType,
6881
+ shadowColor: element.textShadowColor,
6882
+ shadowOffsetX: Number(element.textShadowOffsetX ?? 0) || 0,
6883
+ shadowOffsetY: Number(element.textShadowOffsetY ?? 0) || 0
5790
6884
  };
5791
6885
  }
5792
6886
  function hasTextBackground(cfg) {
5793
6887
  if (!cfg) return false;
6888
+ if (cfg.gradient && cfg.gradient.stops && cfg.gradient.stops.length >= 2) return true;
5794
6889
  const c = (cfg.color || "").toString().trim().toLowerCase();
5795
6890
  return !!c && c !== "transparent" && c !== "none" && c !== "rgba(0,0,0,0)";
5796
6891
  }
@@ -5800,6 +6895,8 @@ function buildTextShadow(element) {
5800
6895
  const ox = Number(element.textShadowOffsetX ?? 0);
5801
6896
  const oy = Number(element.textShadowOffsetY ?? 0);
5802
6897
  if (!color || color === "transparent") return null;
6898
+ const type = element.textShadowType;
6899
+ if (type && type !== "drop") return null;
5803
6900
  return new fabric__namespace.Shadow({
5804
6901
  color: String(color),
5805
6902
  blur: blur || 0,
@@ -5830,11 +6927,35 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
5830
6927
  function applyTextBackground(obj, cfg) {
5831
6928
  var _a;
5832
6929
  obj[PD_BG_KEY] = { ...cfg };
6930
+ try {
6931
+ const hasOffset = (Number(cfg == null ? void 0 : cfg.shadowOffsetX) || 0) !== 0 || (Number(cfg == null ? void 0 : cfg.shadowOffsetY) || 0) !== 0;
6932
+ const isLineOrBlock = (cfg == null ? void 0 : cfg.shadowType) === "line" || (cfg == null ? void 0 : cfg.shadowType) === "block";
6933
+ if (isLineOrBlock && hasOffset && !!(cfg == null ? void 0 : cfg.shadowColor) && cfg.shadowColor !== "transparent") {
6934
+ obj.objectCaching = false;
6935
+ obj.dirty = true;
6936
+ }
6937
+ } catch {
6938
+ }
5833
6939
  if (obj[PATCHED_KEY]) return;
5834
6940
  obj[PATCHED_KEY] = true;
5835
6941
  const originalRender = obj._render.bind(obj);
5836
6942
  obj._render = function(ctx) {
5837
6943
  const bg = this[PD_BG_KEY];
6944
+ const extOX = Number((bg == null ? void 0 : bg.shadowOffsetX) ?? 0) || 0;
6945
+ const extOY = Number((bg == null ? void 0 : bg.shadowOffsetY) ?? 0) || 0;
6946
+ const extDist = Math.hypot(extOX, extOY);
6947
+ const hasShadowColor = !!(bg == null ? void 0 : bg.shadowColor) && bg.shadowColor !== "transparent";
6948
+ const blockShadowActive = !!bg && bg.shadowType === "block" && hasShadowColor && extDist > 0;
6949
+ const lineShadowActive = !!bg && bg.shadowType === "line" && hasShadowColor && extDist > 0;
6950
+ let blockSteps = 0;
6951
+ let blockStepX = 0;
6952
+ let blockStepY = 0;
6953
+ if (blockShadowActive) {
6954
+ const STEP = 0.5;
6955
+ blockSteps = Math.min(600, Math.max(1, Math.ceil(extDist / STEP)));
6956
+ blockStepX = extOX / blockSteps;
6957
+ blockStepY = extOY / blockSteps;
6958
+ }
5838
6959
  if (hasTextBackground(bg)) {
5839
6960
  const w = this.width ?? 0;
5840
6961
  const h = this.height ?? 0;
@@ -5852,24 +6973,172 @@ function applyTextBackground(obj, cfg) {
5852
6973
  }
5853
6974
  const op = typeof bg.opacity === "number" ? Math.max(0, Math.min(1, bg.opacity)) : 1;
5854
6975
  if (op < 1) ctx.globalAlpha = (ctx.globalAlpha ?? 1) * op;
5855
- ctx.fillStyle = bg.color;
5856
- const rects = computeBgRects(this, w, h, pT, pR, pB, pL, !!bg.fitToText);
5857
- for (const r of rects) {
5858
- buildRoundedRectPath2D(
5859
- ctx,
5860
- r.x,
5861
- r.y,
5862
- r.w,
5863
- r.h,
5864
- bg.rxTL ?? 0,
5865
- bg.rxTR ?? 0,
5866
- bg.rxBR ?? 0,
5867
- bg.rxBL ?? 0
5868
- );
5869
- ctx.fill();
6976
+ const tpAny = this.textPath;
6977
+ const isShear = tpAny && (tpAny.preset === "rise" || tpAny.preset === "angle");
6978
+ const ribbonD = !isShear ? buildWarpRibbonD(
6979
+ this,
6980
+ pT,
6981
+ pR,
6982
+ pB,
6983
+ pL,
6984
+ bg.rxTL ?? 0,
6985
+ bg.rxTR ?? 0,
6986
+ bg.rxBR ?? 0,
6987
+ bg.rxBL ?? 0
6988
+ ) : null;
6989
+ let bgBounds;
6990
+ let bgRectsForFill = null;
6991
+ if (ribbonD) {
6992
+ bgBounds = computeRibbonBoundsFor(this, pT, pR, pB, pL);
6993
+ } else {
6994
+ bgRectsForFill = computeBgRects(this, w, h, pT, pR, pB, pL, !!bg.fitToText);
6995
+ bgBounds = unionBounds(bgRectsForFill);
6996
+ }
6997
+ if (blockShadowActive && bg.shadowAffectsBg !== false) {
6998
+ ctx.save();
6999
+ ctx.fillStyle = bg.shadowColor;
7000
+ for (let i = blockSteps; i >= 1; i--) {
7001
+ ctx.save();
7002
+ ctx.translate(blockStepX * i, blockStepY * i);
7003
+ if (ribbonD) {
7004
+ try {
7005
+ ctx.fill(new Path2D(ribbonD));
7006
+ } catch {
7007
+ }
7008
+ } else {
7009
+ const rects = bgRectsForFill;
7010
+ for (const r of rects) {
7011
+ buildRoundedRectPath2D(
7012
+ ctx,
7013
+ r.x,
7014
+ r.y,
7015
+ r.w,
7016
+ r.h,
7017
+ bg.rxTL ?? 0,
7018
+ bg.rxTR ?? 0,
7019
+ bg.rxBR ?? 0,
7020
+ bg.rxBL ?? 0
7021
+ );
7022
+ ctx.fill();
7023
+ }
7024
+ }
7025
+ ctx.restore();
7026
+ }
7027
+ ctx.restore();
7028
+ }
7029
+ if (lineShadowActive && bg.shadowAffectsBg !== false) {
7030
+ ctx.save();
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 (isShear) {
7068
+ const fs = this.fontSize || 16;
7069
+ const ep = tpAny.endpoints;
7070
+ const dL = fs * 1.5;
7071
+ const dR = fs * 0.5;
7072
+ const lY = ep && Number.isFinite(ep.leftY) ? ep.leftY : dL;
7073
+ const rY = ep && Number.isFinite(ep.rightY) ? ep.rightY : dR;
7074
+ const slope = w > 0 ? (rY - lY) / w : 0;
7075
+ const dy = (lY + rY) / 2 - h / 2;
7076
+ ctx.transform(1, slope, 0, 1, 0, dy);
7077
+ }
7078
+ const rects = bgRectsForFill;
7079
+ for (const r of rects) {
7080
+ buildRoundedRectPath2D(
7081
+ ctx,
7082
+ r.x,
7083
+ r.y,
7084
+ r.w,
7085
+ r.h,
7086
+ bg.rxTL ?? 0,
7087
+ bg.rxTR ?? 0,
7088
+ bg.rxBR ?? 0,
7089
+ bg.rxBL ?? 0
7090
+ );
7091
+ ctx.fill();
7092
+ }
5870
7093
  }
5871
7094
  ctx.restore();
5872
7095
  }
7096
+ if (blockShadowActive && bg.shadowAffectsText !== false) {
7097
+ const self = this;
7098
+ const origFill = self.fill;
7099
+ const origStyles = self.styles;
7100
+ const origShadow = self.shadow;
7101
+ try {
7102
+ self.fill = bg.shadowColor;
7103
+ self.styles = {};
7104
+ self.shadow = null;
7105
+ for (let i = blockSteps; i >= 1; i--) {
7106
+ ctx.save();
7107
+ ctx.translate(blockStepX * i, blockStepY * i);
7108
+ originalRender(ctx);
7109
+ ctx.restore();
7110
+ }
7111
+ } finally {
7112
+ self.fill = origFill;
7113
+ self.styles = origStyles;
7114
+ self.shadow = origShadow;
7115
+ }
7116
+ }
7117
+ if (lineShadowActive && bg.shadowAffectsText !== false) {
7118
+ const self = this;
7119
+ const origFill = self.fill;
7120
+ const origStroke = self.stroke;
7121
+ const origStrokeWidth = self.strokeWidth;
7122
+ const origStyles = self.styles;
7123
+ const origShadow = self.shadow;
7124
+ try {
7125
+ self.fill = "transparent";
7126
+ self.stroke = bg.shadowColor;
7127
+ self.strokeWidth = LINE_SHADOW_STROKE_WIDTH;
7128
+ self.styles = {};
7129
+ self.shadow = null;
7130
+ ctx.save();
7131
+ ctx.translate(extOX, extOY);
7132
+ originalRender(ctx);
7133
+ ctx.restore();
7134
+ } finally {
7135
+ self.fill = origFill;
7136
+ self.stroke = origStroke;
7137
+ self.strokeWidth = origStrokeWidth;
7138
+ self.styles = origStyles;
7139
+ self.shadow = origShadow;
7140
+ }
7141
+ }
5873
7142
  const suppressShadowOnText = bg && bg.shadowAffectsText === false;
5874
7143
  if (suppressShadowOnText) {
5875
7144
  ctx.save();
@@ -5888,19 +7157,27 @@ function applyTextBackground(obj, cfg) {
5888
7157
  const out = originalToObject(propertiesToInclude);
5889
7158
  const bg = this[PD_BG_KEY];
5890
7159
  if (hasTextBackground(bg)) {
5891
- out.__pdBg = { ...bg };
7160
+ out.__pdBg = { ...bg, gradient: bg.gradient ? { ...bg.gradient, stops: bg.gradient.stops.map((s) => ({ ...s })) } : void 0 };
5892
7161
  }
5893
7162
  return out;
5894
7163
  };
5895
7164
  const originalToSVG = (_a = obj.toSVG) == null ? void 0 : _a.bind(obj);
5896
7165
  if (typeof originalToSVG === "function") {
5897
7166
  obj.toSVG = function(reviver) {
7167
+ var _a2, _b;
5898
7168
  let svg = originalToSVG(reviver);
5899
7169
  const bg = this[PD_BG_KEY];
5900
7170
  const shadow = this.shadow;
5901
7171
  const hasBg = hasTextBackground(bg);
5902
7172
  const hasShadow = !!shadow && !!shadow.color && shadow.color !== "transparent";
5903
- if (!hasBg && !hasShadow) return svg;
7173
+ const extOX = Number((bg == null ? void 0 : bg.shadowOffsetX) ?? 0) || 0;
7174
+ const extOY = Number((bg == null ? void 0 : bg.shadowOffsetY) ?? 0) || 0;
7175
+ const extDist = Math.hypot(extOX, extOY);
7176
+ const hasExtShadowColor = !!(bg == null ? void 0 : bg.shadowColor) && bg.shadowColor !== "transparent";
7177
+ const hasBlockShadow = !!bg && bg.shadowType === "block" && hasExtShadowColor && extDist > 0;
7178
+ const hasLineShadow = !!bg && bg.shadowType === "line" && hasExtShadowColor && extDist > 0;
7179
+ if (!hasBg && !hasShadow && !hasBlockShadow && !hasLineShadow) return svg;
7180
+ const hasActiveTextPath2 = !!(((_a2 = this.textPath) == null ? void 0 : _a2.preset) && this.textPath.preset !== "none");
5904
7181
  const w = this.width ?? 0;
5905
7182
  const h = this.height ?? 0;
5906
7183
  const pT = Math.max(0, Number((bg == null ? void 0 : bg.padTop) ?? 0));
@@ -5909,7 +7186,18 @@ function applyTextBackground(obj, cfg) {
5909
7186
  const pL = Math.max(0, Number((bg == null ? void 0 : bg.padLeft) ?? 0));
5910
7187
  const fit = !!(bg == null ? void 0 : bg.fitToText);
5911
7188
  const rects = computeBgRects(this, w, h, pT, pR, pB, pL, fit);
5912
- const bgD = rects.map((r) => buildRoundedRectPathD(
7189
+ const ribbonD = buildWarpRibbonD(
7190
+ this,
7191
+ pT,
7192
+ pR,
7193
+ pB,
7194
+ pL,
7195
+ (bg == null ? void 0 : bg.rxTL) ?? 0,
7196
+ (bg == null ? void 0 : bg.rxTR) ?? 0,
7197
+ (bg == null ? void 0 : bg.rxBR) ?? 0,
7198
+ (bg == null ? void 0 : bg.rxBL) ?? 0
7199
+ );
7200
+ const bgD = ribbonD ?? rects.map((r) => buildRoundedRectPathD(
5913
7201
  r.x,
5914
7202
  r.y,
5915
7203
  r.w,
@@ -5922,7 +7210,16 @@ function applyTextBackground(obj, cfg) {
5922
7210
  const bgFill = (bg == null ? void 0 : bg.color) || "";
5923
7211
  const bgOpacity = typeof (bg == null ? void 0 : bg.opacity) === "number" ? Math.max(0, Math.min(1, bg.opacity)) : 1;
5924
7212
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
5925
- const bgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(bgFill)}"${bgOpacityAttr} />` : "";
7213
+ let bgGradDefs = "";
7214
+ let bgFillAttr = escapeXmlAttr(bgFill);
7215
+ if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b = bg.gradient.stops) == null ? void 0 : _b.length) >= 2) {
7216
+ const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7217
+ const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7218
+ const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
7219
+ bgGradDefs = `<defs>${def}</defs>`;
7220
+ bgFillAttr = `url(#${gid})`;
7221
+ }
7222
+ const bgPath = hasBg ? `${bgGradDefs}<path class="__pdTextBgShape" d="${bgD}" fill="${bgFillAttr}"${bgOpacityAttr} />` : "";
5926
7223
  svg = svg.replace(/style="[^"]*filter:\s*url\([^)]+\)[^"]*"/i, "");
5927
7224
  svg = svg.replace(/<filter[\s\S]*?<\/filter>/gi, "");
5928
7225
  let bgShadowMarker = "";
@@ -5948,14 +7245,63 @@ function applyTextBackground(obj, cfg) {
5948
7245
  const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
5949
7246
  bgShadowMarker = wrapShadow(shadowBgPath);
5950
7247
  }
5951
- if ((bg == null ? void 0 : bg.shadowAffectsText) !== false) {
7248
+ if ((bg == null ? void 0 : bg.shadowAffectsText) !== false && !hasActiveTextPath2) {
5952
7249
  const inner = extractGInnerMarkup(svg);
5953
7250
  const recoloredText = recolorSvgFills(inner, shadowColor);
5954
7251
  if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
5955
7252
  }
5956
7253
  }
7254
+ let bgBlockShadowMarker = "";
7255
+ let textBlockShadowMarker = "";
7256
+ if (hasBlockShadow) {
7257
+ const STEP = 0.5;
7258
+ const steps = Math.min(600, Math.max(1, Math.ceil(extDist / STEP)));
7259
+ const stepX = extOX / steps;
7260
+ const stepY = extOY / steps;
7261
+ const shadowColor = String(bg.shadowColor);
7262
+ const fillObj = this.fill;
7263
+ const parentTextIsGradient = !!fillObj && typeof fillObj === "object" && (Array.isArray(fillObj.colorStops) || fillObj.type === "linear" || fillObj.type === "radial");
7264
+ const cloneOutlineAttr = parentTextIsGradient ? ' data-pd-outline-text="1"' : "";
7265
+ const buildCopies = (markup, cls) => {
7266
+ const parts = [];
7267
+ for (let i = steps; i >= 1; i--) {
7268
+ parts.push(`<g class="${cls}"${cloneOutlineAttr} transform="translate(${(stepX * i).toFixed(3)} ${(stepY * i).toFixed(3)})">${markup}</g>`);
7269
+ }
7270
+ return parts.join("");
7271
+ };
7272
+ if (hasBg && bg.shadowAffectsBg !== false) {
7273
+ const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7274
+ const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
7275
+ bgBlockShadowMarker = buildCopies(shadowBgPath, "__pdBgShadowClone");
7276
+ }
7277
+ if (bg.shadowAffectsText !== false) {
7278
+ const inner = extractGInnerMarkup(svg);
7279
+ const recoloredText = recolorSvgFills(inner, shadowColor);
7280
+ if (recoloredText) textBlockShadowMarker = buildCopies(recoloredText, "__pdTextShadowClone");
7281
+ }
7282
+ }
7283
+ let bgLineShadowMarker = "";
7284
+ let textLineShadowMarker = "";
7285
+ if (hasLineShadow) {
7286
+ const shadowColor = String(bg.shadowColor);
7287
+ const tx = extOX.toFixed(3);
7288
+ const ty = extOY.toFixed(3);
7289
+ const fillObjLine = this.fill;
7290
+ const parentTextIsGradientLine = !!fillObjLine && typeof fillObjLine === "object" && (Array.isArray(fillObjLine.colorStops) || fillObjLine.type === "linear" || fillObjLine.type === "radial");
7291
+ const lineOutlineAttr = parentTextIsGradientLine ? ' data-pd-outline-text="1"' : "";
7292
+ if (hasBg && bg.shadowAffectsBg !== false) {
7293
+ const outlineBg = `<path d="${bgD}" fill="none" stroke="${escapeXmlAttr(shadowColor)}" stroke-width="1" stroke-linejoin="round" />`;
7294
+ bgLineShadowMarker = `<g class="__pdBgShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlineBg}</g>`;
7295
+ }
7296
+ if (bg.shadowAffectsText !== false) {
7297
+ const inner = extractGInnerMarkup(svg);
7298
+ const strokeW = LINE_SHADOW_STROKE_WIDTH;
7299
+ const outlined = outlineTextSvgForLineShadow(inner, shadowColor, strokeW);
7300
+ if (outlined) textLineShadowMarker = `<g class="__pdTextShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlined}</g>`;
7301
+ }
7302
+ }
5957
7303
  const openTagMatch = svg.match(/^\s*<g\b[^>]*>/);
5958
- const inserted = bgShadowMarker + bgPath + textShadowMarker;
7304
+ const inserted = bgBlockShadowMarker + bgLineShadowMarker + bgShadowMarker + bgPath + textBlockShadowMarker + textLineShadowMarker + textShadowMarker;
5959
7305
  const shadowBlur = hasShadow ? Math.max(0, Number(shadow.blur ?? 0)) : 0;
5960
7306
  const decorationTags = [
5961
7307
  shadowBlur > 0 ? 'data-pd-shadow-blur="1"' : "",
@@ -5975,7 +7321,38 @@ function recolorSvgFills(svg, color) {
5975
7321
  return _recolorSvgFills(svg, color);
5976
7322
  }
5977
7323
  function _recolorSvgFills(svg, color) {
7324
+ var _a;
5978
7325
  const safe = escapeXmlAttr(color);
7326
+ try {
7327
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svg}</svg>`;
7328
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7329
+ const root = doc.documentElement;
7330
+ if (root && root.nodeName !== "parsererror") {
7331
+ for (const g of Array.from(root.querySelectorAll("linearGradient, radialGradient, pattern"))) {
7332
+ try {
7333
+ (_a = g.parentNode) == null ? void 0 : _a.removeChild(g);
7334
+ } catch {
7335
+ }
7336
+ }
7337
+ for (const el of Array.from(root.querySelectorAll("text, tspan, path, rect"))) {
7338
+ const cur = el.getAttribute("fill");
7339
+ if (cur && (cur.trim().toLowerCase() === "none" || cur.trim().toLowerCase() === "transparent")) continue;
7340
+ const style = el.getAttribute("style");
7341
+ if (style && /fill\s*:/i.test(style)) {
7342
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7343
+ if (cleaned) el.setAttribute("style", cleaned);
7344
+ else el.removeAttribute("style");
7345
+ }
7346
+ el.setAttribute("fill", color);
7347
+ }
7348
+ let out2 = "";
7349
+ for (const child of Array.from(root.childNodes)) {
7350
+ out2 += new XMLSerializer().serializeToString(child);
7351
+ }
7352
+ return out2;
7353
+ }
7354
+ } catch {
7355
+ }
5979
7356
  let out = svg.replace(
5980
7357
  /(<(?:text|tspan|path|rect)\b[^>]*?\sfill=")([^"]*)("[^>]*>)/gi,
5981
7358
  (_m, pre, val, post) => {
@@ -5991,6 +7368,11 @@ function _recolorSvgFills(svg, color) {
5991
7368
  return pre + replaced + post;
5992
7369
  }
5993
7370
  );
7371
+ out = out.replace(/<(text|tspan|path)\b([^>]*)>/gi, (m, tag, attrs) => {
7372
+ if (/\sfill=/i.test(attrs)) return m;
7373
+ if (/style="[^"]*\bfill\s*:/i.test(attrs)) return m;
7374
+ return `<${tag}${attrs} fill="${safe}">`;
7375
+ });
5994
7376
  return out;
5995
7377
  }
5996
7378
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
@@ -6129,6 +7511,239 @@ function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
6129
7511
  function escapeXmlAttr(s) {
6130
7512
  return String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6131
7513
  }
7514
+ function buildWarpRibbonD(obj, pT, pR, pB, pL, rxTL, rxTR, rxBR, rxBL) {
7515
+ const tp = obj == null ? void 0 : obj.textPath;
7516
+ if (!tp || !tp.preset || tp.preset === "none") return null;
7517
+ if (tp.preset === "rise" || tp.preset === "angle") return null;
7518
+ const w = Number(obj.width) || 0;
7519
+ const h = Number(obj.height) || 0;
7520
+ const fs = Number(obj.fontSize) || 16;
7521
+ const resolved = resolveTextPath(tp, w, fs);
7522
+ if (!resolved) return null;
7523
+ const pathEl = measurePath(resolved.d);
7524
+ if (!pathEl) return null;
7525
+ const len = pathEl.getTotalLength();
7526
+ if (!Number.isFinite(len) || len <= 0) return null;
7527
+ const halfW = w / 2;
7528
+ const halfH = h / 2;
7529
+ const topT = halfH + Math.max(0, pT);
7530
+ const botT = halfH + Math.max(0, pB);
7531
+ const padL = Math.max(0, pL);
7532
+ const padR = Math.max(0, pR);
7533
+ const sample = (s) => {
7534
+ const ss = Math.max(0, Math.min(len, s));
7535
+ const p = pathEl.getPointAtLength(ss);
7536
+ const a = pathEl.getPointAtLength(Math.min(len, ss + 0.5));
7537
+ const b = pathEl.getPointAtLength(Math.max(0, ss - 0.5));
7538
+ let tx = a.x - b.x;
7539
+ let ty = a.y - b.y;
7540
+ const tl = Math.hypot(tx, ty) || 1;
7541
+ tx /= tl;
7542
+ ty /= tl;
7543
+ return { px: p.x - halfW, py: p.y - halfH, tx, ty, nx: ty, ny: -tx };
7544
+ };
7545
+ const SAMPLES = 96;
7546
+ const topPts = [];
7547
+ const botPts = [];
7548
+ for (let i = 0; i <= SAMPLES; i++) {
7549
+ const s = len * i / SAMPLES;
7550
+ const { px, py, nx, ny } = sample(s);
7551
+ topPts.push([px + nx * topT, py + ny * topT]);
7552
+ botPts.push([px - nx * botT, py - ny * botT]);
7553
+ }
7554
+ const s0 = sample(0);
7555
+ const s1 = sample(len);
7556
+ const TL = [s0.px - s0.tx * padL + s0.nx * topT, s0.py - s0.ty * padL + s0.ny * topT];
7557
+ const BL = [s0.px - s0.tx * padL - s0.nx * botT, s0.py - s0.ty * padL - s0.ny * botT];
7558
+ const TR = [s1.px + s1.tx * padR + s1.nx * topT, s1.py + s1.ty * padR + s1.ny * topT];
7559
+ const BR = [s1.px + s1.tx * padR - s1.nx * botT, s1.py + s1.ty * padR - s1.ny * botT];
7560
+ topPts[0] = TL;
7561
+ topPts[topPts.length - 1] = TR;
7562
+ botPts[0] = BL;
7563
+ botPts[botPts.length - 1] = BR;
7564
+ const capH = topT + botT;
7565
+ const maxLeftR = Math.min(capH * 0.5, len * 0.5 + padL);
7566
+ const maxRightR = Math.min(capH * 0.5, len * 0.5 + padR);
7567
+ const clamp2 = (r, m) => Math.max(0, Math.min(Number(r) || 0, m));
7568
+ const cTL = clamp2(rxTL, maxLeftR);
7569
+ const cBL = clamp2(rxBL, maxLeftR);
7570
+ const cTR = clamp2(rxTR, maxRightR);
7571
+ const cBR = clamp2(rxBR, maxRightR);
7572
+ const dist = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
7573
+ const inset = (from, toward, d) => {
7574
+ const dx = toward[0] - from[0];
7575
+ const dy = toward[1] - from[1];
7576
+ const L = Math.hypot(dx, dy) || 1;
7577
+ const k = Math.min(d, L) / L;
7578
+ return [from[0] + dx * k, from[1] + dy * k];
7579
+ };
7580
+ const walkEdge = (pts, target, fromStart) => {
7581
+ if (target <= 0) {
7582
+ const idx = fromStart ? 0 : pts.length - 1;
7583
+ return { pt: [pts[idx][0], pts[idx][1]], cutIdx: idx };
7584
+ }
7585
+ let acc = 0;
7586
+ if (fromStart) {
7587
+ for (let i = 1; i < pts.length; i++) {
7588
+ const seg = dist(pts[i - 1], pts[i]);
7589
+ if (acc + seg >= target) {
7590
+ const k = (target - acc) / (seg || 1);
7591
+ const pt = [
7592
+ pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k,
7593
+ pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k
7594
+ ];
7595
+ return { pt, cutIdx: i - 1 };
7596
+ }
7597
+ acc += seg;
7598
+ }
7599
+ return { pt: pts[pts.length - 1], cutIdx: pts.length - 1 };
7600
+ }
7601
+ for (let i = pts.length - 2; i >= 0; i--) {
7602
+ const seg = dist(pts[i + 1], pts[i]);
7603
+ if (acc + seg >= target) {
7604
+ const k = (target - acc) / (seg || 1);
7605
+ const pt = [
7606
+ pts[i + 1][0] + (pts[i][0] - pts[i + 1][0]) * k,
7607
+ pts[i + 1][1] + (pts[i][1] - pts[i + 1][1]) * k
7608
+ ];
7609
+ return { pt, cutIdx: i + 1 };
7610
+ }
7611
+ acc += seg;
7612
+ }
7613
+ return { pt: pts[0], cutIdx: 0 };
7614
+ };
7615
+ const topStart = walkEdge(topPts, cTL, true);
7616
+ const topEnd = walkEdge(topPts, cTR, false);
7617
+ const botStart = walkEdge(botPts, cBL, true);
7618
+ const botEnd = walkEdge(botPts, cBR, false);
7619
+ const insTL_top = topStart.pt;
7620
+ const insTR_top = topEnd.pt;
7621
+ const insBL_bot = botStart.pt;
7622
+ const insBR_bot = botEnd.pt;
7623
+ const insTL_cap = inset(TL, BL, cTL);
7624
+ const insBL_cap = inset(BL, TL, cBL);
7625
+ const insBR_cap = inset(BR, TR, cBR);
7626
+ const insTR_cap = inset(TR, BR, cTR);
7627
+ const fmt = (p) => `${p[0].toFixed(2)} ${p[1].toFixed(2)}`;
7628
+ const parts = [];
7629
+ parts.push(`M ${fmt(insTL_top)}`);
7630
+ for (let i = topStart.cutIdx + 1; i <= topEnd.cutIdx; i++) parts.push(`L ${fmt(topPts[i])}`);
7631
+ parts.push(`L ${fmt(insTR_top)}`);
7632
+ parts.push(`Q ${fmt(TR)} ${fmt(insTR_cap)}`);
7633
+ parts.push(`L ${fmt(insBR_cap)}`);
7634
+ parts.push(`Q ${fmt(BR)} ${fmt(insBR_bot)}`);
7635
+ for (let i = botEnd.cutIdx - 1; i >= botStart.cutIdx + 1; i--) parts.push(`L ${fmt(botPts[i])}`);
7636
+ parts.push(`L ${fmt(insBL_bot)}`);
7637
+ parts.push(`Q ${fmt(BL)} ${fmt(insBL_cap)}`);
7638
+ parts.push(`L ${fmt(insTL_cap)}`);
7639
+ parts.push(`Q ${fmt(TL)} ${fmt(insTL_top)}`);
7640
+ parts.push("Z");
7641
+ return parts.join(" ");
7642
+ }
7643
+ function computeRibbonBoundsFor(obj, pT, pR, pB, pL) {
7644
+ const tp = obj == null ? void 0 : obj.textPath;
7645
+ if (!tp || !tp.preset || tp.preset === "none") {
7646
+ const w2 = Number(obj.width) || 0;
7647
+ const h2 = Number(obj.height) || 0;
7648
+ return { x: -w2 / 2 - pL, y: -h2 / 2 - pT, w: w2 + pL + pR, h: h2 + pT + pB };
7649
+ }
7650
+ const w = Number(obj.width) || 0;
7651
+ const h = Number(obj.height) || 0;
7652
+ const fs = Number(obj.fontSize) || 16;
7653
+ const resolved = resolveTextPath(tp, w, fs);
7654
+ const pathEl = resolved ? measurePath(resolved.d) : null;
7655
+ const len = pathEl ? pathEl.getTotalLength() : 0;
7656
+ if (!pathEl || !Number.isFinite(len) || len <= 0) {
7657
+ return { x: -w / 2 - pL, y: -h / 2 - pT, w: w + pL + pR, h: h + pT + pB };
7658
+ }
7659
+ const halfW = w / 2;
7660
+ const halfH = h / 2;
7661
+ const topT = halfH + Math.max(0, pT);
7662
+ const botT = halfH + Math.max(0, pB);
7663
+ const STEPS = 48;
7664
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
7665
+ const ext = (x, y) => {
7666
+ if (x < minX) minX = x;
7667
+ if (y < minY) minY = y;
7668
+ if (x > maxX) maxX = x;
7669
+ if (y > maxY) maxY = y;
7670
+ };
7671
+ for (let i = 0; i <= STEPS; i++) {
7672
+ const s = len * i / STEPS;
7673
+ const p = pathEl.getPointAtLength(s);
7674
+ const a = pathEl.getPointAtLength(Math.min(len, s + 0.5));
7675
+ const b = pathEl.getPointAtLength(Math.max(0, s - 0.5));
7676
+ let tx = a.x - b.x;
7677
+ let ty = a.y - b.y;
7678
+ const tl = Math.hypot(tx, ty) || 1;
7679
+ tx /= tl;
7680
+ ty /= tl;
7681
+ const nx = ty, ny = -tx;
7682
+ const px = p.x - halfW, py = p.y - halfH;
7683
+ ext(px + nx * topT, py + ny * topT);
7684
+ ext(px - nx * botT, py - ny * botT);
7685
+ }
7686
+ if (pL > 0 || pR > 0) {
7687
+ minX -= pL;
7688
+ maxX += pR;
7689
+ }
7690
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
7691
+ }
7692
+ function normalizeStops(stops) {
7693
+ 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);
7694
+ if (out.length === 0) return out;
7695
+ if (out[0].offset > 0) out.unshift({ color: out[0].color, offset: 0 });
7696
+ if (out[out.length - 1].offset < 1) out.push({ color: out[out.length - 1].color, offset: 1 });
7697
+ return out;
7698
+ }
7699
+ function buildCanvasGradient(ctx, g, bx, by, bw, bh) {
7700
+ const stops = normalizeStops(g.stops);
7701
+ if (stops.length === 0) return "#000";
7702
+ if (g.type === "radial") {
7703
+ const cx = bx + (g.cx ?? 0.5) * bw;
7704
+ const cy = by + (g.cy ?? 0.5) * bh;
7705
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7706
+ const cg2 = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);
7707
+ stops.forEach((s) => {
7708
+ try {
7709
+ cg2.addColorStop(s.offset, s.color);
7710
+ } catch {
7711
+ }
7712
+ });
7713
+ return cg2;
7714
+ }
7715
+ const angle = g.angle ?? 90;
7716
+ const rad = angle * Math.PI / 180;
7717
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7718
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7719
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7720
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7721
+ const cg = ctx.createLinearGradient(x1, y1, x2, y2);
7722
+ stops.forEach((s) => {
7723
+ try {
7724
+ cg.addColorStop(s.offset, s.color);
7725
+ } catch {
7726
+ }
7727
+ });
7728
+ return cg;
7729
+ }
7730
+ function buildSvgGradientDef(g, id, bx, by, bw, bh) {
7731
+ const stops = normalizeStops(g.stops);
7732
+ const stopsXml = stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeXmlAttr(s.color)}" />`).join("");
7733
+ if (g.type === "radial") {
7734
+ const cx = bx + (g.cx ?? 0.5) * bw;
7735
+ const cy = by + (g.cy ?? 0.5) * bh;
7736
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7737
+ 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>`;
7738
+ }
7739
+ const angle = g.angle ?? 90;
7740
+ const rad = angle * Math.PI / 180;
7741
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7742
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7743
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7744
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7745
+ return `<linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${x1.toFixed(3)}" y1="${y1.toFixed(3)}" x2="${x2.toFixed(3)}" y2="${y2.toFixed(3)}">${stopsXml}</linearGradient>`;
7746
+ }
6132
7747
  function extractGInnerMarkup(markup) {
6133
7748
  const openMatch = markup.match(/^\s*<g\b[^>]*>/);
6134
7749
  if (!openMatch) return markup;
@@ -6136,6 +7751,37 @@ function extractGInnerMarkup(markup) {
6136
7751
  if (closeIdx <= openMatch[0].length) return markup;
6137
7752
  return markup.slice(openMatch[0].length, closeIdx);
6138
7753
  }
7754
+ function outlineTextSvgForLineShadow(inner, strokeColor, strokeWidth) {
7755
+ if (!inner) return "";
7756
+ try {
7757
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${inner}</svg>`;
7758
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7759
+ const root = doc.documentElement;
7760
+ if (!root || root.nodeName === "parsererror") return "";
7761
+ const targets = Array.from(root.querySelectorAll("text, tspan"));
7762
+ if (!targets.length) return "";
7763
+ for (const el of targets) {
7764
+ const style = el.getAttribute("style");
7765
+ if (style) {
7766
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/(?:^|;)\s*stroke(?:-[a-z]+)?\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7767
+ if (cleaned) el.setAttribute("style", cleaned);
7768
+ else el.removeAttribute("style");
7769
+ }
7770
+ el.setAttribute("fill", "none");
7771
+ el.setAttribute("stroke", strokeColor);
7772
+ el.setAttribute("stroke-width", strokeWidth.toFixed(3));
7773
+ el.setAttribute("stroke-linejoin", "round");
7774
+ el.setAttribute("paint-order", "stroke");
7775
+ }
7776
+ let out = "";
7777
+ for (const child of Array.from(root.childNodes)) {
7778
+ out += new XMLSerializer().serializeToString(child);
7779
+ }
7780
+ return out;
7781
+ } catch {
7782
+ return "";
7783
+ }
7784
+ }
6139
7785
  const TRIANGLE_STROKE_MITER_LIMIT = 1e6;
6140
7786
  const toSafeNumber = (value, fallback = 0) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
6141
7787
  function normalizeShapeType(shapeType) {
@@ -6764,7 +8410,8 @@ function createText(element) {
6764
8410
  // same value as a fit-target, so the rendered box and the shrink target
6765
8411
  // stay in sync (parity with the Use page / EC2 renderer).
6766
8412
  ...(element.minBoxHeight ?? 0) > 0 ? { minBoxHeight: element.minBoxHeight } : {},
6767
- verticalAlign: element.verticalAlign || "top"
8413
+ verticalAlign: element.verticalAlign || "top",
8414
+ ...element.textPath && element.textPath.preset && element.textPath.preset !== "none" ? { textPath: element.textPath } : {}
6768
8415
  });
6769
8416
  textbox.__formattingEnabled = formattingEnabled;
6770
8417
  textbox.initDimensions();
@@ -6871,6 +8518,9 @@ function createFabricObject(element) {
6871
8518
  if (element.type === "shape" || element.type === "line" || element.type === "text") {
6872
8519
  applyInitialGradients(obj, element);
6873
8520
  }
8521
+ if (element.type === "text" && obj instanceof fabric__namespace.Textbox) {
8522
+ applyTextPathControls(obj);
8523
+ }
6874
8524
  }
6875
8525
  return obj;
6876
8526
  }
@@ -7057,7 +8707,7 @@ const progressDefinition = {
7057
8707
  ],
7058
8708
  render: renderProgressSvg
7059
8709
  };
7060
- function escapeXml(str) {
8710
+ function escapeXml$1(str) {
7061
8711
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7062
8712
  }
7063
8713
  function estimateTextWidth(text, fontSize, fontWeight) {
@@ -7104,7 +8754,7 @@ function renderTableSvg(props, width, height) {
7104
8754
  if (text) {
7105
8755
  const maxChars = Math.max(3, Math.floor(cellW / (fontSize * 0.55)));
7106
8756
  const displayText = text.length > maxChars ? text.slice(0, maxChars - 1) + "…" : text;
7107
- 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>`;
8757
+ 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>`;
7108
8758
  }
7109
8759
  }
7110
8760
  }
@@ -7171,7 +8821,7 @@ function renderAvatarSvg(props, width, height) {
7171
8821
  }
7172
8822
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
7173
8823
  ${shapeSvg}
7174
- <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>
8824
+ <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>
7175
8825
  </svg>`;
7176
8826
  }
7177
8827
  const avatarDefinition = {
@@ -7262,7 +8912,7 @@ function renderBadgeInternal(props, width, height) {
7262
8912
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${finalWidth}" height="${height}" viewBox="0 0 ${finalWidth} ${height}">
7263
8913
  <rect x="${inset}" y="${inset}" width="${finalWidth - borderWidth}" height="${height - borderWidth}" rx="${rx}" fill="${bgColor}" stroke="${borderColor}" stroke-width="${borderWidth}"/>
7264
8914
  ${iconSvg}
7265
- <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>
8915
+ <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>
7266
8916
  </svg>`;
7267
8917
  return { svg, computedWidth, anchor };
7268
8918
  }
@@ -8936,6 +10586,9 @@ const PageCanvas = react.forwardRef(
8936
10586
  if (typeof baked === "number" && baked > 0) {
8937
10587
  elementUpdate.minBoxHeight = baked;
8938
10588
  }
10589
+ if (obj.textPath) {
10590
+ elementUpdate.textPath = obj.textPath;
10591
+ }
8939
10592
  }
8940
10593
  if (sourceElement && sourceElement.opacity !== void 0) {
8941
10594
  elementUpdate.opacity = sourceElement.opacity;
@@ -9670,6 +11323,7 @@ const PageCanvas = react.forwardRef(
9670
11323
  (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.
9671
11324
  JSON.stringify({
9672
11325
  c: element.textBgColor ?? null,
11326
+ g: element.textBgGradient ?? null,
9673
11327
  p: element.textBgPadding ?? 0,
9674
11328
  pt: element.textBgPaddingTop ?? null,
9675
11329
  pr: element.textBgPaddingRight ?? null,
@@ -9688,7 +11342,7 @@ const PageCanvas = react.forwardRef(
9688
11342
  st: element.textShadowAffectsText !== false,
9689
11343
  sa: element.textShadowAffectsBg !== false
9690
11344
  }) !== (existingObj.__lastTextBgShadowJson ?? "") || // CRITICAL: Detect gradient fill/stroke changes — serialise to JSON for deep comparison
9691
- JSON.stringify(element.fillGradient || null) !== (existingObj.__lastFillGradientJson ?? "null") || JSON.stringify(element.strokeGradient || null) !== (existingObj.__lastStrokeGradientJson ?? "null");
11345
+ 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);
9692
11346
  const forceApplyFromPanel = syncTriggeredByPanelRef.current;
9693
11347
  const noPropsOrPositionChanged = !positionChanged && !otherPropsChanged;
9694
11348
  if (noPropsOrPositionChanged && !forceApplyFromPanel || visibilityUpdateInProgressRef.current) {
@@ -10122,7 +11776,7 @@ const PageCanvas = react.forwardRef(
10122
11776
  });
10123
11777
  }, [selectedIds, isActive, ready, elements]);
10124
11778
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
10125
- var _a, _b;
11779
+ var _a, _b, _c;
10126
11780
  const fc = fabricRef.current;
10127
11781
  if (fc && isTransforming(fc)) {
10128
11782
  return;
@@ -10399,6 +12053,8 @@ const PageCanvas = react.forwardRef(
10399
12053
  obj.setCoords();
10400
12054
  }
10401
12055
  if (!isLine) {
12056
+ const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
12057
+ const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
10402
12058
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
10403
12059
  if (!skipPositionUpdate && obj instanceof fabric__namespace.FabricImage && obj.originX === "center") {
10404
12060
  const vW = rW * effectiveScaleX;
@@ -10414,7 +12070,7 @@ const PageCanvas = react.forwardRef(
10414
12070
  scaleY: effectiveScaleY,
10415
12071
  angle: element.angle ?? 0,
10416
12072
  skewX: element.skewX ?? 0,
10417
- skewY: element.skewY ?? 0
12073
+ skewY: appliedSkewY
10418
12074
  });
10419
12075
  } else {
10420
12076
  obj.set({
@@ -10434,7 +12090,7 @@ const PageCanvas = react.forwardRef(
10434
12090
  width: rW,
10435
12091
  angle: element.angle ?? 0,
10436
12092
  skewX: element.skewX ?? 0,
10437
- skewY: element.skewY ?? 0,
12093
+ skewY: appliedSkewY,
10438
12094
  scaleX: effectiveScaleX * baseScaleX,
10439
12095
  scaleY: effectiveScaleY * baseScaleY
10440
12096
  });
@@ -10566,7 +12222,9 @@ const PageCanvas = react.forwardRef(
10566
12222
  dynamicMinWidth: 0,
10567
12223
  fontSize,
10568
12224
  fontFamily: element.fontFamily || "Open Sans",
10569
- fill: element.fill || "#1a1a1a",
12225
+ // If a gradient fill is active, do not briefly overwrite it with the
12226
+ // stored solid fallback while applying textPath/panel updates.
12227
+ fill: element.fillGradient && isGradientConfig(element.fillGradient) ? obj.fill : element.fill || "#1a1a1a",
10570
12228
  fontWeight: element.fontWeight || 400,
10571
12229
  textAlign: element.textAlign || "left",
10572
12230
  fontStyle: element.fontStyle || "normal",
@@ -10583,6 +12241,13 @@ const PageCanvas = react.forwardRef(
10583
12241
  const minBoxH = Math.max(0, Number(element.minBoxHeight) || 0);
10584
12242
  obj.verticalAlign = valign;
10585
12243
  obj.minBoxHeight = minBoxH;
12244
+ const nextTextPath = element.textPath;
12245
+ if (nextTextPath && nextTextPath.preset && nextTextPath.preset !== "none") {
12246
+ obj.textPath = nextTextPath;
12247
+ } else {
12248
+ obj.textPath = void 0;
12249
+ }
12250
+ applyTextPathControls(obj);
10586
12251
  if (element.formattingEnabled === true) {
10587
12252
  obj.styles = parsedStyles || {};
10588
12253
  } else {
@@ -10605,9 +12270,10 @@ const PageCanvas = react.forwardRef(
10605
12270
  } catch {
10606
12271
  }
10607
12272
  obj.dirty = true;
10608
- (_b = obj.setCoords) == null ? void 0 : _b.call(obj);
12273
+ (_c = obj.setCoords) == null ? void 0 : _c.call(obj);
10609
12274
  obj.__lastTextBgShadowJson = JSON.stringify({
10610
12275
  c: element.textBgColor ?? null,
12276
+ g: element.textBgGradient ?? null,
10611
12277
  p: element.textBgPadding ?? 0,
10612
12278
  pt: element.textBgPaddingTop ?? null,
10613
12279
  pr: element.textBgPaddingRight ?? null,
@@ -10624,7 +12290,8 @@ const PageCanvas = react.forwardRef(
10624
12290
  sx: element.textShadowOffsetX ?? 0,
10625
12291
  sy: element.textShadowOffsetY ?? 0,
10626
12292
  st: element.textShadowAffectsText !== false,
10627
- sa: element.textShadowAffectsBg !== false
12293
+ sa: element.textShadowAffectsBg !== false,
12294
+ sty: element.textShadowType ?? null
10628
12295
  });
10629
12296
  obj.dirty = true;
10630
12297
  } catch (err) {
@@ -11037,9 +12704,7 @@ const PageCanvas = react.forwardRef(
11037
12704
  }
11038
12705
  try {
11039
12706
  let url = getProxiedImageUrl(imageUrl);
11040
- const hasColorOverrides = Boolean(element.svgColorMap && Object.keys(element.svgColorMap).length > 0);
11041
- const isInlineSvgDataUrl = imageUrl.startsWith("data:image/svg+xml");
11042
- if (isSvgImage(imageUrl, element.sourceFormat) && (!isInlineSvgDataUrl || hasColorOverrides)) {
12707
+ if (isSvgImage(imageUrl, element.sourceFormat)) {
11043
12708
  const normalized = await getNormalizedSvgUrl(imageUrl, element.svgColorMap, element.sourceFormat);
11044
12709
  if (!isLatestRequest()) return;
11045
12710
  if (normalized) url = normalized;
@@ -15736,7 +17401,7 @@ async function resolveTemplateData(options) {
15736
17401
  }
15737
17402
  }
15738
17403
  if (repeatableSectionsInput.length > 0) {
15739
- paintRepeatableSections(config, repeatableSectionsInput, inlineFormSchema == null ? void 0 : inlineFormSchema.entryFilters);
17404
+ paintRepeatableSections(config, repeatableSectionsInput);
15740
17405
  }
15741
17406
  }
15742
17407
  const mergedFormData = {
@@ -15807,7 +17472,7 @@ async function resolveFromForm(options) {
15807
17472
  normalizeLayoutModes(templateConfig);
15808
17473
  const repeatableFromSchema = templateFormSchema == null ? void 0 : templateFormSchema.repeatableSections;
15809
17474
  if ((repeatableFromSchema == null ? void 0 : repeatableFromSchema.length) && templateConfig.pages) {
15810
- paintRepeatableSections(templateConfig, repeatableFromSchema, templateFormSchema == null ? void 0 : templateFormSchema.entryFilters);
17475
+ paintRepeatableSections(templateConfig, repeatableFromSchema);
15811
17476
  }
15812
17477
  const schemaSections = getRenderableFormSections(formSchema);
15813
17478
  const repeatableNodeMap = /* @__PURE__ */ new Map();
@@ -16806,6 +18471,312 @@ function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
16806
18471
  function isTextboxLike(obj) {
16807
18472
  return !!obj && (obj instanceof fabric__namespace.Textbox || obj.type === "textbox" || Array.isArray(obj == null ? void 0 : obj._textLines) && typeof obj.getLineWidth === "function");
16808
18473
  }
18474
+ function escapeXml(s) {
18475
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
18476
+ }
18477
+ function ensureFabricGradientDef(doc, obj, width, height) {
18478
+ const grad = obj == null ? void 0 : obj.fill;
18479
+ if (!grad || typeof grad !== "object" || !Array.isArray(grad.colorStops) || !grad.coords) return "";
18480
+ const id = `pixldocs_text_grad_${String(obj.__docuforgeId || grad.id || Math.random()).replace(/[^a-zA-Z0-9_-]/g, "_")}`;
18481
+ if (doc.getElementById(id)) return `url(#${id})`;
18482
+ const ns = "http://www.w3.org/2000/svg";
18483
+ let defs = doc.querySelector("defs");
18484
+ if (!defs) {
18485
+ defs = doc.createElementNS(ns, "defs");
18486
+ doc.documentElement.insertBefore(defs, doc.documentElement.firstChild);
18487
+ }
18488
+ const c = grad.coords || {};
18489
+ const el = doc.createElementNS(ns, grad.type === "radial" ? "radialGradient" : "linearGradient");
18490
+ el.setAttribute("id", id);
18491
+ el.setAttribute("gradientUnits", "userSpaceOnUse");
18492
+ if (grad.type === "radial") {
18493
+ el.setAttribute("cx", (Number(c.x2 ?? c.x1 ?? width / 2) - width / 2).toFixed(3));
18494
+ el.setAttribute("cy", (Number(c.y2 ?? c.y1 ?? height / 2) - height / 2).toFixed(3));
18495
+ el.setAttribute("fx", (Number(c.x1 ?? c.x2 ?? width / 2) - width / 2).toFixed(3));
18496
+ el.setAttribute("fy", (Number(c.y1 ?? c.y2 ?? height / 2) - height / 2).toFixed(3));
18497
+ el.setAttribute("r", Math.max(0.1, Number(c.r2 ?? c.r ?? Math.max(width, height) / 2)).toFixed(3));
18498
+ } else {
18499
+ el.setAttribute("x1", (Number(c.x1) - width / 2).toFixed(3));
18500
+ el.setAttribute("y1", (Number(c.y1) - height / 2).toFixed(3));
18501
+ el.setAttribute("x2", (Number(c.x2 ?? width) - width / 2).toFixed(3));
18502
+ el.setAttribute("y2", (Number(c.y2) - height / 2).toFixed(3));
18503
+ }
18504
+ for (const stop of grad.colorStops) {
18505
+ const stopEl = doc.createElementNS(ns, "stop");
18506
+ stopEl.setAttribute("offset", String(Math.max(0, Math.min(1, Number(stop.offset) || 0))));
18507
+ stopEl.setAttribute("stop-color", stop.color || "#000000");
18508
+ el.appendChild(stopEl);
18509
+ }
18510
+ defs.appendChild(el);
18511
+ return `url(#${id})`;
18512
+ }
18513
+ function warpTextboxSvgAlongPath(svg, obj) {
18514
+ var _a, _b, _c, _d, _e;
18515
+ const tp = obj == null ? void 0 : obj.textPath;
18516
+ if (!tp || !tp.preset || tp.preset === "none") return svg;
18517
+ if (tp.preset === "rise" || tp.preset === "angle") {
18518
+ const w2 = Number(obj.width) || 0;
18519
+ const h2 = Number(obj.height) || 0;
18520
+ const fs2 = Number(obj.fontSize) || 16;
18521
+ const ep = tp.endpoints;
18522
+ const defaultLeftY = fs2 * 1.5;
18523
+ const defaultRightY = fs2 * 0.5;
18524
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
18525
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
18526
+ const slope = w2 > 0 ? (rightY - leftY) / w2 : 0;
18527
+ const dy = (leftY + rightY) / 2 - h2 / 2;
18528
+ if (Math.abs(slope) < 1e-6 && Math.abs(dy) < 1e-6) return svg;
18529
+ let doc2;
18530
+ try {
18531
+ doc2 = new DOMParser().parseFromString(svg, "image/svg+xml");
18532
+ } catch {
18533
+ return svg;
18534
+ }
18535
+ const textEls2 = Array.from(doc2.querySelectorAll("text"));
18536
+ if (!textEls2.length) return svg;
18537
+ const matrix = `matrix(1 ${slope.toFixed(6)} 0 1 0 ${dy.toFixed(3)})`;
18538
+ for (const textEl2 of textEls2) {
18539
+ const existing = textEl2.getAttribute("transform");
18540
+ textEl2.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18541
+ }
18542
+ const bgShapes = Array.from(doc2.querySelectorAll("path.__pdTextBgShape"));
18543
+ for (const bgEl of bgShapes) {
18544
+ const existing = bgEl.getAttribute("transform");
18545
+ bgEl.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18546
+ }
18547
+ return new XMLSerializer().serializeToString(doc2.documentElement);
18548
+ }
18549
+ const resolved = resolveTextPath(tp, Number(obj.width) || 0, Number(obj.fontSize) || 16);
18550
+ if (!resolved) return svg;
18551
+ const pathEl = measurePath(resolved.d);
18552
+ if (!pathEl) return svg;
18553
+ const totalLen = pathEl.getTotalLength();
18554
+ if (!Number.isFinite(totalLen) || totalLen <= 0) return svg;
18555
+ const plain = String(obj.text || "").replace(/\n/g, " ");
18556
+ if (!plain) return svg;
18557
+ let doc;
18558
+ try {
18559
+ doc = new DOMParser().parseFromString(svg, "image/svg+xml");
18560
+ } catch {
18561
+ return svg;
18562
+ }
18563
+ for (const marker of Array.from(doc.querySelectorAll("g.__pdShadowRaster"))) {
18564
+ try {
18565
+ (_a = marker.parentNode) == null ? void 0 : _a.removeChild(marker);
18566
+ } catch {
18567
+ }
18568
+ }
18569
+ for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
18570
+ try {
18571
+ (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
18572
+ } catch {
18573
+ }
18574
+ }
18575
+ const textEls = Array.from(doc.querySelectorAll("text"));
18576
+ if (!textEls.length) return svg;
18577
+ const hasFilterInChain = (el) => {
18578
+ let cur = el;
18579
+ while (cur && cur !== doc.documentElement) {
18580
+ if (cur.getAttribute("filter") || /filter\s*:/i.test(cur.getAttribute("style") || "")) return true;
18581
+ cur = cur.parentElement;
18582
+ }
18583
+ return false;
18584
+ };
18585
+ const textEl = textEls.find((el) => !hasFilterInChain(el)) || textEls[textEls.length - 1] || textEls[0];
18586
+ const ff = obj.fontFamily || "Open Sans";
18587
+ const fs = Number(obj.fontSize) || 16;
18588
+ const fw = obj.fontWeight ?? 400;
18589
+ const fst = obj.fontStyle || "normal";
18590
+ const readFill = (el) => {
18591
+ var _a2, _b2;
18592
+ if (!el) return "";
18593
+ 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()) || "";
18594
+ };
18595
+ const w = Number(obj.width) || 0;
18596
+ const h = Number(obj.height) || 0;
18597
+ const synthesizedGradientFill = ensureFabricGradientDef(doc, obj, w, h);
18598
+ const fabricGradientFill = obj.fill && typeof obj.fill === "object" && obj.fill.id != null ? `url(#SVGID_${obj.fill.id})` : "";
18599
+ const fillAttr = synthesizedGradientFill || readFill(textEl) || readFill(textEl.querySelector("tspan")) || fabricGradientFill || (typeof obj.fill === "string" ? obj.fill : "#000");
18600
+ const stripFilter = (el) => {
18601
+ if (!el) return;
18602
+ el.removeAttribute("filter");
18603
+ const style = el.getAttribute("style");
18604
+ if (style && /filter\s*:/i.test(style)) {
18605
+ el.setAttribute("style", style.replace(/(?:^|;)\s*filter\s*:[^;]*/gi, "").replace(/^;+/, ""));
18606
+ }
18607
+ };
18608
+ for (const el of Array.from(doc.querySelectorAll("*"))) stripFilter(el);
18609
+ for (const filter of Array.from(doc.querySelectorAll("filter"))) {
18610
+ try {
18611
+ (_c = filter.parentNode) == null ? void 0 : _c.removeChild(filter);
18612
+ } catch {
18613
+ }
18614
+ }
18615
+ let shadowConfig = null;
18616
+ if (obj.shadow) {
18617
+ const sh = obj.shadow;
18618
+ const color = sh.color || "rgba(0,0,0,0.5)";
18619
+ const blur = Math.max(0, Number(sh.blur) || 0);
18620
+ const dx = Number(sh.offsetX) || 0;
18621
+ const dy = Number(sh.offsetY) || 0;
18622
+ shadowConfig = { color, dx, dy, blur };
18623
+ }
18624
+ const bgCfg = obj.__pdBg;
18625
+ const extOX = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetX) ?? 0) || 0;
18626
+ const extOY = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetY) ?? 0) || 0;
18627
+ const extDist = Math.hypot(extOX, extOY);
18628
+ const extColor = bgCfg == null ? void 0 : bgCfg.shadowColor;
18629
+ const hasExtShadowColor = !!extColor && extColor !== "transparent";
18630
+ const affectsText = !bgCfg || bgCfg.shadowAffectsText !== false;
18631
+ const blockShadowActive = !!bgCfg && bgCfg.shadowType === "block" && hasExtShadowColor && extDist > 0 && affectsText;
18632
+ const lineShadowActive = !!bgCfg && bgCfg.shadowType === "line" && hasExtShadowColor && extDist > 0 && affectsText;
18633
+ let blockSteps = 0;
18634
+ let blockStepX = 0;
18635
+ let blockStepY = 0;
18636
+ if (blockShadowActive) {
18637
+ const STEP = 1;
18638
+ blockSteps = Math.min(200, Math.max(1, Math.ceil(extDist / STEP)));
18639
+ blockStepX = extOX / blockSteps;
18640
+ blockStepY = extOY / blockSteps;
18641
+ }
18642
+ 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;
18643
+ const gradId = (fillAttr.match(/url\(#([^)]+)\)/) || [])[1];
18644
+ if (gradId && w > 0 && h > 0) {
18645
+ const grad = doc.getElementById(gradId);
18646
+ if (grad && grad.getAttribute("gradientUnits") !== "userSpaceOnUse") {
18647
+ const tag = grad.tagName.toLowerCase();
18648
+ const num = (v, fallback) => {
18649
+ if (v == null) return fallback;
18650
+ const n = parseFloat(v);
18651
+ return Number.isFinite(n) ? n : fallback;
18652
+ };
18653
+ const mapX = (u) => -w / 2 + u * w;
18654
+ const mapY = (u) => -h / 2 + u * h;
18655
+ grad.setAttribute("gradientUnits", "userSpaceOnUse");
18656
+ if (tag === "lineargradient") {
18657
+ const x1 = num(grad.getAttribute("x1"), 0);
18658
+ const y1 = num(grad.getAttribute("y1"), 0);
18659
+ const x2 = num(grad.getAttribute("x2"), 1);
18660
+ const y2 = num(grad.getAttribute("y2"), 0);
18661
+ grad.setAttribute("x1", mapX(x1).toFixed(3));
18662
+ grad.setAttribute("y1", mapY(y1).toFixed(3));
18663
+ grad.setAttribute("x2", mapX(x2).toFixed(3));
18664
+ grad.setAttribute("y2", mapY(y2).toFixed(3));
18665
+ } else if (tag === "radialgradient") {
18666
+ const cx = num(grad.getAttribute("cx"), 0.5);
18667
+ const cy = num(grad.getAttribute("cy"), 0.5);
18668
+ const r = num(grad.getAttribute("r"), 0.5);
18669
+ const fx = num(grad.getAttribute("fx"), cx);
18670
+ const fy = num(grad.getAttribute("fy"), cy);
18671
+ grad.setAttribute("cx", mapX(cx).toFixed(3));
18672
+ grad.setAttribute("cy", mapY(cy).toFixed(3));
18673
+ grad.setAttribute("fx", mapX(fx).toFixed(3));
18674
+ grad.setAttribute("fy", mapY(fy).toFixed(3));
18675
+ grad.setAttribute("r", (r * Math.max(w, h)).toFixed(3));
18676
+ }
18677
+ }
18678
+ }
18679
+ const measure = document.createElement("canvas").getContext("2d");
18680
+ if (!measure) return svg;
18681
+ measure.font = `${fst} ${fw} ${fs}px "${ff}"`;
18682
+ const chars = Array.from(plain);
18683
+ const spacing = (Number(obj.charSpacing) || 0) * fs / 1e3;
18684
+ const widths = chars.map((c) => measure.measureText(c).width + spacing);
18685
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
18686
+ const baselineDy = resolveTextPathAlphabeticBaselineDy(measure, plain, fs);
18687
+ let cursor = 0;
18688
+ if (obj.textAlign === "center") cursor = Math.max(0, (totalLen - totalWidth) / 2);
18689
+ else if (obj.textAlign === "right") cursor = Math.max(0, totalLen - totalWidth);
18690
+ const halfW = (Number(obj.width) || 0) / 2;
18691
+ const halfH = (Number(obj.height) || 0) / 2;
18692
+ const glyphSvg = [];
18693
+ const shadowSvg = [];
18694
+ const blockShadowSvg = [];
18695
+ const lineShadowSvg = [];
18696
+ let minX = Number.POSITIVE_INFINITY;
18697
+ let minY = Number.POSITIVE_INFINITY;
18698
+ let maxX = Number.NEGATIVE_INFINITY;
18699
+ let maxY = Number.NEGATIVE_INFINITY;
18700
+ for (let i = 0; i < chars.length; i++) {
18701
+ const cw = widths[i];
18702
+ const mid = cursor + cw / 2;
18703
+ cursor += cw;
18704
+ if (mid > totalLen) break;
18705
+ const p = pathEl.getPointAtLength(Math.max(0, mid));
18706
+ const ahead = pathEl.getPointAtLength(Math.min(totalLen, mid + 0.5));
18707
+ const deg = Math.atan2(ahead.y - p.y, ahead.x - p.x) * 180 / Math.PI;
18708
+ const x = p.x - halfW;
18709
+ const y = p.y - halfH;
18710
+ const glyphPad = Math.max(fs, cw) * 0.75;
18711
+ minX = Math.min(minX, x - glyphPad);
18712
+ minY = Math.min(minY, y - glyphPad - fs);
18713
+ maxX = Math.max(maxX, x + glyphPad);
18714
+ maxY = Math.max(maxY, y + glyphPad);
18715
+ const glyphFill = flowStops ? sampleGradientColor(flowStops, chars.length > 1 ? i / (chars.length - 1) : 0) : fillAttr;
18716
+ if (blockShadowActive) {
18717
+ for (let s = blockSteps; s >= 1; s--) {
18718
+ const bx = x + blockStepX * s;
18719
+ const by = y + blockStepY * s;
18720
+ blockShadowSvg.push(
18721
+ `<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>`
18722
+ );
18723
+ }
18724
+ }
18725
+ if (lineShadowActive) {
18726
+ const lx = x + extOX;
18727
+ const ly = y + extOY;
18728
+ const lineStrokeW = LINE_SHADOW_STROKE_WIDTH;
18729
+ lineShadowSvg.push(
18730
+ `<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>`
18731
+ );
18732
+ }
18733
+ if (shadowConfig) {
18734
+ const sx = x + shadowConfig.dx;
18735
+ const sy = y + shadowConfig.dy;
18736
+ shadowSvg.push(
18737
+ `<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>`
18738
+ );
18739
+ }
18740
+ glyphSvg.push(
18741
+ `<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>`
18742
+ );
18743
+ }
18744
+ const replacement = doc.createElementNS("http://www.w3.org/2000/svg", "g");
18745
+ replacement.setAttribute("data-pixldocs-warped", "true");
18746
+ let innerHtml = "";
18747
+ if (shadowConfig && shadowSvg.length) {
18748
+ const blur = Math.max(0, Number(shadowConfig.blur) || 0);
18749
+ if (blur > 0 && Number.isFinite(minX) && Number.isFinite(maxX)) {
18750
+ const pad = Math.max(8, blur * 3);
18751
+ const sMinX = minX + shadowConfig.dx - pad;
18752
+ const sMinY = minY + shadowConfig.dy - pad;
18753
+ const sMaxX = maxX + shadowConfig.dx + pad;
18754
+ const sMaxY = maxY + shadowConfig.dy + pad;
18755
+ const bw = Math.max(1, sMaxX - sMinX);
18756
+ const bh = Math.max(1, sMaxY - sMinY);
18757
+ 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>`;
18758
+ } else {
18759
+ innerHtml += `<g data-pixldocs-warp-shadow-group="true">${shadowSvg.join("")}</g>`;
18760
+ }
18761
+ }
18762
+ if (lineShadowSvg.length) {
18763
+ innerHtml = `<g data-pixldocs-warp-line-shadow-group="true">${lineShadowSvg.join("")}</g>` + innerHtml;
18764
+ }
18765
+ if (blockShadowSvg.length) {
18766
+ innerHtml = `<g data-pixldocs-warp-block-shadow-group="true">${blockShadowSvg.join("")}</g>` + innerHtml;
18767
+ }
18768
+ innerHtml += glyphSvg.join("");
18769
+ replacement.innerHTML = innerHtml;
18770
+ (_d = textEl.parentNode) == null ? void 0 : _d.replaceChild(replacement, textEl);
18771
+ for (let i = 1; i < textEls.length; i++) {
18772
+ try {
18773
+ (_e = textEls[i].parentNode) == null ? void 0 : _e.removeChild(textEls[i]);
18774
+ } catch {
18775
+ }
18776
+ }
18777
+ const serialized = new XMLSerializer().serializeToString(doc.documentElement);
18778
+ return serialized;
18779
+ }
16809
18780
  function stampFabricLineMetricsOnTextSvg(svg, obj) {
16810
18781
  const lines = Array.isArray(obj == null ? void 0 : obj._textLines) ? obj._textLines : [];
16811
18782
  if (!lines.length || typeof (obj == null ? void 0 : obj.getLineWidth) !== "function") return svg;
@@ -16876,6 +18847,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16876
18847
  obj.toSVG = (reviver) => {
16877
18848
  let svg = originalToSVG(reviver);
16878
18849
  if (isTextboxLike(obj)) svg = stampFabricLineMetricsOnTextSvg(svg, obj);
18850
+ if (isTextboxLike(obj)) svg = warpTextboxSvgAlongPath(svg, obj);
16879
18851
  if (imageId) svg = stampPixldocsImageIdOnSvg(svg, imageId);
16880
18852
  return svg;
16881
18853
  };
@@ -16970,9 +18942,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16970
18942
  }
16971
18943
  return svgString;
16972
18944
  }
16973
- const resolvedPackageVersion = "0.5.205";
18945
+ const resolvedPackageVersion = "0.5.206";
16974
18946
  const PACKAGE_VERSION = resolvedPackageVersion;
16975
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.205";
18947
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.206";
16976
18948
  const roundParityValue = (value) => {
16977
18949
  if (typeof value !== "number") return value;
16978
18950
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -17651,7 +19623,7 @@ class PixldocsRenderer {
17651
19623
  await this.waitForCanvasScene(container, cloned, i);
17652
19624
  }
17653
19625
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
17654
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-LmnOwVt3.cjs"));
19626
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-C0s166DN.cjs"));
17655
19627
  const prepared = preparePagesForExport(
17656
19628
  cloned.pages,
17657
19629
  canvasWidth,
@@ -19835,7 +21807,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19835
21807
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19836
21808
  sanitizeSvgTreeForPdf(svgToDraw);
19837
21809
  try {
19838
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-LmnOwVt3.cjs"));
21810
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-C0s166DN.cjs"));
19839
21811
  try {
19840
21812
  await logTextMeasurementDiagnostic(svgToDraw);
19841
21813
  } catch {
@@ -19967,7 +21939,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
19967
21939
  );
19968
21940
  }
19969
21941
  try {
19970
- const { prepareSvgTextForPdfMode } = await Promise.resolve().then(() => require("./svgTextToPath-hYM9qTeC.cjs"));
21942
+ const { prepareSvgTextForPdfMode } = await Promise.resolve().then(() => require("./svgTextToPath-BT7lcwLT.cjs"));
19971
21943
  pageSvg = await prepareSvgTextForPdfMode(pageSvg, textMode, fontBaseUrl);
19972
21944
  try {
19973
21945
  dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-shared-text-prep");
@@ -20232,4 +22204,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
20232
22204
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
20233
22205
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
20234
22206
  exports.warmTemplateFromForm = warmTemplateFromForm;
20235
- //# sourceMappingURL=index-CGi2L90_.cjs.map
22207
+ //# sourceMappingURL=index-B1kaODSZ.cjs.map