@pixldocs/canvas-renderer 0.3.11 → 0.3.13

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.
package/dist/index.d.ts CHANGED
@@ -36,6 +36,8 @@ export declare interface CanvasSize {
36
36
  height: number;
37
37
  }
38
38
 
39
+ export declare function collectFontDescriptorsFromConfig(config: TemplateConfig): FontDescriptor[];
40
+
39
41
  /**
40
42
  * Collect all font families used in a template config.
41
43
  */
@@ -52,6 +54,35 @@ export declare interface DynamicField {
52
54
  [key: string]: any;
53
55
  }
54
56
 
57
+ /**
58
+ * Ensure all fonts required by a fully-resolved TemplateConfig are loaded
59
+ * and available to Fabric/Canvas before rendering.
60
+ *
61
+ * This is the **single API** consumers (and the renderer internally) should
62
+ * call to guarantee font parity with EC2 `/render-from-form`.
63
+ *
64
+ * It:
65
+ * 1. Walks ALL text nodes (including clones/repeatables) collecting
66
+ * fontFamily + fontWeight + fontStyle.
67
+ * 2. Loads each unique family via Google Fonts CSS v1 (idempotent).
68
+ * 3. Explicitly loads each weight+style combo via `document.fonts.load()`.
69
+ * 4. Awaits `document.fonts.ready` so Fabric never paints with fallback faces.
70
+ *
71
+ * Idempotent — safe to call multiple times for the same config.
72
+ */
73
+ export declare function ensureFontsForResolvedConfig(config: TemplateConfig): Promise<void>;
74
+
75
+ /**
76
+ * Walk a fully-resolved TemplateConfig and collect every unique
77
+ * { fontFamily, fontWeight, fontStyle } tuple from all text nodes
78
+ * (including clones, repeatable children, and per-character Fabric styles).
79
+ */
80
+ export declare interface FontDescriptor {
81
+ family: string;
82
+ weight: number | string;
83
+ style: string;
84
+ }
85
+
55
86
  export { InferredSection }
56
87
 
57
88
  /**
package/dist/index.js CHANGED
@@ -10462,9 +10462,8 @@ async function loadGoogleFontCSS(rawFontFamily) {
10462
10462
  loadedFonts.add(fontFamily);
10463
10463
  return;
10464
10464
  }
10465
- const weights = "100;200;300;400;500;600;700;800;900";
10466
10465
  const encoded = encodeURIComponent(fontFamily);
10467
- const url = `https://fonts.googleapis.com/css2?family=${encoded}:ital,wght@0,${weights};1,${weights}&display=swap`;
10466
+ const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
10468
10467
  const link = document.createElement("link");
10469
10468
  link.rel = "stylesheet";
10470
10469
  link.href = url;
@@ -10497,6 +10496,15 @@ function collectFontsFromConfig(config) {
10497
10496
  for (const node of nodes) {
10498
10497
  if (node.fontFamily) fonts.add(normalizeFontFamily(node.fontFamily));
10499
10498
  if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) fonts.add(normalizeFontFamily(node.smartProps.fontFamily));
10499
+ if (node.styles && Array.isArray(node.styles)) {
10500
+ for (const lineStyle of node.styles) {
10501
+ if (lineStyle && typeof lineStyle === "object") {
10502
+ for (const charStyle of Object.values(lineStyle)) {
10503
+ if (charStyle == null ? void 0 : charStyle.fontFamily) fonts.add(normalizeFontFamily(charStyle.fontFamily));
10504
+ }
10505
+ }
10506
+ }
10507
+ }
10500
10508
  if (node.children) walk(node.children);
10501
10509
  }
10502
10510
  }
@@ -10514,6 +10522,76 @@ function collectFontsFromConfig(config) {
10514
10522
  }
10515
10523
  return fonts;
10516
10524
  }
10525
+ function collectFontDescriptorsFromConfig(config) {
10526
+ var _a;
10527
+ const seen = /* @__PURE__ */ new Set();
10528
+ const descriptors = [];
10529
+ function add(family, weight, style) {
10530
+ const f = normalizeFontFamily(family);
10531
+ if (!f) return;
10532
+ const w = weight ?? 400;
10533
+ const s = style ?? "normal";
10534
+ const key = `${f}|${w}|${s}`;
10535
+ if (seen.has(key)) return;
10536
+ seen.add(key);
10537
+ descriptors.push({ family: f, weight: w, style: s });
10538
+ }
10539
+ function walk(nodes) {
10540
+ var _a2;
10541
+ if (!nodes) return;
10542
+ for (const node of nodes) {
10543
+ if (node.fontFamily) {
10544
+ add(node.fontFamily, node.fontWeight, node.fontStyle);
10545
+ }
10546
+ if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) {
10547
+ add(node.smartProps.fontFamily, node.smartProps.fontWeight, node.smartProps.fontStyle);
10548
+ }
10549
+ if (node.styles && Array.isArray(node.styles)) {
10550
+ for (const lineStyle of node.styles) {
10551
+ if (lineStyle && typeof lineStyle === "object") {
10552
+ for (const charStyle of Object.values(lineStyle)) {
10553
+ if (charStyle == null ? void 0 : charStyle.fontFamily) {
10554
+ add(charStyle.fontFamily, charStyle.fontWeight, charStyle.fontStyle);
10555
+ }
10556
+ }
10557
+ }
10558
+ }
10559
+ }
10560
+ if (node.children) walk(node.children);
10561
+ }
10562
+ }
10563
+ add("Open Sans", 400, "normal");
10564
+ for (const page of config.pages || []) {
10565
+ walk(page.children || []);
10566
+ }
10567
+ if ((_a = config.themeConfig) == null ? void 0 : _a.variables) {
10568
+ for (const def of Object.values(config.themeConfig.variables)) {
10569
+ if (def.value && typeof def.value === "string" && !def.value.startsWith("#") && !def.value.startsWith("rgb")) {
10570
+ if (def.label && /font/i.test(def.label)) {
10571
+ add(def.value);
10572
+ }
10573
+ }
10574
+ }
10575
+ }
10576
+ return descriptors;
10577
+ }
10578
+ async function ensureFontsForResolvedConfig(config) {
10579
+ if (typeof document === "undefined") return;
10580
+ const descriptors = collectFontDescriptorsFromConfig(config);
10581
+ const families = new Set(descriptors.map((d) => d.family));
10582
+ await Promise.all([...families].map((f) => loadGoogleFontCSS(f)));
10583
+ if (document.fonts) {
10584
+ const loadPromises = descriptors.map((d) => {
10585
+ const stylePrefix = d.style === "italic" ? "italic " : "";
10586
+ const weightStr = String(d.weight);
10587
+ const spec = `${stylePrefix}${weightStr} 16px "${d.family}"`;
10588
+ return document.fonts.load(spec).catch(() => {
10589
+ });
10590
+ });
10591
+ await Promise.all(loadPromises);
10592
+ await document.fonts.ready;
10593
+ }
10594
+ }
10517
10595
  class PixldocsRenderer {
10518
10596
  constructor(config) {
10519
10597
  __publicField(this, "config");
@@ -10534,8 +10612,7 @@ class PixldocsRenderer {
10534
10612
  if (!page) {
10535
10613
  throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);
10536
10614
  }
10537
- const fonts = collectFontsFromConfig(templateConfig);
10538
- await Promise.all([...fonts].map((f) => loadGoogleFontCSS(f)));
10615
+ await ensureFontsForResolvedConfig(templateConfig);
10539
10616
  const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
10540
10617
  setPackageApiUrl2(this.config.imageProxyUrl);
10541
10618
  const dataUrl = await this.renderPageViaPreviewCanvas(
@@ -10763,7 +10840,9 @@ export {
10763
10840
  PixldocsPreview,
10764
10841
  PixldocsRenderer,
10765
10842
  applyThemeToConfig,
10843
+ collectFontDescriptorsFromConfig,
10766
10844
  collectFontsFromConfig,
10845
+ ensureFontsForResolvedConfig,
10767
10846
  loadGoogleFontCSS,
10768
10847
  normalizeFontFamily,
10769
10848
  resolveFromForm,