@pixldocs/canvas-renderer 0.3.14 → 0.3.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
@@ -179,10 +179,7 @@ export declare class PixldocsRenderer {
179
179
  * Convenience: fetch by ID with simple flat data and render.
180
180
  */
181
181
  renderById(templateId: string, formData?: Record<string, any>, options?: RenderOptions): Promise<RenderResult>;
182
- /**
183
- * Wait until every image on the Fabric canvas inside the container has loaded.
184
- * Polls img elements since we can't hook into Fabric's internal load events.
185
- */
182
+ private getExpectedImageCount;
186
183
  private waitForCanvasImages;
187
184
  private getNormalizedGradientStops;
188
185
  private paintPageBackground;
package/dist/index.js CHANGED
@@ -10383,12 +10383,7 @@ async function loadGoogleFontCSS(rawFontFamily) {
10383
10383
  const existing = loadingPromises.get(fontFamily);
10384
10384
  if (existing) return existing;
10385
10385
  const promise = (async () => {
10386
- var _a;
10387
10386
  try {
10388
- if ((_a = document.fonts) == null ? void 0 : _a.check(`16px "${fontFamily}"`)) {
10389
- loadedFonts.add(fontFamily);
10390
- return;
10391
- }
10392
10387
  const encoded = encodeURIComponent(fontFamily);
10393
10388
  const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
10394
10389
  const link = document.createElement("link");
@@ -10694,26 +10689,85 @@ class PixldocsRenderer {
10694
10689
  return this.render(resolved.config, options);
10695
10690
  }
10696
10691
  // ─── Internal: render a page using the full PreviewCanvas engine ───
10697
- /**
10698
- * Wait until every image on the Fabric canvas inside the container has loaded.
10699
- * Polls img elements since we can't hook into Fabric's internal load events.
10700
- */
10701
- waitForCanvasImages(container, maxWaitMs = 15e3, pollMs = 200) {
10692
+ getExpectedImageCount(config, pageIndex) {
10693
+ const page = config.pages[pageIndex];
10694
+ if (!(page == null ? void 0 : page.children)) return 0;
10695
+ let count = 0;
10696
+ const walk = (nodes) => {
10697
+ for (const node of nodes) {
10698
+ if (!node || node.visible === false) continue;
10699
+ const src = typeof node.src === "string" ? node.src.trim() : "";
10700
+ const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
10701
+ if (node.type === "image" && (src || imageUrl)) count += 1;
10702
+ if (Array.isArray(node.children) && node.children.length > 0) {
10703
+ walk(node.children);
10704
+ }
10705
+ }
10706
+ };
10707
+ walk(page.children);
10708
+ return count;
10709
+ }
10710
+ waitForCanvasImages(container, expectedImageCount, maxWaitMs = 15e3, pollMs = 120) {
10702
10711
  return new Promise((resolve) => {
10703
10712
  const start = Date.now();
10713
+ let stableFrames = 0;
10714
+ const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
10715
+ const collectRenderableImages = (obj, seen) => {
10716
+ if (!obj || typeof obj !== "object") return;
10717
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
10718
+ for (const candidate of candidates) {
10719
+ if (isRenderableImage(candidate)) {
10720
+ seen.add(candidate);
10721
+ } else if (candidate instanceof HTMLImageElement) {
10722
+ return false;
10723
+ }
10724
+ }
10725
+ const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
10726
+ for (const child of nested) {
10727
+ if (collectRenderableImages(child, seen) === false) return false;
10728
+ }
10729
+ return true;
10730
+ };
10731
+ const getFabricCanvas = () => {
10732
+ const registry2 = window.__fabricCanvasRegistry;
10733
+ if (registry2 instanceof Map) {
10734
+ for (const entry of registry2.values()) {
10735
+ const canvas = entry == null ? void 0 : entry.canvas;
10736
+ const el = (canvas == null ? void 0 : canvas.lowerCanvasEl) || (canvas == null ? void 0 : canvas.upperCanvasEl);
10737
+ if (el && container.contains(el)) return canvas;
10738
+ }
10739
+ }
10740
+ return null;
10741
+ };
10742
+ const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
10704
10743
  const check = () => {
10705
- const images = container.querySelectorAll("img");
10706
- let allLoaded = true;
10707
- images.forEach((img) => {
10708
- if (!img.complete) allLoaded = false;
10709
- });
10710
- if (allLoaded || Date.now() - start > maxWaitMs) {
10711
- requestAnimationFrame(() => setTimeout(resolve, 400));
10744
+ const elapsed = Date.now() - start;
10745
+ const domImages = Array.from(container.querySelectorAll("img"));
10746
+ const allDomLoaded = domImages.every((img) => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
10747
+ const fabricCanvas = getFabricCanvas();
10748
+ const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
10749
+ const renderableImages = /* @__PURE__ */ new Set();
10750
+ const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
10751
+ const actualImageCount = Math.max(domImages.length, renderableImages.size);
10752
+ !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
10753
+ const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
10754
+ const ready = allDomLoaded && fabricReady && hasExpectedAssets;
10755
+ if (ready) {
10756
+ stableFrames += 1;
10757
+ if (stableFrames >= 2) {
10758
+ settle();
10759
+ return;
10760
+ }
10761
+ } else {
10762
+ stableFrames = 0;
10763
+ }
10764
+ if (elapsed >= maxWaitMs) {
10765
+ settle();
10712
10766
  return;
10713
10767
  }
10714
10768
  setTimeout(check, pollMs);
10715
10769
  };
10716
- setTimeout(check, 500);
10770
+ setTimeout(check, 0);
10717
10771
  });
10718
10772
  }
10719
10773
  getNormalizedGradientStops(gradient) {
@@ -10814,7 +10868,8 @@ class PixldocsRenderer {
10814
10868
  container.remove();
10815
10869
  };
10816
10870
  const onReady = () => {
10817
- this.waitForCanvasImages(container).then(() => {
10871
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
10872
+ this.waitForCanvasImages(container, expectedImageCount).then(() => {
10818
10873
  try {
10819
10874
  const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
10820
10875
  if (!fabricCanvas) {