@pixldocs/canvas-renderer 0.5.77 → 0.5.78

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.cjs CHANGED
@@ -11664,6 +11664,232 @@ function applyContentBoundsPagination(config) {
11664
11664
  if (!mutated) return config;
11665
11665
  return { ...config, pages: resultPages };
11666
11666
  }
11667
+ function normalizeFontFamily(fontStack) {
11668
+ const first = fontStack.split(",")[0].trim();
11669
+ return first.replace(/^['"]|['"]$/g, "");
11670
+ }
11671
+ const loadedFonts = /* @__PURE__ */ new Set();
11672
+ const loadingPromises = /* @__PURE__ */ new Map();
11673
+ function withTimeout(promise, timeoutMs = 4e3) {
11674
+ let timeoutId;
11675
+ return Promise.race([
11676
+ promise,
11677
+ new Promise((resolve) => {
11678
+ timeoutId = setTimeout(resolve, timeoutMs);
11679
+ })
11680
+ ]).finally(() => {
11681
+ if (timeoutId) clearTimeout(timeoutId);
11682
+ });
11683
+ }
11684
+ async function loadGoogleFontCSS(rawFontFamily) {
11685
+ if (!rawFontFamily || typeof document === "undefined") return;
11686
+ const fontFamily = normalizeFontFamily(rawFontFamily);
11687
+ if (!fontFamily) return;
11688
+ if (loadedFonts.has(fontFamily)) return;
11689
+ const existing = loadingPromises.get(fontFamily);
11690
+ if (existing) return existing;
11691
+ const promise = (async () => {
11692
+ try {
11693
+ const encoded = encodeURIComponent(fontFamily);
11694
+ const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
11695
+ const link = document.createElement("link");
11696
+ link.rel = "stylesheet";
11697
+ link.href = url;
11698
+ link.crossOrigin = "anonymous";
11699
+ await new Promise((resolve, reject) => {
11700
+ link.onload = () => resolve();
11701
+ link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
11702
+ document.head.appendChild(link);
11703
+ });
11704
+ loadedFonts.add(fontFamily);
11705
+ } catch (e) {
11706
+ console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
11707
+ }
11708
+ })();
11709
+ loadingPromises.set(fontFamily, promise);
11710
+ await promise;
11711
+ loadingPromises.delete(fontFamily);
11712
+ }
11713
+ function collectFontsFromConfig(config) {
11714
+ var _a;
11715
+ const fonts = /* @__PURE__ */ new Set();
11716
+ fonts.add("Open Sans");
11717
+ fonts.add("Hind");
11718
+ function walk(nodes) {
11719
+ var _a2;
11720
+ if (!nodes) return;
11721
+ for (const node of nodes) {
11722
+ if (node.fontFamily) fonts.add(normalizeFontFamily(node.fontFamily));
11723
+ if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) fonts.add(normalizeFontFamily(node.smartProps.fontFamily));
11724
+ if (node.styles && Array.isArray(node.styles)) {
11725
+ for (const lineStyle of node.styles) {
11726
+ if (lineStyle && typeof lineStyle === "object") {
11727
+ for (const charStyle of Object.values(lineStyle)) {
11728
+ if (charStyle == null ? void 0 : charStyle.fontFamily) fonts.add(normalizeFontFamily(charStyle.fontFamily));
11729
+ }
11730
+ }
11731
+ }
11732
+ }
11733
+ if (node.children) walk(node.children);
11734
+ }
11735
+ }
11736
+ for (const page of config.pages || []) {
11737
+ walk(page.children || []);
11738
+ }
11739
+ if ((_a = config.themeConfig) == null ? void 0 : _a.variables) {
11740
+ for (const def of Object.values(config.themeConfig.variables)) {
11741
+ if (def.value && typeof def.value === "string" && !def.value.startsWith("#") && !def.value.startsWith("rgb")) {
11742
+ if (def.label && /font/i.test(def.label)) {
11743
+ fonts.add(normalizeFontFamily(def.value));
11744
+ }
11745
+ }
11746
+ }
11747
+ }
11748
+ return fonts;
11749
+ }
11750
+ function collectFontDescriptorsFromConfig(config) {
11751
+ var _a;
11752
+ const seen = /* @__PURE__ */ new Set();
11753
+ const descriptors = [];
11754
+ function add(family, weight, style) {
11755
+ const f = normalizeFontFamily(family);
11756
+ if (!f) return;
11757
+ const w = weight ?? 400;
11758
+ const s = style ?? "normal";
11759
+ const key = `${f}|${w}|${s}`;
11760
+ if (seen.has(key)) return;
11761
+ seen.add(key);
11762
+ descriptors.push({ family: f, weight: w, style: s });
11763
+ }
11764
+ function walk(nodes) {
11765
+ var _a2;
11766
+ if (!nodes) return;
11767
+ for (const node of nodes) {
11768
+ if (node.fontFamily) {
11769
+ add(node.fontFamily, node.fontWeight, node.fontStyle);
11770
+ if (node.type === "text") {
11771
+ for (const w of [300, 400, 500, 600, 700]) {
11772
+ add(node.fontFamily, w, node.fontStyle);
11773
+ }
11774
+ }
11775
+ }
11776
+ if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) {
11777
+ add(node.smartProps.fontFamily, node.smartProps.fontWeight, node.smartProps.fontStyle);
11778
+ }
11779
+ if (node.styles) {
11780
+ const styleEntries = Array.isArray(node.styles) ? node.styles : Object.values(node.styles);
11781
+ for (const lineStyle of styleEntries) {
11782
+ if (lineStyle && typeof lineStyle === "object") {
11783
+ for (const charStyle of Object.values(lineStyle)) {
11784
+ if (charStyle == null ? void 0 : charStyle.fontFamily) {
11785
+ add(charStyle.fontFamily, charStyle.fontWeight, charStyle.fontStyle);
11786
+ }
11787
+ }
11788
+ }
11789
+ }
11790
+ }
11791
+ if (node.children) walk(node.children);
11792
+ }
11793
+ }
11794
+ add("Open Sans", 400, "normal");
11795
+ add("Hind", 400, "normal");
11796
+ add("Hind", 700, "normal");
11797
+ for (const page of config.pages || []) {
11798
+ walk(page.children || []);
11799
+ }
11800
+ if ((_a = config.themeConfig) == null ? void 0 : _a.variables) {
11801
+ for (const def of Object.values(config.themeConfig.variables)) {
11802
+ if (def.value && typeof def.value === "string" && !def.value.startsWith("#") && !def.value.startsWith("rgb")) {
11803
+ if (def.label && /font/i.test(def.label)) {
11804
+ add(def.value);
11805
+ }
11806
+ }
11807
+ }
11808
+ }
11809
+ return descriptors;
11810
+ }
11811
+ async function ensureFontsForResolvedConfig(config) {
11812
+ if (typeof document === "undefined") return;
11813
+ const descriptors = collectFontDescriptorsFromConfig(config);
11814
+ const families = new Set(descriptors.map((d) => d.family));
11815
+ await withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 3500);
11816
+ if (document.fonts) {
11817
+ descriptors.forEach((d) => {
11818
+ const stylePrefix = d.style === "italic" ? "italic " : "";
11819
+ const weightStr = String(d.weight);
11820
+ const spec = `${stylePrefix}${weightStr} 16px "${d.family}"`;
11821
+ document.fonts.load(spec).catch(() => {
11822
+ });
11823
+ });
11824
+ }
11825
+ }
11826
+ function configHasAutoShrinkText$1(config) {
11827
+ var _a;
11828
+ if (!((_a = config == null ? void 0 : config.pages) == null ? void 0 : _a.length)) return false;
11829
+ const walk = (nodes) => {
11830
+ for (const node of nodes || []) {
11831
+ if (!node) continue;
11832
+ if (node.type === "text" && node.overflowPolicy === "auto-shrink") return true;
11833
+ if (Array.isArray(node.children) && node.children.length && walk(node.children)) return true;
11834
+ }
11835
+ return false;
11836
+ };
11837
+ for (const page of config.pages) {
11838
+ if (walk(page.children || [])) return true;
11839
+ }
11840
+ return false;
11841
+ }
11842
+ async function awaitFontsForConfig(config, maxWaitMs) {
11843
+ if (typeof document === "undefined" || !document.fonts) return;
11844
+ await ensureFontsForResolvedConfig(config);
11845
+ const descriptors = collectFontDescriptorsFromConfig(config);
11846
+ if (descriptors.length === 0) return;
11847
+ const loads = Promise.all(
11848
+ descriptors.map((d) => {
11849
+ const stylePrefix = d.style === "italic" ? "italic " : "";
11850
+ const spec = `${stylePrefix}${d.weight} 16px "${d.family}"`;
11851
+ return document.fonts.load(spec).catch(() => []);
11852
+ })
11853
+ ).then(() => void 0);
11854
+ await Promise.race([
11855
+ loads,
11856
+ new Promise((resolve) => setTimeout(resolve, maxWaitMs))
11857
+ ]);
11858
+ await Promise.race([
11859
+ document.fonts.ready.catch(() => void 0).then(() => void 0),
11860
+ new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
11861
+ ]);
11862
+ const checkSpecs = [];
11863
+ for (const d of descriptors) {
11864
+ const stylePrefix = d.style === "italic" ? "italic " : "";
11865
+ checkSpecs.push(`${stylePrefix}${d.weight} 16px "${d.family}"`);
11866
+ }
11867
+ const startedAt = Date.now();
11868
+ const pollBudget = Math.min(maxWaitMs, 2500);
11869
+ const allReady = () => {
11870
+ for (const spec of checkSpecs) {
11871
+ try {
11872
+ if (!document.fonts.check(spec)) return false;
11873
+ } catch {
11874
+ }
11875
+ }
11876
+ return true;
11877
+ };
11878
+ while (!allReady() && Date.now() - startedAt < pollBudget) {
11879
+ await new Promise((resolve) => setTimeout(resolve, 50));
11880
+ }
11881
+ await new Promise(
11882
+ (resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
11883
+ );
11884
+ }
11885
+ async function awaitFontsBeforeTextReflow(config) {
11886
+ if (typeof document === "undefined" || !configHasAutoShrinkText$1(config)) return;
11887
+ try {
11888
+ await awaitFontsForConfig(config, 4e3);
11889
+ } catch (error) {
11890
+ console.warn("[@pixldocs/canvas-renderer] Font wait before text reflow failed:", error);
11891
+ }
11892
+ }
11667
11893
  function repeatablePageToSection(page) {
11668
11894
  return {
11669
11895
  id: page.id,
@@ -11857,6 +12083,7 @@ async function resolveTemplateData(options) {
11857
12083
  }
11858
12084
  }
11859
12085
  const repeatablePagesInput = deriveRepeatablePagesFromTemplate(config, inlineFormSchema, mergedFormData);
12086
+ await awaitFontsBeforeTextReflow(config);
11860
12087
  const resolvedConfig = applyFormDataToConfig(
11861
12088
  config,
11862
12089
  mappings,
@@ -12001,6 +12228,7 @@ async function resolveFromForm(options) {
12001
12228
  };
12002
12229
  collectFormats(formConfig.sections);
12003
12230
  }
12231
+ await awaitFontsBeforeTextReflow(templateConfig);
12004
12232
  let resolvedConfig = applyFormDataToConfig(
12005
12233
  templateConfig,
12006
12234
  mappings,
@@ -12179,224 +12407,6 @@ function paintRepeatableSections(config, repeatableSections) {
12179
12407
  }
12180
12408
  }
12181
12409
  }
12182
- function normalizeFontFamily(fontStack) {
12183
- const first = fontStack.split(",")[0].trim();
12184
- return first.replace(/^['"]|['"]$/g, "");
12185
- }
12186
- const loadedFonts = /* @__PURE__ */ new Set();
12187
- const loadingPromises = /* @__PURE__ */ new Map();
12188
- function withTimeout(promise, timeoutMs = 4e3) {
12189
- let timeoutId;
12190
- return Promise.race([
12191
- promise,
12192
- new Promise((resolve) => {
12193
- timeoutId = setTimeout(resolve, timeoutMs);
12194
- })
12195
- ]).finally(() => {
12196
- if (timeoutId) clearTimeout(timeoutId);
12197
- });
12198
- }
12199
- async function loadGoogleFontCSS(rawFontFamily) {
12200
- if (!rawFontFamily || typeof document === "undefined") return;
12201
- const fontFamily = normalizeFontFamily(rawFontFamily);
12202
- if (!fontFamily) return;
12203
- if (loadedFonts.has(fontFamily)) return;
12204
- const existing = loadingPromises.get(fontFamily);
12205
- if (existing) return existing;
12206
- const promise = (async () => {
12207
- try {
12208
- const encoded = encodeURIComponent(fontFamily);
12209
- const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
12210
- const link = document.createElement("link");
12211
- link.rel = "stylesheet";
12212
- link.href = url;
12213
- link.crossOrigin = "anonymous";
12214
- await new Promise((resolve, reject) => {
12215
- link.onload = () => resolve();
12216
- link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
12217
- document.head.appendChild(link);
12218
- });
12219
- loadedFonts.add(fontFamily);
12220
- } catch (e) {
12221
- console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
12222
- }
12223
- })();
12224
- loadingPromises.set(fontFamily, promise);
12225
- await promise;
12226
- loadingPromises.delete(fontFamily);
12227
- }
12228
- function collectFontsFromConfig(config) {
12229
- var _a;
12230
- const fonts = /* @__PURE__ */ new Set();
12231
- fonts.add("Open Sans");
12232
- fonts.add("Hind");
12233
- function walk(nodes) {
12234
- var _a2;
12235
- if (!nodes) return;
12236
- for (const node of nodes) {
12237
- if (node.fontFamily) fonts.add(normalizeFontFamily(node.fontFamily));
12238
- if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) fonts.add(normalizeFontFamily(node.smartProps.fontFamily));
12239
- if (node.styles && Array.isArray(node.styles)) {
12240
- for (const lineStyle of node.styles) {
12241
- if (lineStyle && typeof lineStyle === "object") {
12242
- for (const charStyle of Object.values(lineStyle)) {
12243
- if (charStyle == null ? void 0 : charStyle.fontFamily) fonts.add(normalizeFontFamily(charStyle.fontFamily));
12244
- }
12245
- }
12246
- }
12247
- }
12248
- if (node.children) walk(node.children);
12249
- }
12250
- }
12251
- for (const page of config.pages || []) {
12252
- walk(page.children || []);
12253
- }
12254
- if ((_a = config.themeConfig) == null ? void 0 : _a.variables) {
12255
- for (const def of Object.values(config.themeConfig.variables)) {
12256
- if (def.value && typeof def.value === "string" && !def.value.startsWith("#") && !def.value.startsWith("rgb")) {
12257
- if (def.label && /font/i.test(def.label)) {
12258
- fonts.add(normalizeFontFamily(def.value));
12259
- }
12260
- }
12261
- }
12262
- }
12263
- return fonts;
12264
- }
12265
- function collectFontDescriptorsFromConfig(config) {
12266
- var _a;
12267
- const seen = /* @__PURE__ */ new Set();
12268
- const descriptors = [];
12269
- function add(family, weight, style) {
12270
- const f = normalizeFontFamily(family);
12271
- if (!f) return;
12272
- const w = weight ?? 400;
12273
- const s = style ?? "normal";
12274
- const key = `${f}|${w}|${s}`;
12275
- if (seen.has(key)) return;
12276
- seen.add(key);
12277
- descriptors.push({ family: f, weight: w, style: s });
12278
- }
12279
- function walk(nodes) {
12280
- var _a2;
12281
- if (!nodes) return;
12282
- for (const node of nodes) {
12283
- if (node.fontFamily) {
12284
- add(node.fontFamily, node.fontWeight, node.fontStyle);
12285
- if (node.type === "text") {
12286
- for (const w of [300, 400, 500, 600, 700]) {
12287
- add(node.fontFamily, w, node.fontStyle);
12288
- }
12289
- }
12290
- }
12291
- if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) {
12292
- add(node.smartProps.fontFamily, node.smartProps.fontWeight, node.smartProps.fontStyle);
12293
- }
12294
- if (node.styles) {
12295
- const styleEntries = Array.isArray(node.styles) ? node.styles : Object.values(node.styles);
12296
- for (const lineStyle of styleEntries) {
12297
- if (lineStyle && typeof lineStyle === "object") {
12298
- for (const charStyle of Object.values(lineStyle)) {
12299
- if (charStyle == null ? void 0 : charStyle.fontFamily) {
12300
- add(charStyle.fontFamily, charStyle.fontWeight, charStyle.fontStyle);
12301
- }
12302
- }
12303
- }
12304
- }
12305
- }
12306
- if (node.children) walk(node.children);
12307
- }
12308
- }
12309
- add("Open Sans", 400, "normal");
12310
- add("Hind", 400, "normal");
12311
- add("Hind", 700, "normal");
12312
- for (const page of config.pages || []) {
12313
- walk(page.children || []);
12314
- }
12315
- if ((_a = config.themeConfig) == null ? void 0 : _a.variables) {
12316
- for (const def of Object.values(config.themeConfig.variables)) {
12317
- if (def.value && typeof def.value === "string" && !def.value.startsWith("#") && !def.value.startsWith("rgb")) {
12318
- if (def.label && /font/i.test(def.label)) {
12319
- add(def.value);
12320
- }
12321
- }
12322
- }
12323
- }
12324
- return descriptors;
12325
- }
12326
- async function ensureFontsForResolvedConfig(config) {
12327
- if (typeof document === "undefined") return;
12328
- const descriptors = collectFontDescriptorsFromConfig(config);
12329
- const families = new Set(descriptors.map((d) => d.family));
12330
- await withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 3500);
12331
- if (document.fonts) {
12332
- descriptors.forEach((d) => {
12333
- const stylePrefix = d.style === "italic" ? "italic " : "";
12334
- const weightStr = String(d.weight);
12335
- const spec = `${stylePrefix}${weightStr} 16px "${d.family}"`;
12336
- document.fonts.load(spec).catch(() => {
12337
- });
12338
- });
12339
- }
12340
- }
12341
- function configHasAutoShrinkText$1(config) {
12342
- var _a;
12343
- if (!((_a = config == null ? void 0 : config.pages) == null ? void 0 : _a.length)) return false;
12344
- const walk = (nodes) => {
12345
- for (const node of nodes || []) {
12346
- if (!node) continue;
12347
- if (node.type === "text" && node.overflowPolicy === "auto-shrink") return true;
12348
- if (Array.isArray(node.children) && node.children.length && walk(node.children)) return true;
12349
- }
12350
- return false;
12351
- };
12352
- for (const page of config.pages) {
12353
- if (walk(page.children || [])) return true;
12354
- }
12355
- return false;
12356
- }
12357
- async function awaitFontsForConfig(config, maxWaitMs) {
12358
- if (typeof document === "undefined" || !document.fonts) return;
12359
- await ensureFontsForResolvedConfig(config);
12360
- const descriptors = collectFontDescriptorsFromConfig(config);
12361
- if (descriptors.length === 0) return;
12362
- const loads = Promise.all(
12363
- descriptors.map((d) => {
12364
- const stylePrefix = d.style === "italic" ? "italic " : "";
12365
- const spec = `${stylePrefix}${d.weight} 16px "${d.family}"`;
12366
- return document.fonts.load(spec).catch(() => []);
12367
- })
12368
- ).then(() => void 0);
12369
- await Promise.race([
12370
- loads,
12371
- new Promise((resolve) => setTimeout(resolve, maxWaitMs))
12372
- ]);
12373
- await Promise.race([
12374
- document.fonts.ready.catch(() => void 0).then(() => void 0),
12375
- new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
12376
- ]);
12377
- const checkSpecs = [];
12378
- for (const d of descriptors) {
12379
- const stylePrefix = d.style === "italic" ? "italic " : "";
12380
- checkSpecs.push(`${stylePrefix}${d.weight} 16px "${d.family}"`);
12381
- }
12382
- const startedAt = Date.now();
12383
- const pollBudget = Math.min(maxWaitMs, 2500);
12384
- const allReady = () => {
12385
- for (const spec of checkSpecs) {
12386
- try {
12387
- if (!document.fonts.check(spec)) return false;
12388
- } catch {
12389
- }
12390
- }
12391
- return true;
12392
- };
12393
- while (!allReady() && Date.now() - startedAt < pollBudget) {
12394
- await new Promise((resolve) => setTimeout(resolve, 50));
12395
- }
12396
- await new Promise(
12397
- (resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
12398
- );
12399
- }
12400
12410
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
12401
12411
  function countUnderlinedNodes(config) {
12402
12412
  var _a;
@@ -12587,7 +12597,7 @@ function PixldocsPreview(props) {
12587
12597
  !canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
12588
12598
  ] });
12589
12599
  }
12590
- const PACKAGE_VERSION = "0.5.77";
12600
+ const PACKAGE_VERSION = "0.5.78";
12591
12601
  const roundParityValue = (value) => {
12592
12602
  if (typeof value !== "number") return value;
12593
12603
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;