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