@pixldocs/canvas-renderer 0.5.205 → 0.5.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,182 @@ 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 shearTransform = null;
6990
+ if (isShear) {
6991
+ const fs = this.fontSize || 16;
6992
+ const ep = tpAny.endpoints;
6993
+ const dL = fs * 1.5;
6994
+ const dR = fs * 0.5;
6995
+ const lY = ep && Number.isFinite(ep.leftY) ? ep.leftY : dL;
6996
+ const rY = ep && Number.isFinite(ep.rightY) ? ep.rightY : dR;
6997
+ const slope = w > 0 ? (rY - lY) / w : 0;
6998
+ const dy = (lY + rY) / 2 - h / 2;
6999
+ shearTransform = { slope, dy };
7000
+ }
7001
+ let bgBounds;
7002
+ let bgRectsForFill = null;
7003
+ if (ribbonD) {
7004
+ bgBounds = computeRibbonBoundsFor(this, pT, pR, pB, pL);
7005
+ } else {
7006
+ bgRectsForFill = computeBgRects(this, w, h, pT, pR, pB, pL, !!bg.fitToText);
7007
+ bgBounds = unionBounds(bgRectsForFill);
7008
+ }
7009
+ if (blockShadowActive && bg.shadowAffectsBg !== false) {
7010
+ ctx.save();
7011
+ if (shearTransform) {
7012
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
7013
+ }
7014
+ ctx.fillStyle = bg.shadowColor;
7015
+ for (let i = blockSteps; i >= 1; i--) {
7016
+ ctx.save();
7017
+ ctx.translate(blockStepX * i, blockStepY * i);
7018
+ if (ribbonD) {
7019
+ try {
7020
+ ctx.fill(new Path2D(ribbonD));
7021
+ } catch {
7022
+ }
7023
+ } else {
7024
+ const rects = bgRectsForFill;
7025
+ for (const r of rects) {
7026
+ buildRoundedRectPath2D(
7027
+ ctx,
7028
+ r.x,
7029
+ r.y,
7030
+ r.w,
7031
+ r.h,
7032
+ bg.rxTL ?? 0,
7033
+ bg.rxTR ?? 0,
7034
+ bg.rxBR ?? 0,
7035
+ bg.rxBL ?? 0
7036
+ );
7037
+ ctx.fill();
7038
+ }
7039
+ }
7040
+ ctx.restore();
7041
+ }
7042
+ ctx.restore();
7043
+ }
7044
+ if (lineShadowActive && bg.shadowAffectsBg !== false) {
7045
+ ctx.save();
7046
+ if (shearTransform) {
7047
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
7048
+ }
7049
+ ctx.translate(extOX, extOY);
7050
+ ctx.strokeStyle = bg.shadowColor;
7051
+ ctx.lineWidth = 1;
7052
+ ctx.lineJoin = "round";
7053
+ if (ribbonD) {
7054
+ try {
7055
+ ctx.stroke(new Path2D(ribbonD));
7056
+ } catch {
7057
+ }
7058
+ } else {
7059
+ const rects = bgRectsForFill;
7060
+ for (const r of rects) {
7061
+ buildRoundedRectPath2D(
7062
+ ctx,
7063
+ r.x,
7064
+ r.y,
7065
+ r.w,
7066
+ r.h,
7067
+ bg.rxTL ?? 0,
7068
+ bg.rxTR ?? 0,
7069
+ bg.rxBR ?? 0,
7070
+ bg.rxBL ?? 0
7071
+ );
7072
+ ctx.stroke();
7073
+ }
7074
+ }
7075
+ ctx.restore();
7076
+ }
7077
+ ctx.fillStyle = bg.gradient ? buildCanvasGradient(ctx, bg.gradient, bgBounds.x, bgBounds.y, bgBounds.w, bgBounds.h) : bg.color || "#000";
7078
+ if (ribbonD) {
7079
+ try {
7080
+ const p2 = new Path2D(ribbonD);
7081
+ ctx.fill(p2);
7082
+ } catch {
7083
+ }
7084
+ } else {
7085
+ if (shearTransform) {
7086
+ ctx.transform(1, shearTransform.slope, 0, 1, 0, shearTransform.dy);
7087
+ }
7088
+ const rects = bgRectsForFill;
7089
+ for (const r of rects) {
7090
+ buildRoundedRectPath2D(
7091
+ ctx,
7092
+ r.x,
7093
+ r.y,
7094
+ r.w,
7095
+ r.h,
7096
+ bg.rxTL ?? 0,
7097
+ bg.rxTR ?? 0,
7098
+ bg.rxBR ?? 0,
7099
+ bg.rxBL ?? 0
7100
+ );
7101
+ ctx.fill();
7102
+ }
5870
7103
  }
5871
7104
  ctx.restore();
5872
7105
  }
7106
+ if (blockShadowActive && bg.shadowAffectsText !== false) {
7107
+ const self = this;
7108
+ const origFill = self.fill;
7109
+ const origStyles = self.styles;
7110
+ const origShadow = self.shadow;
7111
+ try {
7112
+ self.fill = bg.shadowColor;
7113
+ self.styles = {};
7114
+ self.shadow = null;
7115
+ for (let i = blockSteps; i >= 1; i--) {
7116
+ ctx.save();
7117
+ ctx.translate(blockStepX * i, blockStepY * i);
7118
+ originalRender(ctx);
7119
+ ctx.restore();
7120
+ }
7121
+ } finally {
7122
+ self.fill = origFill;
7123
+ self.styles = origStyles;
7124
+ self.shadow = origShadow;
7125
+ }
7126
+ }
7127
+ if (lineShadowActive && bg.shadowAffectsText !== false) {
7128
+ const self = this;
7129
+ const origFill = self.fill;
7130
+ const origStroke = self.stroke;
7131
+ const origStrokeWidth = self.strokeWidth;
7132
+ const origStyles = self.styles;
7133
+ const origShadow = self.shadow;
7134
+ try {
7135
+ self.fill = "transparent";
7136
+ self.stroke = bg.shadowColor;
7137
+ self.strokeWidth = LINE_SHADOW_STROKE_WIDTH;
7138
+ self.styles = {};
7139
+ self.shadow = null;
7140
+ ctx.save();
7141
+ ctx.translate(extOX, extOY);
7142
+ originalRender(ctx);
7143
+ ctx.restore();
7144
+ } finally {
7145
+ self.fill = origFill;
7146
+ self.stroke = origStroke;
7147
+ self.strokeWidth = origStrokeWidth;
7148
+ self.styles = origStyles;
7149
+ self.shadow = origShadow;
7150
+ }
7151
+ }
5873
7152
  const suppressShadowOnText = bg && bg.shadowAffectsText === false;
5874
7153
  if (suppressShadowOnText) {
5875
7154
  ctx.save();
@@ -5888,19 +7167,27 @@ function applyTextBackground(obj, cfg) {
5888
7167
  const out = originalToObject(propertiesToInclude);
5889
7168
  const bg = this[PD_BG_KEY];
5890
7169
  if (hasTextBackground(bg)) {
5891
- out.__pdBg = { ...bg };
7170
+ out.__pdBg = { ...bg, gradient: bg.gradient ? { ...bg.gradient, stops: bg.gradient.stops.map((s) => ({ ...s })) } : void 0 };
5892
7171
  }
5893
7172
  return out;
5894
7173
  };
5895
7174
  const originalToSVG = (_a = obj.toSVG) == null ? void 0 : _a.bind(obj);
5896
7175
  if (typeof originalToSVG === "function") {
5897
7176
  obj.toSVG = function(reviver) {
7177
+ var _a2, _b;
5898
7178
  let svg = originalToSVG(reviver);
5899
7179
  const bg = this[PD_BG_KEY];
5900
7180
  const shadow = this.shadow;
5901
7181
  const hasBg = hasTextBackground(bg);
5902
7182
  const hasShadow = !!shadow && !!shadow.color && shadow.color !== "transparent";
5903
- if (!hasBg && !hasShadow) return svg;
7183
+ const extOX = Number((bg == null ? void 0 : bg.shadowOffsetX) ?? 0) || 0;
7184
+ const extOY = Number((bg == null ? void 0 : bg.shadowOffsetY) ?? 0) || 0;
7185
+ const extDist = Math.hypot(extOX, extOY);
7186
+ const hasExtShadowColor = !!(bg == null ? void 0 : bg.shadowColor) && bg.shadowColor !== "transparent";
7187
+ const hasBlockShadow = !!bg && bg.shadowType === "block" && hasExtShadowColor && extDist > 0;
7188
+ const hasLineShadow = !!bg && bg.shadowType === "line" && hasExtShadowColor && extDist > 0;
7189
+ if (!hasBg && !hasShadow && !hasBlockShadow && !hasLineShadow) return svg;
7190
+ const hasActiveTextPath2 = !!(((_a2 = this.textPath) == null ? void 0 : _a2.preset) && this.textPath.preset !== "none");
5904
7191
  const w = this.width ?? 0;
5905
7192
  const h = this.height ?? 0;
5906
7193
  const pT = Math.max(0, Number((bg == null ? void 0 : bg.padTop) ?? 0));
@@ -5909,7 +7196,18 @@ function applyTextBackground(obj, cfg) {
5909
7196
  const pL = Math.max(0, Number((bg == null ? void 0 : bg.padLeft) ?? 0));
5910
7197
  const fit = !!(bg == null ? void 0 : bg.fitToText);
5911
7198
  const rects = computeBgRects(this, w, h, pT, pR, pB, pL, fit);
5912
- const bgD = rects.map((r) => buildRoundedRectPathD(
7199
+ const ribbonD = buildWarpRibbonD(
7200
+ this,
7201
+ pT,
7202
+ pR,
7203
+ pB,
7204
+ pL,
7205
+ (bg == null ? void 0 : bg.rxTL) ?? 0,
7206
+ (bg == null ? void 0 : bg.rxTR) ?? 0,
7207
+ (bg == null ? void 0 : bg.rxBR) ?? 0,
7208
+ (bg == null ? void 0 : bg.rxBL) ?? 0
7209
+ );
7210
+ const bgD = ribbonD ?? rects.map((r) => buildRoundedRectPathD(
5913
7211
  r.x,
5914
7212
  r.y,
5915
7213
  r.w,
@@ -5922,7 +7220,16 @@ function applyTextBackground(obj, cfg) {
5922
7220
  const bgFill = (bg == null ? void 0 : bg.color) || "";
5923
7221
  const bgOpacity = typeof (bg == null ? void 0 : bg.opacity) === "number" ? Math.max(0, Math.min(1, bg.opacity)) : 1;
5924
7222
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
5925
- const bgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(bgFill)}"${bgOpacityAttr} />` : "";
7223
+ let bgGradDefs = "";
7224
+ let bgFillAttr = escapeXmlAttr(bgFill);
7225
+ if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b = bg.gradient.stops) == null ? void 0 : _b.length) >= 2) {
7226
+ const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7227
+ const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7228
+ const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
7229
+ bgGradDefs = `<defs>${def}</defs>`;
7230
+ bgFillAttr = `url(#${gid})`;
7231
+ }
7232
+ const bgPath = hasBg ? `${bgGradDefs}<path class="__pdTextBgShape" d="${bgD}" fill="${bgFillAttr}"${bgOpacityAttr} />` : "";
5926
7233
  svg = svg.replace(/style="[^"]*filter:\s*url\([^)]+\)[^"]*"/i, "");
5927
7234
  svg = svg.replace(/<filter[\s\S]*?<\/filter>/gi, "");
5928
7235
  let bgShadowMarker = "";
@@ -5948,14 +7255,63 @@ function applyTextBackground(obj, cfg) {
5948
7255
  const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
5949
7256
  bgShadowMarker = wrapShadow(shadowBgPath);
5950
7257
  }
5951
- if ((bg == null ? void 0 : bg.shadowAffectsText) !== false) {
7258
+ if ((bg == null ? void 0 : bg.shadowAffectsText) !== false && !hasActiveTextPath2) {
5952
7259
  const inner = extractGInnerMarkup(svg);
5953
7260
  const recoloredText = recolorSvgFills(inner, shadowColor);
5954
7261
  if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
5955
7262
  }
5956
7263
  }
7264
+ let bgBlockShadowMarker = "";
7265
+ let textBlockShadowMarker = "";
7266
+ if (hasBlockShadow) {
7267
+ const STEP = 0.5;
7268
+ const steps = Math.min(600, Math.max(1, Math.ceil(extDist / STEP)));
7269
+ const stepX = extOX / steps;
7270
+ const stepY = extOY / steps;
7271
+ const shadowColor = String(bg.shadowColor);
7272
+ const fillObj = this.fill;
7273
+ const parentTextIsGradient = !!fillObj && typeof fillObj === "object" && (Array.isArray(fillObj.colorStops) || fillObj.type === "linear" || fillObj.type === "radial");
7274
+ const cloneOutlineAttr = parentTextIsGradient ? ' data-pd-outline-text="1"' : "";
7275
+ const buildCopies = (markup, cls) => {
7276
+ const parts = [];
7277
+ for (let i = steps; i >= 1; i--) {
7278
+ parts.push(`<g class="${cls}"${cloneOutlineAttr} transform="translate(${(stepX * i).toFixed(3)} ${(stepY * i).toFixed(3)})">${markup}</g>`);
7279
+ }
7280
+ return parts.join("");
7281
+ };
7282
+ if (hasBg && bg.shadowAffectsBg !== false) {
7283
+ const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7284
+ const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
7285
+ bgBlockShadowMarker = buildCopies(shadowBgPath, "__pdBgShadowClone");
7286
+ }
7287
+ if (bg.shadowAffectsText !== false) {
7288
+ const inner = extractGInnerMarkup(svg);
7289
+ const recoloredText = recolorSvgFills(inner, shadowColor);
7290
+ if (recoloredText) textBlockShadowMarker = buildCopies(recoloredText, "__pdTextShadowClone");
7291
+ }
7292
+ }
7293
+ let bgLineShadowMarker = "";
7294
+ let textLineShadowMarker = "";
7295
+ if (hasLineShadow) {
7296
+ const shadowColor = String(bg.shadowColor);
7297
+ const tx = extOX.toFixed(3);
7298
+ const ty = extOY.toFixed(3);
7299
+ const fillObjLine = this.fill;
7300
+ const parentTextIsGradientLine = !!fillObjLine && typeof fillObjLine === "object" && (Array.isArray(fillObjLine.colorStops) || fillObjLine.type === "linear" || fillObjLine.type === "radial");
7301
+ const lineOutlineAttr = parentTextIsGradientLine ? ' data-pd-outline-text="1"' : "";
7302
+ if (hasBg && bg.shadowAffectsBg !== false) {
7303
+ const outlineBg = `<path d="${bgD}" fill="none" stroke="${escapeXmlAttr(shadowColor)}" stroke-width="1" stroke-linejoin="round" />`;
7304
+ bgLineShadowMarker = `<g class="__pdBgShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlineBg}</g>`;
7305
+ }
7306
+ if (bg.shadowAffectsText !== false) {
7307
+ const inner = extractGInnerMarkup(svg);
7308
+ const strokeW = LINE_SHADOW_STROKE_WIDTH;
7309
+ const outlined = outlineTextSvgForLineShadow(inner, shadowColor, strokeW);
7310
+ if (outlined) textLineShadowMarker = `<g class="__pdTextShadowClone"${lineOutlineAttr} transform="translate(${tx} ${ty})">${outlined}</g>`;
7311
+ }
7312
+ }
5957
7313
  const openTagMatch = svg.match(/^\s*<g\b[^>]*>/);
5958
- const inserted = bgShadowMarker + bgPath + textShadowMarker;
7314
+ const inserted = bgBlockShadowMarker + bgLineShadowMarker + bgShadowMarker + bgPath + textBlockShadowMarker + textLineShadowMarker + textShadowMarker;
5959
7315
  const shadowBlur = hasShadow ? Math.max(0, Number(shadow.blur ?? 0)) : 0;
5960
7316
  const decorationTags = [
5961
7317
  shadowBlur > 0 ? 'data-pd-shadow-blur="1"' : "",
@@ -5975,7 +7331,38 @@ function recolorSvgFills(svg, color) {
5975
7331
  return _recolorSvgFills(svg, color);
5976
7332
  }
5977
7333
  function _recolorSvgFills(svg, color) {
7334
+ var _a;
5978
7335
  const safe = escapeXmlAttr(color);
7336
+ try {
7337
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svg}</svg>`;
7338
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7339
+ const root = doc.documentElement;
7340
+ if (root && root.nodeName !== "parsererror") {
7341
+ for (const g of Array.from(root.querySelectorAll("linearGradient, radialGradient, pattern"))) {
7342
+ try {
7343
+ (_a = g.parentNode) == null ? void 0 : _a.removeChild(g);
7344
+ } catch {
7345
+ }
7346
+ }
7347
+ for (const el of Array.from(root.querySelectorAll("text, tspan, path, rect"))) {
7348
+ const cur = el.getAttribute("fill");
7349
+ if (cur && (cur.trim().toLowerCase() === "none" || cur.trim().toLowerCase() === "transparent")) continue;
7350
+ const style = el.getAttribute("style");
7351
+ if (style && /fill\s*:/i.test(style)) {
7352
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7353
+ if (cleaned) el.setAttribute("style", cleaned);
7354
+ else el.removeAttribute("style");
7355
+ }
7356
+ el.setAttribute("fill", color);
7357
+ }
7358
+ let out2 = "";
7359
+ for (const child of Array.from(root.childNodes)) {
7360
+ out2 += new XMLSerializer().serializeToString(child);
7361
+ }
7362
+ return out2;
7363
+ }
7364
+ } catch {
7365
+ }
5979
7366
  let out = svg.replace(
5980
7367
  /(<(?:text|tspan|path|rect)\b[^>]*?\sfill=")([^"]*)("[^>]*>)/gi,
5981
7368
  (_m, pre, val, post) => {
@@ -5991,6 +7378,11 @@ function _recolorSvgFills(svg, color) {
5991
7378
  return pre + replaced + post;
5992
7379
  }
5993
7380
  );
7381
+ out = out.replace(/<(text|tspan|path)\b([^>]*)>/gi, (m, tag, attrs) => {
7382
+ if (/\sfill=/i.test(attrs)) return m;
7383
+ if (/style="[^"]*\bfill\s*:/i.test(attrs)) return m;
7384
+ return `<${tag}${attrs} fill="${safe}">`;
7385
+ });
5994
7386
  return out;
5995
7387
  }
5996
7388
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
@@ -6129,6 +7521,239 @@ function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
6129
7521
  function escapeXmlAttr(s) {
6130
7522
  return String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6131
7523
  }
7524
+ function buildWarpRibbonD(obj, pT, pR, pB, pL, rxTL, rxTR, rxBR, rxBL) {
7525
+ const tp = obj == null ? void 0 : obj.textPath;
7526
+ if (!tp || !tp.preset || tp.preset === "none") return null;
7527
+ if (tp.preset === "rise" || tp.preset === "angle") return null;
7528
+ const w = Number(obj.width) || 0;
7529
+ const h = Number(obj.height) || 0;
7530
+ const fs = Number(obj.fontSize) || 16;
7531
+ const resolved = resolveTextPath(tp, w, fs);
7532
+ if (!resolved) return null;
7533
+ const pathEl = measurePath(resolved.d);
7534
+ if (!pathEl) return null;
7535
+ const len = pathEl.getTotalLength();
7536
+ if (!Number.isFinite(len) || len <= 0) return null;
7537
+ const halfW = w / 2;
7538
+ const halfH = h / 2;
7539
+ const topT = halfH + Math.max(0, pT);
7540
+ const botT = halfH + Math.max(0, pB);
7541
+ const padL = Math.max(0, pL);
7542
+ const padR = Math.max(0, pR);
7543
+ const sample = (s) => {
7544
+ const ss = Math.max(0, Math.min(len, s));
7545
+ const p = pathEl.getPointAtLength(ss);
7546
+ const a = pathEl.getPointAtLength(Math.min(len, ss + 0.5));
7547
+ const b = pathEl.getPointAtLength(Math.max(0, ss - 0.5));
7548
+ let tx = a.x - b.x;
7549
+ let ty = a.y - b.y;
7550
+ const tl = Math.hypot(tx, ty) || 1;
7551
+ tx /= tl;
7552
+ ty /= tl;
7553
+ return { px: p.x - halfW, py: p.y - halfH, tx, ty, nx: ty, ny: -tx };
7554
+ };
7555
+ const SAMPLES = 96;
7556
+ const topPts = [];
7557
+ const botPts = [];
7558
+ for (let i = 0; i <= SAMPLES; i++) {
7559
+ const s = len * i / SAMPLES;
7560
+ const { px, py, nx, ny } = sample(s);
7561
+ topPts.push([px + nx * topT, py + ny * topT]);
7562
+ botPts.push([px - nx * botT, py - ny * botT]);
7563
+ }
7564
+ const s0 = sample(0);
7565
+ const s1 = sample(len);
7566
+ const TL = [s0.px - s0.tx * padL + s0.nx * topT, s0.py - s0.ty * padL + s0.ny * topT];
7567
+ const BL = [s0.px - s0.tx * padL - s0.nx * botT, s0.py - s0.ty * padL - s0.ny * botT];
7568
+ const TR = [s1.px + s1.tx * padR + s1.nx * topT, s1.py + s1.ty * padR + s1.ny * topT];
7569
+ const BR = [s1.px + s1.tx * padR - s1.nx * botT, s1.py + s1.ty * padR - s1.ny * botT];
7570
+ topPts[0] = TL;
7571
+ topPts[topPts.length - 1] = TR;
7572
+ botPts[0] = BL;
7573
+ botPts[botPts.length - 1] = BR;
7574
+ const capH = topT + botT;
7575
+ const maxLeftR = Math.min(capH * 0.5, len * 0.5 + padL);
7576
+ const maxRightR = Math.min(capH * 0.5, len * 0.5 + padR);
7577
+ const clamp2 = (r, m) => Math.max(0, Math.min(Number(r) || 0, m));
7578
+ const cTL = clamp2(rxTL, maxLeftR);
7579
+ const cBL = clamp2(rxBL, maxLeftR);
7580
+ const cTR = clamp2(rxTR, maxRightR);
7581
+ const cBR = clamp2(rxBR, maxRightR);
7582
+ const dist = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
7583
+ const inset = (from, toward, d) => {
7584
+ const dx = toward[0] - from[0];
7585
+ const dy = toward[1] - from[1];
7586
+ const L = Math.hypot(dx, dy) || 1;
7587
+ const k = Math.min(d, L) / L;
7588
+ return [from[0] + dx * k, from[1] + dy * k];
7589
+ };
7590
+ const walkEdge = (pts, target, fromStart) => {
7591
+ if (target <= 0) {
7592
+ const idx = fromStart ? 0 : pts.length - 1;
7593
+ return { pt: [pts[idx][0], pts[idx][1]], cutIdx: idx };
7594
+ }
7595
+ let acc = 0;
7596
+ if (fromStart) {
7597
+ for (let i = 1; i < pts.length; i++) {
7598
+ const seg = dist(pts[i - 1], pts[i]);
7599
+ if (acc + seg >= target) {
7600
+ const k = (target - acc) / (seg || 1);
7601
+ const pt = [
7602
+ pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k,
7603
+ pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k
7604
+ ];
7605
+ return { pt, cutIdx: i - 1 };
7606
+ }
7607
+ acc += seg;
7608
+ }
7609
+ return { pt: pts[pts.length - 1], cutIdx: pts.length - 1 };
7610
+ }
7611
+ for (let i = pts.length - 2; i >= 0; i--) {
7612
+ const seg = dist(pts[i + 1], pts[i]);
7613
+ if (acc + seg >= target) {
7614
+ const k = (target - acc) / (seg || 1);
7615
+ const pt = [
7616
+ pts[i + 1][0] + (pts[i][0] - pts[i + 1][0]) * k,
7617
+ pts[i + 1][1] + (pts[i][1] - pts[i + 1][1]) * k
7618
+ ];
7619
+ return { pt, cutIdx: i + 1 };
7620
+ }
7621
+ acc += seg;
7622
+ }
7623
+ return { pt: pts[0], cutIdx: 0 };
7624
+ };
7625
+ const topStart = walkEdge(topPts, cTL, true);
7626
+ const topEnd = walkEdge(topPts, cTR, false);
7627
+ const botStart = walkEdge(botPts, cBL, true);
7628
+ const botEnd = walkEdge(botPts, cBR, false);
7629
+ const insTL_top = topStart.pt;
7630
+ const insTR_top = topEnd.pt;
7631
+ const insBL_bot = botStart.pt;
7632
+ const insBR_bot = botEnd.pt;
7633
+ const insTL_cap = inset(TL, BL, cTL);
7634
+ const insBL_cap = inset(BL, TL, cBL);
7635
+ const insBR_cap = inset(BR, TR, cBR);
7636
+ const insTR_cap = inset(TR, BR, cTR);
7637
+ const fmt = (p) => `${p[0].toFixed(2)} ${p[1].toFixed(2)}`;
7638
+ const parts = [];
7639
+ parts.push(`M ${fmt(insTL_top)}`);
7640
+ for (let i = topStart.cutIdx + 1; i <= topEnd.cutIdx; i++) parts.push(`L ${fmt(topPts[i])}`);
7641
+ parts.push(`L ${fmt(insTR_top)}`);
7642
+ parts.push(`Q ${fmt(TR)} ${fmt(insTR_cap)}`);
7643
+ parts.push(`L ${fmt(insBR_cap)}`);
7644
+ parts.push(`Q ${fmt(BR)} ${fmt(insBR_bot)}`);
7645
+ for (let i = botEnd.cutIdx - 1; i >= botStart.cutIdx + 1; i--) parts.push(`L ${fmt(botPts[i])}`);
7646
+ parts.push(`L ${fmt(insBL_bot)}`);
7647
+ parts.push(`Q ${fmt(BL)} ${fmt(insBL_cap)}`);
7648
+ parts.push(`L ${fmt(insTL_cap)}`);
7649
+ parts.push(`Q ${fmt(TL)} ${fmt(insTL_top)}`);
7650
+ parts.push("Z");
7651
+ return parts.join(" ");
7652
+ }
7653
+ function computeRibbonBoundsFor(obj, pT, pR, pB, pL) {
7654
+ const tp = obj == null ? void 0 : obj.textPath;
7655
+ if (!tp || !tp.preset || tp.preset === "none") {
7656
+ const w2 = Number(obj.width) || 0;
7657
+ const h2 = Number(obj.height) || 0;
7658
+ return { x: -w2 / 2 - pL, y: -h2 / 2 - pT, w: w2 + pL + pR, h: h2 + pT + pB };
7659
+ }
7660
+ const w = Number(obj.width) || 0;
7661
+ const h = Number(obj.height) || 0;
7662
+ const fs = Number(obj.fontSize) || 16;
7663
+ const resolved = resolveTextPath(tp, w, fs);
7664
+ const pathEl = resolved ? measurePath(resolved.d) : null;
7665
+ const len = pathEl ? pathEl.getTotalLength() : 0;
7666
+ if (!pathEl || !Number.isFinite(len) || len <= 0) {
7667
+ return { x: -w / 2 - pL, y: -h / 2 - pT, w: w + pL + pR, h: h + pT + pB };
7668
+ }
7669
+ const halfW = w / 2;
7670
+ const halfH = h / 2;
7671
+ const topT = halfH + Math.max(0, pT);
7672
+ const botT = halfH + Math.max(0, pB);
7673
+ const STEPS = 48;
7674
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
7675
+ const ext = (x, y) => {
7676
+ if (x < minX) minX = x;
7677
+ if (y < minY) minY = y;
7678
+ if (x > maxX) maxX = x;
7679
+ if (y > maxY) maxY = y;
7680
+ };
7681
+ for (let i = 0; i <= STEPS; i++) {
7682
+ const s = len * i / STEPS;
7683
+ const p = pathEl.getPointAtLength(s);
7684
+ const a = pathEl.getPointAtLength(Math.min(len, s + 0.5));
7685
+ const b = pathEl.getPointAtLength(Math.max(0, s - 0.5));
7686
+ let tx = a.x - b.x;
7687
+ let ty = a.y - b.y;
7688
+ const tl = Math.hypot(tx, ty) || 1;
7689
+ tx /= tl;
7690
+ ty /= tl;
7691
+ const nx = ty, ny = -tx;
7692
+ const px = p.x - halfW, py = p.y - halfH;
7693
+ ext(px + nx * topT, py + ny * topT);
7694
+ ext(px - nx * botT, py - ny * botT);
7695
+ }
7696
+ if (pL > 0 || pR > 0) {
7697
+ minX -= pL;
7698
+ maxX += pR;
7699
+ }
7700
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
7701
+ }
7702
+ function normalizeStops(stops) {
7703
+ 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);
7704
+ if (out.length === 0) return out;
7705
+ if (out[0].offset > 0) out.unshift({ color: out[0].color, offset: 0 });
7706
+ if (out[out.length - 1].offset < 1) out.push({ color: out[out.length - 1].color, offset: 1 });
7707
+ return out;
7708
+ }
7709
+ function buildCanvasGradient(ctx, g, bx, by, bw, bh) {
7710
+ const stops = normalizeStops(g.stops);
7711
+ if (stops.length === 0) return "#000";
7712
+ if (g.type === "radial") {
7713
+ const cx = bx + (g.cx ?? 0.5) * bw;
7714
+ const cy = by + (g.cy ?? 0.5) * bh;
7715
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7716
+ const cg2 = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);
7717
+ stops.forEach((s) => {
7718
+ try {
7719
+ cg2.addColorStop(s.offset, s.color);
7720
+ } catch {
7721
+ }
7722
+ });
7723
+ return cg2;
7724
+ }
7725
+ const angle = g.angle ?? 90;
7726
+ const rad = angle * Math.PI / 180;
7727
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7728
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7729
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7730
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7731
+ const cg = ctx.createLinearGradient(x1, y1, x2, y2);
7732
+ stops.forEach((s) => {
7733
+ try {
7734
+ cg.addColorStop(s.offset, s.color);
7735
+ } catch {
7736
+ }
7737
+ });
7738
+ return cg;
7739
+ }
7740
+ function buildSvgGradientDef(g, id, bx, by, bw, bh) {
7741
+ const stops = normalizeStops(g.stops);
7742
+ const stopsXml = stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeXmlAttr(s.color)}" />`).join("");
7743
+ if (g.type === "radial") {
7744
+ const cx = bx + (g.cx ?? 0.5) * bw;
7745
+ const cy = by + (g.cy ?? 0.5) * bh;
7746
+ const r = Math.max(1, (g.r ?? 0.5) * Math.max(bw, bh));
7747
+ 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>`;
7748
+ }
7749
+ const angle = g.angle ?? 90;
7750
+ const rad = angle * Math.PI / 180;
7751
+ const x1 = bx + (0.5 - Math.sin(rad) * 0.5) * bw;
7752
+ const y1 = by + (0.5 + Math.cos(rad) * 0.5) * bh;
7753
+ const x2 = bx + (0.5 + Math.sin(rad) * 0.5) * bw;
7754
+ const y2 = by + (0.5 - Math.cos(rad) * 0.5) * bh;
7755
+ return `<linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${x1.toFixed(3)}" y1="${y1.toFixed(3)}" x2="${x2.toFixed(3)}" y2="${y2.toFixed(3)}">${stopsXml}</linearGradient>`;
7756
+ }
6132
7757
  function extractGInnerMarkup(markup) {
6133
7758
  const openMatch = markup.match(/^\s*<g\b[^>]*>/);
6134
7759
  if (!openMatch) return markup;
@@ -6136,6 +7761,37 @@ function extractGInnerMarkup(markup) {
6136
7761
  if (closeIdx <= openMatch[0].length) return markup;
6137
7762
  return markup.slice(openMatch[0].length, closeIdx);
6138
7763
  }
7764
+ function outlineTextSvgForLineShadow(inner, strokeColor, strokeWidth) {
7765
+ if (!inner) return "";
7766
+ try {
7767
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${inner}</svg>`;
7768
+ const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
7769
+ const root = doc.documentElement;
7770
+ if (!root || root.nodeName === "parsererror") return "";
7771
+ const targets = Array.from(root.querySelectorAll("text, tspan"));
7772
+ if (!targets.length) return "";
7773
+ for (const el of targets) {
7774
+ const style = el.getAttribute("style");
7775
+ if (style) {
7776
+ const cleaned = style.replace(/(?:^|;)\s*fill\s*:[^;]*/gi, "").replace(/(?:^|;)\s*stroke(?:-[a-z]+)?\s*:[^;]*/gi, "").replace(/^;+/, "").trim();
7777
+ if (cleaned) el.setAttribute("style", cleaned);
7778
+ else el.removeAttribute("style");
7779
+ }
7780
+ el.setAttribute("fill", "none");
7781
+ el.setAttribute("stroke", strokeColor);
7782
+ el.setAttribute("stroke-width", strokeWidth.toFixed(3));
7783
+ el.setAttribute("stroke-linejoin", "round");
7784
+ el.setAttribute("paint-order", "stroke");
7785
+ }
7786
+ let out = "";
7787
+ for (const child of Array.from(root.childNodes)) {
7788
+ out += new XMLSerializer().serializeToString(child);
7789
+ }
7790
+ return out;
7791
+ } catch {
7792
+ return "";
7793
+ }
7794
+ }
6139
7795
  const TRIANGLE_STROKE_MITER_LIMIT = 1e6;
6140
7796
  const toSafeNumber = (value, fallback = 0) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
6141
7797
  function normalizeShapeType(shapeType) {
@@ -6764,7 +8420,8 @@ function createText(element) {
6764
8420
  // same value as a fit-target, so the rendered box and the shrink target
6765
8421
  // stay in sync (parity with the Use page / EC2 renderer).
6766
8422
  ...(element.minBoxHeight ?? 0) > 0 ? { minBoxHeight: element.minBoxHeight } : {},
6767
- verticalAlign: element.verticalAlign || "top"
8423
+ verticalAlign: element.verticalAlign || "top",
8424
+ ...element.textPath && element.textPath.preset && element.textPath.preset !== "none" ? { textPath: element.textPath } : {}
6768
8425
  });
6769
8426
  textbox.__formattingEnabled = formattingEnabled;
6770
8427
  textbox.initDimensions();
@@ -6871,6 +8528,9 @@ function createFabricObject(element) {
6871
8528
  if (element.type === "shape" || element.type === "line" || element.type === "text") {
6872
8529
  applyInitialGradients(obj, element);
6873
8530
  }
8531
+ if (element.type === "text" && obj instanceof fabric__namespace.Textbox) {
8532
+ applyTextPathControls(obj);
8533
+ }
6874
8534
  }
6875
8535
  return obj;
6876
8536
  }
@@ -7057,7 +8717,7 @@ const progressDefinition = {
7057
8717
  ],
7058
8718
  render: renderProgressSvg
7059
8719
  };
7060
- function escapeXml(str) {
8720
+ function escapeXml$1(str) {
7061
8721
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7062
8722
  }
7063
8723
  function estimateTextWidth(text, fontSize, fontWeight) {
@@ -7104,7 +8764,7 @@ function renderTableSvg(props, width, height) {
7104
8764
  if (text) {
7105
8765
  const maxChars = Math.max(3, Math.floor(cellW / (fontSize * 0.55)));
7106
8766
  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>`;
8767
+ 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
8768
  }
7109
8769
  }
7110
8770
  }
@@ -7171,7 +8831,7 @@ function renderAvatarSvg(props, width, height) {
7171
8831
  }
7172
8832
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
7173
8833
  ${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>
8834
+ <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
8835
  </svg>`;
7176
8836
  }
7177
8837
  const avatarDefinition = {
@@ -7262,7 +8922,7 @@ function renderBadgeInternal(props, width, height) {
7262
8922
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${finalWidth}" height="${height}" viewBox="0 0 ${finalWidth} ${height}">
7263
8923
  <rect x="${inset}" y="${inset}" width="${finalWidth - borderWidth}" height="${height - borderWidth}" rx="${rx}" fill="${bgColor}" stroke="${borderColor}" stroke-width="${borderWidth}"/>
7264
8924
  ${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>
8925
+ <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
8926
  </svg>`;
7267
8927
  return { svg, computedWidth, anchor };
7268
8928
  }
@@ -8936,6 +10596,9 @@ const PageCanvas = react.forwardRef(
8936
10596
  if (typeof baked === "number" && baked > 0) {
8937
10597
  elementUpdate.minBoxHeight = baked;
8938
10598
  }
10599
+ if (obj.textPath) {
10600
+ elementUpdate.textPath = obj.textPath;
10601
+ }
8939
10602
  }
8940
10603
  if (sourceElement && sourceElement.opacity !== void 0) {
8941
10604
  elementUpdate.opacity = sourceElement.opacity;
@@ -9670,6 +11333,7 @@ const PageCanvas = react.forwardRef(
9670
11333
  (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
11334
  JSON.stringify({
9672
11335
  c: element.textBgColor ?? null,
11336
+ g: element.textBgGradient ?? null,
9673
11337
  p: element.textBgPadding ?? 0,
9674
11338
  pt: element.textBgPaddingTop ?? null,
9675
11339
  pr: element.textBgPaddingRight ?? null,
@@ -9688,7 +11352,7 @@ const PageCanvas = react.forwardRef(
9688
11352
  st: element.textShadowAffectsText !== false,
9689
11353
  sa: element.textShadowAffectsBg !== false
9690
11354
  }) !== (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");
11355
+ 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
11356
  const forceApplyFromPanel = syncTriggeredByPanelRef.current;
9693
11357
  const noPropsOrPositionChanged = !positionChanged && !otherPropsChanged;
9694
11358
  if (noPropsOrPositionChanged && !forceApplyFromPanel || visibilityUpdateInProgressRef.current) {
@@ -10122,7 +11786,7 @@ const PageCanvas = react.forwardRef(
10122
11786
  });
10123
11787
  }, [selectedIds, isActive, ready, elements]);
10124
11788
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
10125
- var _a, _b;
11789
+ var _a, _b, _c;
10126
11790
  const fc = fabricRef.current;
10127
11791
  if (fc && isTransforming(fc)) {
10128
11792
  return;
@@ -10399,6 +12063,8 @@ const PageCanvas = react.forwardRef(
10399
12063
  obj.setCoords();
10400
12064
  }
10401
12065
  if (!isLine) {
12066
+ const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
12067
+ const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
10402
12068
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
10403
12069
  if (!skipPositionUpdate && obj instanceof fabric__namespace.FabricImage && obj.originX === "center") {
10404
12070
  const vW = rW * effectiveScaleX;
@@ -10414,7 +12080,7 @@ const PageCanvas = react.forwardRef(
10414
12080
  scaleY: effectiveScaleY,
10415
12081
  angle: element.angle ?? 0,
10416
12082
  skewX: element.skewX ?? 0,
10417
- skewY: element.skewY ?? 0
12083
+ skewY: appliedSkewY
10418
12084
  });
10419
12085
  } else {
10420
12086
  obj.set({
@@ -10434,7 +12100,7 @@ const PageCanvas = react.forwardRef(
10434
12100
  width: rW,
10435
12101
  angle: element.angle ?? 0,
10436
12102
  skewX: element.skewX ?? 0,
10437
- skewY: element.skewY ?? 0,
12103
+ skewY: appliedSkewY,
10438
12104
  scaleX: effectiveScaleX * baseScaleX,
10439
12105
  scaleY: effectiveScaleY * baseScaleY
10440
12106
  });
@@ -10566,7 +12232,9 @@ const PageCanvas = react.forwardRef(
10566
12232
  dynamicMinWidth: 0,
10567
12233
  fontSize,
10568
12234
  fontFamily: element.fontFamily || "Open Sans",
10569
- fill: element.fill || "#1a1a1a",
12235
+ // If a gradient fill is active, do not briefly overwrite it with the
12236
+ // stored solid fallback while applying textPath/panel updates.
12237
+ fill: element.fillGradient && isGradientConfig(element.fillGradient) ? obj.fill : element.fill || "#1a1a1a",
10570
12238
  fontWeight: element.fontWeight || 400,
10571
12239
  textAlign: element.textAlign || "left",
10572
12240
  fontStyle: element.fontStyle || "normal",
@@ -10583,6 +12251,13 @@ const PageCanvas = react.forwardRef(
10583
12251
  const minBoxH = Math.max(0, Number(element.minBoxHeight) || 0);
10584
12252
  obj.verticalAlign = valign;
10585
12253
  obj.minBoxHeight = minBoxH;
12254
+ const nextTextPath = element.textPath;
12255
+ if (nextTextPath && nextTextPath.preset && nextTextPath.preset !== "none") {
12256
+ obj.textPath = nextTextPath;
12257
+ } else {
12258
+ obj.textPath = void 0;
12259
+ }
12260
+ applyTextPathControls(obj);
10586
12261
  if (element.formattingEnabled === true) {
10587
12262
  obj.styles = parsedStyles || {};
10588
12263
  } else {
@@ -10605,9 +12280,10 @@ const PageCanvas = react.forwardRef(
10605
12280
  } catch {
10606
12281
  }
10607
12282
  obj.dirty = true;
10608
- (_b = obj.setCoords) == null ? void 0 : _b.call(obj);
12283
+ (_c = obj.setCoords) == null ? void 0 : _c.call(obj);
10609
12284
  obj.__lastTextBgShadowJson = JSON.stringify({
10610
12285
  c: element.textBgColor ?? null,
12286
+ g: element.textBgGradient ?? null,
10611
12287
  p: element.textBgPadding ?? 0,
10612
12288
  pt: element.textBgPaddingTop ?? null,
10613
12289
  pr: element.textBgPaddingRight ?? null,
@@ -10624,7 +12300,8 @@ const PageCanvas = react.forwardRef(
10624
12300
  sx: element.textShadowOffsetX ?? 0,
10625
12301
  sy: element.textShadowOffsetY ?? 0,
10626
12302
  st: element.textShadowAffectsText !== false,
10627
- sa: element.textShadowAffectsBg !== false
12303
+ sa: element.textShadowAffectsBg !== false,
12304
+ sty: element.textShadowType ?? null
10628
12305
  });
10629
12306
  obj.dirty = true;
10630
12307
  } catch (err) {
@@ -11037,9 +12714,7 @@ const PageCanvas = react.forwardRef(
11037
12714
  }
11038
12715
  try {
11039
12716
  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)) {
12717
+ if (isSvgImage(imageUrl, element.sourceFormat)) {
11043
12718
  const normalized = await getNormalizedSvgUrl(imageUrl, element.svgColorMap, element.sourceFormat);
11044
12719
  if (!isLatestRequest()) return;
11045
12720
  if (normalized) url = normalized;
@@ -15736,7 +17411,7 @@ async function resolveTemplateData(options) {
15736
17411
  }
15737
17412
  }
15738
17413
  if (repeatableSectionsInput.length > 0) {
15739
- paintRepeatableSections(config, repeatableSectionsInput, inlineFormSchema == null ? void 0 : inlineFormSchema.entryFilters);
17414
+ paintRepeatableSections(config, repeatableSectionsInput);
15740
17415
  }
15741
17416
  }
15742
17417
  const mergedFormData = {
@@ -15807,7 +17482,7 @@ async function resolveFromForm(options) {
15807
17482
  normalizeLayoutModes(templateConfig);
15808
17483
  const repeatableFromSchema = templateFormSchema == null ? void 0 : templateFormSchema.repeatableSections;
15809
17484
  if ((repeatableFromSchema == null ? void 0 : repeatableFromSchema.length) && templateConfig.pages) {
15810
- paintRepeatableSections(templateConfig, repeatableFromSchema, templateFormSchema == null ? void 0 : templateFormSchema.entryFilters);
17485
+ paintRepeatableSections(templateConfig, repeatableFromSchema);
15811
17486
  }
15812
17487
  const schemaSections = getRenderableFormSections(formSchema);
15813
17488
  const repeatableNodeMap = /* @__PURE__ */ new Map();
@@ -16806,6 +18481,312 @@ function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
16806
18481
  function isTextboxLike(obj) {
16807
18482
  return !!obj && (obj instanceof fabric__namespace.Textbox || obj.type === "textbox" || Array.isArray(obj == null ? void 0 : obj._textLines) && typeof obj.getLineWidth === "function");
16808
18483
  }
18484
+ function escapeXml(s) {
18485
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
18486
+ }
18487
+ function ensureFabricGradientDef(doc, obj, width, height) {
18488
+ const grad = obj == null ? void 0 : obj.fill;
18489
+ if (!grad || typeof grad !== "object" || !Array.isArray(grad.colorStops) || !grad.coords) return "";
18490
+ const id = `pixldocs_text_grad_${String(obj.__docuforgeId || grad.id || Math.random()).replace(/[^a-zA-Z0-9_-]/g, "_")}`;
18491
+ if (doc.getElementById(id)) return `url(#${id})`;
18492
+ const ns = "http://www.w3.org/2000/svg";
18493
+ let defs = doc.querySelector("defs");
18494
+ if (!defs) {
18495
+ defs = doc.createElementNS(ns, "defs");
18496
+ doc.documentElement.insertBefore(defs, doc.documentElement.firstChild);
18497
+ }
18498
+ const c = grad.coords || {};
18499
+ const el = doc.createElementNS(ns, grad.type === "radial" ? "radialGradient" : "linearGradient");
18500
+ el.setAttribute("id", id);
18501
+ el.setAttribute("gradientUnits", "userSpaceOnUse");
18502
+ if (grad.type === "radial") {
18503
+ el.setAttribute("cx", (Number(c.x2 ?? c.x1 ?? width / 2) - width / 2).toFixed(3));
18504
+ el.setAttribute("cy", (Number(c.y2 ?? c.y1 ?? height / 2) - height / 2).toFixed(3));
18505
+ el.setAttribute("fx", (Number(c.x1 ?? c.x2 ?? width / 2) - width / 2).toFixed(3));
18506
+ el.setAttribute("fy", (Number(c.y1 ?? c.y2 ?? height / 2) - height / 2).toFixed(3));
18507
+ el.setAttribute("r", Math.max(0.1, Number(c.r2 ?? c.r ?? Math.max(width, height) / 2)).toFixed(3));
18508
+ } else {
18509
+ el.setAttribute("x1", (Number(c.x1) - width / 2).toFixed(3));
18510
+ el.setAttribute("y1", (Number(c.y1) - height / 2).toFixed(3));
18511
+ el.setAttribute("x2", (Number(c.x2 ?? width) - width / 2).toFixed(3));
18512
+ el.setAttribute("y2", (Number(c.y2) - height / 2).toFixed(3));
18513
+ }
18514
+ for (const stop of grad.colorStops) {
18515
+ const stopEl = doc.createElementNS(ns, "stop");
18516
+ stopEl.setAttribute("offset", String(Math.max(0, Math.min(1, Number(stop.offset) || 0))));
18517
+ stopEl.setAttribute("stop-color", stop.color || "#000000");
18518
+ el.appendChild(stopEl);
18519
+ }
18520
+ defs.appendChild(el);
18521
+ return `url(#${id})`;
18522
+ }
18523
+ function warpTextboxSvgAlongPath(svg, obj) {
18524
+ var _a, _b, _c, _d, _e;
18525
+ const tp = obj == null ? void 0 : obj.textPath;
18526
+ if (!tp || !tp.preset || tp.preset === "none") return svg;
18527
+ if (tp.preset === "rise" || tp.preset === "angle") {
18528
+ const w2 = Number(obj.width) || 0;
18529
+ const h2 = Number(obj.height) || 0;
18530
+ const fs2 = Number(obj.fontSize) || 16;
18531
+ const ep = tp.endpoints;
18532
+ const defaultLeftY = fs2 * 1.5;
18533
+ const defaultRightY = fs2 * 0.5;
18534
+ const leftY = ep && Number.isFinite(ep.leftY) ? ep.leftY : defaultLeftY;
18535
+ const rightY = ep && Number.isFinite(ep.rightY) ? ep.rightY : defaultRightY;
18536
+ const slope = w2 > 0 ? (rightY - leftY) / w2 : 0;
18537
+ const dy = (leftY + rightY) / 2 - h2 / 2;
18538
+ if (Math.abs(slope) < 1e-6 && Math.abs(dy) < 1e-6) return svg;
18539
+ let doc2;
18540
+ try {
18541
+ doc2 = new DOMParser().parseFromString(svg, "image/svg+xml");
18542
+ } catch {
18543
+ return svg;
18544
+ }
18545
+ const textEls2 = Array.from(doc2.querySelectorAll("text"));
18546
+ if (!textEls2.length) return svg;
18547
+ const matrix = `matrix(1 ${slope.toFixed(6)} 0 1 0 ${dy.toFixed(3)})`;
18548
+ for (const textEl2 of textEls2) {
18549
+ const existing = textEl2.getAttribute("transform");
18550
+ textEl2.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18551
+ }
18552
+ const bgShapes = Array.from(doc2.querySelectorAll("path.__pdTextBgShape"));
18553
+ for (const bgEl of bgShapes) {
18554
+ const existing = bgEl.getAttribute("transform");
18555
+ bgEl.setAttribute("transform", existing ? `${matrix} ${existing}` : matrix);
18556
+ }
18557
+ return new XMLSerializer().serializeToString(doc2.documentElement);
18558
+ }
18559
+ const resolved = resolveTextPath(tp, Number(obj.width) || 0, Number(obj.fontSize) || 16);
18560
+ if (!resolved) return svg;
18561
+ const pathEl = measurePath(resolved.d);
18562
+ if (!pathEl) return svg;
18563
+ const totalLen = pathEl.getTotalLength();
18564
+ if (!Number.isFinite(totalLen) || totalLen <= 0) return svg;
18565
+ const plain = String(obj.text || "").replace(/\n/g, " ");
18566
+ if (!plain) return svg;
18567
+ let doc;
18568
+ try {
18569
+ doc = new DOMParser().parseFromString(svg, "image/svg+xml");
18570
+ } catch {
18571
+ return svg;
18572
+ }
18573
+ for (const marker of Array.from(doc.querySelectorAll("g.__pdShadowRaster"))) {
18574
+ try {
18575
+ (_a = marker.parentNode) == null ? void 0 : _a.removeChild(marker);
18576
+ } catch {
18577
+ }
18578
+ }
18579
+ for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
18580
+ try {
18581
+ (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
18582
+ } catch {
18583
+ }
18584
+ }
18585
+ const textEls = Array.from(doc.querySelectorAll("text"));
18586
+ if (!textEls.length) return svg;
18587
+ const hasFilterInChain = (el) => {
18588
+ let cur = el;
18589
+ while (cur && cur !== doc.documentElement) {
18590
+ if (cur.getAttribute("filter") || /filter\s*:/i.test(cur.getAttribute("style") || "")) return true;
18591
+ cur = cur.parentElement;
18592
+ }
18593
+ return false;
18594
+ };
18595
+ const textEl = textEls.find((el) => !hasFilterInChain(el)) || textEls[textEls.length - 1] || textEls[0];
18596
+ const ff = obj.fontFamily || "Open Sans";
18597
+ const fs = Number(obj.fontSize) || 16;
18598
+ const fw = obj.fontWeight ?? 400;
18599
+ const fst = obj.fontStyle || "normal";
18600
+ const readFill = (el) => {
18601
+ var _a2, _b2;
18602
+ if (!el) return "";
18603
+ 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()) || "";
18604
+ };
18605
+ const w = Number(obj.width) || 0;
18606
+ const h = Number(obj.height) || 0;
18607
+ const synthesizedGradientFill = ensureFabricGradientDef(doc, obj, w, h);
18608
+ const fabricGradientFill = obj.fill && typeof obj.fill === "object" && obj.fill.id != null ? `url(#SVGID_${obj.fill.id})` : "";
18609
+ const fillAttr = synthesizedGradientFill || readFill(textEl) || readFill(textEl.querySelector("tspan")) || fabricGradientFill || (typeof obj.fill === "string" ? obj.fill : "#000");
18610
+ const stripFilter = (el) => {
18611
+ if (!el) return;
18612
+ el.removeAttribute("filter");
18613
+ const style = el.getAttribute("style");
18614
+ if (style && /filter\s*:/i.test(style)) {
18615
+ el.setAttribute("style", style.replace(/(?:^|;)\s*filter\s*:[^;]*/gi, "").replace(/^;+/, ""));
18616
+ }
18617
+ };
18618
+ for (const el of Array.from(doc.querySelectorAll("*"))) stripFilter(el);
18619
+ for (const filter of Array.from(doc.querySelectorAll("filter"))) {
18620
+ try {
18621
+ (_c = filter.parentNode) == null ? void 0 : _c.removeChild(filter);
18622
+ } catch {
18623
+ }
18624
+ }
18625
+ let shadowConfig = null;
18626
+ if (obj.shadow) {
18627
+ const sh = obj.shadow;
18628
+ const color = sh.color || "rgba(0,0,0,0.5)";
18629
+ const blur = Math.max(0, Number(sh.blur) || 0);
18630
+ const dx = Number(sh.offsetX) || 0;
18631
+ const dy = Number(sh.offsetY) || 0;
18632
+ shadowConfig = { color, dx, dy, blur };
18633
+ }
18634
+ const bgCfg = obj.__pdBg;
18635
+ const extOX = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetX) ?? 0) || 0;
18636
+ const extOY = Number((bgCfg == null ? void 0 : bgCfg.shadowOffsetY) ?? 0) || 0;
18637
+ const extDist = Math.hypot(extOX, extOY);
18638
+ const extColor = bgCfg == null ? void 0 : bgCfg.shadowColor;
18639
+ const hasExtShadowColor = !!extColor && extColor !== "transparent";
18640
+ const affectsText = !bgCfg || bgCfg.shadowAffectsText !== false;
18641
+ const blockShadowActive = !!bgCfg && bgCfg.shadowType === "block" && hasExtShadowColor && extDist > 0 && affectsText;
18642
+ const lineShadowActive = !!bgCfg && bgCfg.shadowType === "line" && hasExtShadowColor && extDist > 0 && affectsText;
18643
+ let blockSteps = 0;
18644
+ let blockStepX = 0;
18645
+ let blockStepY = 0;
18646
+ if (blockShadowActive) {
18647
+ const STEP = 1;
18648
+ blockSteps = Math.min(200, Math.max(1, Math.ceil(extDist / STEP)));
18649
+ blockStepX = extOX / blockSteps;
18650
+ blockStepY = extOY / blockSteps;
18651
+ }
18652
+ 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;
18653
+ const gradId = (fillAttr.match(/url\(#([^)]+)\)/) || [])[1];
18654
+ if (gradId && w > 0 && h > 0) {
18655
+ const grad = doc.getElementById(gradId);
18656
+ if (grad && grad.getAttribute("gradientUnits") !== "userSpaceOnUse") {
18657
+ const tag = grad.tagName.toLowerCase();
18658
+ const num = (v, fallback) => {
18659
+ if (v == null) return fallback;
18660
+ const n = parseFloat(v);
18661
+ return Number.isFinite(n) ? n : fallback;
18662
+ };
18663
+ const mapX = (u) => -w / 2 + u * w;
18664
+ const mapY = (u) => -h / 2 + u * h;
18665
+ grad.setAttribute("gradientUnits", "userSpaceOnUse");
18666
+ if (tag === "lineargradient") {
18667
+ const x1 = num(grad.getAttribute("x1"), 0);
18668
+ const y1 = num(grad.getAttribute("y1"), 0);
18669
+ const x2 = num(grad.getAttribute("x2"), 1);
18670
+ const y2 = num(grad.getAttribute("y2"), 0);
18671
+ grad.setAttribute("x1", mapX(x1).toFixed(3));
18672
+ grad.setAttribute("y1", mapY(y1).toFixed(3));
18673
+ grad.setAttribute("x2", mapX(x2).toFixed(3));
18674
+ grad.setAttribute("y2", mapY(y2).toFixed(3));
18675
+ } else if (tag === "radialgradient") {
18676
+ const cx = num(grad.getAttribute("cx"), 0.5);
18677
+ const cy = num(grad.getAttribute("cy"), 0.5);
18678
+ const r = num(grad.getAttribute("r"), 0.5);
18679
+ const fx = num(grad.getAttribute("fx"), cx);
18680
+ const fy = num(grad.getAttribute("fy"), cy);
18681
+ grad.setAttribute("cx", mapX(cx).toFixed(3));
18682
+ grad.setAttribute("cy", mapY(cy).toFixed(3));
18683
+ grad.setAttribute("fx", mapX(fx).toFixed(3));
18684
+ grad.setAttribute("fy", mapY(fy).toFixed(3));
18685
+ grad.setAttribute("r", (r * Math.max(w, h)).toFixed(3));
18686
+ }
18687
+ }
18688
+ }
18689
+ const measure = document.createElement("canvas").getContext("2d");
18690
+ if (!measure) return svg;
18691
+ measure.font = `${fst} ${fw} ${fs}px "${ff}"`;
18692
+ const chars = Array.from(plain);
18693
+ const spacing = (Number(obj.charSpacing) || 0) * fs / 1e3;
18694
+ const widths = chars.map((c) => measure.measureText(c).width + spacing);
18695
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
18696
+ const baselineDy = resolveTextPathAlphabeticBaselineDy(measure, plain, fs);
18697
+ let cursor = 0;
18698
+ if (obj.textAlign === "center") cursor = Math.max(0, (totalLen - totalWidth) / 2);
18699
+ else if (obj.textAlign === "right") cursor = Math.max(0, totalLen - totalWidth);
18700
+ const halfW = (Number(obj.width) || 0) / 2;
18701
+ const halfH = (Number(obj.height) || 0) / 2;
18702
+ const glyphSvg = [];
18703
+ const shadowSvg = [];
18704
+ const blockShadowSvg = [];
18705
+ const lineShadowSvg = [];
18706
+ let minX = Number.POSITIVE_INFINITY;
18707
+ let minY = Number.POSITIVE_INFINITY;
18708
+ let maxX = Number.NEGATIVE_INFINITY;
18709
+ let maxY = Number.NEGATIVE_INFINITY;
18710
+ for (let i = 0; i < chars.length; i++) {
18711
+ const cw = widths[i];
18712
+ const mid = cursor + cw / 2;
18713
+ cursor += cw;
18714
+ if (mid > totalLen) break;
18715
+ const p = pathEl.getPointAtLength(Math.max(0, mid));
18716
+ const ahead = pathEl.getPointAtLength(Math.min(totalLen, mid + 0.5));
18717
+ const deg = Math.atan2(ahead.y - p.y, ahead.x - p.x) * 180 / Math.PI;
18718
+ const x = p.x - halfW;
18719
+ const y = p.y - halfH;
18720
+ const glyphPad = Math.max(fs, cw) * 0.75;
18721
+ minX = Math.min(minX, x - glyphPad);
18722
+ minY = Math.min(minY, y - glyphPad - fs);
18723
+ maxX = Math.max(maxX, x + glyphPad);
18724
+ maxY = Math.max(maxY, y + glyphPad);
18725
+ const glyphFill = flowStops ? sampleGradientColor(flowStops, chars.length > 1 ? i / (chars.length - 1) : 0) : fillAttr;
18726
+ if (blockShadowActive) {
18727
+ for (let s = blockSteps; s >= 1; s--) {
18728
+ const bx = x + blockStepX * s;
18729
+ const by = y + blockStepY * s;
18730
+ blockShadowSvg.push(
18731
+ `<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>`
18732
+ );
18733
+ }
18734
+ }
18735
+ if (lineShadowActive) {
18736
+ const lx = x + extOX;
18737
+ const ly = y + extOY;
18738
+ const lineStrokeW = LINE_SHADOW_STROKE_WIDTH;
18739
+ lineShadowSvg.push(
18740
+ `<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>`
18741
+ );
18742
+ }
18743
+ if (shadowConfig) {
18744
+ const sx = x + shadowConfig.dx;
18745
+ const sy = y + shadowConfig.dy;
18746
+ shadowSvg.push(
18747
+ `<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>`
18748
+ );
18749
+ }
18750
+ glyphSvg.push(
18751
+ `<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>`
18752
+ );
18753
+ }
18754
+ const replacement = doc.createElementNS("http://www.w3.org/2000/svg", "g");
18755
+ replacement.setAttribute("data-pixldocs-warped", "true");
18756
+ let innerHtml = "";
18757
+ if (shadowConfig && shadowSvg.length) {
18758
+ const blur = Math.max(0, Number(shadowConfig.blur) || 0);
18759
+ if (blur > 0 && Number.isFinite(minX) && Number.isFinite(maxX)) {
18760
+ const pad = Math.max(8, blur * 3);
18761
+ const sMinX = minX + shadowConfig.dx - pad;
18762
+ const sMinY = minY + shadowConfig.dy - pad;
18763
+ const sMaxX = maxX + shadowConfig.dx + pad;
18764
+ const sMaxY = maxY + shadowConfig.dy + pad;
18765
+ const bw = Math.max(1, sMaxX - sMinX);
18766
+ const bh = Math.max(1, sMaxY - sMinY);
18767
+ 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>`;
18768
+ } else {
18769
+ innerHtml += `<g data-pixldocs-warp-shadow-group="true">${shadowSvg.join("")}</g>`;
18770
+ }
18771
+ }
18772
+ if (lineShadowSvg.length) {
18773
+ innerHtml = `<g data-pixldocs-warp-line-shadow-group="true">${lineShadowSvg.join("")}</g>` + innerHtml;
18774
+ }
18775
+ if (blockShadowSvg.length) {
18776
+ innerHtml = `<g data-pixldocs-warp-block-shadow-group="true">${blockShadowSvg.join("")}</g>` + innerHtml;
18777
+ }
18778
+ innerHtml += glyphSvg.join("");
18779
+ replacement.innerHTML = innerHtml;
18780
+ (_d = textEl.parentNode) == null ? void 0 : _d.replaceChild(replacement, textEl);
18781
+ for (let i = 1; i < textEls.length; i++) {
18782
+ try {
18783
+ (_e = textEls[i].parentNode) == null ? void 0 : _e.removeChild(textEls[i]);
18784
+ } catch {
18785
+ }
18786
+ }
18787
+ const serialized = new XMLSerializer().serializeToString(doc.documentElement);
18788
+ return serialized;
18789
+ }
16809
18790
  function stampFabricLineMetricsOnTextSvg(svg, obj) {
16810
18791
  const lines = Array.isArray(obj == null ? void 0 : obj._textLines) ? obj._textLines : [];
16811
18792
  if (!lines.length || typeof (obj == null ? void 0 : obj.getLineWidth) !== "function") return svg;
@@ -16876,6 +18857,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16876
18857
  obj.toSVG = (reviver) => {
16877
18858
  let svg = originalToSVG(reviver);
16878
18859
  if (isTextboxLike(obj)) svg = stampFabricLineMetricsOnTextSvg(svg, obj);
18860
+ if (isTextboxLike(obj)) svg = warpTextboxSvgAlongPath(svg, obj);
16879
18861
  if (imageId) svg = stampPixldocsImageIdOnSvg(svg, imageId);
16880
18862
  return svg;
16881
18863
  };
@@ -16970,9 +18952,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16970
18952
  }
16971
18953
  return svgString;
16972
18954
  }
16973
- const resolvedPackageVersion = "0.5.205";
18955
+ const resolvedPackageVersion = "0.5.207";
16974
18956
  const PACKAGE_VERSION = resolvedPackageVersion;
16975
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.205";
18957
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.207";
16976
18958
  const roundParityValue = (value) => {
16977
18959
  if (typeof value !== "number") return value;
16978
18960
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -17651,7 +19633,7 @@ class PixldocsRenderer {
17651
19633
  await this.waitForCanvasScene(container, cloned, i);
17652
19634
  }
17653
19635
  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"));
19636
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Cz5kaWEh.cjs"));
17655
19637
  const prepared = preparePagesForExport(
17656
19638
  cloned.pages,
17657
19639
  canvasWidth,
@@ -19835,7 +21817,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19835
21817
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19836
21818
  sanitizeSvgTreeForPdf(svgToDraw);
19837
21819
  try {
19838
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-LmnOwVt3.cjs"));
21820
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Cz5kaWEh.cjs"));
19839
21821
  try {
19840
21822
  await logTextMeasurementDiagnostic(svgToDraw);
19841
21823
  } catch {
@@ -19967,7 +21949,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
19967
21949
  );
19968
21950
  }
19969
21951
  try {
19970
- const { prepareSvgTextForPdfMode } = await Promise.resolve().then(() => require("./svgTextToPath-hYM9qTeC.cjs"));
21952
+ const { prepareSvgTextForPdfMode } = await Promise.resolve().then(() => require("./svgTextToPath-BT7lcwLT.cjs"));
19971
21953
  pageSvg = await prepareSvgTextForPdfMode(pageSvg, textMode, fontBaseUrl);
19972
21954
  try {
19973
21955
  dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-shared-text-prep");
@@ -20232,4 +22214,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
20232
22214
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
20233
22215
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
20234
22216
  exports.warmTemplateFromForm = warmTemplateFromForm;
20235
- //# sourceMappingURL=index-CGi2L90_.cjs.map
22217
+ //# sourceMappingURL=index-BqxoYEaM.cjs.map