@pixldocs/canvas-renderer 0.5.134 → 0.5.136

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.
@@ -14608,6 +14608,38 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
14608
14608
  resolveFontWeight,
14609
14609
  rewriteSvgFontsForJsPDF
14610
14610
  }, Symbol.toStringTag, { value: "Module" }));
14611
+ const FONTSHARE_SLUGS = {
14612
+ "Satoshi": "satoshi",
14613
+ "Cabinet Grotesk": "cabinet-grotesk",
14614
+ "Clash Display": "clash-display",
14615
+ "Clash Grotesk": "clash-grotesk",
14616
+ "General Sans": "general-sans",
14617
+ "Switzer": "switzer",
14618
+ "Supreme": "supreme",
14619
+ "Author": "author",
14620
+ "Boska": "boska",
14621
+ "Excon": "excon",
14622
+ "Khand": "khand",
14623
+ "Sentient": "sentient",
14624
+ "Synonym": "synonym",
14625
+ "Erode": "erode",
14626
+ "Ranade": "ranade",
14627
+ "Tanker": "tanker",
14628
+ "Zodiak": "zodiak",
14629
+ "Gambarino": "gambarino",
14630
+ "Melodrama": "melodrama",
14631
+ "Bespoke Serif": "bespoke-serif",
14632
+ "Bespoke Stencil": "bespoke-stencil",
14633
+ "Panchang": "panchang",
14634
+ "Quincy CF": "quincy-cf",
14635
+ "Pally": "pally",
14636
+ "Tabular": "tabular",
14637
+ "Sharpie": "sharpie",
14638
+ "Stardom": "stardom",
14639
+ "Rebond Grotesque": "rebond-grotesque",
14640
+ "Telma": "telma",
14641
+ "Nippo": "nippo"
14642
+ };
14611
14643
  function normalizeFontFamily(fontStack) {
14612
14644
  const first = fontStack.split(",")[0].trim();
14613
14645
  return first.replace(/^['"]|['"]$/g, "");
@@ -14615,6 +14647,114 @@ function normalizeFontFamily(fontStack) {
14615
14647
  const loadedFonts = /* @__PURE__ */ new Set();
14616
14648
  const loadingPromises = /* @__PURE__ */ new Map();
14617
14649
  const registeredLocalFontFaces = /* @__PURE__ */ new Set();
14650
+ const registeredRemoteFontFaces = /* @__PURE__ */ new Set();
14651
+ const remoteFontDataUriPromises = /* @__PURE__ */ new Map();
14652
+ let localFontFaceStyleEl = null;
14653
+ function ensureLocalFontFaceStyle() {
14654
+ if (typeof document === "undefined") return null;
14655
+ if (localFontFaceStyleEl && localFontFaceStyleEl.isConnected) return localFontFaceStyleEl;
14656
+ try {
14657
+ localFontFaceStyleEl = document.createElement("style");
14658
+ localFontFaceStyleEl.setAttribute("data-pixldocs-local-fontfaces", "1");
14659
+ document.head.appendChild(localFontFaceStyleEl);
14660
+ return localFontFaceStyleEl;
14661
+ } catch {
14662
+ return null;
14663
+ }
14664
+ }
14665
+ function appendLocalFontFaceRule(family, weight, style, file) {
14666
+ const styleEl = ensureLocalFontFaceStyle();
14667
+ if (!styleEl) return;
14668
+ const cssText = `@font-face{font-family:"${family}";src:url("/fonts/${file}");font-weight:${weight};font-style:${style};font-display:swap;}
14669
+ `;
14670
+ styleEl.appendChild(document.createTextNode(cssText));
14671
+ }
14672
+ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
14673
+ const styleEl = ensureLocalFontFaceStyle();
14674
+ if (!styleEl) return;
14675
+ const cssText = `@font-face{font-family:"${family}";src:url("${dataUri}") format("truetype");font-weight:${weight};font-style:${style};font-display:swap;}
14676
+ `;
14677
+ styleEl.appendChild(document.createTextNode(cssText));
14678
+ }
14679
+ function resolveHarnessFontProxyUrl() {
14680
+ var _a, _b;
14681
+ try {
14682
+ const runtimeBase = typeof window !== "undefined" && (window.__PIXLDOCS_SUPABASE_URL || ((_a = window.__CONFIG__) == null ? void 0 : _a.supabaseUrl)) || typeof globalThis !== "undefined" && (globalThis.__PIXLDOCS_SUPABASE_URL || ((_b = globalThis.__CONFIG__) == null ? void 0 : _b.supabaseUrl)) || "";
14683
+ const base = String(runtimeBase || "").replace(/\/$/, "");
14684
+ return base ? `${base}/functions/v1/font-proxy` : "";
14685
+ } catch {
14686
+ return "";
14687
+ }
14688
+ }
14689
+ function isEmbeddableTrueType(bytes) {
14690
+ if (bytes.length < 12) return false;
14691
+ const signature = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3]);
14692
+ return signature === "\0\0\0" || signature === "true";
14693
+ }
14694
+ async function arrayBufferToDataUri(buf) {
14695
+ const blob = new Blob([buf], { type: "font/ttf" });
14696
+ return await new Promise((resolve, reject) => {
14697
+ const reader = new FileReader();
14698
+ reader.onload = () => resolve(String(reader.result));
14699
+ reader.onerror = () => reject(reader.error || new Error("Failed to read font bytes"));
14700
+ reader.readAsDataURL(blob);
14701
+ });
14702
+ }
14703
+ async function fetchFontProxyDataUri(family, weight, style, source) {
14704
+ const proxyUrl = resolveHarnessFontProxyUrl();
14705
+ if (!proxyUrl) return null;
14706
+ const key = `${source}|${family}|${weight}|${style}`;
14707
+ const existing = remoteFontDataUriPromises.get(key);
14708
+ if (existing) return existing;
14709
+ const promise = (async () => {
14710
+ try {
14711
+ const url = `${proxyUrl}?family=${encodeURIComponent(family)}&weight=${weight}&italic=${style === "italic" ? 1 : 0}&source=${source}`;
14712
+ const res = await fetch(url, { credentials: "omit" });
14713
+ if (!res.ok) return null;
14714
+ const contentType = res.headers.get("content-type") || "";
14715
+ if (/application\/json/i.test(contentType)) return null;
14716
+ const buf = await res.arrayBuffer();
14717
+ const bytes = new Uint8Array(buf);
14718
+ if (!isEmbeddableTrueType(bytes)) return null;
14719
+ return await arrayBufferToDataUri(buf);
14720
+ } catch {
14721
+ return null;
14722
+ }
14723
+ })();
14724
+ remoteFontDataUriPromises.set(key, promise);
14725
+ return promise;
14726
+ }
14727
+ async function registerRemoteFontFaceViaProxy(family, requestedWeight, styleRaw) {
14728
+ if (typeof document === "undefined" || typeof FontFace === "undefined" || !document.fonts) return false;
14729
+ if (!resolveHarnessFontProxyUrl()) return false;
14730
+ const style = /italic|oblique/i.test(styleRaw || "") ? "italic" : "normal";
14731
+ const parsed = typeof requestedWeight === "number" ? requestedWeight : Number.parseInt(String(requestedWeight || "400"), 10);
14732
+ const resolved = Number.isFinite(parsed) ? parsed <= 350 ? 300 : parsed <= 450 ? 400 : parsed <= 550 ? 500 : parsed <= 650 ? 600 : 700 : 400;
14733
+ const weightLadder = [resolved];
14734
+ for (const w of [400, 500, 700, 600, 300]) if (!weightLadder.includes(w)) weightLadder.push(w);
14735
+ const sourceOrder = FONTSHARE_SLUGS[family] ? ["fontshare", "google"] : ["google", "fontshare"];
14736
+ for (const actualWeight of weightLadder) {
14737
+ for (const source of sourceOrder) {
14738
+ const faceKey = `${family}|${actualWeight}|${style}|${source}`;
14739
+ if (registeredRemoteFontFaces.has(faceKey)) return true;
14740
+ const dataUri = await fetchFontProxyDataUri(family, actualWeight, style, source);
14741
+ if (!dataUri) continue;
14742
+ try {
14743
+ const face = new FontFace(family, `url("${dataUri}")`, {
14744
+ weight: String(actualWeight),
14745
+ style
14746
+ });
14747
+ await face.load();
14748
+ document.fonts.add(face);
14749
+ appendDataUriFontFaceRule(family, actualWeight, style, dataUri);
14750
+ registeredRemoteFontFaces.add(faceKey);
14751
+ return true;
14752
+ } catch {
14753
+ }
14754
+ }
14755
+ }
14756
+ return false;
14757
+ }
14618
14758
  const LOCAL_FONT_FACE_VARIANTS = [
14619
14759
  { key: "regular", weight: 400, style: "normal" },
14620
14760
  { key: "bold", weight: 700, style: "normal" },
@@ -14644,6 +14784,7 @@ async function registerLocalFontFaces(fontFamily) {
14644
14784
  style: variant.style
14645
14785
  });
14646
14786
  document.fonts.add(face);
14787
+ appendLocalFontFaceRule(fontFamily, variant.weight, variant.style, file);
14647
14788
  loads.push(face.load().catch(() => void 0));
14648
14789
  } catch {
14649
14790
  }
@@ -14808,6 +14949,9 @@ async function ensureFontsForResolvedConfig(config) {
14808
14949
  const descriptors = collectFontDescriptorsFromConfig(config);
14809
14950
  const families = new Set(descriptors.map((d) => d.family));
14810
14951
  await withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 3500);
14952
+ await withTimeout(Promise.all(
14953
+ descriptors.map((d) => registerRemoteFontFaceViaProxy(d.family, d.weight, d.style))
14954
+ ), 5e3);
14811
14955
  if (document.fonts) {
14812
14956
  descriptors.forEach((d) => {
14813
14957
  const stylePrefix = d.style === "italic" ? "italic " : "";
@@ -15795,7 +15939,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
15795
15939
  }
15796
15940
  return svgString;
15797
15941
  }
15798
- const PACKAGE_VERSION = "0.5.131";
15942
+ const PACKAGE_VERSION = "0.5.136";
15799
15943
  const roundParityValue = (value) => {
15800
15944
  if (typeof value !== "number") return value;
15801
15945
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16214,7 +16358,7 @@ class PixldocsRenderer {
16214
16358
  await this.waitForCanvasScene(container, cloned, i);
16215
16359
  }
16216
16360
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16217
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-BA6bhLs-.js");
16361
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-D3L64sAJ.js");
16218
16362
  const prepared = preparePagesForExport(
16219
16363
  cloned.pages,
16220
16364
  canvasWidth,
@@ -18117,7 +18261,6 @@ function rasterSvgToPngDataUrl(svgMarkup, pxW, pxH) {
18117
18261
  const blob = new Blob([svgMarkup], { type: "image/svg+xml;charset=utf-8" });
18118
18262
  const url = URL.createObjectURL(blob);
18119
18263
  const img = new Image();
18120
- img.crossOrigin = "anonymous";
18121
18264
  const cleanup = () => {
18122
18265
  try {
18123
18266
  URL.revokeObjectURL(url);
@@ -18135,6 +18278,7 @@ function rasterSvgToPngDataUrl(svgMarkup, pxW, pxH) {
18135
18278
  resolve(null);
18136
18279
  return;
18137
18280
  }
18281
+ ctx.clearRect(0, 0, pxW, pxH);
18138
18282
  ctx.drawImage(img, 0, 0, pxW, pxH);
18139
18283
  const dataUrl = canvas.toDataURL("image/png");
18140
18284
  cleanup();
@@ -18306,7 +18450,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18306
18450
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18307
18451
  sanitizeSvgTreeForPdf(svgToDraw);
18308
18452
  try {
18309
- const { bakeTextAnchorPositionsFromLiveSvg } = await import("./vectorPdfExport-BA6bhLs-.js");
18453
+ const { bakeTextAnchorPositionsFromLiveSvg } = await import("./vectorPdfExport-D3L64sAJ.js");
18310
18454
  await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
18311
18455
  } catch (e) {
18312
18456
  console.warn("[canvas-renderer][pdf-export] anchor-bake pass failed (continuing):", e);
@@ -18651,4 +18795,4 @@ export {
18651
18795
  collectFontDescriptorsFromConfig as y,
18652
18796
  collectFontsFromConfig as z
18653
18797
  };
18654
- //# sourceMappingURL=index-Sc4qRn5o.js.map
18798
+ //# sourceMappingURL=index-BHa3ekoF.js.map