@pixldocs/canvas-renderer 0.5.104 → 0.5.106
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 +879 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +879 -157
- package/dist/index.js.map +1 -1
- package/dist/{svgTextToPath-CpWdqc8K.js → svgTextToPath-C20Obtt2.js} +88 -18
- package/dist/svgTextToPath-C20Obtt2.js.map +1 -0
- package/dist/{svgTextToPath-CWlhIf-q.cjs → svgTextToPath-DTKsddnS.cjs} +88 -18
- package/dist/svgTextToPath-DTKsddnS.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/svgTextToPath-CWlhIf-q.cjs.map +0 -1
- package/dist/svgTextToPath-CpWdqc8K.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
5
5
|
import { forwardRef, useRef, useState, useMemo, useEffect, useCallback, useImperativeHandle, createElement } from "react";
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
|
+
import { toast } from "sonner";
|
|
7
8
|
import { create } from "zustand";
|
|
8
9
|
import * as fabric from "fabric";
|
|
9
10
|
import { createRoot } from "react-dom/client";
|
|
@@ -2487,9 +2488,17 @@ const useEditorStore = create((set, get) => ({
|
|
|
2487
2488
|
}));
|
|
2488
2489
|
let canvasRegistry = /* @__PURE__ */ new Map();
|
|
2489
2490
|
function registerFabricCanvas(pageId, canvas) {
|
|
2490
|
-
canvasRegistry.
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2491
|
+
const existing = canvasRegistry.get(pageId);
|
|
2492
|
+
const registryKey = existing && existing !== canvas ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
2493
|
+
canvasRegistry.set(registryKey, canvas);
|
|
2494
|
+
return registryKey;
|
|
2495
|
+
}
|
|
2496
|
+
function unregisterFabricCanvas(pageId, canvas) {
|
|
2497
|
+
if (canvas) {
|
|
2498
|
+
const existing = canvasRegistry.get(pageId);
|
|
2499
|
+
if (existing === canvas) canvasRegistry.delete(pageId);
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2493
2502
|
canvasRegistry.delete(pageId);
|
|
2494
2503
|
}
|
|
2495
2504
|
const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -2561,6 +2570,8 @@ const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
|
2561
2570
|
]);
|
|
2562
2571
|
const loadedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2563
2572
|
const failedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2573
|
+
const loadedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2574
|
+
const failedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2564
2575
|
const loadingPromises$1 = /* @__PURE__ */ new Map();
|
|
2565
2576
|
async function loadGoogleFont(fontFamily, weights) {
|
|
2566
2577
|
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
@@ -2620,6 +2631,346 @@ async function _doLoadGoogleFont(fontFamily, weights) {
|
|
|
2620
2631
|
console.warn(`[GoogleFonts] Failed to load: ${fontFamily}`);
|
|
2621
2632
|
return false;
|
|
2622
2633
|
}
|
|
2634
|
+
async function loadFontshareFont(fontFamily, slug, weights) {
|
|
2635
|
+
if (loadedFontshareFonts.has(fontFamily)) return true;
|
|
2636
|
+
if (failedFontshareFonts.has(fontFamily)) return false;
|
|
2637
|
+
const existing = loadingPromises$1.get(`fontshare:${fontFamily}`);
|
|
2638
|
+
if (existing) return existing;
|
|
2639
|
+
const promise = (async () => {
|
|
2640
|
+
try {
|
|
2641
|
+
const weightStr = (weights || [300, 400, 500, 700]).join(",");
|
|
2642
|
+
const url = `https://api.fontshare.com/v2/css?f[]=${slug}@${weightStr}&display=swap`;
|
|
2643
|
+
if (document.querySelector(`link[href="${url}"]`)) return true;
|
|
2644
|
+
const link = document.createElement("link");
|
|
2645
|
+
link.rel = "stylesheet";
|
|
2646
|
+
link.href = url;
|
|
2647
|
+
return await new Promise((resolve) => {
|
|
2648
|
+
link.onload = async () => {
|
|
2649
|
+
try {
|
|
2650
|
+
await document.fonts.load(`16px "${fontFamily}"`);
|
|
2651
|
+
await document.fonts.load(`bold 16px "${fontFamily}"`);
|
|
2652
|
+
} catch {
|
|
2653
|
+
}
|
|
2654
|
+
resolve(true);
|
|
2655
|
+
};
|
|
2656
|
+
link.onerror = () => {
|
|
2657
|
+
console.warn(`[Fontshare] Failed to load: ${fontFamily}`);
|
|
2658
|
+
resolve(false);
|
|
2659
|
+
};
|
|
2660
|
+
document.head.appendChild(link);
|
|
2661
|
+
});
|
|
2662
|
+
} catch (e) {
|
|
2663
|
+
console.warn(`[Fontshare] Error loading ${fontFamily}:`, e);
|
|
2664
|
+
return false;
|
|
2665
|
+
}
|
|
2666
|
+
})();
|
|
2667
|
+
loadingPromises$1.set(`fontshare:${fontFamily}`, promise);
|
|
2668
|
+
try {
|
|
2669
|
+
const result = await promise;
|
|
2670
|
+
if (result) loadedFontshareFonts.add(fontFamily);
|
|
2671
|
+
else failedFontshareFonts.add(fontFamily);
|
|
2672
|
+
return result;
|
|
2673
|
+
} finally {
|
|
2674
|
+
loadingPromises$1.delete(`fontshare:${fontFamily}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
async function loadFont(fontFamily) {
|
|
2678
|
+
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
2679
|
+
const entry = findFontEntry(fontFamily);
|
|
2680
|
+
if ((entry == null ? void 0 : entry.source) === "fontshare" && entry.fontshareSlug) {
|
|
2681
|
+
return loadFontshareFont(fontFamily, entry.fontshareSlug);
|
|
2682
|
+
}
|
|
2683
|
+
return loadGoogleFont(fontFamily);
|
|
2684
|
+
}
|
|
2685
|
+
const EXTENDED_FONT_LIST = [
|
|
2686
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2687
|
+
// PREMIUM (Fontshare) — modern, professional aesthetic
|
|
2688
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2689
|
+
{ name: "Satoshi", category: "Premium", local: false, source: "fontshare", fontshareSlug: "satoshi", popular: true },
|
|
2690
|
+
{ name: "Cabinet Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "cabinet-grotesk", popular: true },
|
|
2691
|
+
{ name: "Clash Display", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-display", popular: true },
|
|
2692
|
+
{ name: "Clash Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-grotesk", popular: true },
|
|
2693
|
+
{ name: "General Sans", category: "Premium", local: false, source: "fontshare", fontshareSlug: "general-sans", popular: true },
|
|
2694
|
+
{ name: "Switzer", category: "Premium", local: false, source: "fontshare", fontshareSlug: "switzer" },
|
|
2695
|
+
{ name: "Supreme", category: "Premium", local: false, source: "fontshare", fontshareSlug: "supreme" },
|
|
2696
|
+
{ name: "Author", category: "Premium", local: false, source: "fontshare", fontshareSlug: "author" },
|
|
2697
|
+
{ name: "Boska", category: "Premium", local: false, source: "fontshare", fontshareSlug: "boska" },
|
|
2698
|
+
{ name: "Excon", category: "Premium", local: false, source: "fontshare", fontshareSlug: "excon" },
|
|
2699
|
+
{ name: "Khand", category: "Premium", local: false, source: "fontshare", fontshareSlug: "khand" },
|
|
2700
|
+
{ name: "Sentient", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sentient" },
|
|
2701
|
+
{ name: "Synonym", category: "Premium", local: false, source: "fontshare", fontshareSlug: "synonym" },
|
|
2702
|
+
{ name: "Erode", category: "Premium", local: false, source: "fontshare", fontshareSlug: "erode" },
|
|
2703
|
+
{ name: "Ranade", category: "Premium", local: false, source: "fontshare", fontshareSlug: "ranade" },
|
|
2704
|
+
{ name: "Tanker", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tanker" },
|
|
2705
|
+
{ name: "Zodiak", category: "Premium", local: false, source: "fontshare", fontshareSlug: "zodiak" },
|
|
2706
|
+
{ name: "Gambarino", category: "Premium", local: false, source: "fontshare", fontshareSlug: "gambarino" },
|
|
2707
|
+
{ name: "Melodrama", category: "Premium", local: false, source: "fontshare", fontshareSlug: "melodrama" },
|
|
2708
|
+
{ name: "Bespoke Serif", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-serif" },
|
|
2709
|
+
{ name: "Bespoke Stencil", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-stencil" },
|
|
2710
|
+
{ name: "Panchang", category: "Premium", local: false, source: "fontshare", fontshareSlug: "panchang" },
|
|
2711
|
+
{ name: "Pally", category: "Premium", local: false, source: "fontshare", fontshareSlug: "pally" },
|
|
2712
|
+
{ name: "Tabular", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tabular" },
|
|
2713
|
+
{ name: "Sharpie", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sharpie" },
|
|
2714
|
+
{ name: "Stardom", category: "Premium", local: false, source: "fontshare", fontshareSlug: "stardom" },
|
|
2715
|
+
{ name: "Telma", category: "Premium", local: false, source: "fontshare", fontshareSlug: "telma" },
|
|
2716
|
+
{ name: "Nippo", category: "Premium", local: false, source: "fontshare", fontshareSlug: "nippo" },
|
|
2717
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2718
|
+
// SERIF — editorial, classical, elegant
|
|
2719
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2720
|
+
{ name: "Playfair Display", category: "Serif", local: true, popular: true },
|
|
2721
|
+
{ name: "Cormorant", category: "Serif", local: false, popular: true },
|
|
2722
|
+
{ name: "Cormorant Garamond", category: "Serif", local: false },
|
|
2723
|
+
{ name: "Cinzel", category: "Serif", local: false, popular: true },
|
|
2724
|
+
{ name: "Cinzel Decorative", category: "Serif", local: false },
|
|
2725
|
+
{ name: "Bodoni Moda", category: "Serif", local: false, popular: true },
|
|
2726
|
+
{ name: "DM Serif Display", category: "Serif", local: true },
|
|
2727
|
+
{ name: "DM Serif Text", category: "Serif", local: false },
|
|
2728
|
+
{ name: "Italiana", category: "Serif", local: false },
|
|
2729
|
+
{ name: "Marcellus", category: "Serif", local: false },
|
|
2730
|
+
{ name: "Marcellus SC", category: "Serif", local: false },
|
|
2731
|
+
{ name: "Yeseva One", category: "Serif", local: false },
|
|
2732
|
+
{ name: "Prata", category: "Serif", local: false },
|
|
2733
|
+
{ name: "Tenor Sans", category: "Serif", local: false },
|
|
2734
|
+
{ name: "Fraunces", category: "Serif", local: false, popular: true },
|
|
2735
|
+
{ name: "Newsreader", category: "Serif", local: false },
|
|
2736
|
+
{ name: "Source Serif Pro", category: "Serif", local: false },
|
|
2737
|
+
{ name: "Merriweather", category: "Serif", local: true },
|
|
2738
|
+
{ name: "Lora", category: "Serif", local: true },
|
|
2739
|
+
{ name: "EB Garamond", category: "Serif", local: true },
|
|
2740
|
+
{ name: "Libre Baskerville", category: "Serif", local: true },
|
|
2741
|
+
{ name: "Libre Caslon Text", category: "Serif", local: false },
|
|
2742
|
+
{ name: "Libre Caslon Display", category: "Serif", local: false },
|
|
2743
|
+
{ name: "Crimson Text", category: "Serif", local: true },
|
|
2744
|
+
{ name: "Crimson Pro", category: "Serif", local: false },
|
|
2745
|
+
{ name: "Noto Serif", category: "Serif", local: false },
|
|
2746
|
+
{ name: "Noto Serif Display", category: "Serif", local: false },
|
|
2747
|
+
{ name: "PT Serif", category: "Serif", local: false },
|
|
2748
|
+
{ name: "Bitter", category: "Serif", local: false },
|
|
2749
|
+
{ name: "Spectral", category: "Serif", local: false },
|
|
2750
|
+
{ name: "Cardo", category: "Serif", local: false },
|
|
2751
|
+
{ name: "Old Standard TT", category: "Serif", local: false },
|
|
2752
|
+
{ name: "Vollkorn", category: "Serif", local: false },
|
|
2753
|
+
{ name: "Cantata One", category: "Serif", local: false },
|
|
2754
|
+
{ name: "Domine", category: "Serif", local: false },
|
|
2755
|
+
{ name: "Gentium Plus", category: "Serif", local: false },
|
|
2756
|
+
{ name: "Tinos", category: "Serif", local: false },
|
|
2757
|
+
{ name: "Trirong", category: "Serif", local: false },
|
|
2758
|
+
{ name: "Sorts Mill Goudy", category: "Serif", local: false },
|
|
2759
|
+
{ name: "IM Fell English", category: "Serif", local: false },
|
|
2760
|
+
{ name: "IM Fell DW Pica", category: "Serif", local: false },
|
|
2761
|
+
{ name: "Petrona", category: "Serif", local: false },
|
|
2762
|
+
{ name: "Rozha One", category: "Serif", local: false },
|
|
2763
|
+
{ name: "Tiro Devanagari Hindi", category: "Serif", local: false },
|
|
2764
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2765
|
+
// SANS-SERIF — clean, modern, workhorse
|
|
2766
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2767
|
+
{ name: "Inter", category: "Sans-Serif", local: true, popular: true },
|
|
2768
|
+
{ name: "Montserrat", category: "Sans-Serif", local: true, popular: true },
|
|
2769
|
+
{ name: "Poppins", category: "Sans-Serif", local: true, popular: true },
|
|
2770
|
+
{ name: "Open Sans", category: "Sans-Serif", local: true },
|
|
2771
|
+
{ name: "Roboto", category: "Sans-Serif", local: true },
|
|
2772
|
+
{ name: "Lato", category: "Sans-Serif", local: true },
|
|
2773
|
+
{ name: "Raleway", category: "Sans-Serif", local: true },
|
|
2774
|
+
{ name: "Nunito", category: "Sans-Serif", local: true },
|
|
2775
|
+
{ name: "Source Sans Pro", category: "Sans-Serif", local: true },
|
|
2776
|
+
{ name: "Work Sans", category: "Sans-Serif", local: true },
|
|
2777
|
+
{ name: "DM Sans", category: "Sans-Serif", local: true, popular: true },
|
|
2778
|
+
{ name: "Outfit", category: "Sans-Serif", local: true, popular: true },
|
|
2779
|
+
{ name: "Figtree", category: "Sans-Serif", local: true },
|
|
2780
|
+
{ name: "Manrope", category: "Sans-Serif", local: true, popular: true },
|
|
2781
|
+
{ name: "Space Grotesk", category: "Sans-Serif", local: true, popular: true },
|
|
2782
|
+
{ name: "Mulish", category: "Sans-Serif", local: true },
|
|
2783
|
+
{ name: "Quicksand", category: "Sans-Serif", local: true },
|
|
2784
|
+
{ name: "Rubik", category: "Sans-Serif", local: true },
|
|
2785
|
+
{ name: "Karla", category: "Sans-Serif", local: true },
|
|
2786
|
+
{ name: "Plus Jakarta Sans", category: "Sans-Serif", local: true },
|
|
2787
|
+
{ name: "Libre Franklin", category: "Sans-Serif", local: true },
|
|
2788
|
+
{ name: "Sora", category: "Sans-Serif", local: true },
|
|
2789
|
+
{ name: "Urbanist", category: "Sans-Serif", local: true },
|
|
2790
|
+
{ name: "Lexend", category: "Sans-Serif", local: true },
|
|
2791
|
+
{ name: "Albert Sans", category: "Sans-Serif", local: true },
|
|
2792
|
+
{ name: "Noto Sans", category: "Sans-Serif", local: true },
|
|
2793
|
+
{ name: "Cabin", category: "Sans-Serif", local: true },
|
|
2794
|
+
{ name: "Barlow", category: "Sans-Serif", local: true },
|
|
2795
|
+
{ name: "Barlow Condensed", category: "Sans-Serif", local: false },
|
|
2796
|
+
{ name: "Josefin Sans", category: "Sans-Serif", local: true },
|
|
2797
|
+
{ name: "Archivo", category: "Sans-Serif", local: true },
|
|
2798
|
+
{ name: "Archivo Narrow", category: "Sans-Serif", local: false },
|
|
2799
|
+
{ name: "Overpass", category: "Sans-Serif", local: true },
|
|
2800
|
+
{ name: "Exo 2", category: "Sans-Serif", local: true },
|
|
2801
|
+
{ name: "Onest", category: "Sans-Serif", local: false, popular: true },
|
|
2802
|
+
{ name: "Be Vietnam Pro", category: "Sans-Serif", local: false },
|
|
2803
|
+
{ name: "Public Sans", category: "Sans-Serif", local: false },
|
|
2804
|
+
{ name: "Red Hat Display", category: "Sans-Serif", local: false },
|
|
2805
|
+
{ name: "Red Hat Text", category: "Sans-Serif", local: false },
|
|
2806
|
+
{ name: "Sen", category: "Sans-Serif", local: false },
|
|
2807
|
+
{ name: "Hanken Grotesk", category: "Sans-Serif", local: false },
|
|
2808
|
+
{ name: "Schibsted Grotesk", category: "Sans-Serif", local: false },
|
|
2809
|
+
{ name: "Reddit Sans", category: "Sans-Serif", local: false },
|
|
2810
|
+
{ name: "Instrument Sans", category: "Sans-Serif", local: false },
|
|
2811
|
+
{ name: "Geist", category: "Sans-Serif", local: false, popular: true },
|
|
2812
|
+
{ name: "Nunito Sans", category: "Sans-Serif", local: false },
|
|
2813
|
+
{ name: "PT Sans", category: "Sans-Serif", local: false },
|
|
2814
|
+
{ name: "PT Sans Narrow", category: "Sans-Serif", local: false },
|
|
2815
|
+
{ name: "Mukta", category: "Sans-Serif", local: false },
|
|
2816
|
+
{ name: "Anek Devanagari", category: "Sans-Serif", local: false },
|
|
2817
|
+
{ name: "Hind", category: "Sans-Serif", local: true },
|
|
2818
|
+
{ name: "Hind Vadodara", category: "Sans-Serif", local: false },
|
|
2819
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2820
|
+
// DISPLAY — bold, attention-grabbing headlines
|
|
2821
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2822
|
+
{ name: "Bebas Neue", category: "Display", local: true, popular: true },
|
|
2823
|
+
{ name: "Anton", category: "Display", local: true, popular: true },
|
|
2824
|
+
{ name: "Oswald", category: "Display", local: true },
|
|
2825
|
+
{ name: "Abril Fatface", category: "Display", local: true, popular: true },
|
|
2826
|
+
{ name: "League Spartan", category: "Display", local: true },
|
|
2827
|
+
{ name: "Teko", category: "Display", local: true },
|
|
2828
|
+
{ name: "Righteous", category: "Display", local: false },
|
|
2829
|
+
{ name: "Alfa Slab One", category: "Display", local: false, popular: true },
|
|
2830
|
+
{ name: "Archivo Black", category: "Display", local: false },
|
|
2831
|
+
{ name: "Fredoka", category: "Display", local: false },
|
|
2832
|
+
{ name: "Passion One", category: "Display", local: false },
|
|
2833
|
+
{ name: "Bowlby One", category: "Display", local: false },
|
|
2834
|
+
{ name: "Bowlby One SC", category: "Display", local: false },
|
|
2835
|
+
{ name: "Secular One", category: "Display", local: false },
|
|
2836
|
+
{ name: "Lilita One", category: "Display", local: false },
|
|
2837
|
+
{ name: "Titan One", category: "Display", local: false },
|
|
2838
|
+
{ name: "Russo One", category: "Display", local: false },
|
|
2839
|
+
{ name: "Staatliches", category: "Display", local: false, popular: true },
|
|
2840
|
+
{ name: "Dela Gothic One", category: "Display", local: false },
|
|
2841
|
+
{ name: "Ultra", category: "Display", local: false },
|
|
2842
|
+
{ name: "Sigmar One", category: "Display", local: false },
|
|
2843
|
+
{ name: "Sigmar", category: "Display", local: false },
|
|
2844
|
+
{ name: "Modak", category: "Display", local: false },
|
|
2845
|
+
{ name: "Bagel Fat One", category: "Display", local: false },
|
|
2846
|
+
{ name: "Climate Crisis", category: "Display", local: false },
|
|
2847
|
+
{ name: "Yatra One", category: "Display", local: false },
|
|
2848
|
+
{ name: "Bungee", category: "Display", local: false },
|
|
2849
|
+
{ name: "Bungee Shade", category: "Display", local: false },
|
|
2850
|
+
{ name: "Bungee Outline", category: "Display", local: false },
|
|
2851
|
+
{ name: "Bungee Inline", category: "Display", local: false },
|
|
2852
|
+
{ name: "Monoton", category: "Display", local: false, popular: true },
|
|
2853
|
+
{ name: "Black Ops One", category: "Display", local: false },
|
|
2854
|
+
{ name: "Faster One", category: "Display", local: false },
|
|
2855
|
+
{ name: "Rubik Glitch", category: "Display", local: false },
|
|
2856
|
+
{ name: "Rubik Mono One", category: "Display", local: false },
|
|
2857
|
+
{ name: "Rubik Wet Paint", category: "Display", local: false },
|
|
2858
|
+
{ name: "Rubik Bubbles", category: "Display", local: false },
|
|
2859
|
+
{ name: "Rubik Beastly", category: "Display", local: false },
|
|
2860
|
+
{ name: "Rubik Burned", category: "Display", local: false },
|
|
2861
|
+
{ name: "Rubik Distressed", category: "Display", local: false },
|
|
2862
|
+
{ name: "Rubik Iso", category: "Display", local: false },
|
|
2863
|
+
{ name: "Rubik Marker Hatch", category: "Display", local: false },
|
|
2864
|
+
{ name: "Rubik Maze", category: "Display", local: false },
|
|
2865
|
+
{ name: "Rubik Pixels", category: "Display", local: false },
|
|
2866
|
+
{ name: "Rubik Puddles", category: "Display", local: false },
|
|
2867
|
+
{ name: "Rubik Spray Paint", category: "Display", local: false },
|
|
2868
|
+
{ name: "Rubik Vinyl", category: "Display", local: false },
|
|
2869
|
+
{ name: "Saira Stencil One", category: "Display", local: false },
|
|
2870
|
+
{ name: "Audiowide", category: "Display", local: false },
|
|
2871
|
+
{ name: "Orbitron", category: "Display", local: false },
|
|
2872
|
+
{ name: "Plaster", category: "Display", local: false },
|
|
2873
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2874
|
+
// HANDWRITING / SCRIPT — fluid, personal, calligraphic
|
|
2875
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2876
|
+
{ name: "Dancing Script", category: "Handwriting", local: true, popular: true },
|
|
2877
|
+
{ name: "Pacifico", category: "Handwriting", local: true, popular: true },
|
|
2878
|
+
{ name: "Great Vibes", category: "Handwriting", local: true, popular: true },
|
|
2879
|
+
{ name: "Sacramento", category: "Handwriting", local: true },
|
|
2880
|
+
{ name: "Alex Brush", category: "Handwriting", local: true },
|
|
2881
|
+
{ name: "Allura", category: "Handwriting", local: true },
|
|
2882
|
+
{ name: "Caveat", category: "Handwriting", local: true },
|
|
2883
|
+
{ name: "Caveat Brush", category: "Handwriting", local: false },
|
|
2884
|
+
{ name: "Lobster", category: "Handwriting", local: true },
|
|
2885
|
+
{ name: "Lobster Two", category: "Handwriting", local: false },
|
|
2886
|
+
{ name: "Comfortaa", category: "Handwriting", local: true },
|
|
2887
|
+
{ name: "Tangerine", category: "Handwriting", local: false, popular: true },
|
|
2888
|
+
{ name: "Yellowtail", category: "Handwriting", local: false, popular: true },
|
|
2889
|
+
{ name: "Kaushan Script", category: "Handwriting", local: false, popular: true },
|
|
2890
|
+
{ name: "Parisienne", category: "Handwriting", local: false },
|
|
2891
|
+
{ name: "Petit Formal Script", category: "Handwriting", local: false },
|
|
2892
|
+
{ name: "Pinyon Script", category: "Handwriting", local: false },
|
|
2893
|
+
{ name: "Mrs Saint Delafield", category: "Handwriting", local: false },
|
|
2894
|
+
{ name: "Marck Script", category: "Handwriting", local: false },
|
|
2895
|
+
{ name: "Niconne", category: "Handwriting", local: false },
|
|
2896
|
+
{ name: "Homemade Apple", category: "Handwriting", local: false },
|
|
2897
|
+
{ name: "Permanent Marker", category: "Handwriting", local: false },
|
|
2898
|
+
{ name: "Reenie Beanie", category: "Handwriting", local: false },
|
|
2899
|
+
{ name: "Satisfy", category: "Handwriting", local: false },
|
|
2900
|
+
{ name: "Kalam", category: "Handwriting", local: false },
|
|
2901
|
+
{ name: "Indie Flower", category: "Handwriting", local: false },
|
|
2902
|
+
{ name: "Courgette", category: "Handwriting", local: false },
|
|
2903
|
+
{ name: "Cookie", category: "Handwriting", local: false },
|
|
2904
|
+
{ name: "Shadows Into Light", category: "Handwriting", local: false },
|
|
2905
|
+
{ name: "Patrick Hand", category: "Handwriting", local: false },
|
|
2906
|
+
{ name: "Amatic SC", category: "Handwriting", local: false },
|
|
2907
|
+
{ name: "Architects Daughter", category: "Handwriting", local: false },
|
|
2908
|
+
{ name: "Gloria Hallelujah", category: "Handwriting", local: false },
|
|
2909
|
+
{ name: "La Belle Aurore", category: "Handwriting", local: false },
|
|
2910
|
+
{ name: "Mr Dafoe", category: "Handwriting", local: false },
|
|
2911
|
+
{ name: "Italianno", category: "Handwriting", local: false },
|
|
2912
|
+
{ name: "Rouge Script", category: "Handwriting", local: false },
|
|
2913
|
+
{ name: "Grand Hotel", category: "Handwriting", local: false },
|
|
2914
|
+
{ name: "Bilbo Swash Caps", category: "Handwriting", local: false },
|
|
2915
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2916
|
+
// DECORATIVE / FUN — quirky, themed, special-occasion
|
|
2917
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2918
|
+
{ name: "Frijole", category: "Decorative", local: false },
|
|
2919
|
+
{ name: "Creepster", category: "Decorative", local: false },
|
|
2920
|
+
{ name: "Nosifer", category: "Decorative", local: false },
|
|
2921
|
+
{ name: "Ewert", category: "Decorative", local: false },
|
|
2922
|
+
{ name: "Lakki Reddy", category: "Decorative", local: false },
|
|
2923
|
+
{ name: "Henny Penny", category: "Decorative", local: false },
|
|
2924
|
+
{ name: "Special Elite", category: "Decorative", local: false, popular: true },
|
|
2925
|
+
{ name: "Vast Shadow", category: "Decorative", local: false },
|
|
2926
|
+
{ name: "Almendra Display", category: "Decorative", local: false },
|
|
2927
|
+
{ name: "Eater", category: "Decorative", local: false },
|
|
2928
|
+
{ name: "Butcherman", category: "Decorative", local: false },
|
|
2929
|
+
{ name: "Pirata One", category: "Decorative", local: false },
|
|
2930
|
+
{ name: "Metamorphous", category: "Decorative", local: false },
|
|
2931
|
+
{ name: "MedievalSharp", category: "Decorative", local: false },
|
|
2932
|
+
{ name: "Fascinate", category: "Decorative", local: false },
|
|
2933
|
+
{ name: "Fascinate Inline", category: "Decorative", local: false },
|
|
2934
|
+
{ name: "Sancreek", category: "Decorative", local: false },
|
|
2935
|
+
{ name: "Smokum", category: "Decorative", local: false },
|
|
2936
|
+
{ name: "Vampiro One", category: "Decorative", local: false },
|
|
2937
|
+
{ name: "Mountains of Christmas", category: "Decorative", local: false },
|
|
2938
|
+
{ name: "Caesar Dressing", category: "Decorative", local: false },
|
|
2939
|
+
{ name: "Megrim", category: "Decorative", local: false },
|
|
2940
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2941
|
+
// BLACKLETTER — gothic, medieval, formal
|
|
2942
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2943
|
+
{ name: "UnifrakturCook", category: "Blackletter", local: false },
|
|
2944
|
+
{ name: "UnifrakturMaguntia", category: "Blackletter", local: false },
|
|
2945
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2946
|
+
// MONOSPACE — code, technical
|
|
2947
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2948
|
+
{ name: "Roboto Mono", category: "Monospace", local: true },
|
|
2949
|
+
{ name: "Fira Code", category: "Monospace", local: true },
|
|
2950
|
+
{ name: "JetBrains Mono", category: "Monospace", local: true },
|
|
2951
|
+
{ name: "Source Code Pro", category: "Monospace", local: true },
|
|
2952
|
+
{ name: "IBM Plex Mono", category: "Monospace", local: true },
|
|
2953
|
+
{ name: "Space Mono", category: "Monospace", local: true },
|
|
2954
|
+
{ name: "Geist Mono", category: "Monospace", local: false },
|
|
2955
|
+
{ name: "DM Mono", category: "Monospace", local: false },
|
|
2956
|
+
{ name: "Inconsolata", category: "Monospace", local: false },
|
|
2957
|
+
{ name: "Cousine", category: "Monospace", local: false },
|
|
2958
|
+
{ name: "Anonymous Pro", category: "Monospace", local: false },
|
|
2959
|
+
{ name: "Cutive Mono", category: "Monospace", local: false },
|
|
2960
|
+
{ name: "Major Mono Display", category: "Monospace", local: false },
|
|
2961
|
+
{ name: "VT323", category: "Monospace", local: false },
|
|
2962
|
+
{ name: "Share Tech Mono", category: "Monospace", local: false }
|
|
2963
|
+
];
|
|
2964
|
+
const _fontLookupCache = /* @__PURE__ */ new Map();
|
|
2965
|
+
function findFontEntry(name) {
|
|
2966
|
+
const key = name.toLowerCase().replace(/[\s\-_]/g, "");
|
|
2967
|
+
if (_fontLookupCache.has(key)) return _fontLookupCache.get(key);
|
|
2968
|
+
const entry = EXTENDED_FONT_LIST.find(
|
|
2969
|
+
(f) => f.name.toLowerCase().replace(/[\s\-_]/g, "") === key
|
|
2970
|
+
);
|
|
2971
|
+
if (entry) _fontLookupCache.set(key, entry);
|
|
2972
|
+
return entry;
|
|
2973
|
+
}
|
|
2623
2974
|
const getObjectId = (obj) => obj.__docuforgeId;
|
|
2624
2975
|
const setObjectData = (obj, id) => {
|
|
2625
2976
|
obj.__docuforgeId = id;
|
|
@@ -2841,9 +3192,27 @@ const ensureFontLoaded = async (fontFamily) => {
|
|
|
2841
3192
|
return;
|
|
2842
3193
|
}
|
|
2843
3194
|
try {
|
|
2844
|
-
await
|
|
3195
|
+
await loadFont(fontFamily);
|
|
2845
3196
|
} catch (e) {
|
|
2846
|
-
console.warn(`Failed to load
|
|
3197
|
+
console.warn(`Failed to load font: ${fontFamily}`, e);
|
|
3198
|
+
try {
|
|
3199
|
+
await loadGoogleFont(fontFamily);
|
|
3200
|
+
} catch {
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
try {
|
|
3204
|
+
if (document.fonts) {
|
|
3205
|
+
await Promise.race([
|
|
3206
|
+
Promise.all([
|
|
3207
|
+
document.fonts.load(`16px "${fontFamily}"`),
|
|
3208
|
+
document.fonts.load(`bold 16px "${fontFamily}"`),
|
|
3209
|
+
document.fonts.load(`italic 16px "${fontFamily}"`),
|
|
3210
|
+
document.fonts.load(`bold italic 16px "${fontFamily}"`)
|
|
3211
|
+
]),
|
|
3212
|
+
new Promise((r) => setTimeout(r, 2500))
|
|
3213
|
+
]);
|
|
3214
|
+
}
|
|
3215
|
+
} catch {
|
|
2847
3216
|
}
|
|
2848
3217
|
};
|
|
2849
3218
|
const setupFontLoadingListener = (canvas, afterRerender) => {
|
|
@@ -5344,6 +5713,172 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
|
|
|
5344
5713
|
];
|
|
5345
5714
|
return parts.join(" ");
|
|
5346
5715
|
}
|
|
5716
|
+
let activeThemeColors = {};
|
|
5717
|
+
function setMarkdownThemeColors(c) {
|
|
5718
|
+
activeThemeColors = { ...c };
|
|
5719
|
+
}
|
|
5720
|
+
function resolveColorToken(token, theme) {
|
|
5721
|
+
const raw = token.trim();
|
|
5722
|
+
const t = raw.toLowerCase();
|
|
5723
|
+
if (t === "primary") return theme.primary;
|
|
5724
|
+
if (t === "secondary") return theme.secondary;
|
|
5725
|
+
if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
|
|
5726
|
+
if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
|
|
5727
|
+
if (/^[a-z]+$/i.test(raw)) return raw;
|
|
5728
|
+
return void 0;
|
|
5729
|
+
}
|
|
5730
|
+
function mergeStyle(a, b) {
|
|
5731
|
+
return { ...a, ...b };
|
|
5732
|
+
}
|
|
5733
|
+
function tokenize(input, theme) {
|
|
5734
|
+
const runs = [];
|
|
5735
|
+
const stack = [];
|
|
5736
|
+
let buf = "";
|
|
5737
|
+
const activeStyle = () => {
|
|
5738
|
+
let s = {};
|
|
5739
|
+
for (const e of stack) s = mergeStyle(s, e.style);
|
|
5740
|
+
return s;
|
|
5741
|
+
};
|
|
5742
|
+
const flush = () => {
|
|
5743
|
+
if (buf.length === 0) return;
|
|
5744
|
+
runs.push({ text: buf, style: activeStyle() });
|
|
5745
|
+
buf = "";
|
|
5746
|
+
};
|
|
5747
|
+
let i = 0;
|
|
5748
|
+
const n = input.length;
|
|
5749
|
+
const peek = (s, at = i) => input.startsWith(s, at);
|
|
5750
|
+
const findUnescaped = (needle, from) => {
|
|
5751
|
+
let p = from;
|
|
5752
|
+
while (p < n) {
|
|
5753
|
+
if (input[p] === "\\" && p + 1 < n) {
|
|
5754
|
+
p += 2;
|
|
5755
|
+
continue;
|
|
5756
|
+
}
|
|
5757
|
+
if (input.startsWith(needle, p)) return p;
|
|
5758
|
+
p++;
|
|
5759
|
+
}
|
|
5760
|
+
return -1;
|
|
5761
|
+
};
|
|
5762
|
+
const tryOpenBracket = () => {
|
|
5763
|
+
if (input[i] !== "[") return -1;
|
|
5764
|
+
const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
|
|
5765
|
+
if (!m) return -1;
|
|
5766
|
+
const kind = m[1];
|
|
5767
|
+
const tokenRaw = m[2];
|
|
5768
|
+
const closer = kind === "c" ? "[/c]" : "[/bg]";
|
|
5769
|
+
if (findUnescaped(closer, i + m[0].length) === -1) return -1;
|
|
5770
|
+
const color = resolveColorToken(tokenRaw, theme);
|
|
5771
|
+
const style = {};
|
|
5772
|
+
if (color) {
|
|
5773
|
+
if (kind === "c") style.fill = color;
|
|
5774
|
+
else style.textBackgroundColor = color;
|
|
5775
|
+
}
|
|
5776
|
+
flush();
|
|
5777
|
+
stack.push({ kind, style, closer });
|
|
5778
|
+
return i + m[0].length;
|
|
5779
|
+
};
|
|
5780
|
+
const tryCloseBracket = () => {
|
|
5781
|
+
for (let s = stack.length - 1; s >= 0; s--) {
|
|
5782
|
+
const top = stack[s];
|
|
5783
|
+
if (top.kind !== "c" && top.kind !== "bg") continue;
|
|
5784
|
+
if (peek(top.closer)) {
|
|
5785
|
+
if (s !== stack.length - 1) stack.length = s + 1;
|
|
5786
|
+
flush();
|
|
5787
|
+
stack.pop();
|
|
5788
|
+
return i + top.closer.length;
|
|
5789
|
+
}
|
|
5790
|
+
break;
|
|
5791
|
+
}
|
|
5792
|
+
return -1;
|
|
5793
|
+
};
|
|
5794
|
+
const toggle = (delim, kind, style) => {
|
|
5795
|
+
if (!peek(delim)) return null;
|
|
5796
|
+
const topIdx = stack.findIndex((e) => e.kind === kind);
|
|
5797
|
+
if (topIdx >= 0) {
|
|
5798
|
+
if (topIdx !== stack.length - 1) return null;
|
|
5799
|
+
flush();
|
|
5800
|
+
stack.length = topIdx;
|
|
5801
|
+
return i + delim.length;
|
|
5802
|
+
}
|
|
5803
|
+
if (findUnescaped(delim, i + delim.length) === -1) return null;
|
|
5804
|
+
flush();
|
|
5805
|
+
stack.push({ kind, style, closer: delim });
|
|
5806
|
+
return i + delim.length;
|
|
5807
|
+
};
|
|
5808
|
+
while (i < n) {
|
|
5809
|
+
const ch = input[i];
|
|
5810
|
+
if (ch === "\\" && i + 1 < n) {
|
|
5811
|
+
buf += input[i + 1];
|
|
5812
|
+
i += 2;
|
|
5813
|
+
continue;
|
|
5814
|
+
}
|
|
5815
|
+
if (ch === "[") {
|
|
5816
|
+
const closed = tryCloseBracket();
|
|
5817
|
+
if (closed > 0) {
|
|
5818
|
+
i = closed;
|
|
5819
|
+
continue;
|
|
5820
|
+
}
|
|
5821
|
+
const opened = tryOpenBracket();
|
|
5822
|
+
if (opened > 0) {
|
|
5823
|
+
i = opened;
|
|
5824
|
+
continue;
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
let next;
|
|
5828
|
+
if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
|
|
5829
|
+
i = next;
|
|
5830
|
+
continue;
|
|
5831
|
+
}
|
|
5832
|
+
if ((next = toggle("__", "under", { underline: true })) !== null) {
|
|
5833
|
+
i = next;
|
|
5834
|
+
continue;
|
|
5835
|
+
}
|
|
5836
|
+
if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
|
|
5837
|
+
i = next;
|
|
5838
|
+
continue;
|
|
5839
|
+
}
|
|
5840
|
+
if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
|
|
5841
|
+
i = next;
|
|
5842
|
+
continue;
|
|
5843
|
+
}
|
|
5844
|
+
if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
|
|
5845
|
+
i = next;
|
|
5846
|
+
continue;
|
|
5847
|
+
}
|
|
5848
|
+
buf += ch;
|
|
5849
|
+
i++;
|
|
5850
|
+
}
|
|
5851
|
+
flush();
|
|
5852
|
+
return runs;
|
|
5853
|
+
}
|
|
5854
|
+
function parseTextMarkdown(input, themeColors) {
|
|
5855
|
+
const theme = activeThemeColors;
|
|
5856
|
+
const runs = tokenize(input ?? "", theme);
|
|
5857
|
+
let plain = "";
|
|
5858
|
+
const styles = {};
|
|
5859
|
+
let lineIdx = 0;
|
|
5860
|
+
let charIdx = 0;
|
|
5861
|
+
let hasFormatting = false;
|
|
5862
|
+
for (const run of runs) {
|
|
5863
|
+
const styleHasContent = Object.keys(run.style).length > 0;
|
|
5864
|
+
if (styleHasContent) hasFormatting = true;
|
|
5865
|
+
for (const ch of run.text) {
|
|
5866
|
+
if (ch === "\n") {
|
|
5867
|
+
plain += "\n";
|
|
5868
|
+
lineIdx++;
|
|
5869
|
+
charIdx = 0;
|
|
5870
|
+
continue;
|
|
5871
|
+
}
|
|
5872
|
+
plain += ch;
|
|
5873
|
+
if (styleHasContent) {
|
|
5874
|
+
if (!styles[lineIdx]) styles[lineIdx] = {};
|
|
5875
|
+
styles[lineIdx][charIdx] = { ...run.style };
|
|
5876
|
+
}
|
|
5877
|
+
charIdx++;
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
return { plainText: plain, styles, hasFormatting };
|
|
5881
|
+
}
|
|
5347
5882
|
const roundDiag = (value) => {
|
|
5348
5883
|
if (typeof value !== "number") return value;
|
|
5349
5884
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -5482,6 +6017,13 @@ function createText(element) {
|
|
|
5482
6017
|
let fontSize = element.fontSize || 16;
|
|
5483
6018
|
const minFontSize = element.minFontSize || 8;
|
|
5484
6019
|
const maxLines = element.maxLines || 3;
|
|
6020
|
+
const formattingEnabled = element.formattingEnabled === true;
|
|
6021
|
+
let parsedStyles = {};
|
|
6022
|
+
if (formattingEnabled) {
|
|
6023
|
+
const parsed = parseTextMarkdown(text);
|
|
6024
|
+
text = parsed.plainText || " ";
|
|
6025
|
+
parsedStyles = parsed.styles;
|
|
6026
|
+
}
|
|
5485
6027
|
const baseWidth = element.width && element.width > 0 ? element.width : 200;
|
|
5486
6028
|
const baseHeight = element.height;
|
|
5487
6029
|
const fixedWidth = Math.max(baseWidth, 1);
|
|
@@ -5502,7 +6044,8 @@ function createText(element) {
|
|
|
5502
6044
|
fontStyle: element.fontStyle || "normal",
|
|
5503
6045
|
lineHeight: element.lineHeight || 1.2,
|
|
5504
6046
|
charSpacing: element.charSpacing || 0,
|
|
5505
|
-
splitByGrapheme: false
|
|
6047
|
+
splitByGrapheme: false,
|
|
6048
|
+
...formattingEnabled ? { styles: parsedStyles } : {}
|
|
5506
6049
|
});
|
|
5507
6050
|
testTextbox.initDimensions();
|
|
5508
6051
|
const textHeight = testTextbox.height || 0;
|
|
@@ -5614,8 +6157,16 @@ function createText(element) {
|
|
|
5614
6157
|
objectCaching: false,
|
|
5615
6158
|
noScaleCache: true,
|
|
5616
6159
|
splitByGrapheme,
|
|
5617
|
-
|
|
6160
|
+
// When inline markdown formatting is enabled, the displayed text is the
|
|
6161
|
+
// PARSED plain text (markdown source lives separately on the element).
|
|
6162
|
+
// Allowing canvas inline editing would let the user edit that plain text
|
|
6163
|
+
// and on commit we'd save it back as the new markdown source — wiping all
|
|
6164
|
+
// formatting tokens (**, __, [c=...], etc). Disable inline edit and steer
|
|
6165
|
+
// users to the right-panel text field which exposes the raw markdown.
|
|
6166
|
+
editable: !formattingEnabled,
|
|
6167
|
+
...formattingEnabled ? { styles: parsedStyles } : element.styles ? { styles: element.styles } : {}
|
|
5618
6168
|
});
|
|
6169
|
+
textbox.__formattingEnabled = formattingEnabled;
|
|
5619
6170
|
textbox.initDimensions();
|
|
5620
6171
|
textbox.set({
|
|
5621
6172
|
width: targetWidth,
|
|
@@ -5636,6 +6187,15 @@ function createText(element) {
|
|
|
5636
6187
|
textbox.setCoords();
|
|
5637
6188
|
}
|
|
5638
6189
|
textbox.dirty = true;
|
|
6190
|
+
if (formattingEnabled) {
|
|
6191
|
+
try {
|
|
6192
|
+
textbox._forceClearCache = true;
|
|
6193
|
+
if (typeof textbox._clearCache === "function") {
|
|
6194
|
+
textbox._clearCache();
|
|
6195
|
+
}
|
|
6196
|
+
} catch {
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
5639
6199
|
if (overflowPolicy === "auto-shrink" && typeof window !== "undefined" && window.__pixldocsDebugAutoShrink === true) {
|
|
5640
6200
|
console.log("[auto-shrink][final-textbox] " + stringifyDiag({
|
|
5641
6201
|
id: element.id,
|
|
@@ -6372,6 +6932,10 @@ const PageCanvas = forwardRef(
|
|
|
6372
6932
|
skipFontReadyWait = false,
|
|
6373
6933
|
onReady
|
|
6374
6934
|
}, ref) => {
|
|
6935
|
+
setMarkdownThemeColors({
|
|
6936
|
+
primary: projectSettings.primaryColor || "#7c3aed",
|
|
6937
|
+
secondary: projectSettings.secondaryColor || "#ffe066"
|
|
6938
|
+
});
|
|
6375
6939
|
const isEditorMode = mode === "editor";
|
|
6376
6940
|
const isPreviewMode = mode === "preview";
|
|
6377
6941
|
const allowEditing = isEditorMode;
|
|
@@ -6467,8 +7031,8 @@ const PageCanvas = forwardRef(
|
|
|
6467
7031
|
}, [selectedIds]);
|
|
6468
7032
|
useEffect(() => {
|
|
6469
7033
|
isActiveRef.current = isActive;
|
|
6470
|
-
if (isActive && fabricRef.current) ;
|
|
6471
|
-
}, [isActive, pageId]);
|
|
7034
|
+
if (isEditorMode && isActive && fabricRef.current) ;
|
|
7035
|
+
}, [isActive, isEditorMode, pageId]);
|
|
6472
7036
|
const getObjId = useCallback((obj) => {
|
|
6473
7037
|
return obj.__docuforgeId;
|
|
6474
7038
|
}, []);
|
|
@@ -6606,11 +7170,18 @@ const PageCanvas = forwardRef(
|
|
|
6606
7170
|
const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
|
|
6607
7171
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
6608
7172
|
const splitByGrapheme = overflowPolicy === "auto-shrink" ? false : element.splitByGrapheme ?? element.wordWrap === "break-word";
|
|
7173
|
+
let reflowText = element.text || "Text";
|
|
7174
|
+
let reflowParsedStyles = null;
|
|
7175
|
+
if (element.formattingEnabled === true) {
|
|
7176
|
+
const parsed = parseTextMarkdown(reflowText);
|
|
7177
|
+
reflowText = parsed.plainText || " ";
|
|
7178
|
+
reflowParsedStyles = parsed.styles;
|
|
7179
|
+
}
|
|
6609
7180
|
obj.set({
|
|
6610
7181
|
width: targetWidth,
|
|
6611
7182
|
minWidth: 1,
|
|
6612
7183
|
dynamicMinWidth: 0,
|
|
6613
|
-
text:
|
|
7184
|
+
text: reflowText,
|
|
6614
7185
|
fontSize: element.fontSize || 16,
|
|
6615
7186
|
fontFamily: element.fontFamily || "Open Sans",
|
|
6616
7187
|
fontWeight: element.fontWeight || 400,
|
|
@@ -6619,6 +7190,11 @@ const PageCanvas = forwardRef(
|
|
|
6619
7190
|
charSpacing: element.charSpacing || 0,
|
|
6620
7191
|
splitByGrapheme
|
|
6621
7192
|
});
|
|
7193
|
+
if (element.formattingEnabled === true) {
|
|
7194
|
+
obj.styles = reflowParsedStyles || {};
|
|
7195
|
+
}
|
|
7196
|
+
obj.editable = element.formattingEnabled !== true;
|
|
7197
|
+
obj.__formattingEnabled = element.formattingEnabled === true;
|
|
6622
7198
|
obj.initDimensions();
|
|
6623
7199
|
if (Math.abs((obj.width ?? 0) - targetWidth) > 0.01) {
|
|
6624
7200
|
obj.width = targetWidth;
|
|
@@ -6626,10 +7202,26 @@ const PageCanvas = forwardRef(
|
|
|
6626
7202
|
obj.dynamicMinWidth = 0;
|
|
6627
7203
|
obj.setCoords();
|
|
6628
7204
|
obj.dirty = true;
|
|
7205
|
+
try {
|
|
7206
|
+
obj._forceClearCache = true;
|
|
7207
|
+
if (typeof obj._clearCache === "function") obj._clearCache();
|
|
7208
|
+
} catch {
|
|
7209
|
+
}
|
|
6629
7210
|
didReflow = true;
|
|
6630
7211
|
};
|
|
6631
7212
|
canvas2.getObjects().forEach(reflowObject);
|
|
6632
|
-
if (didReflow)
|
|
7213
|
+
if (didReflow) {
|
|
7214
|
+
canvas2.requestRenderAll();
|
|
7215
|
+
try {
|
|
7216
|
+
requestAnimationFrame(() => {
|
|
7217
|
+
try {
|
|
7218
|
+
canvas2.requestRenderAll();
|
|
7219
|
+
} catch {
|
|
7220
|
+
}
|
|
7221
|
+
});
|
|
7222
|
+
} catch {
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
6633
7225
|
}, []);
|
|
6634
7226
|
useEffect(() => {
|
|
6635
7227
|
if (!canvasElRef.current) return;
|
|
@@ -6676,7 +7268,8 @@ const PageCanvas = forwardRef(
|
|
|
6676
7268
|
absolutePositioned: true
|
|
6677
7269
|
});
|
|
6678
7270
|
fabricRef.current = fabricCanvas;
|
|
6679
|
-
registerFabricCanvas(pageId, fabricCanvas);
|
|
7271
|
+
const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
|
|
7272
|
+
fabricCanvas.__storeRegistryKey = storeRegistryKey;
|
|
6680
7273
|
const initFonts = async () => {
|
|
6681
7274
|
try {
|
|
6682
7275
|
await preloadAllFonts();
|
|
@@ -6969,7 +7562,10 @@ const PageCanvas = forwardRef(
|
|
|
6969
7562
|
if (!window.__fabricCanvasRegistry || !(window.__fabricCanvasRegistry instanceof Map)) {
|
|
6970
7563
|
window.__fabricCanvasRegistry = /* @__PURE__ */ new Map();
|
|
6971
7564
|
}
|
|
6972
|
-
window.__fabricCanvasRegistry
|
|
7565
|
+
const reg = window.__fabricCanvasRegistry;
|
|
7566
|
+
const registryKey = reg.has(pageId) ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
7567
|
+
fabricCanvas.__registryKey = registryKey;
|
|
7568
|
+
reg.set(registryKey, {
|
|
6973
7569
|
canvas: fabricCanvas,
|
|
6974
7570
|
forceUnlockEdits
|
|
6975
7571
|
});
|
|
@@ -7739,9 +8335,6 @@ const PageCanvas = forwardRef(
|
|
|
7739
8335
|
scaleY: finalScaleY,
|
|
7740
8336
|
transformMatrix: finalAbsoluteMatrix
|
|
7741
8337
|
};
|
|
7742
|
-
if (obj instanceof fabric.Textbox) {
|
|
7743
|
-
elementUpdate.text = obj.text || "";
|
|
7744
|
-
}
|
|
7745
8338
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
7746
8339
|
elementUpdate.opacity = sourceElement.opacity;
|
|
7747
8340
|
}
|
|
@@ -7820,6 +8413,12 @@ const PageCanvas = forwardRef(
|
|
|
7820
8413
|
}
|
|
7821
8414
|
if (target && target instanceof fabric.Textbox) {
|
|
7822
8415
|
const elementId = getObjectId(target);
|
|
8416
|
+
if (target.__formattingEnabled === true || target.editable === false) {
|
|
8417
|
+
toast.info("Inline formatting is on — edit the text in the right panel.", {
|
|
8418
|
+
description: "This protects your **bold**, [c=...] and other formatting tokens."
|
|
8419
|
+
});
|
|
8420
|
+
return;
|
|
8421
|
+
}
|
|
7823
8422
|
editingTextIdRef.current = elementId || null;
|
|
7824
8423
|
target.enterEditing();
|
|
7825
8424
|
target.selectAll();
|
|
@@ -7884,7 +8483,19 @@ const PageCanvas = forwardRef(
|
|
|
7884
8483
|
});
|
|
7885
8484
|
return () => {
|
|
7886
8485
|
setReady(false);
|
|
7887
|
-
unregisterFabricCanvas(pageId);
|
|
8486
|
+
unregisterFabricCanvas(fabricCanvas.__storeRegistryKey ?? pageId, fabricCanvas);
|
|
8487
|
+
try {
|
|
8488
|
+
if (typeof window !== "undefined") {
|
|
8489
|
+
const reg = window.__fabricCanvasRegistry;
|
|
8490
|
+
if (reg instanceof Map) {
|
|
8491
|
+
const key = fabricCanvas.__registryKey ?? pageId;
|
|
8492
|
+
const entry = reg.get(key);
|
|
8493
|
+
const entryCanvas = (entry == null ? void 0 : entry.canvas) ?? entry;
|
|
8494
|
+
if (entryCanvas === fabricCanvas) reg.delete(key);
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
} catch {
|
|
8498
|
+
}
|
|
7888
8499
|
if (fabricCanvas.__fontCleanup) {
|
|
7889
8500
|
fabricCanvas.__fontCleanup();
|
|
7890
8501
|
}
|
|
@@ -9246,6 +9857,12 @@ const PageCanvas = forwardRef(
|
|
|
9246
9857
|
} else if (obj instanceof fabric.Textbox) {
|
|
9247
9858
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
9248
9859
|
let text = element.text || "Text";
|
|
9860
|
+
let parsedStyles = null;
|
|
9861
|
+
if (element.formattingEnabled === true) {
|
|
9862
|
+
const parsed = parseTextMarkdown(text);
|
|
9863
|
+
text = parsed.plainText || " ";
|
|
9864
|
+
parsedStyles = parsed.styles;
|
|
9865
|
+
}
|
|
9249
9866
|
let fontSize = element.fontSize || 16;
|
|
9250
9867
|
element.minFontSize || 8;
|
|
9251
9868
|
const maxLines = element.maxLines || 3;
|
|
@@ -9342,6 +9959,11 @@ const PageCanvas = forwardRef(
|
|
|
9342
9959
|
splitByGrapheme,
|
|
9343
9960
|
text
|
|
9344
9961
|
});
|
|
9962
|
+
if (element.formattingEnabled === true) {
|
|
9963
|
+
obj.styles = parsedStyles || {};
|
|
9964
|
+
} else {
|
|
9965
|
+
obj.styles = element.styles || {};
|
|
9966
|
+
}
|
|
9345
9967
|
obj.initDimensions();
|
|
9346
9968
|
if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {
|
|
9347
9969
|
obj.width = textboxWidth;
|
|
@@ -10346,6 +10968,7 @@ function PreviewCanvas({
|
|
|
10346
10968
|
zoom = 1,
|
|
10347
10969
|
absoluteZoom = false,
|
|
10348
10970
|
skipFontReadyWait = false,
|
|
10971
|
+
pageIdOverride,
|
|
10349
10972
|
className,
|
|
10350
10973
|
onDynamicFieldClick,
|
|
10351
10974
|
onReady
|
|
@@ -10412,13 +11035,19 @@ function PreviewCanvas({
|
|
|
10412
11035
|
backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
|
|
10413
11036
|
};
|
|
10414
11037
|
}, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
|
|
10415
|
-
const projectSettings = useMemo(() =>
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
11038
|
+
const projectSettings = useMemo(() => {
|
|
11039
|
+
var _a2, _b2, _c2;
|
|
11040
|
+
const vars = ((_a2 = config.themeConfig) == null ? void 0 : _a2.variables) || {};
|
|
11041
|
+
return {
|
|
11042
|
+
showGrid: false,
|
|
11043
|
+
snapToGrid: false,
|
|
11044
|
+
gridSize: 10,
|
|
11045
|
+
snapToGuides: false,
|
|
11046
|
+
snapThreshold: 5,
|
|
11047
|
+
primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
|
|
11048
|
+
secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
|
|
11049
|
+
};
|
|
11050
|
+
}, [config.themeConfig]);
|
|
10422
11051
|
const handleDynamicFieldClick = useCallback((elementId) => {
|
|
10423
11052
|
const fieldInfo = elementToFieldMap.get(elementId);
|
|
10424
11053
|
if (fieldInfo && onDynamicFieldClick) {
|
|
@@ -10488,7 +11117,7 @@ function PreviewCanvas({
|
|
|
10488
11117
|
PageCanvas,
|
|
10489
11118
|
{
|
|
10490
11119
|
ref: canvasRef,
|
|
10491
|
-
pageId: page.id || `page-${pageIndex}`,
|
|
11120
|
+
pageId: pageIdOverride || page.id || `page-${pageIndex}`,
|
|
10492
11121
|
elements,
|
|
10493
11122
|
pageChildren: laidOutPageChildren,
|
|
10494
11123
|
pageSettings,
|
|
@@ -12343,38 +12972,6 @@ function applyContentBoundsPagination(config) {
|
|
|
12343
12972
|
if (!mutated) return config;
|
|
12344
12973
|
return { ...config, pages: resultPages };
|
|
12345
12974
|
}
|
|
12346
|
-
const FONTSHARE_SLUGS = {
|
|
12347
|
-
"Satoshi": "satoshi",
|
|
12348
|
-
"Cabinet Grotesk": "cabinet-grotesk",
|
|
12349
|
-
"Clash Display": "clash-display",
|
|
12350
|
-
"Clash Grotesk": "clash-grotesk",
|
|
12351
|
-
"General Sans": "general-sans",
|
|
12352
|
-
"Switzer": "switzer",
|
|
12353
|
-
"Supreme": "supreme",
|
|
12354
|
-
"Author": "author",
|
|
12355
|
-
"Boska": "boska",
|
|
12356
|
-
"Excon": "excon",
|
|
12357
|
-
"Khand": "khand",
|
|
12358
|
-
"Sentient": "sentient",
|
|
12359
|
-
"Synonym": "synonym",
|
|
12360
|
-
"Erode": "erode",
|
|
12361
|
-
"Ranade": "ranade",
|
|
12362
|
-
"Tanker": "tanker",
|
|
12363
|
-
"Zodiak": "zodiak",
|
|
12364
|
-
"Gambarino": "gambarino",
|
|
12365
|
-
"Melodrama": "melodrama",
|
|
12366
|
-
"Bespoke Serif": "bespoke-serif",
|
|
12367
|
-
"Bespoke Stencil": "bespoke-stencil",
|
|
12368
|
-
"Panchang": "panchang",
|
|
12369
|
-
"Quincy CF": "quincy-cf",
|
|
12370
|
-
"Pally": "pally",
|
|
12371
|
-
"Tabular": "tabular",
|
|
12372
|
-
"Sharpie": "sharpie",
|
|
12373
|
-
"Stardom": "stardom",
|
|
12374
|
-
"Rebond Grotesque": "rebond-grotesque",
|
|
12375
|
-
"Telma": "telma",
|
|
12376
|
-
"Nippo": "nippo"
|
|
12377
|
-
};
|
|
12378
12975
|
function normalizeFontFamily(fontStack) {
|
|
12379
12976
|
const first = fontStack.split(",")[0].trim();
|
|
12380
12977
|
return first.replace(/^['"]|['"]$/g, "");
|
|
@@ -12401,31 +12998,11 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12401
12998
|
if (existing) return existing;
|
|
12402
12999
|
const promise = (async () => {
|
|
12403
13000
|
try {
|
|
12404
|
-
|
|
12405
|
-
if (fontshareSlug) {
|
|
12406
|
-
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12407
|
-
const link2 = document.createElement("link");
|
|
12408
|
-
link2.rel = "stylesheet";
|
|
12409
|
-
link2.href = url2;
|
|
12410
|
-
await new Promise((resolve, reject) => {
|
|
12411
|
-
link2.onload = () => resolve();
|
|
12412
|
-
link2.onerror = () => reject(new Error(`Failed to load Fontshare font: ${fontFamily}`));
|
|
12413
|
-
document.head.appendChild(link2);
|
|
12414
|
-
});
|
|
13001
|
+
if (LOCAL_FONTS.has(fontFamily)) {
|
|
12415
13002
|
loadedFonts.add(fontFamily);
|
|
12416
13003
|
return;
|
|
12417
13004
|
}
|
|
12418
|
-
|
|
12419
|
-
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12420
|
-
const link = document.createElement("link");
|
|
12421
|
-
link.rel = "stylesheet";
|
|
12422
|
-
link.href = url;
|
|
12423
|
-
link.crossOrigin = "anonymous";
|
|
12424
|
-
await new Promise((resolve, reject) => {
|
|
12425
|
-
link.onload = () => resolve();
|
|
12426
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12427
|
-
document.head.appendChild(link);
|
|
12428
|
-
});
|
|
13005
|
+
await withTimeout(loadFont(fontFamily), 4e3);
|
|
12429
13006
|
loadedFonts.add(fontFamily);
|
|
12430
13007
|
} catch (e) {
|
|
12431
13008
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12495,6 +13072,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12495
13072
|
if (node.type === "text") {
|
|
12496
13073
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12497
13074
|
add(node.fontFamily, w, node.fontStyle);
|
|
13075
|
+
add(node.fontFamily, w, "italic");
|
|
13076
|
+
}
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
if (node.formattingEnabled === true && node.fontFamily) {
|
|
13080
|
+
const parsed = parseTextMarkdown(String(node.text ?? ""));
|
|
13081
|
+
const parsedStyleEntries = Object.values(parsed.styles || {});
|
|
13082
|
+
for (const lineStyle of parsedStyleEntries) {
|
|
13083
|
+
if (lineStyle && typeof lineStyle === "object") {
|
|
13084
|
+
for (const charStyle of Object.values(lineStyle)) {
|
|
13085
|
+
if (!charStyle || typeof charStyle !== "object") continue;
|
|
13086
|
+
add(
|
|
13087
|
+
charStyle.fontFamily || node.fontFamily,
|
|
13088
|
+
charStyle.fontWeight ?? node.fontWeight,
|
|
13089
|
+
charStyle.fontStyle ?? node.fontStyle
|
|
13090
|
+
);
|
|
13091
|
+
}
|
|
12498
13092
|
}
|
|
12499
13093
|
}
|
|
12500
13094
|
}
|
|
@@ -13404,11 +13998,20 @@ function PixldocsPreview(props) {
|
|
|
13404
13998
|
!canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
|
|
13405
13999
|
] });
|
|
13406
14000
|
}
|
|
13407
|
-
const PACKAGE_VERSION = "0.5.
|
|
14001
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13408
14002
|
const roundParityValue = (value) => {
|
|
13409
14003
|
if (typeof value !== "number") return value;
|
|
13410
14004
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13411
14005
|
};
|
|
14006
|
+
function isolatePageForCapture(config, pageIndex) {
|
|
14007
|
+
var _a;
|
|
14008
|
+
const capturePageId = `__pixldocs_capture_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}_${pageIndex}`;
|
|
14009
|
+
const cloned = JSON.parse(JSON.stringify(config));
|
|
14010
|
+
if ((_a = cloned.pages) == null ? void 0 : _a[pageIndex]) {
|
|
14011
|
+
cloned.pages[pageIndex].id = capturePageId;
|
|
14012
|
+
}
|
|
14013
|
+
return { config: cloned, pageId: capturePageId };
|
|
14014
|
+
}
|
|
13412
14015
|
function logJsonLine(tag, payload) {
|
|
13413
14016
|
try {
|
|
13414
14017
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13446,6 +14049,9 @@ function installUnderlineFix(fab) {
|
|
|
13446
14049
|
const hasOwn = !!this[type];
|
|
13447
14050
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13448
14051
|
if (!hasOwn && !hasStyled) return;
|
|
14052
|
+
if (!hasOwn && hasStyled) {
|
|
14053
|
+
return original.call(this, ctx, type);
|
|
14054
|
+
}
|
|
13449
14055
|
const lines = this._textLines;
|
|
13450
14056
|
const offsets = this.offsets;
|
|
13451
14057
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13457,7 +14063,7 @@ function installUnderlineFix(fab) {
|
|
|
13457
14063
|
let topOffset = this._getTopOffset();
|
|
13458
14064
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13459
14065
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13460
|
-
const lineHas = !!this[type]
|
|
14066
|
+
const lineHas = !!this[type];
|
|
13461
14067
|
if (!lineHas) {
|
|
13462
14068
|
topOffset += heightOfLine;
|
|
13463
14069
|
continue;
|
|
@@ -13987,9 +14593,11 @@ class PixldocsRenderer {
|
|
|
13987
14593
|
}
|
|
13988
14594
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
13989
14595
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
13990
|
-
const
|
|
13991
|
-
const
|
|
13992
|
-
const
|
|
14596
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14597
|
+
const renderConfig = capture.config;
|
|
14598
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
14599
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
14600
|
+
const hasAutoShrink = configHasAutoShrinkText(renderConfig);
|
|
13993
14601
|
let firstMountSettled = false;
|
|
13994
14602
|
let lateFontSettleDetected = false;
|
|
13995
14603
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14037,12 +14645,12 @@ class PixldocsRenderer {
|
|
|
14037
14645
|
root = createRoot(container);
|
|
14038
14646
|
await new Promise((settle) => {
|
|
14039
14647
|
const onReadyOnce = () => {
|
|
14040
|
-
this.waitForCanvasScene(container,
|
|
14648
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14041
14649
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14042
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14650
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14043
14651
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14044
|
-
await this.waitForStableTextMetrics(container,
|
|
14045
|
-
await this.waitForCanvasScene(container,
|
|
14652
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14653
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14046
14654
|
if (!fabricInstance) return settle();
|
|
14047
14655
|
settle();
|
|
14048
14656
|
}).catch(() => settle());
|
|
@@ -14050,8 +14658,9 @@ class PixldocsRenderer {
|
|
|
14050
14658
|
root.render(
|
|
14051
14659
|
createElement(PreviewCanvas2, {
|
|
14052
14660
|
key: `remount-${mountKey}`,
|
|
14053
|
-
config,
|
|
14661
|
+
config: renderConfig,
|
|
14054
14662
|
pageIndex,
|
|
14663
|
+
pageIdOverride: capture.pageId,
|
|
14055
14664
|
zoom: pixelRatio,
|
|
14056
14665
|
absoluteZoom: true,
|
|
14057
14666
|
skipFontReadyWait: false,
|
|
@@ -14061,13 +14670,13 @@ class PixldocsRenderer {
|
|
|
14061
14670
|
});
|
|
14062
14671
|
};
|
|
14063
14672
|
const onReady = () => {
|
|
14064
|
-
this.waitForCanvasScene(container,
|
|
14673
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14065
14674
|
try {
|
|
14066
14675
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14067
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14676
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14068
14677
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14069
|
-
await this.waitForStableTextMetrics(container,
|
|
14070
|
-
await this.waitForCanvasScene(container,
|
|
14678
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14679
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14071
14680
|
firstMountSettled = true;
|
|
14072
14681
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14073
14682
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14093,7 +14702,7 @@ class PixldocsRenderer {
|
|
|
14093
14702
|
}
|
|
14094
14703
|
exportCtx.save();
|
|
14095
14704
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14096
|
-
this.paintPageBackground(exportCtx,
|
|
14705
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14097
14706
|
exportCtx.restore();
|
|
14098
14707
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14099
14708
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14109,8 +14718,9 @@ class PixldocsRenderer {
|
|
|
14109
14718
|
root = createRoot(container);
|
|
14110
14719
|
root.render(
|
|
14111
14720
|
createElement(PreviewCanvas2, {
|
|
14112
|
-
config,
|
|
14721
|
+
config: renderConfig,
|
|
14113
14722
|
pageIndex,
|
|
14723
|
+
pageIdOverride: capture.pageId,
|
|
14114
14724
|
zoom: pixelRatio,
|
|
14115
14725
|
absoluteZoom: true,
|
|
14116
14726
|
skipFontReadyWait: false,
|
|
@@ -14131,6 +14741,8 @@ class PixldocsRenderer {
|
|
|
14131
14741
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14132
14742
|
return new Promise(async (resolve, reject) => {
|
|
14133
14743
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14744
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14745
|
+
const renderConfig = capture.config;
|
|
14134
14746
|
const container = document.createElement("div");
|
|
14135
14747
|
container.style.cssText = `
|
|
14136
14748
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14157,8 +14769,9 @@ class PixldocsRenderer {
|
|
|
14157
14769
|
root.render(
|
|
14158
14770
|
createElement(PreviewCanvas2, {
|
|
14159
14771
|
key: `svg-capture-${mountKey}`,
|
|
14160
|
-
config,
|
|
14772
|
+
config: renderConfig,
|
|
14161
14773
|
pageIndex,
|
|
14774
|
+
pageIdOverride: capture.pageId,
|
|
14162
14775
|
zoom: 1,
|
|
14163
14776
|
absoluteZoom: true,
|
|
14164
14777
|
skipFontReadyWait: false,
|
|
@@ -14167,13 +14780,13 @@ class PixldocsRenderer {
|
|
|
14167
14780
|
);
|
|
14168
14781
|
};
|
|
14169
14782
|
const onReady = () => {
|
|
14170
|
-
this.waitForCanvasScene(container,
|
|
14783
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14171
14784
|
var _a, _b;
|
|
14172
14785
|
try {
|
|
14173
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14786
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14174
14787
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14175
|
-
await this.waitForStableTextMetrics(container,
|
|
14176
|
-
await this.waitForCanvasScene(container,
|
|
14788
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14789
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14177
14790
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14178
14791
|
if (!fabricInstance) {
|
|
14179
14792
|
cleanup();
|
|
@@ -14262,7 +14875,7 @@ class PixldocsRenderer {
|
|
|
14262
14875
|
);
|
|
14263
14876
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14264
14877
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14265
|
-
const page =
|
|
14878
|
+
const page = renderConfig.pages[pageIndex];
|
|
14266
14879
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14267
14880
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14268
14881
|
cleanup();
|
|
@@ -14944,8 +15557,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14944
15557
|
}
|
|
14945
15558
|
return files.regular;
|
|
14946
15559
|
}
|
|
14947
|
-
function
|
|
14948
|
-
|
|
15560
|
+
function isExactWeightItalicMatch(files, weight, isItalic, path) {
|
|
15561
|
+
const exactKey = isItalic ? weight === 300 ? "lightItalic" : weight === 500 ? "mediumItalic" : weight === 600 ? "semiboldItalic" : weight === 700 ? "boldItalic" : "italic" : weight === 300 ? "light" : weight === 500 ? "medium" : weight === 600 ? "semibold" : weight === 700 ? "bold" : "regular";
|
|
15562
|
+
return files[exactKey] === path;
|
|
14949
15563
|
}
|
|
14950
15564
|
function getJsPDFFontName(fontName) {
|
|
14951
15565
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15018,10 +15632,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15018
15632
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15019
15633
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15020
15634
|
if (!fontPath) return false;
|
|
15021
|
-
|
|
15022
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15635
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15636
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15023
15637
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15024
|
-
const italicSuffix =
|
|
15638
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15025
15639
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15026
15640
|
const url = baseUrl + fontPath;
|
|
15027
15641
|
try {
|
|
@@ -15036,6 +15650,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15036
15650
|
}
|
|
15037
15651
|
}
|
|
15038
15652
|
registeredFamilies.add(fontName);
|
|
15653
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15039
15654
|
return true;
|
|
15040
15655
|
} catch (e) {
|
|
15041
15656
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15044,6 +15659,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15044
15659
|
}
|
|
15045
15660
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15046
15661
|
const fontshareNotFound = /* @__PURE__ */ new Set();
|
|
15662
|
+
const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontWeight(weight)}|${isItalic ? "i" : "n"}`;
|
|
15663
|
+
const registeredVariants = /* @__PURE__ */ new Set();
|
|
15664
|
+
const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
|
|
15665
|
+
const resolveBestRegisteredVariant = (family, weight, italic) => {
|
|
15666
|
+
const want = resolveFontWeight(weight);
|
|
15667
|
+
const weights = [300, 400, 500, 600, 700];
|
|
15668
|
+
if (registeredVariants.has(variantKey(family, want, italic))) return { weight: want, italic };
|
|
15669
|
+
const nearest = [...weights].sort((a, b) => Math.abs(a - want) - Math.abs(b - want) || (want >= 500 ? b - a : a - b));
|
|
15670
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, italic))) return { weight: w, italic };
|
|
15671
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, !italic))) return { weight: w, italic: !italic };
|
|
15672
|
+
return null;
|
|
15673
|
+
};
|
|
15047
15674
|
function bytesToBase64(bytes) {
|
|
15048
15675
|
let binary = "";
|
|
15049
15676
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15072,7 +15699,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15072
15699
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15073
15700
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15074
15701
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15075
|
-
|
|
15702
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15703
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15076
15704
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15077
15705
|
if (proxyBytes) {
|
|
15078
15706
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15090,7 +15718,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15090
15718
|
}
|
|
15091
15719
|
});
|
|
15092
15720
|
if (!cssRes.ok) {
|
|
15093
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15721
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15094
15722
|
return null;
|
|
15095
15723
|
}
|
|
15096
15724
|
const css = await cssRes.text();
|
|
@@ -15168,6 +15796,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15168
15796
|
}
|
|
15169
15797
|
}
|
|
15170
15798
|
registeredFamilies.add(fontName);
|
|
15799
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15171
15800
|
return true;
|
|
15172
15801
|
} catch (err) {
|
|
15173
15802
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15182,16 +15811,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15182
15811
|
const resolved = resolveFontWeight(weight);
|
|
15183
15812
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15184
15813
|
if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
|
|
15185
|
-
if (isItalic) {
|
|
15186
|
-
const fsUpright = await fetchFontshareTTF(fontName, resolved, false);
|
|
15187
|
-
if (fsUpright) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsUpright);
|
|
15188
|
-
}
|
|
15189
15814
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15190
15815
|
if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
|
|
15191
|
-
if (isItalic) {
|
|
15192
|
-
const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);
|
|
15193
|
-
if (uprightB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);
|
|
15194
|
-
}
|
|
15195
15816
|
return false;
|
|
15196
15817
|
}
|
|
15197
15818
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15211,9 +15832,32 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15211
15832
|
};
|
|
15212
15833
|
const walkElements = (elements) => {
|
|
15213
15834
|
for (const el of elements) {
|
|
15835
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15836
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15837
|
+
};
|
|
15214
15838
|
if (el.fontFamily) {
|
|
15215
15839
|
const w = normalizeWeight(el.fontWeight);
|
|
15216
|
-
|
|
15840
|
+
addFontVariant(el.fontFamily, w, /italic|oblique/i.test(String(el.fontStyle ?? "")));
|
|
15841
|
+
addFontVariant(el.fontFamily, 700, false);
|
|
15842
|
+
addFontVariant(el.fontFamily, 400, true);
|
|
15843
|
+
addFontVariant(el.fontFamily, 700, true);
|
|
15844
|
+
if (el.formattingEnabled === true && typeof el.text === "string") {
|
|
15845
|
+
try {
|
|
15846
|
+
const parsed = parseTextMarkdown(el.text);
|
|
15847
|
+
for (const lineStyles of Object.values(parsed.styles || {})) {
|
|
15848
|
+
if (!lineStyles || typeof lineStyles !== "object") continue;
|
|
15849
|
+
for (const s of Object.values(lineStyles)) {
|
|
15850
|
+
if (!s) continue;
|
|
15851
|
+
const family = s.fontFamily || el.fontFamily;
|
|
15852
|
+
if (!family) continue;
|
|
15853
|
+
const ww = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15854
|
+
const it = /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? ""));
|
|
15855
|
+
addFontVariant(family, ww, it);
|
|
15856
|
+
}
|
|
15857
|
+
}
|
|
15858
|
+
} catch {
|
|
15859
|
+
}
|
|
15860
|
+
}
|
|
15217
15861
|
}
|
|
15218
15862
|
if (el.styles && typeof el.styles === "object") {
|
|
15219
15863
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15221,9 +15865,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15221
15865
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15222
15866
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15223
15867
|
const s = lineStyles[charKey];
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15868
|
+
const styledFamily = (s == null ? void 0 : s.fontFamily) || el.fontFamily;
|
|
15869
|
+
if (styledFamily) {
|
|
15870
|
+
const w = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15871
|
+
addFontVariant(styledFamily, w, /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? "")));
|
|
15227
15872
|
}
|
|
15228
15873
|
}
|
|
15229
15874
|
}
|
|
@@ -15237,19 +15882,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15237
15882
|
if (page.children) walkElements(page.children);
|
|
15238
15883
|
if (page.elements) walkElements(page.elements);
|
|
15239
15884
|
}
|
|
15240
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15885
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15241
15886
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15242
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15887
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15243
15888
|
}
|
|
15244
15889
|
const embedded = /* @__PURE__ */ new Set();
|
|
15245
15890
|
const tasks = [];
|
|
15246
15891
|
for (const key of fontKeys) {
|
|
15247
15892
|
const sep = key.indexOf(SEP);
|
|
15893
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15248
15894
|
const fontName = key.slice(0, sep);
|
|
15249
|
-
const weight = parseInt(key.slice(sep + 1), 10);
|
|
15895
|
+
const weight = parseInt(key.slice(sep + 1, secondSep), 10);
|
|
15896
|
+
const italic = key.slice(secondSep + 1) === "italic";
|
|
15250
15897
|
if (!isFontAvailable(fontName)) continue;
|
|
15251
15898
|
tasks.push(
|
|
15252
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15899
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15253
15900
|
if (ok) embedded.add(key);
|
|
15254
15901
|
})
|
|
15255
15902
|
);
|
|
@@ -15305,7 +15952,7 @@ function splitIntoRuns(text) {
|
|
|
15305
15952
|
return runs;
|
|
15306
15953
|
}
|
|
15307
15954
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15308
|
-
var _a, _b;
|
|
15955
|
+
var _a, _b, _c;
|
|
15309
15956
|
const parser = new DOMParser();
|
|
15310
15957
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15311
15958
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15337,6 +15984,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15337
15984
|
stylePairs.push(`font-style: normal`);
|
|
15338
15985
|
return stylePairs.join("; ");
|
|
15339
15986
|
};
|
|
15987
|
+
const applySyntheticItalicTransform = (el) => {
|
|
15988
|
+
const x = Number.parseFloat(resolveInheritedValue(el, "x") || "0") || 0;
|
|
15989
|
+
const y = Number.parseFloat(resolveInheritedValue(el, "y") || "0") || 0;
|
|
15990
|
+
const existing = el.getAttribute("transform") || "";
|
|
15991
|
+
const synth = `translate(${x} ${y}) skewX(-12) translate(${-x} ${-y})`;
|
|
15992
|
+
el.setAttribute("transform", existing ? `${existing} ${synth}` : synth);
|
|
15993
|
+
};
|
|
15340
15994
|
const getDepth = (el) => {
|
|
15341
15995
|
let depth = 0;
|
|
15342
15996
|
let current = el.parentElement;
|
|
@@ -15356,20 +16010,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15356
16010
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15357
16011
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15358
16012
|
const weight = resolveWeightNum(weightRaw);
|
|
15359
|
-
const
|
|
15360
|
-
const
|
|
15361
|
-
const
|
|
15362
|
-
|
|
16013
|
+
const requested = resolveFontWeight(weight);
|
|
16014
|
+
const requestedItalic = /italic|oblique/i.test(styleRaw);
|
|
16015
|
+
const best = resolveBestRegisteredVariant(clean, requested, requestedItalic);
|
|
16016
|
+
if (!best) continue;
|
|
16017
|
+
const jsPdfName = getEmbeddedJsPDFFontName(clean, best.weight, best.italic);
|
|
16018
|
+
sourceMeta.set(el, { clean, requested, resolved: best.weight, isItalic: requestedItalic, actualItalic: best.italic, jsPdfName, inlineStyle, weight });
|
|
15363
16019
|
}
|
|
15364
16020
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15365
16021
|
for (const el of textEls) {
|
|
15366
16022
|
if (!el.isConnected) continue;
|
|
15367
16023
|
const meta = sourceMeta.get(el);
|
|
15368
16024
|
if (!meta) continue;
|
|
15369
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16025
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15370
16026
|
el.setAttribute("data-source-font-family", clean);
|
|
15371
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16027
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15372
16028
|
el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
|
|
16029
|
+
if (requested >= 600 && resolved < 600) {
|
|
16030
|
+
const fill = resolveInheritedValue(el, "fill") || readStyleToken(inlineStyle, "fill") || "#000000";
|
|
16031
|
+
el.setAttribute("stroke", fill);
|
|
16032
|
+
el.setAttribute("stroke-width", String(requested === 700 ? 0.7 : 0.5));
|
|
16033
|
+
el.setAttribute("stroke-linejoin", "round");
|
|
16034
|
+
}
|
|
16035
|
+
if (isItalic && !actualItalic) {
|
|
16036
|
+
applySyntheticItalicTransform(el);
|
|
16037
|
+
try {
|
|
16038
|
+
console.log("[Vector PDF][synthetic-italic]", {
|
|
16039
|
+
tag: el.tagName,
|
|
16040
|
+
family: clean,
|
|
16041
|
+
weight: requested,
|
|
16042
|
+
textPreview: (el.textContent || "").slice(0, 40)
|
|
16043
|
+
});
|
|
16044
|
+
} catch {
|
|
16045
|
+
}
|
|
16046
|
+
}
|
|
15373
16047
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15374
16048
|
const hasDevanagari = containsDevanagari(directText);
|
|
15375
16049
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15412,6 +16086,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15412
16086
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15413
16087
|
}
|
|
15414
16088
|
}
|
|
16089
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
16090
|
+
const transformedTspans = Array.from(doc.querySelectorAll("tspan[transform]"));
|
|
16091
|
+
for (const tspan of transformedTspans) {
|
|
16092
|
+
const parentText = tspan.parentElement;
|
|
16093
|
+
if (!parentText || parentText.tagName.toLowerCase() !== "text") continue;
|
|
16094
|
+
const transformVal = tspan.getAttribute("transform") || "";
|
|
16095
|
+
if (!/skewX/i.test(transformVal)) continue;
|
|
16096
|
+
const wrapper = doc.createElementNS(SVG_NS, "text");
|
|
16097
|
+
let parentTransform = "";
|
|
16098
|
+
for (const attr of Array.from(parentText.attributes)) {
|
|
16099
|
+
if (attr.name === "transform") {
|
|
16100
|
+
parentTransform = attr.value;
|
|
16101
|
+
continue;
|
|
16102
|
+
}
|
|
16103
|
+
wrapper.setAttribute(attr.name, attr.value);
|
|
16104
|
+
}
|
|
16105
|
+
const combined = parentTransform ? `${parentTransform} ${transformVal}` : transformVal;
|
|
16106
|
+
wrapper.setAttribute("transform", combined);
|
|
16107
|
+
tspan.removeAttribute("transform");
|
|
16108
|
+
wrapper.appendChild(tspan);
|
|
16109
|
+
(_c = parentText.parentNode) == null ? void 0 : _c.insertBefore(wrapper, parentText.nextSibling);
|
|
16110
|
+
}
|
|
15415
16111
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15416
16112
|
}
|
|
15417
16113
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15442,14 +16138,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15442
16138
|
if (ok) embedded.add(`${family}${w}`);
|
|
15443
16139
|
})
|
|
15444
16140
|
);
|
|
16141
|
+
tasks.push(
|
|
16142
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16143
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16144
|
+
})
|
|
16145
|
+
);
|
|
15445
16146
|
}
|
|
15446
16147
|
} else {
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
}
|
|
15452
|
-
|
|
16148
|
+
const variants = [
|
|
16149
|
+
{ w: 400, italic: false },
|
|
16150
|
+
{ w: 700, italic: false },
|
|
16151
|
+
{ w: 400, italic: true },
|
|
16152
|
+
{ w: 700, italic: true }
|
|
16153
|
+
];
|
|
16154
|
+
for (const v of variants) {
|
|
16155
|
+
tasks.push(
|
|
16156
|
+
embedFontWithGoogleFallback(pdf, family, v.w, fontBaseUrl, v.italic).then((ok) => {
|
|
16157
|
+
if (ok) embedded.add(`${family}${v.w}${v.italic ? "i" : ""}`);
|
|
16158
|
+
else if (v.w === 400 && !v.italic) {
|
|
16159
|
+
console.warn(`[pdf-fonts] No TTF (local/Google/Fontshare) for "${family}" — will use Helvetica fallback`);
|
|
16160
|
+
}
|
|
16161
|
+
})
|
|
16162
|
+
);
|
|
16163
|
+
}
|
|
15453
16164
|
}
|
|
15454
16165
|
}
|
|
15455
16166
|
await Promise.all(tasks);
|
|
@@ -15470,6 +16181,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15470
16181
|
getEmbeddedJsPDFFontName,
|
|
15471
16182
|
getFontPathForWeight,
|
|
15472
16183
|
isFontAvailable,
|
|
16184
|
+
resolveBestRegisteredVariant,
|
|
15473
16185
|
resolveFontWeight,
|
|
15474
16186
|
rewriteSvgFontsForJsPDF
|
|
15475
16187
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15509,9 +16221,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15509
16221
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15510
16222
|
var _a, _b;
|
|
15511
16223
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15512
|
-
const tspanInfo = tspans.slice(0,
|
|
16224
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15513
16225
|
x: s.getAttribute("x"),
|
|
15514
16226
|
y: s.getAttribute("y"),
|
|
16227
|
+
fs: s.getAttribute("font-style"),
|
|
16228
|
+
fw: s.getAttribute("font-weight"),
|
|
16229
|
+
ff: s.getAttribute("font-family"),
|
|
16230
|
+
td: s.getAttribute("text-decoration"),
|
|
16231
|
+
style: (s.getAttribute("style") || "").slice(0, 120),
|
|
15515
16232
|
text: (s.textContent || "").slice(0, 40)
|
|
15516
16233
|
}));
|
|
15517
16234
|
let containerWidth = null;
|
|
@@ -16546,13 +17263,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16546
17263
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16547
17264
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16548
17265
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17266
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16549
17267
|
for (let si = 0; si < tspans.length; si++) {
|
|
16550
17268
|
const tspan = tspans[si];
|
|
16551
17269
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16552
17270
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16553
17271
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16554
17272
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16555
|
-
|
|
17273
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17274
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16556
17275
|
const content = tspan.textContent || "";
|
|
16557
17276
|
if (!content.trim()) continue;
|
|
16558
17277
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16607,23 +17326,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16607
17326
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16608
17327
|
lineEndX = lineStartX + textWidth;
|
|
16609
17328
|
}
|
|
16610
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16611
17329
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
16612
|
-
const
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
textEl.parentElement
|
|
16624
|
-
|
|
17330
|
+
const makeLine = (yPos) => {
|
|
17331
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
17332
|
+
line.setAttribute("x1", String(lineStartX));
|
|
17333
|
+
line.setAttribute("y1", String(yPos));
|
|
17334
|
+
line.setAttribute("x2", String(lineEndX));
|
|
17335
|
+
line.setAttribute("y2", String(yPos));
|
|
17336
|
+
line.setAttribute("stroke", fill.startsWith("url(") ? "#000000" : fill);
|
|
17337
|
+
line.setAttribute("stroke-width", String(thickness));
|
|
17338
|
+
line.setAttribute("stroke-linecap", "butt");
|
|
17339
|
+
line.setAttribute("fill", "none");
|
|
17340
|
+
if (fillOpacity) line.setAttribute("stroke-opacity", fillOpacity);
|
|
17341
|
+
if (textEl.parentElement) {
|
|
17342
|
+
textEl.parentElement.insertBefore(line, textEl.nextSibling);
|
|
17343
|
+
}
|
|
17344
|
+
};
|
|
17345
|
+
if (hasUnderline) makeLine(baselineY + fontSize * 0.15);
|
|
17346
|
+
if (hasLinethrough) makeLine(baselineY - fontSize * 0.3);
|
|
16625
17347
|
stripTextDecoration(tspan);
|
|
16626
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17348
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16627
17349
|
}
|
|
16628
17350
|
}
|
|
16629
17351
|
if (tempContainer) {
|
|
@@ -16637,7 +17359,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16637
17359
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16638
17360
|
return svgStr;
|
|
16639
17361
|
}
|
|
16640
|
-
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr)) {
|
|
17362
|
+
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr) && !/line-through/i.test(svgStr)) {
|
|
16641
17363
|
return svgStr;
|
|
16642
17364
|
}
|
|
16643
17365
|
try {
|
|
@@ -16929,7 +17651,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16929
17651
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16930
17652
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16931
17653
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16932
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17654
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16933
17655
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16934
17656
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16935
17657
|
let pageSvg = page.svg;
|
|
@@ -16943,7 +17665,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16943
17665
|
}
|
|
16944
17666
|
if (shouldOutlineText) {
|
|
16945
17667
|
try {
|
|
16946
|
-
const { convertAllTextToPath } = await import("./svgTextToPath-
|
|
17668
|
+
const { convertAllTextToPath } = await import("./svgTextToPath-C20Obtt2.js");
|
|
16947
17669
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16948
17670
|
try {
|
|
16949
17671
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|