@pixldocs/canvas-renderer 0.5.157 → 0.5.159

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.
@@ -15937,6 +15937,93 @@ function PixldocsPreview(props) {
15937
15937
  !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
15938
  ] });
15939
15939
  }
15940
+ function isFabricTextLike(obj) {
15941
+ return !!obj && (obj.type === "textbox" || obj.type === "i-text" || obj.type === "text" || Array.isArray(obj._textLines) && typeof obj.getLineWidth === "function");
15942
+ }
15943
+ function bakeSelectableTextSvgToFabricLineStarts(svg, obj) {
15944
+ try {
15945
+ const rawAlign = String((obj == null ? void 0 : obj.textAlign) || "left").toLowerCase();
15946
+ if (!/center|right|end/.test(rawAlign)) return svg;
15947
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
15948
+ const width = Number((obj == null ? void 0 : obj.width) ?? 0);
15949
+ if (!Array.isArray(lines) || lines.length === 0 || !Number.isFinite(width) || width <= 0) return svg;
15950
+ const halfW = width / 2;
15951
+ const lineStarts = lines.map((_line, i) => {
15952
+ var _a, _b, _c;
15953
+ let lineLeft = 0;
15954
+ let firstGlyphAdjust = 0;
15955
+ try {
15956
+ lineLeft = Number(((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0);
15957
+ } catch {
15958
+ lineLeft = 0;
15959
+ }
15960
+ try {
15961
+ const firstBox = (_c = (_b = obj.__charBounds) == null ? void 0 : _b[i]) == null ? void 0 : _c[0];
15962
+ const kerned = Number(firstBox == null ? void 0 : firstBox.kernedWidth);
15963
+ const glyphW = Number(firstBox == null ? void 0 : firstBox.width);
15964
+ if (Number.isFinite(kerned) && Number.isFinite(glyphW)) firstGlyphAdjust = kerned - glyphW;
15965
+ } catch {
15966
+ firstGlyphAdjust = 0;
15967
+ }
15968
+ return -halfW + (Number.isFinite(lineLeft) ? lineLeft : 0) + (Number.isFinite(firstGlyphAdjust) ? firstGlyphAdjust : 0);
15969
+ });
15970
+ 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) => {
15971
+ const cleaned = val.replace(/text-anchor\s*:\s*[^;"']+;?/gi, "").replace(/text-align\s*:\s*[^;"']+;?/gi, "").trim();
15972
+ return cleaned ? `${pre}${cleaned}${post}` : "";
15973
+ });
15974
+ const textOpenMatch = svg.match(/<text\b([^>]*)>/i);
15975
+ if (!textOpenMatch) return svg;
15976
+ const textAttrs = stripAnchorAttrs(textOpenMatch[1]);
15977
+ let lineIdx = 0;
15978
+ let lastY = "";
15979
+ let replacedLine = false;
15980
+ const patched = svg.replace(textOpenMatch[0], `<text text-anchor="start"${textAttrs}>`).replace(
15981
+ /<tspan\b([^>]*?)\sx="([^"]*)"([^>]*)>/gi,
15982
+ (match, pre, _oldX, post) => {
15983
+ const attrs = `${pre} ${post}`;
15984
+ const yMatch = attrs.match(/\sy\s*=\s*"([^"]*)"/i);
15985
+ const y = (yMatch == null ? void 0 : yMatch[1]) ?? "";
15986
+ const isLineStart = y !== "" ? y !== lastY : lineIdx < lineStarts.length;
15987
+ if (!isLineStart) return match;
15988
+ if (y !== "") lastY = y;
15989
+ const x2 = lineStarts[lineIdx++];
15990
+ if (!Number.isFinite(x2)) return match;
15991
+ replacedLine = true;
15992
+ return `<tspan${stripAnchorAttrs(pre)} x="${x2.toFixed(3)}" text-anchor="start" data-pd-line-anchor="1"${stripAnchorAttrs(post)}>`;
15993
+ }
15994
+ );
15995
+ if (replacedLine) return patched;
15996
+ const x = lineStarts[0];
15997
+ if (!Number.isFinite(x)) return patched;
15998
+ return patched.replace(/<text\b([^>]*)>/i, (_m, attrs) => {
15999
+ const clean = stripAnchorAttrs(attrs).replace(/\s+x\s*=\s*"[^"]*"/i, "");
16000
+ return `<text x="${x.toFixed(3)}" text-anchor="start" data-pd-line-anchor="1"${clean}>`;
16001
+ });
16002
+ } catch {
16003
+ return svg;
16004
+ }
16005
+ }
16006
+ function patchTextObjectsForSelectableSvg(fabricInstance) {
16007
+ var _a;
16008
+ const records = [];
16009
+ const visit = (obj) => {
16010
+ if (!obj || typeof obj !== "object") return;
16011
+ if (isFabricTextLike(obj) && typeof obj.toSVG === "function") {
16012
+ const original = obj.toSVG;
16013
+ obj.toSVG = function patchedSelectableTextToSVG(reviver) {
16014
+ return bakeSelectableTextSvgToFabricLineStarts(original.call(this, reviver), this);
16015
+ };
16016
+ records.push({ obj, toSVG: original });
16017
+ }
16018
+ const children = typeof obj.getObjects === "function" ? obj.getObjects() : obj._objects;
16019
+ if (Array.isArray(children)) children.forEach(visit);
16020
+ };
16021
+ try {
16022
+ (_a = fabricInstance == null ? void 0 : fabricInstance.getObjects) == null ? void 0 : _a.call(fabricInstance).forEach(visit);
16023
+ } catch {
16024
+ }
16025
+ return records;
16026
+ }
15940
16027
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
15941
16028
  let normalized = svg;
15942
16029
  if (/\bwidth="[^"]*"/i.test(normalized)) {
@@ -15990,6 +16077,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
15990
16077
  } catch {
15991
16078
  }
15992
16079
  const fadeBakeRecords = [];
16080
+ const textSvgPatchRecords = patchTextObjectsForSelectableSvg(fabricInstance);
15993
16081
  try {
15994
16082
  const objs = fabricInstance.getObjects().slice();
15995
16083
  for (const obj of objs) {
@@ -16038,6 +16126,12 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16038
16126
  const raw = fabricInstance.toSVG();
16039
16127
  svgString = normalizeSvgDimensions(raw, canvasWidth, canvasHeight);
16040
16128
  } finally {
16129
+ for (const rec of textSvgPatchRecords) {
16130
+ try {
16131
+ rec.obj.toSVG = rec.toSVG;
16132
+ } catch {
16133
+ }
16134
+ }
16041
16135
  for (const rec of fadeBakeRecords) {
16042
16136
  try {
16043
16137
  fabricInstance.remove(rec.replacement);
@@ -16058,9 +16152,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16058
16152
  }
16059
16153
  return svgString;
16060
16154
  }
16061
- const resolvedPackageVersion = "0.5.157";
16155
+ const resolvedPackageVersion = "0.5.159";
16062
16156
  const PACKAGE_VERSION = resolvedPackageVersion;
16063
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.157";
16157
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.159";
16064
16158
  const roundParityValue = (value) => {
16065
16159
  if (typeof value !== "number") return value;
16066
16160
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16495,7 +16589,7 @@ class PixldocsRenderer {
16495
16589
  await this.waitForCanvasScene(container, cloned, i);
16496
16590
  }
16497
16591
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16498
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CZyyQbwm.js");
16592
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-BRxfHxZU.js");
16499
16593
  const prepared = preparePagesForExport(
16500
16594
  cloned.pages,
16501
16595
  canvasWidth,
@@ -18597,7 +18691,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18597
18691
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18598
18692
  sanitizeSvgTreeForPdf(svgToDraw);
18599
18693
  try {
18600
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CZyyQbwm.js");
18694
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-BRxfHxZU.js");
18601
18695
  try {
18602
18696
  await logTextMeasurementDiagnostic(svgToDraw);
18603
18697
  } catch {
@@ -18718,7 +18812,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
18718
18812
  const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
18719
18813
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
18720
18814
  const shouldStripBg = stripPageBackground ?? hasGradient;
18721
- const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "auto");
18815
+ const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
18722
18816
  const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
18723
18817
  const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
18724
18818
  let pageSvg = page.svg;
@@ -18732,7 +18826,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
18732
18826
  }
18733
18827
  if (shouldOutlineText) {
18734
18828
  try {
18735
- const { convertAllTextToPath } = await import("./svgTextToPath-V1vC0SQt.js").then((n) => n.s);
18829
+ const { convertAllTextToPath } = await import("./svgTextToPath-BXAzwaaR.js");
18736
18830
  pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
18737
18831
  try {
18738
18832
  dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
@@ -18947,4 +19041,4 @@ export {
18947
19041
  collectFontDescriptorsFromConfig as y,
18948
19042
  collectFontsFromConfig as z
18949
19043
  };
18950
- //# sourceMappingURL=index-xn-L7QHB.js.map
19044
+ //# sourceMappingURL=index-ogNxtubz.js.map