@pixldocs/canvas-renderer 0.5.143 → 0.5.146

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.
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jspdf = require("jspdf");
4
4
  const svg2pdf_js = require("svg2pdf.js");
5
5
  const fabric = require("fabric");
6
- const index = require("./index-r5VzSOHa.cjs");
6
+ const index = require("./index-DoPXmxDJ.cjs");
7
7
  const pdfFonts = require("./pdfFonts-BTEVnYX8.cjs");
8
8
  function _interopNamespaceDefault(e) {
9
9
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -142,6 +142,33 @@ const yieldToUI = () => new Promise((resolve) => {
142
142
  requestAnimationFrame(() => setTimeout(resolve, 0));
143
143
  });
144
144
  const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
145
+ function collectFontSpecsFromShadowMarkup(markup) {
146
+ const specs = /* @__PURE__ */ new Set();
147
+ const re = /<(?:text|tspan)\b[^>]*>/gi;
148
+ let match;
149
+ while ((match = re.exec(markup)) !== null) {
150
+ const tag = match[0];
151
+ const get = (attr) => {
152
+ const m = tag.match(new RegExp(`\\s${attr}\\s*=\\s*"([^"]*)"`, "i"));
153
+ if (m) return m[1];
154
+ const styleM = tag.match(/\sstyle\s*=\s*"([^"]*)"/i);
155
+ if (styleM) {
156
+ const sm = styleM[1].match(new RegExp(`${attr}\\s*:\\s*([^;]+)`, "i"));
157
+ if (sm) return sm[1].trim();
158
+ }
159
+ return null;
160
+ };
161
+ const family = (get("font-family") || "").split(",")[0].replace(/['"]/g, "").trim();
162
+ if (!family) continue;
163
+ const weight = get("font-weight") || "400";
164
+ const style = get("font-style") || "normal";
165
+ const size = get("font-size") || "16px";
166
+ const sizePx = /[a-z%]/i.test(size) ? size : `${size}px`;
167
+ const famSpec = /\s/.test(family) ? `"${family}"` : family;
168
+ specs.add(`${style} ${weight} ${sizePx} ${famSpec}`);
169
+ }
170
+ return Array.from(specs);
171
+ }
145
172
  let debugSvgDrawSequence = 0;
146
173
  function debugLog(...args) {
147
174
  }
@@ -1227,6 +1254,105 @@ async function bakeTextAnchorPositionsFromLiveSvg(svg) {
1227
1254
  }
1228
1255
  }
1229
1256
  }
1257
+ async function logTextMeasurementDiagnostic(svg) {
1258
+ var _a, _b, _c, _d, _e;
1259
+ if (typeof window === "undefined" || typeof document === "undefined") return;
1260
+ try {
1261
+ const params = new URLSearchParams(((_a = window.location) == null ? void 0 : _a.search) || "");
1262
+ if (params.get("pdfdiag") !== "1") return;
1263
+ } catch {
1264
+ return;
1265
+ }
1266
+ if (!svg) return;
1267
+ const tempContainer = document.createElement("div");
1268
+ tempContainer.style.cssText = "position:fixed;left:-9999px;top:-9999px;visibility:hidden;pointer-events:none;";
1269
+ const clone = svg.cloneNode(true);
1270
+ for (const tn of clone.querySelectorAll("text, tspan, textPath")) {
1271
+ const sf = tn.getAttribute("data-source-font-family");
1272
+ const sw = tn.getAttribute("data-source-font-weight");
1273
+ const ss = tn.getAttribute("data-source-font-style");
1274
+ if (sf) tn.setAttribute("font-family", sf);
1275
+ if (sw) tn.setAttribute("font-weight", sw);
1276
+ if (ss) tn.setAttribute("font-style", ss);
1277
+ }
1278
+ tempContainer.appendChild(clone);
1279
+ document.body.appendChild(tempContainer);
1280
+ const measureCanvas = document.createElement("canvas");
1281
+ const mctx = measureCanvas.getContext("2d");
1282
+ try {
1283
+ const liveTexts = Array.from(clone.querySelectorAll("text"));
1284
+ const srcTexts = Array.from(svg.querySelectorAll("text"));
1285
+ const rows = [];
1286
+ for (let i = 0; i < liveTexts.length; i++) {
1287
+ const live = liveTexts[i];
1288
+ const src = srcTexts[i];
1289
+ if (!live || !src) continue;
1290
+ const liveTspans = Array.from(live.querySelectorAll("tspan"));
1291
+ const srcTspans = Array.from(src.querySelectorAll("tspan"));
1292
+ const tspans = liveTspans.length ? liveTspans : [live];
1293
+ const srcs = srcTspans.length ? srcTspans : [src];
1294
+ for (let j = 0; j < tspans.length; j++) {
1295
+ const ln = tspans[j];
1296
+ const sn = srcs[j];
1297
+ const text = ln.textContent || "";
1298
+ const fam = (ln.getAttribute("font-family") || "").replace(/['"]/g, "").trim();
1299
+ const wt = ln.getAttribute("font-weight") || "400";
1300
+ const sty = ln.getAttribute("font-style") || "normal";
1301
+ const sz = parseFloat(ln.getAttribute("font-size") || src.getAttribute("font-size") || "16") || 16;
1302
+ let domX = null;
1303
+ let bboxW = null;
1304
+ try {
1305
+ if (ln.getNumberOfChars && ln.getNumberOfChars() > 0) {
1306
+ domX = ln.getStartPositionOfChar(0).x;
1307
+ }
1308
+ } catch {
1309
+ }
1310
+ try {
1311
+ bboxW = ((_b = ln.getBBox) == null ? void 0 : _b.call(ln).width) ?? null;
1312
+ } catch {
1313
+ }
1314
+ let canvasW = null;
1315
+ if (mctx) {
1316
+ mctx.font = `${sty} ${wt} ${sz}px "${fam}"`;
1317
+ try {
1318
+ canvasW = mctx.measureText(text).width;
1319
+ } catch {
1320
+ }
1321
+ }
1322
+ rows.push({
1323
+ line: text.length > 40 ? text.slice(0, 37) + "..." : text,
1324
+ font: `${fam} ${wt} ${sty}`,
1325
+ size: sz,
1326
+ srcAnchor: sn.getAttribute("text-anchor") || ((_c = sn.parentElement) == null ? void 0 : _c.getAttribute("text-anchor")) || "",
1327
+ srcX: sn.getAttribute("x") || ((_d = sn.parentElement) == null ? void 0 : _d.getAttribute("x")) || "",
1328
+ domX: domX !== null ? +domX.toFixed(2) : null,
1329
+ bboxW: bboxW !== null ? +bboxW.toFixed(2) : null,
1330
+ measureCtxW: canvasW !== null ? +canvasW.toFixed(2) : null
1331
+ });
1332
+ }
1333
+ }
1334
+ if (rows.length > 0) {
1335
+ console.log("[PDF-DIAG] ===== text measurement diagnostic =====");
1336
+ console.log(
1337
+ "[PDF-DIAG] env=",
1338
+ typeof navigator !== "undefined" ? navigator.userAgent.slice(0, 80) : "n/a",
1339
+ "dpr=",
1340
+ window.devicePixelRatio,
1341
+ "fontsReady=",
1342
+ !!((_e = document.fonts) == null ? void 0 : _e.ready)
1343
+ );
1344
+ for (const r of rows) {
1345
+ console.log("[PDF-DIAG]", JSON.stringify(r));
1346
+ }
1347
+ console.log("[PDF-DIAG] ===== end =====");
1348
+ }
1349
+ } finally {
1350
+ try {
1351
+ document.body.removeChild(tempContainer);
1352
+ } catch {
1353
+ }
1354
+ }
1355
+ }
1230
1356
  async function _convertTextDecorationsToLines_impl(svg) {
1231
1357
  const doc = svg.ownerDocument;
1232
1358
  if (!doc) return;
@@ -1423,7 +1549,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
1423
1549
  }
1424
1550
  }
1425
1551
  async function rasterizeShadowMarkers(svg) {
1426
- var _a, _b, _c, _d, _e;
1552
+ var _a, _b, _c, _d, _e, _f;
1427
1553
  if (typeof window === "undefined" || typeof document === "undefined") return;
1428
1554
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
1429
1555
  if (markers.length === 0) return;
@@ -1450,6 +1576,17 @@ async function rasterizeShadowMarkers(svg) {
1450
1576
  const innerXml = restoreSourceFontsForShadowRaster(
1451
1577
  Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("")
1452
1578
  );
1579
+ try {
1580
+ const fontSpecs = collectFontSpecsFromShadowMarkup(innerXml);
1581
+ if (fontSpecs.length > 0 && ((_c = document.fonts) == null ? void 0 : _c.load)) {
1582
+ await Promise.all(
1583
+ fontSpecs.map(
1584
+ (spec) => document.fonts.load(spec).catch(() => void 0)
1585
+ )
1586
+ );
1587
+ }
1588
+ } catch {
1589
+ }
1453
1590
  const scale = 2;
1454
1591
  const pxW = Math.min(4096, Math.max(8, Math.ceil(bw * scale)));
1455
1592
  const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
@@ -1459,7 +1596,7 @@ async function rasterizeShadowMarkers(svg) {
1459
1596
  const miniSvg = `<svg xmlns="${SVG_NS}" xmlns:xlink="${XLINK_NS}" width="${pxW}" height="${pxH}" viewBox="${bx} ${by} ${bw} ${bh}">${styleBlock}<defs><filter id="${filterId}" filterUnits="userSpaceOnUse" x="${bx}" y="${by}" width="${bw}" height="${bh}" color-interpolation-filters="sRGB"><feOffset dx="${ox}" dy="${oy}" result="offsetShadow" /><feGaussianBlur in="offsetShadow" stdDeviation="${stdDev}" /></filter></defs><g filter="url(#${filterId})">${innerXml}</g></svg>`;
1460
1597
  const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
1461
1598
  if (!dataUrl) {
1462
- (_c = marker.parentNode) == null ? void 0 : _c.removeChild(marker);
1599
+ (_d = marker.parentNode) == null ? void 0 : _d.removeChild(marker);
1463
1600
  continue;
1464
1601
  }
1465
1602
  const img = svg.ownerDocument.createElementNS(SVG_NS, "image");
@@ -1471,11 +1608,11 @@ async function rasterizeShadowMarkers(svg) {
1471
1608
  img.setAttribute("preserveAspectRatio", "none");
1472
1609
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
1473
1610
  img.setAttribute("href", dataUrl);
1474
- (_d = marker.parentNode) == null ? void 0 : _d.replaceChild(img, marker);
1611
+ (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
1475
1612
  } catch (error) {
1476
1613
  console.warn("[Vector PDF] text shadow rasterization failed for one marker:", error);
1477
1614
  try {
1478
- (_e = marker.parentNode) == null ? void 0 : _e.removeChild(marker);
1615
+ (_f = marker.parentNode) == null ? void 0 : _f.removeChild(marker);
1479
1616
  } catch {
1480
1617
  }
1481
1618
  }
@@ -1677,6 +1814,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
1677
1814
  }
1678
1815
  sanitizeSvgTreeForPdf(svgToDraw);
1679
1816
  try {
1817
+ await logTextMeasurementDiagnostic(svgToDraw);
1680
1818
  await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
1681
1819
  } catch (e) {
1682
1820
  console.warn("[Vector PDF] anchor-bake pass failed (continuing):", e);
@@ -2267,7 +2405,7 @@ async function fetchSvgAsElement(imageUrl, colorMap) {
2267
2405
  async function getRecoloredSvgDataUrl(imageUrl, colorMap) {
2268
2406
  if (!colorMap || Object.keys(colorMap).length === 0) return null;
2269
2407
  try {
2270
- const { getNormalizedSvgUrl } = await Promise.resolve().then(() => require("./index-r5VzSOHa.cjs")).then((n) => n.canvasImageLoader);
2408
+ const { getNormalizedSvgUrl } = await Promise.resolve().then(() => require("./index-DoPXmxDJ.cjs")).then((n) => n.canvasImageLoader);
2271
2409
  return await getNormalizedSvgUrl(imageUrl, colorMap);
2272
2410
  } catch {
2273
2411
  return null;
@@ -3048,7 +3186,7 @@ async function fetchImageAsBase64(imageUrl, opts = {}) {
3048
3186
  }
3049
3187
  let fetchUrl = imageUrl;
3050
3188
  if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
3051
- const { isPrivateUrl } = await Promise.resolve().then(() => require("./index-r5VzSOHa.cjs")).then((n) => n.canvasImageLoader);
3189
+ const { isPrivateUrl } = await Promise.resolve().then(() => require("./index-DoPXmxDJ.cjs")).then((n) => n.canvasImageLoader);
3052
3190
  if (isPrivateUrl(imageUrl)) return null;
3053
3191
  const proxyUrl = new URL(`${index.API_URL}/image-proxy`);
3054
3192
  proxyUrl.searchParams.set("url", imageUrl);
@@ -5027,6 +5165,7 @@ exports.drawPreparedLiveCanvasSvgPageToPdf = drawPreparedLiveCanvasSvgPageToPdf;
5027
5165
  exports.embedFontsForSvg = embedFontsForSvg;
5028
5166
  exports.exportFabricCanvasToVectorPdf = exportFabricCanvasToVectorPdf;
5029
5167
  exports.exportMultiPagePdf = exportMultiPagePdf;
5168
+ exports.logTextMeasurementDiagnostic = logTextMeasurementDiagnostic;
5030
5169
  exports.preparePagesForExport = preparePagesForExport;
5031
5170
  exports.rewriteSvgFontsForJsPDFWithSourceMeta = rewriteSvgFontsForJsPDFWithSourceMeta;
5032
- //# sourceMappingURL=vectorPdfExport-JVEe_tSQ.cjs.map
5171
+ //# sourceMappingURL=vectorPdfExport-SMvSP0el.cjs.map