@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.
@@ -5804,12 +5804,16 @@ function bakeTextSvgToFabricLineStarts(svg, obj, w) {
5804
5804
  if (canUseAlignmentAnchor && Number.isFinite(lineW) && lineW > 0) {
5805
5805
  lineAnchors.push({
5806
5806
  x: alignmentAnchor === "end" ? baseStart + lineW : baseStart + lineW / 2,
5807
- anchor: alignmentAnchor
5807
+ anchor: alignmentAnchor,
5808
+ lineWidth: lineW,
5809
+ lineStart: baseStart
5808
5810
  });
5809
5811
  } else {
5810
5812
  lineAnchors.push({
5811
5813
  x: baseStart + (Number.isFinite(firstGlyphAdjust) ? firstGlyphAdjust : 0),
5812
- anchor: "start"
5814
+ anchor: "start",
5815
+ lineWidth: Number.isFinite(lineW) ? lineW : 0,
5816
+ lineStart: baseStart
5813
5817
  });
5814
5818
  }
5815
5819
  }
@@ -5847,7 +5851,7 @@ function bakeTextSvgToFabricLineStarts(svg, obj, w) {
5847
5851
  if (!lineAnchor || typeof lineAnchor.x !== "number" || !Number.isFinite(lineAnchor.x)) return _m;
5848
5852
  const cleanPre = stripAnchorAttrs(pre);
5849
5853
  const cleanPost = stripAnchorAttrs(post);
5850
- return `<tspan${cleanPre} x="${lineAnchor.x.toFixed(3)}" text-anchor="${lineAnchor.anchor}" data-pd-line-anchor="1"${cleanPost}>`;
5854
+ 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}>`;
5851
5855
  }
5852
5856
  );
5853
5857
  return newSvg;
@@ -15955,6 +15959,121 @@ function PixldocsPreview(props) {
15955
15959
  !canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
15956
15960
  ] });
15957
15961
  }
15962
+ function isFabricTextLike(obj) {
15963
+ return !!obj && (obj.type === "textbox" || obj.type === "i-text" || obj.type === "text" || Array.isArray(obj._textLines) && typeof obj.getLineWidth === "function");
15964
+ }
15965
+ function bakeSelectableTextSvgToFabricLineAnchors(svg, obj) {
15966
+ try {
15967
+ if (/\bdata-pd-line-anchor\s*=\s*"1"/i.test(svg)) return svg;
15968
+ const rawAlign = String((obj == null ? void 0 : obj.textAlign) || "left").toLowerCase();
15969
+ if (!/center|right|end/.test(rawAlign)) return svg;
15970
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
15971
+ const width = Number((obj == null ? void 0 : obj.width) ?? 0);
15972
+ if (!Array.isArray(lines) || lines.length === 0 || !Number.isFinite(width) || width <= 0) return svg;
15973
+ const alignmentAnchor = /right|end/.test(rawAlign) ? "end" : "middle";
15974
+ const hasStyledChars = (() => {
15975
+ const styles = obj == null ? void 0 : obj.styles;
15976
+ if (!styles || typeof styles !== "object") return false;
15977
+ return Object.values(styles).some((line2) => line2 && typeof line2 === "object" && Object.keys(line2).length > 0);
15978
+ })();
15979
+ const canUseLineAnchor = !hasStyledChars && !(obj == null ? void 0 : obj.path) && !Number((obj == null ? void 0 : obj.charSpacing) ?? 0);
15980
+ const halfW = width / 2;
15981
+ const lineAnchors = lines.map((_line, i) => {
15982
+ var _a, _b, _c, _d;
15983
+ let lineLeft = 0;
15984
+ let lineW = 0;
15985
+ let firstGlyphAdjust = 0;
15986
+ try {
15987
+ lineLeft = Number(((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0);
15988
+ } catch {
15989
+ lineLeft = 0;
15990
+ }
15991
+ try {
15992
+ lineW = Number(((_b = obj.getLineWidth) == null ? void 0 : _b.call(obj, i)) ?? 0);
15993
+ } catch {
15994
+ lineW = 0;
15995
+ }
15996
+ try {
15997
+ const firstBox = (_d = (_c = obj.__charBounds) == null ? void 0 : _c[i]) == null ? void 0 : _d[0];
15998
+ const kerned = Number(firstBox == null ? void 0 : firstBox.kernedWidth);
15999
+ const glyphW = Number(firstBox == null ? void 0 : firstBox.width);
16000
+ if (Number.isFinite(kerned) && Number.isFinite(glyphW)) firstGlyphAdjust = kerned - glyphW;
16001
+ } catch {
16002
+ firstGlyphAdjust = 0;
16003
+ }
16004
+ const baseStart = -halfW + (Number.isFinite(lineLeft) ? lineLeft : 0);
16005
+ if (canUseLineAnchor && Number.isFinite(lineW) && lineW > 0) {
16006
+ return {
16007
+ x: alignmentAnchor === "end" ? baseStart + lineW : baseStart + lineW / 2,
16008
+ anchor: alignmentAnchor,
16009
+ lineWidth: lineW,
16010
+ lineStart: baseStart
16011
+ };
16012
+ }
16013
+ return {
16014
+ x: baseStart + (Number.isFinite(firstGlyphAdjust) ? firstGlyphAdjust : 0),
16015
+ anchor: "start",
16016
+ lineWidth: Number.isFinite(lineW) ? lineW : 0,
16017
+ lineStart: baseStart
16018
+ };
16019
+ });
16020
+ 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) => {
16021
+ const cleaned = val.replace(/text-anchor\s*:\s*[^;"']+;?/gi, "").replace(/text-align\s*:\s*[^;"']+;?/gi, "").trim();
16022
+ return cleaned ? `${pre}${cleaned}${post}` : "";
16023
+ });
16024
+ const textOpenMatch = svg.match(/<text\b([^>]*)>/i);
16025
+ if (!textOpenMatch) return svg;
16026
+ const textAttrs = stripAnchorAttrs(textOpenMatch[1]);
16027
+ let lineIdx = 0;
16028
+ let lastY = "";
16029
+ let replacedLine = false;
16030
+ const patched = svg.replace(textOpenMatch[0], `<text text-anchor="start"${textAttrs}>`).replace(
16031
+ /<tspan\b([^>]*?)\sx="([^"]*)"([^>]*)>/gi,
16032
+ (match, pre, _oldX, post) => {
16033
+ const attrs = `${pre} ${post}`;
16034
+ const yMatch = attrs.match(/\sy\s*=\s*"([^"]*)"/i);
16035
+ const y = (yMatch == null ? void 0 : yMatch[1]) ?? "";
16036
+ const isLineStart = y !== "" ? y !== lastY : lineIdx < lineAnchors.length;
16037
+ if (!isLineStart) return match;
16038
+ if (y !== "") lastY = y;
16039
+ const line2 = lineAnchors[lineIdx++];
16040
+ if (!line2 || !Number.isFinite(line2.x)) return match;
16041
+ replacedLine = true;
16042
+ 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)}>`;
16043
+ }
16044
+ );
16045
+ if (replacedLine) return patched;
16046
+ const line = lineAnchors[0];
16047
+ if (!line || !Number.isFinite(line.x)) return patched;
16048
+ return patched.replace(/<text\b([^>]*)>/i, (_m, attrs) => {
16049
+ const clean = stripAnchorAttrs(attrs).replace(/\s+x\s*=\s*"[^"]*"/i, "");
16050
+ 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}>`;
16051
+ });
16052
+ } catch {
16053
+ return svg;
16054
+ }
16055
+ }
16056
+ function patchTextObjectsForSelectableSvg(fabricInstance) {
16057
+ var _a;
16058
+ const records = [];
16059
+ const visit = (obj) => {
16060
+ if (!obj || typeof obj !== "object") return;
16061
+ if (isFabricTextLike(obj) && typeof obj.toSVG === "function") {
16062
+ const original = obj.toSVG;
16063
+ obj.toSVG = function patchedSelectableTextToSVG(reviver) {
16064
+ return bakeSelectableTextSvgToFabricLineAnchors(original.call(this, reviver), this);
16065
+ };
16066
+ records.push({ obj, toSVG: original });
16067
+ }
16068
+ const children = typeof obj.getObjects === "function" ? obj.getObjects() : obj._objects;
16069
+ if (Array.isArray(children)) children.forEach(visit);
16070
+ };
16071
+ try {
16072
+ (_a = fabricInstance == null ? void 0 : fabricInstance.getObjects) == null ? void 0 : _a.call(fabricInstance).forEach(visit);
16073
+ } catch {
16074
+ }
16075
+ return records;
16076
+ }
15958
16077
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
15959
16078
  let normalized = svg;
15960
16079
  if (/\bwidth="[^"]*"/i.test(normalized)) {
@@ -16008,6 +16127,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16008
16127
  } catch {
16009
16128
  }
16010
16129
  const fadeBakeRecords = [];
16130
+ const textSvgPatchRecords = patchTextObjectsForSelectableSvg(fabricInstance);
16011
16131
  try {
16012
16132
  const objs = fabricInstance.getObjects().slice();
16013
16133
  for (const obj of objs) {
@@ -16056,6 +16176,12 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16056
16176
  const raw = fabricInstance.toSVG();
16057
16177
  svgString = normalizeSvgDimensions(raw, canvasWidth, canvasHeight);
16058
16178
  } finally {
16179
+ for (const rec of textSvgPatchRecords) {
16180
+ try {
16181
+ rec.obj.toSVG = rec.toSVG;
16182
+ } catch {
16183
+ }
16184
+ }
16059
16185
  for (const rec of fadeBakeRecords) {
16060
16186
  try {
16061
16187
  fabricInstance.remove(rec.replacement);
@@ -16076,9 +16202,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16076
16202
  }
16077
16203
  return svgString;
16078
16204
  }
16079
- const resolvedPackageVersion = "0.5.158";
16205
+ const resolvedPackageVersion = "0.5.160";
16080
16206
  const PACKAGE_VERSION = resolvedPackageVersion;
16081
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.158";
16207
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.160";
16082
16208
  const roundParityValue = (value) => {
16083
16209
  if (typeof value !== "number") return value;
16084
16210
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16513,7 +16639,7 @@ class PixldocsRenderer {
16513
16639
  await this.waitForCanvasScene(container, cloned, i);
16514
16640
  }
16515
16641
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16516
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-vZ8VHnu6.cjs"));
16642
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-DEMJeh_t.cjs"));
16517
16643
  const prepared = preparePagesForExport(
16518
16644
  cloned.pages,
16519
16645
  canvasWidth,
@@ -18615,7 +18741,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18615
18741
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18616
18742
  sanitizeSvgTreeForPdf(svgToDraw);
18617
18743
  try {
18618
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-vZ8VHnu6.cjs"));
18744
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DEMJeh_t.cjs"));
18619
18745
  try {
18620
18746
  await logTextMeasurementDiagnostic(svgToDraw);
18621
18747
  } catch {
@@ -18962,4 +19088,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
18962
19088
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
18963
19089
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
18964
19090
  exports.warmTemplateFromForm = warmTemplateFromForm;
18965
- //# sourceMappingURL=index-B1f4qE6u.cjs.map
19091
+ //# sourceMappingURL=index-CtUEIsYl.cjs.map