@pixldocs/canvas-renderer 0.5.129 → 0.5.132

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.
@@ -1,7 +1,7 @@
1
1
  import { jsPDF, ShadingPattern } from "jspdf";
2
2
  import { svg2pdf } from "svg2pdf.js";
3
3
  import * as fabric from "fabric";
4
- import { g as getCanvasForPage, c as captureFabricCanvasSvgForPdf, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, d as bakeEdgeFade, i as isElement, e as isGroup, j as buildRoundedTrianglePath, A as API_URL, k as getImageProxyFetchOptions, l as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, m as getTrianglePoints } from "./index-BinoVoB5.js";
4
+ import { g as getCanvasForPage, c as captureFabricCanvasSvgForPdf, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, d as bakeEdgeFade, i as isElement, e as isGroup, j as buildRoundedTrianglePath, A as API_URL, k as getImageProxyFetchOptions, l as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, m as getTrianglePoints } from "./index-TWLUgM18.js";
5
5
  import { resetPdfFontRegistry, FONT_FALLBACK_SYMBOLS, FONT_FALLBACK_MATH, FONT_FALLBACK_DEVANAGARI, embedFontWithGoogleFallback, getEmbeddedVariantsList, isFontAvailable, isFamilyEmbedded, resolveBestRegisteredVariant, getEmbeddedJsPDFFontName, resolveFontWeight, doesVariantSupportChar } from "./pdfFonts-Y49FLHuG.js";
6
6
  async function embedFontsForSvg(pdf, svgStr) {
7
7
  var _a;
@@ -1078,6 +1078,112 @@ function stripSuspiciousFullPageOverlayNodes(svg) {
1078
1078
  }
1079
1079
  }
1080
1080
  async function convertTextDecorationsToLines(svg) {
1081
+ return _convertTextDecorationsToLines_impl(svg);
1082
+ }
1083
+ async function bakeTextAnchorPositionsFromLiveSvg(svg) {
1084
+ var _a;
1085
+ if (typeof document === "undefined" || typeof window === "undefined") return;
1086
+ if (!svg) return;
1087
+ const _resolveAnchor = (el) => {
1088
+ let cur = el;
1089
+ while (cur) {
1090
+ const a = cur.getAttribute("text-anchor");
1091
+ if (a) return a.trim().toLowerCase();
1092
+ const style = cur.getAttribute("style") || "";
1093
+ const m = style.match(/(?:^|;)\s*text-anchor\s*:\s*([^;]+)/i);
1094
+ if (m) return m[1].trim().toLowerCase();
1095
+ cur = cur.parentElement;
1096
+ }
1097
+ return "start";
1098
+ };
1099
+ const allTextish = Array.from(svg.querySelectorAll("text, tspan"));
1100
+ let needsBake = false;
1101
+ for (const el of allTextish) {
1102
+ const a = _resolveAnchor(el);
1103
+ if (a === "middle" || a === "end") {
1104
+ needsBake = true;
1105
+ break;
1106
+ }
1107
+ }
1108
+ if (!needsBake) return;
1109
+ try {
1110
+ if ((_a = document.fonts) == null ? void 0 : _a.ready) await document.fonts.ready;
1111
+ } catch {
1112
+ }
1113
+ const tempContainer = document.createElement("div");
1114
+ tempContainer.style.cssText = "position:fixed;left:-9999px;top:-9999px;visibility:hidden;pointer-events:none;";
1115
+ const clone = svg.cloneNode(true);
1116
+ for (const tn of clone.querySelectorAll("text, tspan, textPath")) {
1117
+ const sf = tn.getAttribute("data-source-font-family");
1118
+ const sw = tn.getAttribute("data-source-font-weight");
1119
+ const ss = tn.getAttribute("data-source-font-style");
1120
+ if (sf) tn.setAttribute("font-family", sf);
1121
+ if (sw) tn.setAttribute("font-weight", sw);
1122
+ if (ss) tn.setAttribute("font-style", ss);
1123
+ const inlineStyle = tn.getAttribute("style") || "";
1124
+ if (sf || sw || ss) {
1125
+ const stylePairs = inlineStyle.split(";").map((p) => p.trim()).filter(Boolean).filter((p) => !/^font-(family|weight|style)\s*:/i.test(p));
1126
+ if (sf) stylePairs.push(`font-family: ${sf}`);
1127
+ if (sw) stylePairs.push(`font-weight: ${sw}`);
1128
+ if (ss) stylePairs.push(`font-style: ${ss}`);
1129
+ if (stylePairs.length > 0) tn.setAttribute("style", stylePairs.join("; "));
1130
+ }
1131
+ }
1132
+ tempContainer.appendChild(clone);
1133
+ document.body.appendChild(tempContainer);
1134
+ let baked = 0;
1135
+ try {
1136
+ const srcTexts = Array.from(svg.querySelectorAll("text"));
1137
+ const liveTexts = Array.from(clone.querySelectorAll("text"));
1138
+ for (let i = 0; i < srcTexts.length; i++) {
1139
+ const srcText = srcTexts[i];
1140
+ const liveText = liveTexts[i];
1141
+ if (!liveText) continue;
1142
+ const srcTspans = Array.from(srcText.querySelectorAll("tspan"));
1143
+ const liveTspans = Array.from(liveText.querySelectorAll("tspan"));
1144
+ const bakeNode = (srcNode, liveNode, isTspan) => {
1145
+ const anchor = _resolveAnchor(srcNode);
1146
+ if (anchor !== "middle" && anchor !== "end") return;
1147
+ try {
1148
+ const n = typeof liveNode.getNumberOfChars === "function" ? liveNode.getNumberOfChars() : 0;
1149
+ if (!n) return;
1150
+ const start = liveNode.getStartPositionOfChar(0);
1151
+ if (!Number.isFinite(start == null ? void 0 : start.x)) return;
1152
+ srcNode.setAttribute("x", String(start.x));
1153
+ srcNode.setAttribute("text-anchor", "start");
1154
+ const style = srcNode.getAttribute("style") || "";
1155
+ if (/text-anchor\s*:/i.test(style)) {
1156
+ const cleaned = style.split(";").map((p) => p.trim()).filter(Boolean).filter((p) => !/^text-anchor\s*:/i.test(p)).join("; ");
1157
+ if (cleaned) srcNode.setAttribute("style", cleaned);
1158
+ else srcNode.removeAttribute("style");
1159
+ }
1160
+ baked++;
1161
+ } catch {
1162
+ }
1163
+ };
1164
+ if (srcTspans.length > 0) {
1165
+ bakeNode(srcText, liveText, false);
1166
+ for (let j = 0; j < srcTspans.length; j++) {
1167
+ if (liveTspans[j]) bakeNode(srcTspans[j], liveTspans[j], true);
1168
+ }
1169
+ } else {
1170
+ bakeNode(srcText, liveText, false);
1171
+ }
1172
+ }
1173
+ } finally {
1174
+ try {
1175
+ document.body.removeChild(tempContainer);
1176
+ } catch {
1177
+ }
1178
+ }
1179
+ if (baked > 0) {
1180
+ try {
1181
+ console.log(`[Vector PDF][parity] baked text-anchor → start on ${baked} node(s) using live canvas measurement`);
1182
+ } catch {
1183
+ }
1184
+ }
1185
+ }
1186
+ async function _convertTextDecorationsToLines_impl(svg) {
1081
1187
  const doc = svg.ownerDocument;
1082
1188
  if (!doc) return;
1083
1189
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
@@ -1495,7 +1601,7 @@ async function collectInlinedFontFaceCss() {
1495
1601
  return cachedInlinedFontFaceCss;
1496
1602
  }
1497
1603
  async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
1498
- var _a;
1604
+ var _a, _b;
1499
1605
  try {
1500
1606
  const parser = new DOMParser();
1501
1607
  const processedSvg = inlineNestedSvgImageDataUris(rawSvg, parser);
@@ -1526,11 +1632,39 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
1526
1632
  stripRootPageBackgroundFromSvg(svgToDraw);
1527
1633
  }
1528
1634
  sanitizeSvgTreeForPdf(svgToDraw);
1635
+ try {
1636
+ await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
1637
+ } catch (e) {
1638
+ console.warn("[Vector PDF] anchor-bake pass failed (continuing):", e);
1639
+ }
1640
+ try {
1641
+ const hasShadowText = !!svgToDraw.querySelector("[data-pd-shadow-blur]") || !!svgToDraw.querySelector("g.__pdShadowRaster text");
1642
+ if (hasShadowText) {
1643
+ const { convertAllTextToPath } = await import("./svgTextToPath-Bne0QyE7.js");
1644
+ const serialized = new XMLSerializer().serializeToString(svgToDraw);
1645
+ const outlined = await convertAllTextToPath(serialized, void 0, { mode: "shadow-bound" });
1646
+ const reparsed = parser.parseFromString(outlined, "image/svg+xml");
1647
+ if (!reparsed.querySelector("parsererror") && ((_a = reparsed.documentElement) == null ? void 0 : _a.tagName.toLowerCase()) === "svg") {
1648
+ while (svgToDraw.firstChild) svgToDraw.removeChild(svgToDraw.firstChild);
1649
+ for (const child of Array.from(reparsed.documentElement.childNodes)) {
1650
+ svgToDraw.appendChild(svgToDraw.ownerDocument.importNode(child, true));
1651
+ }
1652
+ for (const attr of Array.from(reparsed.documentElement.attributes)) {
1653
+ try {
1654
+ svgToDraw.setAttribute(attr.name, attr.value);
1655
+ } catch {
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ } catch (e) {
1661
+ console.warn("[Vector PDF] shadow-bound text outlining failed (continuing without parity outlining):", e);
1662
+ }
1529
1663
  await rasterizeShadowMarkers(svgToDraw);
1530
1664
  await convertTextDecorationsToLines(svgToDraw);
1531
1665
  const rewritten = rewriteSvgFontsForJsPDFWithSourceMeta(new XMLSerializer().serializeToString(svgToDraw));
1532
1666
  const rewrittenDoc = parser.parseFromString(rewritten, "image/svg+xml");
1533
- if (!rewrittenDoc.querySelector("parsererror") && ((_a = rewrittenDoc.documentElement) == null ? void 0 : _a.tagName.toLowerCase()) === "svg") {
1667
+ if (!rewrittenDoc.querySelector("parsererror") && ((_b = rewrittenDoc.documentElement) == null ? void 0 : _b.tagName.toLowerCase()) === "svg") {
1534
1668
  return rewrittenDoc.documentElement;
1535
1669
  }
1536
1670
  return svgToDraw;
@@ -2112,7 +2246,7 @@ async function fetchSvgAsElement(imageUrl, colorMap) {
2112
2246
  async function getRecoloredSvgDataUrl(imageUrl, colorMap) {
2113
2247
  if (!colorMap || Object.keys(colorMap).length === 0) return null;
2114
2248
  try {
2115
- const { getNormalizedSvgUrl } = await import("./index-BinoVoB5.js").then((n) => n._);
2249
+ const { getNormalizedSvgUrl } = await import("./index-TWLUgM18.js").then((n) => n._);
2116
2250
  return await getNormalizedSvgUrl(imageUrl, colorMap);
2117
2251
  } catch {
2118
2252
  return null;
@@ -2893,7 +3027,7 @@ async function fetchImageAsBase64(imageUrl, opts = {}) {
2893
3027
  }
2894
3028
  let fetchUrl = imageUrl;
2895
3029
  if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
2896
- const { isPrivateUrl } = await import("./index-BinoVoB5.js").then((n) => n._);
3030
+ const { isPrivateUrl } = await import("./index-TWLUgM18.js").then((n) => n._);
2897
3031
  if (isPrivateUrl(imageUrl)) return null;
2898
3032
  const proxyUrl = new URL(`${API_URL}/image-proxy`);
2899
3033
  proxyUrl.searchParams.set("url", imageUrl);
@@ -4867,6 +5001,7 @@ async function exportMultiPagePdf(pages, options) {
4867
5001
  pdf.save(filename);
4868
5002
  }
4869
5003
  export {
5004
+ bakeTextAnchorPositionsFromLiveSvg,
4870
5005
  convertSvgTextDecorationsToLinesString,
4871
5006
  drawPreparedLiveCanvasSvgPageToPdf,
4872
5007
  embedFontsForSvg,
@@ -4875,4 +5010,4 @@ export {
4875
5010
  preparePagesForExport,
4876
5011
  rewriteSvgFontsForJsPDFWithSourceMeta
4877
5012
  };
4878
- //# sourceMappingURL=vectorPdfExport-DrXSoUpy.js.map
5013
+ //# sourceMappingURL=vectorPdfExport-3QFNDStb.js.map