@pixldocs/canvas-renderer 0.5.158 → 0.5.160

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.
@@ -5786,12 +5786,16 @@ function bakeTextSvgToFabricLineStarts(svg, obj, w) {
5786
5786
  if (canUseAlignmentAnchor && Number.isFinite(lineW) && lineW > 0) {
5787
5787
  lineAnchors.push({
5788
5788
  x: alignmentAnchor === "end" ? baseStart + lineW : baseStart + lineW / 2,
5789
- anchor: alignmentAnchor
5789
+ anchor: alignmentAnchor,
5790
+ lineWidth: lineW,
5791
+ lineStart: baseStart
5790
5792
  });
5791
5793
  } else {
5792
5794
  lineAnchors.push({
5793
5795
  x: baseStart + (Number.isFinite(firstGlyphAdjust) ? firstGlyphAdjust : 0),
5794
- anchor: "start"
5796
+ anchor: "start",
5797
+ lineWidth: Number.isFinite(lineW) ? lineW : 0,
5798
+ lineStart: baseStart
5795
5799
  });
5796
5800
  }
5797
5801
  }
@@ -5829,7 +5833,7 @@ function bakeTextSvgToFabricLineStarts(svg, obj, w) {
5829
5833
  if (!lineAnchor || typeof lineAnchor.x !== "number" || !Number.isFinite(lineAnchor.x)) return _m;
5830
5834
  const cleanPre = stripAnchorAttrs(pre);
5831
5835
  const cleanPost = stripAnchorAttrs(post);
5832
- return `<tspan${cleanPre} x="${lineAnchor.x.toFixed(3)}" text-anchor="${lineAnchor.anchor}" data-pd-line-anchor="1"${cleanPost}>`;
5836
+ return `<tspan${cleanPre} x="${lineAnchor.x.toFixed(3)}" text-anchor="${lineAnchor.anchor}" data-pd-line-anchor="1" data-pd-line-width="${lineAnchor.lineWidth.toFixed(3)}" data-pd-line-start="${lineAnchor.lineStart.toFixed(3)}"${cleanPost}>`;
5833
5837
  }
5834
5838
  );
5835
5839
  return newSvg;
@@ -15937,6 +15941,121 @@ function PixldocsPreview(props) {
15937
15941
  !canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
15938
15942
  ] });
15939
15943
  }
15944
+ function isFabricTextLike(obj) {
15945
+ return !!obj && (obj.type === "textbox" || obj.type === "i-text" || obj.type === "text" || Array.isArray(obj._textLines) && typeof obj.getLineWidth === "function");
15946
+ }
15947
+ function bakeSelectableTextSvgToFabricLineAnchors(svg, obj) {
15948
+ try {
15949
+ if (/\bdata-pd-line-anchor\s*=\s*"1"/i.test(svg)) return svg;
15950
+ const rawAlign = String((obj == null ? void 0 : obj.textAlign) || "left").toLowerCase();
15951
+ if (!/center|right|end/.test(rawAlign)) return svg;
15952
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
15953
+ const width = Number((obj == null ? void 0 : obj.width) ?? 0);
15954
+ if (!Array.isArray(lines) || lines.length === 0 || !Number.isFinite(width) || width <= 0) return svg;
15955
+ const alignmentAnchor = /right|end/.test(rawAlign) ? "end" : "middle";
15956
+ const hasStyledChars = (() => {
15957
+ const styles = obj == null ? void 0 : obj.styles;
15958
+ if (!styles || typeof styles !== "object") return false;
15959
+ return Object.values(styles).some((line2) => line2 && typeof line2 === "object" && Object.keys(line2).length > 0);
15960
+ })();
15961
+ const canUseLineAnchor = !hasStyledChars && !(obj == null ? void 0 : obj.path) && !Number((obj == null ? void 0 : obj.charSpacing) ?? 0);
15962
+ const halfW = width / 2;
15963
+ const lineAnchors = lines.map((_line, i) => {
15964
+ var _a, _b, _c, _d;
15965
+ let lineLeft = 0;
15966
+ let lineW = 0;
15967
+ let firstGlyphAdjust = 0;
15968
+ try {
15969
+ lineLeft = Number(((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0);
15970
+ } catch {
15971
+ lineLeft = 0;
15972
+ }
15973
+ try {
15974
+ lineW = Number(((_b = obj.getLineWidth) == null ? void 0 : _b.call(obj, i)) ?? 0);
15975
+ } catch {
15976
+ lineW = 0;
15977
+ }
15978
+ try {
15979
+ const firstBox = (_d = (_c = obj.__charBounds) == null ? void 0 : _c[i]) == null ? void 0 : _d[0];
15980
+ const kerned = Number(firstBox == null ? void 0 : firstBox.kernedWidth);
15981
+ const glyphW = Number(firstBox == null ? void 0 : firstBox.width);
15982
+ if (Number.isFinite(kerned) && Number.isFinite(glyphW)) firstGlyphAdjust = kerned - glyphW;
15983
+ } catch {
15984
+ firstGlyphAdjust = 0;
15985
+ }
15986
+ const baseStart = -halfW + (Number.isFinite(lineLeft) ? lineLeft : 0);
15987
+ if (canUseLineAnchor && Number.isFinite(lineW) && lineW > 0) {
15988
+ return {
15989
+ x: alignmentAnchor === "end" ? baseStart + lineW : baseStart + lineW / 2,
15990
+ anchor: alignmentAnchor,
15991
+ lineWidth: lineW,
15992
+ lineStart: baseStart
15993
+ };
15994
+ }
15995
+ return {
15996
+ x: baseStart + (Number.isFinite(firstGlyphAdjust) ? firstGlyphAdjust : 0),
15997
+ anchor: "start",
15998
+ lineWidth: Number.isFinite(lineW) ? lineW : 0,
15999
+ lineStart: baseStart
16000
+ };
16001
+ });
16002
+ const stripAnchorAttrs = (attrs) => attrs.replace(/\s+text-anchor\s*=\s*"[^"]*"/gi, "").replace(/\s+data-pd-line-anchor\s*=\s*"[^"]*"/gi, "").replace(/(\sstyle=")([^"]*)(")/i, (_m, pre, val, post) => {
16003
+ const cleaned = val.replace(/text-anchor\s*:\s*[^;"']+;?/gi, "").replace(/text-align\s*:\s*[^;"']+;?/gi, "").trim();
16004
+ return cleaned ? `${pre}${cleaned}${post}` : "";
16005
+ });
16006
+ const textOpenMatch = svg.match(/<text\b([^>]*)>/i);
16007
+ if (!textOpenMatch) return svg;
16008
+ const textAttrs = stripAnchorAttrs(textOpenMatch[1]);
16009
+ let lineIdx = 0;
16010
+ let lastY = "";
16011
+ let replacedLine = false;
16012
+ const patched = svg.replace(textOpenMatch[0], `<text text-anchor="start"${textAttrs}>`).replace(
16013
+ /<tspan\b([^>]*?)\sx="([^"]*)"([^>]*)>/gi,
16014
+ (match, pre, _oldX, post) => {
16015
+ const attrs = `${pre} ${post}`;
16016
+ const yMatch = attrs.match(/\sy\s*=\s*"([^"]*)"/i);
16017
+ const y = (yMatch == null ? void 0 : yMatch[1]) ?? "";
16018
+ const isLineStart = y !== "" ? y !== lastY : lineIdx < lineAnchors.length;
16019
+ if (!isLineStart) return match;
16020
+ if (y !== "") lastY = y;
16021
+ const line2 = lineAnchors[lineIdx++];
16022
+ if (!line2 || !Number.isFinite(line2.x)) return match;
16023
+ replacedLine = true;
16024
+ return `<tspan${stripAnchorAttrs(pre)} x="${line2.x.toFixed(3)}" text-anchor="${line2.anchor}" data-pd-line-anchor="1" data-pd-line-width="${line2.lineWidth.toFixed(3)}" data-pd-line-start="${line2.lineStart.toFixed(3)}"${stripAnchorAttrs(post)}>`;
16025
+ }
16026
+ );
16027
+ if (replacedLine) return patched;
16028
+ const line = lineAnchors[0];
16029
+ if (!line || !Number.isFinite(line.x)) return patched;
16030
+ return patched.replace(/<text\b([^>]*)>/i, (_m, attrs) => {
16031
+ const clean = stripAnchorAttrs(attrs).replace(/\s+x\s*=\s*"[^"]*"/i, "");
16032
+ return `<text x="${line.x.toFixed(3)}" text-anchor="${line.anchor}" data-pd-line-anchor="1" data-pd-line-width="${line.lineWidth.toFixed(3)}" data-pd-line-start="${line.lineStart.toFixed(3)}"${clean}>`;
16033
+ });
16034
+ } catch {
16035
+ return svg;
16036
+ }
16037
+ }
16038
+ function patchTextObjectsForSelectableSvg(fabricInstance) {
16039
+ var _a;
16040
+ const records = [];
16041
+ const visit = (obj) => {
16042
+ if (!obj || typeof obj !== "object") return;
16043
+ if (isFabricTextLike(obj) && typeof obj.toSVG === "function") {
16044
+ const original = obj.toSVG;
16045
+ obj.toSVG = function patchedSelectableTextToSVG(reviver) {
16046
+ return bakeSelectableTextSvgToFabricLineAnchors(original.call(this, reviver), this);
16047
+ };
16048
+ records.push({ obj, toSVG: original });
16049
+ }
16050
+ const children = typeof obj.getObjects === "function" ? obj.getObjects() : obj._objects;
16051
+ if (Array.isArray(children)) children.forEach(visit);
16052
+ };
16053
+ try {
16054
+ (_a = fabricInstance == null ? void 0 : fabricInstance.getObjects) == null ? void 0 : _a.call(fabricInstance).forEach(visit);
16055
+ } catch {
16056
+ }
16057
+ return records;
16058
+ }
15940
16059
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
15941
16060
  let normalized = svg;
15942
16061
  if (/\bwidth="[^"]*"/i.test(normalized)) {
@@ -15990,6 +16109,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
15990
16109
  } catch {
15991
16110
  }
15992
16111
  const fadeBakeRecords = [];
16112
+ const textSvgPatchRecords = patchTextObjectsForSelectableSvg(fabricInstance);
15993
16113
  try {
15994
16114
  const objs = fabricInstance.getObjects().slice();
15995
16115
  for (const obj of objs) {
@@ -16038,6 +16158,12 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16038
16158
  const raw = fabricInstance.toSVG();
16039
16159
  svgString = normalizeSvgDimensions(raw, canvasWidth, canvasHeight);
16040
16160
  } finally {
16161
+ for (const rec of textSvgPatchRecords) {
16162
+ try {
16163
+ rec.obj.toSVG = rec.toSVG;
16164
+ } catch {
16165
+ }
16166
+ }
16041
16167
  for (const rec of fadeBakeRecords) {
16042
16168
  try {
16043
16169
  fabricInstance.remove(rec.replacement);
@@ -16058,9 +16184,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16058
16184
  }
16059
16185
  return svgString;
16060
16186
  }
16061
- const resolvedPackageVersion = "0.5.158";
16187
+ const resolvedPackageVersion = "0.5.160";
16062
16188
  const PACKAGE_VERSION = resolvedPackageVersion;
16063
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.158";
16189
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.160";
16064
16190
  const roundParityValue = (value) => {
16065
16191
  if (typeof value !== "number") return value;
16066
16192
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16495,7 +16621,7 @@ class PixldocsRenderer {
16495
16621
  await this.waitForCanvasScene(container, cloned, i);
16496
16622
  }
16497
16623
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16498
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DwZylzI2.js");
16624
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-2NNilLNC.js");
16499
16625
  const prepared = preparePagesForExport(
16500
16626
  cloned.pages,
16501
16627
  canvasWidth,
@@ -18597,7 +18723,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18597
18723
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18598
18724
  sanitizeSvgTreeForPdf(svgToDraw);
18599
18725
  try {
18600
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DwZylzI2.js");
18726
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-2NNilLNC.js");
18601
18727
  try {
18602
18728
  await logTextMeasurementDiagnostic(svgToDraw);
18603
18729
  } catch {
@@ -18947,4 +19073,4 @@ export {
18947
19073
  collectFontDescriptorsFromConfig as y,
18948
19074
  collectFontsFromConfig as z
18949
19075
  };
18950
- //# sourceMappingURL=index-D0AWrDab.js.map
19076
+ //# sourceMappingURL=index-CmhkyT2o.js.map