@pixldocs/canvas-renderer 0.5.132 → 0.5.134

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.
@@ -5730,7 +5730,8 @@ function applyTextBackground(obj, cfg) {
5730
5730
  const dataAttrs = `data-blur="${blur.toFixed(3)}" data-ox="${ox.toFixed(3)}" data-oy="${oy.toFixed(3)}" data-bx="${bx.toFixed(3)}" data-by="${by.toFixed(3)}" data-bw="${bw.toFixed(3)}" data-bh="${bh.toFixed(3)}" data-color="${escapeXmlAttr(shadowColor)}"`;
5731
5731
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
5732
5732
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
5733
- const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}" />`;
5733
+ const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
5734
+ const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}"${shadowOpacityAttr} />`;
5734
5735
  bgShadowMarker = wrapShadow(shadowBgPath);
5735
5736
  }
5736
5737
  if ((bg == null ? void 0 : bg.shadowAffectsText) !== false) {
@@ -14031,7 +14032,8 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
14031
14032
  async function fetchFontshareTTF(fontFamily, weight, isItalic = false) {
14032
14033
  const cacheKey = `fs:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
14033
14034
  if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
14034
- if (fontshareNotFound.has(fontFamily)) return null;
14035
+ const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
14036
+ if (fontshareNotFound.has(notFoundKey)) return null;
14035
14037
  const slug = fontFamily.trim().toLowerCase().replace(/\s+/g, "-");
14036
14038
  const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "fontshare");
14037
14039
  if (proxyBytes) {
@@ -14044,13 +14046,13 @@ async function fetchFontshareTTF(fontFamily, weight, isItalic = false) {
14044
14046
  const cssUrl = `https://api.fontshare.com/v2/css?f[]=${slug}@${weight}${styleSuffix}&display=swap`;
14045
14047
  const cssRes = await fetch(cssUrl);
14046
14048
  if (!cssRes.ok) {
14047
- if (cssRes.status === 400 || cssRes.status === 404) fontshareNotFound.add(fontFamily);
14049
+ if (cssRes.status === 400 || cssRes.status === 404) fontshareNotFound.add(notFoundKey);
14048
14050
  return null;
14049
14051
  }
14050
14052
  const css = await cssRes.text();
14051
14053
  const ttMatch = css.match(/url\(([^)]+)\)\s+format\(['"]?truetype['"]?\)/i);
14052
14054
  if (!ttMatch) {
14053
- fontshareNotFound.add(fontFamily);
14055
+ fontshareNotFound.add(notFoundKey);
14054
14056
  return null;
14055
14057
  }
14056
14058
  let ttfUrl = ttMatch[1].replace(/['"]/g, "").trim();
@@ -14097,11 +14099,38 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
14097
14099
  if (ok) return true;
14098
14100
  }
14099
14101
  const resolved = resolveFontWeight(weight);
14100
- const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
14101
- if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
14102
- const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
14103
- if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
14104
- return false;
14102
+ const weightLadder = [resolved];
14103
+ for (const w of [400, 500, 700, 600, 300]) {
14104
+ if (!weightLadder.includes(w)) weightLadder.push(w);
14105
+ }
14106
+ const tryFetch = async (w, italic) => {
14107
+ const fs = await fetchFontshareTTF(fontName, w, italic);
14108
+ if (fs) return fs;
14109
+ const g = await fetchGoogleFontTTF(fontName, w, italic);
14110
+ return g;
14111
+ };
14112
+ let b64 = null;
14113
+ let usedItalic = isItalic;
14114
+ let usedWeight = resolved;
14115
+ for (const w of weightLadder) {
14116
+ b64 = await tryFetch(w, isItalic);
14117
+ if (b64) {
14118
+ usedWeight = w;
14119
+ break;
14120
+ }
14121
+ }
14122
+ if (!b64 && isItalic) {
14123
+ for (const w of weightLadder) {
14124
+ b64 = await tryFetch(w, false);
14125
+ if (b64) {
14126
+ usedItalic = false;
14127
+ usedWeight = w;
14128
+ break;
14129
+ }
14130
+ }
14131
+ }
14132
+ if (!b64) return false;
14133
+ return registerJsPdfFont(pdf, fontName, usedWeight, usedItalic, b64);
14105
14134
  }
14106
14135
  async function embedFontsForConfig(pdf, config, fontBaseUrl) {
14107
14136
  const fontKeys = /* @__PURE__ */ new Set();
@@ -16203,7 +16232,7 @@ class PixldocsRenderer {
16203
16232
  await this.waitForCanvasScene(container, cloned, i);
16204
16233
  }
16205
16234
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16206
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BmqYmqUF.cjs"));
16235
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-8Q3O7Z_Q.cjs"));
16207
16236
  const prepared = preparePagesForExport(
16208
16237
  cloned.pages,
16209
16238
  canvasWidth,
@@ -18041,7 +18070,7 @@ async function rasterizeShadowMarkers(svg) {
18041
18070
  if ((_a = document.fonts) == null ? void 0 : _a.ready) await document.fonts.ready;
18042
18071
  } catch {
18043
18072
  }
18044
- const fontFaceCss = collectDocumentFontFaceCss();
18073
+ const fontFaceCss = await collectInlinedFontFaceCss();
18045
18074
  for (const marker of markers) {
18046
18075
  try {
18047
18076
  const blur = parseFloat(marker.getAttribute("data-blur") || "0");
@@ -18055,7 +18084,9 @@ async function rasterizeShadowMarkers(svg) {
18055
18084
  (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
18056
18085
  continue;
18057
18086
  }
18058
- const innerXml = Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
18087
+ const innerXml = restoreSourceFontsForShadowRaster(
18088
+ Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("")
18089
+ );
18059
18090
  try {
18060
18091
  const fontSpecs = collectFontSpecsFromMarkup(innerXml);
18061
18092
  if (fontSpecs.length > 0 && ((_c = document.fonts) == null ? void 0 : _c.load)) {
@@ -18166,6 +18197,103 @@ function collectDocumentFontFaceCss() {
18166
18197
  cachedFontFaceCss = out.join("\n");
18167
18198
  return cachedFontFaceCss;
18168
18199
  }
18200
+ let cachedInlinedFontFaceCss = null;
18201
+ const fontUrlDataCache = /* @__PURE__ */ new Map();
18202
+ async function fetchFontAsDataUri(url) {
18203
+ if (fontUrlDataCache.has(url)) return fontUrlDataCache.get(url) ?? null;
18204
+ try {
18205
+ const resp = await fetch(url, { mode: "cors", credentials: "omit" });
18206
+ if (!resp.ok) {
18207
+ fontUrlDataCache.set(url, null);
18208
+ return null;
18209
+ }
18210
+ const blob = await resp.blob();
18211
+ const dataUri = await new Promise((resolve, reject) => {
18212
+ const fr = new FileReader();
18213
+ fr.onload = () => resolve(String(fr.result));
18214
+ fr.onerror = () => reject(fr.error);
18215
+ fr.readAsDataURL(blob);
18216
+ });
18217
+ fontUrlDataCache.set(url, dataUri);
18218
+ return dataUri;
18219
+ } catch {
18220
+ fontUrlDataCache.set(url, null);
18221
+ return null;
18222
+ }
18223
+ }
18224
+ async function inlineUrlsInCss(css) {
18225
+ const urlRe = /url\((['"]?)([^'")]+)\1\)/gi;
18226
+ const matches = [];
18227
+ let m;
18228
+ while ((m = urlRe.exec(css)) !== null) {
18229
+ const raw = m[2].trim();
18230
+ if (raw.startsWith("data:")) continue;
18231
+ let abs = raw;
18232
+ try {
18233
+ abs = new URL(raw, document.baseURI).toString();
18234
+ } catch {
18235
+ }
18236
+ matches.push({ full: m[0], url: abs });
18237
+ }
18238
+ if (matches.length === 0) return css;
18239
+ const unique = Array.from(new Set(matches.map((mm) => mm.url)));
18240
+ const results = await Promise.all(unique.map((u) => fetchFontAsDataUri(u)));
18241
+ const map = /* @__PURE__ */ new Map();
18242
+ unique.forEach((u, i) => map.set(u, results[i]));
18243
+ let out = css;
18244
+ for (const { full, url } of matches) {
18245
+ const data = map.get(url);
18246
+ if (!data) continue;
18247
+ const safeFull = full.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18248
+ out = out.replace(new RegExp(safeFull, "g"), `url("${data}")`);
18249
+ }
18250
+ return out;
18251
+ }
18252
+ async function collectInlinedFontFaceCss() {
18253
+ if (cachedInlinedFontFaceCss !== null) return cachedInlinedFontFaceCss;
18254
+ const raw = collectDocumentFontFaceCss();
18255
+ if (!raw) {
18256
+ cachedInlinedFontFaceCss = "";
18257
+ return "";
18258
+ }
18259
+ try {
18260
+ cachedInlinedFontFaceCss = await inlineUrlsInCss(raw);
18261
+ } catch {
18262
+ cachedInlinedFontFaceCss = raw;
18263
+ }
18264
+ return cachedInlinedFontFaceCss;
18265
+ }
18266
+ function restoreSourceFontsForShadowRaster(markup) {
18267
+ try {
18268
+ const parser = new DOMParser();
18269
+ const doc = parser.parseFromString(`<svg xmlns="http://www.w3.org/2000/svg">${markup}</svg>`, "image/svg+xml");
18270
+ if (doc.querySelector("parsererror")) return markup;
18271
+ for (const node of Array.from(doc.querySelectorAll("text, tspan, textPath"))) {
18272
+ const family = node.getAttribute("data-source-font-family");
18273
+ const weight = node.getAttribute("data-source-font-weight");
18274
+ const style = node.getAttribute("data-source-font-style");
18275
+ if (!family && !weight && !style) continue;
18276
+ const stylePairs = (node.getAttribute("style") || "").split(";").map((part) => part.trim()).filter(Boolean).filter((part) => !/^font-family\s*:/i.test(part) && !/^font-weight\s*:/i.test(part) && !/^font-style\s*:/i.test(part));
18277
+ if (family) {
18278
+ node.setAttribute("font-family", family);
18279
+ stylePairs.push(`font-family: ${family}`);
18280
+ }
18281
+ if (weight) {
18282
+ node.setAttribute("font-weight", weight);
18283
+ stylePairs.push(`font-weight: ${weight}`);
18284
+ }
18285
+ if (style) {
18286
+ node.setAttribute("font-style", style);
18287
+ stylePairs.push(`font-style: ${style}`);
18288
+ }
18289
+ if (stylePairs.length > 0) node.setAttribute("style", stylePairs.join("; "));
18290
+ }
18291
+ const root = doc.documentElement;
18292
+ return Array.from(root.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
18293
+ } catch {
18294
+ return markup;
18295
+ }
18296
+ }
18169
18297
  async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
18170
18298
  try {
18171
18299
  const parser = new DOMParser();
@@ -18196,7 +18324,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18196
18324
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18197
18325
  sanitizeSvgTreeForPdf(svgToDraw);
18198
18326
  try {
18199
- const { bakeTextAnchorPositionsFromLiveSvg } = await Promise.resolve().then(() => require("./vectorPdfExport-BmqYmqUF.cjs"));
18327
+ const { bakeTextAnchorPositionsFromLiveSvg } = await Promise.resolve().then(() => require("./vectorPdfExport-8Q3O7Z_Q.cjs"));
18200
18328
  await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
18201
18329
  } catch (e) {
18202
18330
  console.warn("[canvas-renderer][pdf-export] anchor-bake pass failed (continuing):", e);
@@ -18327,7 +18455,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
18327
18455
  }
18328
18456
  if (shouldOutlineText) {
18329
18457
  try {
18330
- const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-D6xJoPO2.cjs"));
18458
+ const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-GeR0CexD.cjs"));
18331
18459
  pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
18332
18460
  try {
18333
18461
  dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
@@ -18538,4 +18666,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
18538
18666
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
18539
18667
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
18540
18668
  exports.warmTemplateFromForm = warmTemplateFromForm;
18541
- //# sourceMappingURL=index-4IzWx9f-.cjs.map
18669
+ //# sourceMappingURL=index-DmqoeVsF.cjs.map