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