@pixldocs/canvas-renderer 0.5.14 → 0.5.16

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
@@ -91,8 +91,7 @@ export declare function embedFontsForConfig(pdf: jsPDF, config: any, fontBaseUrl
91
91
  export declare function embedFontsInPdf(pdf: jsPDF, fontFamilies: Set<string>, fontBaseUrl: string): Promise<Set<string>>;
92
92
 
93
93
  /**
94
- * Ensure all fonts required by a fully-resolved TemplateConfig are loaded
95
- * and available to Fabric/Canvas before rendering.
94
+ * Start loading all fonts required by a fully-resolved TemplateConfig.
96
95
  *
97
96
  * This is the **single API** consumers (and the renderer internally) should
98
97
  * call to guarantee font parity with EC2 `/render-from-form`.
@@ -101,8 +100,8 @@ export declare function embedFontsInPdf(pdf: jsPDF, fontFamilies: Set<string>, f
101
100
  * 1. Walks ALL text nodes (including clones/repeatables) collecting
102
101
  * fontFamily + fontWeight + fontStyle.
103
102
  * 2. Loads each unique family via Google Fonts CSS v1 (idempotent).
104
- * 3. Explicitly loads each weight+style combo via `document.fonts.load()`.
105
- * 4. Awaits `document.fonts.ready` so Fabric never paints with fallback faces.
103
+ * 3. Kicks off each weight+style combo via `document.fonts.load()` without
104
+ * blocking render completion; late font load reflow handles final metrics.
106
105
  *
107
106
  * Idempotent — safe to call multiple times for the same config.
108
107
  */
package/dist/index.js CHANGED
@@ -2637,6 +2637,19 @@ const FONTS_TO_PRELOAD = [
2637
2637
  ];
2638
2638
  let fontsLoaded = false;
2639
2639
  let fontsLoadingPromise = null;
2640
+ const withFontTimeout = async (promise, timeoutMs = 4e3) => {
2641
+ let timeoutId;
2642
+ try {
2643
+ return await Promise.race([
2644
+ promise,
2645
+ new Promise((resolve) => {
2646
+ timeoutId = setTimeout(resolve, timeoutMs);
2647
+ })
2648
+ ]);
2649
+ } finally {
2650
+ if (timeoutId) clearTimeout(timeoutId);
2651
+ }
2652
+ };
2640
2653
  const preloadFont = async (fontFamily) => {
2641
2654
  try {
2642
2655
  if (document.fonts) {
@@ -2657,11 +2670,11 @@ const preloadAllFonts = async () => {
2657
2670
  }
2658
2671
  fontsLoadingPromise = (async () => {
2659
2672
  if (document.fonts) {
2660
- await document.fonts.ready;
2673
+ await withFontTimeout(document.fonts.ready, 2500);
2661
2674
  }
2662
- await Promise.all(FONTS_TO_PRELOAD.map((font) => preloadFont(font)));
2675
+ await withFontTimeout(Promise.all(FONTS_TO_PRELOAD.map((font) => preloadFont(font))).then(() => void 0), 5e3);
2663
2676
  if (document.fonts) {
2664
- await document.fonts.ready;
2677
+ await withFontTimeout(document.fonts.ready, 2500);
2665
2678
  }
2666
2679
  fontsLoaded = true;
2667
2680
  })();
@@ -2669,14 +2682,12 @@ const preloadAllFonts = async () => {
2669
2682
  };
2670
2683
  const waitForFontsReady = async () => {
2671
2684
  if (!document.fonts) return;
2672
- await document.fonts.ready;
2685
+ await withFontTimeout(document.fonts.ready, 2500);
2673
2686
  };
2674
- const DEFAULT_FONT_CHECK_TIMEOUT_MS = 3500;
2675
- const FONT_CHECK_POLL_MS = 60;
2676
2687
  const waitUntilFontsAvailable = async (fontFamilies, options) => {
2677
2688
  if (!document.fonts || fontFamilies.length === 0) return;
2678
- const timeoutMs = (options == null ? void 0 : options.timeoutMs) ?? DEFAULT_FONT_CHECK_TIMEOUT_MS;
2679
- const pollMs = (options == null ? void 0 : options.pollIntervalMs) ?? FONT_CHECK_POLL_MS;
2689
+ const timeoutMs = options == null ? void 0 : options.timeoutMs;
2690
+ const pollMs = options == null ? void 0 : options.pollIntervalMs;
2680
2691
  const deadline = Date.now() + timeoutMs;
2681
2692
  const check = () => fontFamilies.every(
2682
2693
  (f) => document.fonts.check(`16px "${f}"`) && document.fonts.check(`bold 16px "${f}"`)
@@ -11675,6 +11686,17 @@ function normalizeFontFamily(fontStack) {
11675
11686
  }
11676
11687
  const loadedFonts = /* @__PURE__ */ new Set();
11677
11688
  const loadingPromises = /* @__PURE__ */ new Map();
11689
+ function withTimeout(promise, timeoutMs = 4e3) {
11690
+ let timeoutId;
11691
+ return Promise.race([
11692
+ promise,
11693
+ new Promise((resolve) => {
11694
+ timeoutId = setTimeout(resolve, timeoutMs);
11695
+ })
11696
+ ]).finally(() => {
11697
+ if (timeoutId) clearTimeout(timeoutId);
11698
+ });
11699
+ }
11678
11700
  async function loadGoogleFontCSS(rawFontFamily) {
11679
11701
  if (!rawFontFamily || typeof document === "undefined") return;
11680
11702
  const fontFamily = normalizeFontFamily(rawFontFamily);
@@ -11695,10 +11717,6 @@ async function loadGoogleFontCSS(rawFontFamily) {
11695
11717
  link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
11696
11718
  document.head.appendChild(link);
11697
11719
  });
11698
- if (document.fonts) {
11699
- await document.fonts.load(`16px "${fontFamily}"`);
11700
- await document.fonts.ready;
11701
- }
11702
11720
  loadedFonts.add(fontFamily);
11703
11721
  } catch (e) {
11704
11722
  console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
@@ -11810,17 +11828,15 @@ async function ensureFontsForResolvedConfig(config) {
11810
11828
  if (typeof document === "undefined") return;
11811
11829
  const descriptors = collectFontDescriptorsFromConfig(config);
11812
11830
  const families = new Set(descriptors.map((d) => d.family));
11813
- await Promise.all([...families].map((f) => loadGoogleFontCSS(f)));
11831
+ void withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 2500);
11814
11832
  if (document.fonts) {
11815
- const loadPromises = descriptors.map((d) => {
11833
+ descriptors.forEach((d) => {
11816
11834
  const stylePrefix = d.style === "italic" ? "italic " : "";
11817
11835
  const weightStr = String(d.weight);
11818
11836
  const spec = `${stylePrefix}${weightStr} 16px "${d.family}"`;
11819
- return document.fonts.load(spec).catch(() => {
11837
+ document.fonts.load(spec).catch(() => {
11820
11838
  });
11821
11839
  });
11822
- await Promise.all(loadPromises);
11823
- await document.fonts.ready;
11824
11840
  }
11825
11841
  }
11826
11842
  function PixldocsPreview(props) {
@@ -12604,14 +12620,8 @@ class PixldocsRenderer {
12604
12620
  return null;
12605
12621
  }
12606
12622
  async waitForStableTextMetrics(container, config) {
12607
- var _a;
12608
12623
  if (typeof document !== "undefined") {
12609
12624
  await ensureFontsForResolvedConfig(config);
12610
- await ((_a = document.fonts) == null ? void 0 : _a.ready);
12611
- const fontFamilies = [...collectFontsFromConfig(config)].filter(Boolean);
12612
- if (fontFamilies.length > 0) {
12613
- await waitUntilFontsAvailable(fontFamilies, { timeoutMs: 4e3, pollIntervalMs: 50 });
12614
- }
12615
12625
  }
12616
12626
  const fabricInstance = this.getFabricCanvasFromContainer(container);
12617
12627
  if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
@@ -12621,9 +12631,9 @@ class PixldocsRenderer {
12621
12631
  clearMeasurementCache();
12622
12632
  };
12623
12633
  const reflowTextboxes = () => {
12624
- var _a2, _b, _c;
12634
+ var _a, _b, _c;
12625
12635
  const walk = (obj) => {
12626
- var _a3, _b2, _c2, _d;
12636
+ var _a2, _b2, _c2, _d;
12627
12637
  if (!obj) return;
12628
12638
  const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
12629
12639
  if (children.length) children.forEach(walk);
@@ -12635,8 +12645,8 @@ class PixldocsRenderer {
12635
12645
  scaleY: obj.scaleY
12636
12646
  };
12637
12647
  const resetTextboxLayoutInternals = () => {
12638
- var _a4;
12639
- (_a4 = obj._clearCache) == null ? void 0 : _a4.call(obj);
12648
+ var _a3;
12649
+ (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
12640
12650
  obj.__charBounds = [];
12641
12651
  obj.__lineWidths = [];
12642
12652
  obj.__lineHeights = [];
@@ -12650,7 +12660,7 @@ class PixldocsRenderer {
12650
12660
  resetTextboxLayoutInternals();
12651
12661
  obj.initDimensions();
12652
12662
  if (saved.width != null) {
12653
- (_a3 = obj.set) == null ? void 0 : _a3.call(obj, {
12663
+ (_a2 = obj.set) == null ? void 0 : _a2.call(obj, {
12654
12664
  width: saved.width,
12655
12665
  scaleX: saved.scaleX,
12656
12666
  scaleY: saved.scaleY
@@ -12669,7 +12679,7 @@ class PixldocsRenderer {
12669
12679
  }
12670
12680
  };
12671
12681
  fabricInstance.getObjects().forEach(walk);
12672
- (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
12682
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
12673
12683
  (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
12674
12684
  (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
12675
12685
  };