@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.
@@ -14626,6 +14626,38 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
14626
14626
  resolveFontWeight,
14627
14627
  rewriteSvgFontsForJsPDF
14628
14628
  }, Symbol.toStringTag, { value: "Module" }));
14629
+ const FONTSHARE_SLUGS = {
14630
+ "Satoshi": "satoshi",
14631
+ "Cabinet Grotesk": "cabinet-grotesk",
14632
+ "Clash Display": "clash-display",
14633
+ "Clash Grotesk": "clash-grotesk",
14634
+ "General Sans": "general-sans",
14635
+ "Switzer": "switzer",
14636
+ "Supreme": "supreme",
14637
+ "Author": "author",
14638
+ "Boska": "boska",
14639
+ "Excon": "excon",
14640
+ "Khand": "khand",
14641
+ "Sentient": "sentient",
14642
+ "Synonym": "synonym",
14643
+ "Erode": "erode",
14644
+ "Ranade": "ranade",
14645
+ "Tanker": "tanker",
14646
+ "Zodiak": "zodiak",
14647
+ "Gambarino": "gambarino",
14648
+ "Melodrama": "melodrama",
14649
+ "Bespoke Serif": "bespoke-serif",
14650
+ "Bespoke Stencil": "bespoke-stencil",
14651
+ "Panchang": "panchang",
14652
+ "Quincy CF": "quincy-cf",
14653
+ "Pally": "pally",
14654
+ "Tabular": "tabular",
14655
+ "Sharpie": "sharpie",
14656
+ "Stardom": "stardom",
14657
+ "Rebond Grotesque": "rebond-grotesque",
14658
+ "Telma": "telma",
14659
+ "Nippo": "nippo"
14660
+ };
14629
14661
  function normalizeFontFamily(fontStack) {
14630
14662
  const first = fontStack.split(",")[0].trim();
14631
14663
  return first.replace(/^['"]|['"]$/g, "");
@@ -14633,6 +14665,114 @@ function normalizeFontFamily(fontStack) {
14633
14665
  const loadedFonts = /* @__PURE__ */ new Set();
14634
14666
  const loadingPromises = /* @__PURE__ */ new Map();
14635
14667
  const registeredLocalFontFaces = /* @__PURE__ */ new Set();
14668
+ const registeredRemoteFontFaces = /* @__PURE__ */ new Set();
14669
+ const remoteFontDataUriPromises = /* @__PURE__ */ new Map();
14670
+ let localFontFaceStyleEl = null;
14671
+ function ensureLocalFontFaceStyle() {
14672
+ if (typeof document === "undefined") return null;
14673
+ if (localFontFaceStyleEl && localFontFaceStyleEl.isConnected) return localFontFaceStyleEl;
14674
+ try {
14675
+ localFontFaceStyleEl = document.createElement("style");
14676
+ localFontFaceStyleEl.setAttribute("data-pixldocs-local-fontfaces", "1");
14677
+ document.head.appendChild(localFontFaceStyleEl);
14678
+ return localFontFaceStyleEl;
14679
+ } catch {
14680
+ return null;
14681
+ }
14682
+ }
14683
+ function appendLocalFontFaceRule(family, weight, style, file) {
14684
+ const styleEl = ensureLocalFontFaceStyle();
14685
+ if (!styleEl) return;
14686
+ const cssText = `@font-face{font-family:"${family}";src:url("/fonts/${file}");font-weight:${weight};font-style:${style};font-display:swap;}
14687
+ `;
14688
+ styleEl.appendChild(document.createTextNode(cssText));
14689
+ }
14690
+ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
14691
+ const styleEl = ensureLocalFontFaceStyle();
14692
+ if (!styleEl) return;
14693
+ const cssText = `@font-face{font-family:"${family}";src:url("${dataUri}") format("truetype");font-weight:${weight};font-style:${style};font-display:swap;}
14694
+ `;
14695
+ styleEl.appendChild(document.createTextNode(cssText));
14696
+ }
14697
+ function resolveHarnessFontProxyUrl() {
14698
+ var _a, _b;
14699
+ try {
14700
+ 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)) || "";
14701
+ const base = String(runtimeBase || "").replace(/\/$/, "");
14702
+ return base ? `${base}/functions/v1/font-proxy` : "";
14703
+ } catch {
14704
+ return "";
14705
+ }
14706
+ }
14707
+ function isEmbeddableTrueType(bytes) {
14708
+ if (bytes.length < 12) return false;
14709
+ const signature = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3]);
14710
+ return signature === "\0\0\0" || signature === "true";
14711
+ }
14712
+ async function arrayBufferToDataUri(buf) {
14713
+ const blob = new Blob([buf], { type: "font/ttf" });
14714
+ return await new Promise((resolve, reject) => {
14715
+ const reader = new FileReader();
14716
+ reader.onload = () => resolve(String(reader.result));
14717
+ reader.onerror = () => reject(reader.error || new Error("Failed to read font bytes"));
14718
+ reader.readAsDataURL(blob);
14719
+ });
14720
+ }
14721
+ async function fetchFontProxyDataUri(family, weight, style, source) {
14722
+ const proxyUrl = resolveHarnessFontProxyUrl();
14723
+ if (!proxyUrl) return null;
14724
+ const key = `${source}|${family}|${weight}|${style}`;
14725
+ const existing = remoteFontDataUriPromises.get(key);
14726
+ if (existing) return existing;
14727
+ const promise = (async () => {
14728
+ try {
14729
+ const url = `${proxyUrl}?family=${encodeURIComponent(family)}&weight=${weight}&italic=${style === "italic" ? 1 : 0}&source=${source}`;
14730
+ const res = await fetch(url, { credentials: "omit" });
14731
+ if (!res.ok) return null;
14732
+ const contentType = res.headers.get("content-type") || "";
14733
+ if (/application\/json/i.test(contentType)) return null;
14734
+ const buf = await res.arrayBuffer();
14735
+ const bytes = new Uint8Array(buf);
14736
+ if (!isEmbeddableTrueType(bytes)) return null;
14737
+ return await arrayBufferToDataUri(buf);
14738
+ } catch {
14739
+ return null;
14740
+ }
14741
+ })();
14742
+ remoteFontDataUriPromises.set(key, promise);
14743
+ return promise;
14744
+ }
14745
+ async function registerRemoteFontFaceViaProxy(family, requestedWeight, styleRaw) {
14746
+ if (typeof document === "undefined" || typeof FontFace === "undefined" || !document.fonts) return false;
14747
+ if (!resolveHarnessFontProxyUrl()) return false;
14748
+ const style = /italic|oblique/i.test(styleRaw || "") ? "italic" : "normal";
14749
+ const parsed = typeof requestedWeight === "number" ? requestedWeight : Number.parseInt(String(requestedWeight || "400"), 10);
14750
+ const resolved = Number.isFinite(parsed) ? parsed <= 350 ? 300 : parsed <= 450 ? 400 : parsed <= 550 ? 500 : parsed <= 650 ? 600 : 700 : 400;
14751
+ const weightLadder = [resolved];
14752
+ for (const w of [400, 500, 700, 600, 300]) if (!weightLadder.includes(w)) weightLadder.push(w);
14753
+ const sourceOrder = FONTSHARE_SLUGS[family] ? ["fontshare", "google"] : ["google", "fontshare"];
14754
+ for (const actualWeight of weightLadder) {
14755
+ for (const source of sourceOrder) {
14756
+ const faceKey = `${family}|${actualWeight}|${style}|${source}`;
14757
+ if (registeredRemoteFontFaces.has(faceKey)) return true;
14758
+ const dataUri = await fetchFontProxyDataUri(family, actualWeight, style, source);
14759
+ if (!dataUri) continue;
14760
+ try {
14761
+ const face = new FontFace(family, `url("${dataUri}")`, {
14762
+ weight: String(actualWeight),
14763
+ style
14764
+ });
14765
+ await face.load();
14766
+ document.fonts.add(face);
14767
+ appendDataUriFontFaceRule(family, actualWeight, style, dataUri);
14768
+ registeredRemoteFontFaces.add(faceKey);
14769
+ return true;
14770
+ } catch {
14771
+ }
14772
+ }
14773
+ }
14774
+ return false;
14775
+ }
14636
14776
  const LOCAL_FONT_FACE_VARIANTS = [
14637
14777
  { key: "regular", weight: 400, style: "normal" },
14638
14778
  { key: "bold", weight: 700, style: "normal" },
@@ -14662,6 +14802,7 @@ async function registerLocalFontFaces(fontFamily) {
14662
14802
  style: variant.style
14663
14803
  });
14664
14804
  document.fonts.add(face);
14805
+ appendLocalFontFaceRule(fontFamily, variant.weight, variant.style, file);
14665
14806
  loads.push(face.load().catch(() => void 0));
14666
14807
  } catch {
14667
14808
  }
@@ -14826,6 +14967,9 @@ async function ensureFontsForResolvedConfig(config) {
14826
14967
  const descriptors = collectFontDescriptorsFromConfig(config);
14827
14968
  const families = new Set(descriptors.map((d) => d.family));
14828
14969
  await withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 3500);
14970
+ await withTimeout(Promise.all(
14971
+ descriptors.map((d) => registerRemoteFontFaceViaProxy(d.family, d.weight, d.style))
14972
+ ), 5e3);
14829
14973
  if (document.fonts) {
14830
14974
  descriptors.forEach((d) => {
14831
14975
  const stylePrefix = d.style === "italic" ? "italic " : "";
@@ -15813,7 +15957,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
15813
15957
  }
15814
15958
  return svgString;
15815
15959
  }
15816
- const PACKAGE_VERSION = "0.5.131";
15960
+ const PACKAGE_VERSION = "0.5.136";
15817
15961
  const roundParityValue = (value) => {
15818
15962
  if (typeof value !== "number") return value;
15819
15963
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16232,7 +16376,7 @@ class PixldocsRenderer {
16232
16376
  await this.waitForCanvasScene(container, cloned, i);
16233
16377
  }
16234
16378
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16235
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-8Q3O7Z_Q.cjs"));
16379
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Bq-_OarH.cjs"));
16236
16380
  const prepared = preparePagesForExport(
16237
16381
  cloned.pages,
16238
16382
  canvasWidth,
@@ -18135,7 +18279,6 @@ function rasterSvgToPngDataUrl(svgMarkup, pxW, pxH) {
18135
18279
  const blob = new Blob([svgMarkup], { type: "image/svg+xml;charset=utf-8" });
18136
18280
  const url = URL.createObjectURL(blob);
18137
18281
  const img = new Image();
18138
- img.crossOrigin = "anonymous";
18139
18282
  const cleanup = () => {
18140
18283
  try {
18141
18284
  URL.revokeObjectURL(url);
@@ -18153,6 +18296,7 @@ function rasterSvgToPngDataUrl(svgMarkup, pxW, pxH) {
18153
18296
  resolve(null);
18154
18297
  return;
18155
18298
  }
18299
+ ctx.clearRect(0, 0, pxW, pxH);
18156
18300
  ctx.drawImage(img, 0, 0, pxW, pxH);
18157
18301
  const dataUrl = canvas.toDataURL("image/png");
18158
18302
  cleanup();
@@ -18324,7 +18468,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
18324
18468
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
18325
18469
  sanitizeSvgTreeForPdf(svgToDraw);
18326
18470
  try {
18327
- const { bakeTextAnchorPositionsFromLiveSvg } = await Promise.resolve().then(() => require("./vectorPdfExport-8Q3O7Z_Q.cjs"));
18471
+ const { bakeTextAnchorPositionsFromLiveSvg } = await Promise.resolve().then(() => require("./vectorPdfExport-Bq-_OarH.cjs"));
18328
18472
  await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
18329
18473
  } catch (e) {
18330
18474
  console.warn("[canvas-renderer][pdf-export] anchor-bake pass failed (continuing):", e);
@@ -18666,4 +18810,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
18666
18810
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
18667
18811
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
18668
18812
  exports.warmTemplateFromForm = warmTemplateFromForm;
18669
- //# sourceMappingURL=index-DmqoeVsF.cjs.map
18813
+ //# sourceMappingURL=index-DUHE7WhJ.cjs.map