@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.cjs
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
6
6
|
const jsxRuntime = require("react/jsx-runtime");
|
|
7
7
|
const react = require("react");
|
|
8
8
|
const reactDom = require("react-dom");
|
|
9
|
+
const sonner = require("sonner");
|
|
9
10
|
const zustand = require("zustand");
|
|
10
11
|
const fabric = require("fabric");
|
|
11
12
|
const client = require("react-dom/client");
|
|
@@ -2506,9 +2507,17 @@ const useEditorStore = zustand.create((set, get) => ({
|
|
|
2506
2507
|
}));
|
|
2507
2508
|
let canvasRegistry = /* @__PURE__ */ new Map();
|
|
2508
2509
|
function registerFabricCanvas(pageId, canvas) {
|
|
2509
|
-
canvasRegistry.
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2510
|
+
const existing = canvasRegistry.get(pageId);
|
|
2511
|
+
const registryKey = existing && existing !== canvas ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
2512
|
+
canvasRegistry.set(registryKey, canvas);
|
|
2513
|
+
return registryKey;
|
|
2514
|
+
}
|
|
2515
|
+
function unregisterFabricCanvas(pageId, canvas) {
|
|
2516
|
+
if (canvas) {
|
|
2517
|
+
const existing = canvasRegistry.get(pageId);
|
|
2518
|
+
if (existing === canvas) canvasRegistry.delete(pageId);
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2512
2521
|
canvasRegistry.delete(pageId);
|
|
2513
2522
|
}
|
|
2514
2523
|
const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -2580,6 +2589,8 @@ const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
|
2580
2589
|
]);
|
|
2581
2590
|
const loadedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2582
2591
|
const failedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2592
|
+
const loadedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2593
|
+
const failedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2583
2594
|
const loadingPromises$1 = /* @__PURE__ */ new Map();
|
|
2584
2595
|
async function loadGoogleFont(fontFamily, weights) {
|
|
2585
2596
|
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
@@ -2639,6 +2650,346 @@ async function _doLoadGoogleFont(fontFamily, weights) {
|
|
|
2639
2650
|
console.warn(`[GoogleFonts] Failed to load: ${fontFamily}`);
|
|
2640
2651
|
return false;
|
|
2641
2652
|
}
|
|
2653
|
+
async function loadFontshareFont(fontFamily, slug, weights) {
|
|
2654
|
+
if (loadedFontshareFonts.has(fontFamily)) return true;
|
|
2655
|
+
if (failedFontshareFonts.has(fontFamily)) return false;
|
|
2656
|
+
const existing = loadingPromises$1.get(`fontshare:${fontFamily}`);
|
|
2657
|
+
if (existing) return existing;
|
|
2658
|
+
const promise = (async () => {
|
|
2659
|
+
try {
|
|
2660
|
+
const weightStr = (weights || [300, 400, 500, 700]).join(",");
|
|
2661
|
+
const url = `https://api.fontshare.com/v2/css?f[]=${slug}@${weightStr}&display=swap`;
|
|
2662
|
+
if (document.querySelector(`link[href="${url}"]`)) return true;
|
|
2663
|
+
const link = document.createElement("link");
|
|
2664
|
+
link.rel = "stylesheet";
|
|
2665
|
+
link.href = url;
|
|
2666
|
+
return await new Promise((resolve) => {
|
|
2667
|
+
link.onload = async () => {
|
|
2668
|
+
try {
|
|
2669
|
+
await document.fonts.load(`16px "${fontFamily}"`);
|
|
2670
|
+
await document.fonts.load(`bold 16px "${fontFamily}"`);
|
|
2671
|
+
} catch {
|
|
2672
|
+
}
|
|
2673
|
+
resolve(true);
|
|
2674
|
+
};
|
|
2675
|
+
link.onerror = () => {
|
|
2676
|
+
console.warn(`[Fontshare] Failed to load: ${fontFamily}`);
|
|
2677
|
+
resolve(false);
|
|
2678
|
+
};
|
|
2679
|
+
document.head.appendChild(link);
|
|
2680
|
+
});
|
|
2681
|
+
} catch (e) {
|
|
2682
|
+
console.warn(`[Fontshare] Error loading ${fontFamily}:`, e);
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2685
|
+
})();
|
|
2686
|
+
loadingPromises$1.set(`fontshare:${fontFamily}`, promise);
|
|
2687
|
+
try {
|
|
2688
|
+
const result = await promise;
|
|
2689
|
+
if (result) loadedFontshareFonts.add(fontFamily);
|
|
2690
|
+
else failedFontshareFonts.add(fontFamily);
|
|
2691
|
+
return result;
|
|
2692
|
+
} finally {
|
|
2693
|
+
loadingPromises$1.delete(`fontshare:${fontFamily}`);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
async function loadFont(fontFamily) {
|
|
2697
|
+
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
2698
|
+
const entry = findFontEntry(fontFamily);
|
|
2699
|
+
if ((entry == null ? void 0 : entry.source) === "fontshare" && entry.fontshareSlug) {
|
|
2700
|
+
return loadFontshareFont(fontFamily, entry.fontshareSlug);
|
|
2701
|
+
}
|
|
2702
|
+
return loadGoogleFont(fontFamily);
|
|
2703
|
+
}
|
|
2704
|
+
const EXTENDED_FONT_LIST = [
|
|
2705
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2706
|
+
// PREMIUM (Fontshare) — modern, professional aesthetic
|
|
2707
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2708
|
+
{ name: "Satoshi", category: "Premium", local: false, source: "fontshare", fontshareSlug: "satoshi", popular: true },
|
|
2709
|
+
{ name: "Cabinet Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "cabinet-grotesk", popular: true },
|
|
2710
|
+
{ name: "Clash Display", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-display", popular: true },
|
|
2711
|
+
{ name: "Clash Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-grotesk", popular: true },
|
|
2712
|
+
{ name: "General Sans", category: "Premium", local: false, source: "fontshare", fontshareSlug: "general-sans", popular: true },
|
|
2713
|
+
{ name: "Switzer", category: "Premium", local: false, source: "fontshare", fontshareSlug: "switzer" },
|
|
2714
|
+
{ name: "Supreme", category: "Premium", local: false, source: "fontshare", fontshareSlug: "supreme" },
|
|
2715
|
+
{ name: "Author", category: "Premium", local: false, source: "fontshare", fontshareSlug: "author" },
|
|
2716
|
+
{ name: "Boska", category: "Premium", local: false, source: "fontshare", fontshareSlug: "boska" },
|
|
2717
|
+
{ name: "Excon", category: "Premium", local: false, source: "fontshare", fontshareSlug: "excon" },
|
|
2718
|
+
{ name: "Khand", category: "Premium", local: false, source: "fontshare", fontshareSlug: "khand" },
|
|
2719
|
+
{ name: "Sentient", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sentient" },
|
|
2720
|
+
{ name: "Synonym", category: "Premium", local: false, source: "fontshare", fontshareSlug: "synonym" },
|
|
2721
|
+
{ name: "Erode", category: "Premium", local: false, source: "fontshare", fontshareSlug: "erode" },
|
|
2722
|
+
{ name: "Ranade", category: "Premium", local: false, source: "fontshare", fontshareSlug: "ranade" },
|
|
2723
|
+
{ name: "Tanker", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tanker" },
|
|
2724
|
+
{ name: "Zodiak", category: "Premium", local: false, source: "fontshare", fontshareSlug: "zodiak" },
|
|
2725
|
+
{ name: "Gambarino", category: "Premium", local: false, source: "fontshare", fontshareSlug: "gambarino" },
|
|
2726
|
+
{ name: "Melodrama", category: "Premium", local: false, source: "fontshare", fontshareSlug: "melodrama" },
|
|
2727
|
+
{ name: "Bespoke Serif", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-serif" },
|
|
2728
|
+
{ name: "Bespoke Stencil", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-stencil" },
|
|
2729
|
+
{ name: "Panchang", category: "Premium", local: false, source: "fontshare", fontshareSlug: "panchang" },
|
|
2730
|
+
{ name: "Pally", category: "Premium", local: false, source: "fontshare", fontshareSlug: "pally" },
|
|
2731
|
+
{ name: "Tabular", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tabular" },
|
|
2732
|
+
{ name: "Sharpie", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sharpie" },
|
|
2733
|
+
{ name: "Stardom", category: "Premium", local: false, source: "fontshare", fontshareSlug: "stardom" },
|
|
2734
|
+
{ name: "Telma", category: "Premium", local: false, source: "fontshare", fontshareSlug: "telma" },
|
|
2735
|
+
{ name: "Nippo", category: "Premium", local: false, source: "fontshare", fontshareSlug: "nippo" },
|
|
2736
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2737
|
+
// SERIF — editorial, classical, elegant
|
|
2738
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2739
|
+
{ name: "Playfair Display", category: "Serif", local: true, popular: true },
|
|
2740
|
+
{ name: "Cormorant", category: "Serif", local: false, popular: true },
|
|
2741
|
+
{ name: "Cormorant Garamond", category: "Serif", local: false },
|
|
2742
|
+
{ name: "Cinzel", category: "Serif", local: false, popular: true },
|
|
2743
|
+
{ name: "Cinzel Decorative", category: "Serif", local: false },
|
|
2744
|
+
{ name: "Bodoni Moda", category: "Serif", local: false, popular: true },
|
|
2745
|
+
{ name: "DM Serif Display", category: "Serif", local: true },
|
|
2746
|
+
{ name: "DM Serif Text", category: "Serif", local: false },
|
|
2747
|
+
{ name: "Italiana", category: "Serif", local: false },
|
|
2748
|
+
{ name: "Marcellus", category: "Serif", local: false },
|
|
2749
|
+
{ name: "Marcellus SC", category: "Serif", local: false },
|
|
2750
|
+
{ name: "Yeseva One", category: "Serif", local: false },
|
|
2751
|
+
{ name: "Prata", category: "Serif", local: false },
|
|
2752
|
+
{ name: "Tenor Sans", category: "Serif", local: false },
|
|
2753
|
+
{ name: "Fraunces", category: "Serif", local: false, popular: true },
|
|
2754
|
+
{ name: "Newsreader", category: "Serif", local: false },
|
|
2755
|
+
{ name: "Source Serif Pro", category: "Serif", local: false },
|
|
2756
|
+
{ name: "Merriweather", category: "Serif", local: true },
|
|
2757
|
+
{ name: "Lora", category: "Serif", local: true },
|
|
2758
|
+
{ name: "EB Garamond", category: "Serif", local: true },
|
|
2759
|
+
{ name: "Libre Baskerville", category: "Serif", local: true },
|
|
2760
|
+
{ name: "Libre Caslon Text", category: "Serif", local: false },
|
|
2761
|
+
{ name: "Libre Caslon Display", category: "Serif", local: false },
|
|
2762
|
+
{ name: "Crimson Text", category: "Serif", local: true },
|
|
2763
|
+
{ name: "Crimson Pro", category: "Serif", local: false },
|
|
2764
|
+
{ name: "Noto Serif", category: "Serif", local: false },
|
|
2765
|
+
{ name: "Noto Serif Display", category: "Serif", local: false },
|
|
2766
|
+
{ name: "PT Serif", category: "Serif", local: false },
|
|
2767
|
+
{ name: "Bitter", category: "Serif", local: false },
|
|
2768
|
+
{ name: "Spectral", category: "Serif", local: false },
|
|
2769
|
+
{ name: "Cardo", category: "Serif", local: false },
|
|
2770
|
+
{ name: "Old Standard TT", category: "Serif", local: false },
|
|
2771
|
+
{ name: "Vollkorn", category: "Serif", local: false },
|
|
2772
|
+
{ name: "Cantata One", category: "Serif", local: false },
|
|
2773
|
+
{ name: "Domine", category: "Serif", local: false },
|
|
2774
|
+
{ name: "Gentium Plus", category: "Serif", local: false },
|
|
2775
|
+
{ name: "Tinos", category: "Serif", local: false },
|
|
2776
|
+
{ name: "Trirong", category: "Serif", local: false },
|
|
2777
|
+
{ name: "Sorts Mill Goudy", category: "Serif", local: false },
|
|
2778
|
+
{ name: "IM Fell English", category: "Serif", local: false },
|
|
2779
|
+
{ name: "IM Fell DW Pica", category: "Serif", local: false },
|
|
2780
|
+
{ name: "Petrona", category: "Serif", local: false },
|
|
2781
|
+
{ name: "Rozha One", category: "Serif", local: false },
|
|
2782
|
+
{ name: "Tiro Devanagari Hindi", category: "Serif", local: false },
|
|
2783
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2784
|
+
// SANS-SERIF — clean, modern, workhorse
|
|
2785
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2786
|
+
{ name: "Inter", category: "Sans-Serif", local: true, popular: true },
|
|
2787
|
+
{ name: "Montserrat", category: "Sans-Serif", local: true, popular: true },
|
|
2788
|
+
{ name: "Poppins", category: "Sans-Serif", local: true, popular: true },
|
|
2789
|
+
{ name: "Open Sans", category: "Sans-Serif", local: true },
|
|
2790
|
+
{ name: "Roboto", category: "Sans-Serif", local: true },
|
|
2791
|
+
{ name: "Lato", category: "Sans-Serif", local: true },
|
|
2792
|
+
{ name: "Raleway", category: "Sans-Serif", local: true },
|
|
2793
|
+
{ name: "Nunito", category: "Sans-Serif", local: true },
|
|
2794
|
+
{ name: "Source Sans Pro", category: "Sans-Serif", local: true },
|
|
2795
|
+
{ name: "Work Sans", category: "Sans-Serif", local: true },
|
|
2796
|
+
{ name: "DM Sans", category: "Sans-Serif", local: true, popular: true },
|
|
2797
|
+
{ name: "Outfit", category: "Sans-Serif", local: true, popular: true },
|
|
2798
|
+
{ name: "Figtree", category: "Sans-Serif", local: true },
|
|
2799
|
+
{ name: "Manrope", category: "Sans-Serif", local: true, popular: true },
|
|
2800
|
+
{ name: "Space Grotesk", category: "Sans-Serif", local: true, popular: true },
|
|
2801
|
+
{ name: "Mulish", category: "Sans-Serif", local: true },
|
|
2802
|
+
{ name: "Quicksand", category: "Sans-Serif", local: true },
|
|
2803
|
+
{ name: "Rubik", category: "Sans-Serif", local: true },
|
|
2804
|
+
{ name: "Karla", category: "Sans-Serif", local: true },
|
|
2805
|
+
{ name: "Plus Jakarta Sans", category: "Sans-Serif", local: true },
|
|
2806
|
+
{ name: "Libre Franklin", category: "Sans-Serif", local: true },
|
|
2807
|
+
{ name: "Sora", category: "Sans-Serif", local: true },
|
|
2808
|
+
{ name: "Urbanist", category: "Sans-Serif", local: true },
|
|
2809
|
+
{ name: "Lexend", category: "Sans-Serif", local: true },
|
|
2810
|
+
{ name: "Albert Sans", category: "Sans-Serif", local: true },
|
|
2811
|
+
{ name: "Noto Sans", category: "Sans-Serif", local: true },
|
|
2812
|
+
{ name: "Cabin", category: "Sans-Serif", local: true },
|
|
2813
|
+
{ name: "Barlow", category: "Sans-Serif", local: true },
|
|
2814
|
+
{ name: "Barlow Condensed", category: "Sans-Serif", local: false },
|
|
2815
|
+
{ name: "Josefin Sans", category: "Sans-Serif", local: true },
|
|
2816
|
+
{ name: "Archivo", category: "Sans-Serif", local: true },
|
|
2817
|
+
{ name: "Archivo Narrow", category: "Sans-Serif", local: false },
|
|
2818
|
+
{ name: "Overpass", category: "Sans-Serif", local: true },
|
|
2819
|
+
{ name: "Exo 2", category: "Sans-Serif", local: true },
|
|
2820
|
+
{ name: "Onest", category: "Sans-Serif", local: false, popular: true },
|
|
2821
|
+
{ name: "Be Vietnam Pro", category: "Sans-Serif", local: false },
|
|
2822
|
+
{ name: "Public Sans", category: "Sans-Serif", local: false },
|
|
2823
|
+
{ name: "Red Hat Display", category: "Sans-Serif", local: false },
|
|
2824
|
+
{ name: "Red Hat Text", category: "Sans-Serif", local: false },
|
|
2825
|
+
{ name: "Sen", category: "Sans-Serif", local: false },
|
|
2826
|
+
{ name: "Hanken Grotesk", category: "Sans-Serif", local: false },
|
|
2827
|
+
{ name: "Schibsted Grotesk", category: "Sans-Serif", local: false },
|
|
2828
|
+
{ name: "Reddit Sans", category: "Sans-Serif", local: false },
|
|
2829
|
+
{ name: "Instrument Sans", category: "Sans-Serif", local: false },
|
|
2830
|
+
{ name: "Geist", category: "Sans-Serif", local: false, popular: true },
|
|
2831
|
+
{ name: "Nunito Sans", category: "Sans-Serif", local: false },
|
|
2832
|
+
{ name: "PT Sans", category: "Sans-Serif", local: false },
|
|
2833
|
+
{ name: "PT Sans Narrow", category: "Sans-Serif", local: false },
|
|
2834
|
+
{ name: "Mukta", category: "Sans-Serif", local: false },
|
|
2835
|
+
{ name: "Anek Devanagari", category: "Sans-Serif", local: false },
|
|
2836
|
+
{ name: "Hind", category: "Sans-Serif", local: true },
|
|
2837
|
+
{ name: "Hind Vadodara", category: "Sans-Serif", local: false },
|
|
2838
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2839
|
+
// DISPLAY — bold, attention-grabbing headlines
|
|
2840
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2841
|
+
{ name: "Bebas Neue", category: "Display", local: true, popular: true },
|
|
2842
|
+
{ name: "Anton", category: "Display", local: true, popular: true },
|
|
2843
|
+
{ name: "Oswald", category: "Display", local: true },
|
|
2844
|
+
{ name: "Abril Fatface", category: "Display", local: true, popular: true },
|
|
2845
|
+
{ name: "League Spartan", category: "Display", local: true },
|
|
2846
|
+
{ name: "Teko", category: "Display", local: true },
|
|
2847
|
+
{ name: "Righteous", category: "Display", local: false },
|
|
2848
|
+
{ name: "Alfa Slab One", category: "Display", local: false, popular: true },
|
|
2849
|
+
{ name: "Archivo Black", category: "Display", local: false },
|
|
2850
|
+
{ name: "Fredoka", category: "Display", local: false },
|
|
2851
|
+
{ name: "Passion One", category: "Display", local: false },
|
|
2852
|
+
{ name: "Bowlby One", category: "Display", local: false },
|
|
2853
|
+
{ name: "Bowlby One SC", category: "Display", local: false },
|
|
2854
|
+
{ name: "Secular One", category: "Display", local: false },
|
|
2855
|
+
{ name: "Lilita One", category: "Display", local: false },
|
|
2856
|
+
{ name: "Titan One", category: "Display", local: false },
|
|
2857
|
+
{ name: "Russo One", category: "Display", local: false },
|
|
2858
|
+
{ name: "Staatliches", category: "Display", local: false, popular: true },
|
|
2859
|
+
{ name: "Dela Gothic One", category: "Display", local: false },
|
|
2860
|
+
{ name: "Ultra", category: "Display", local: false },
|
|
2861
|
+
{ name: "Sigmar One", category: "Display", local: false },
|
|
2862
|
+
{ name: "Sigmar", category: "Display", local: false },
|
|
2863
|
+
{ name: "Modak", category: "Display", local: false },
|
|
2864
|
+
{ name: "Bagel Fat One", category: "Display", local: false },
|
|
2865
|
+
{ name: "Climate Crisis", category: "Display", local: false },
|
|
2866
|
+
{ name: "Yatra One", category: "Display", local: false },
|
|
2867
|
+
{ name: "Bungee", category: "Display", local: false },
|
|
2868
|
+
{ name: "Bungee Shade", category: "Display", local: false },
|
|
2869
|
+
{ name: "Bungee Outline", category: "Display", local: false },
|
|
2870
|
+
{ name: "Bungee Inline", category: "Display", local: false },
|
|
2871
|
+
{ name: "Monoton", category: "Display", local: false, popular: true },
|
|
2872
|
+
{ name: "Black Ops One", category: "Display", local: false },
|
|
2873
|
+
{ name: "Faster One", category: "Display", local: false },
|
|
2874
|
+
{ name: "Rubik Glitch", category: "Display", local: false },
|
|
2875
|
+
{ name: "Rubik Mono One", category: "Display", local: false },
|
|
2876
|
+
{ name: "Rubik Wet Paint", category: "Display", local: false },
|
|
2877
|
+
{ name: "Rubik Bubbles", category: "Display", local: false },
|
|
2878
|
+
{ name: "Rubik Beastly", category: "Display", local: false },
|
|
2879
|
+
{ name: "Rubik Burned", category: "Display", local: false },
|
|
2880
|
+
{ name: "Rubik Distressed", category: "Display", local: false },
|
|
2881
|
+
{ name: "Rubik Iso", category: "Display", local: false },
|
|
2882
|
+
{ name: "Rubik Marker Hatch", category: "Display", local: false },
|
|
2883
|
+
{ name: "Rubik Maze", category: "Display", local: false },
|
|
2884
|
+
{ name: "Rubik Pixels", category: "Display", local: false },
|
|
2885
|
+
{ name: "Rubik Puddles", category: "Display", local: false },
|
|
2886
|
+
{ name: "Rubik Spray Paint", category: "Display", local: false },
|
|
2887
|
+
{ name: "Rubik Vinyl", category: "Display", local: false },
|
|
2888
|
+
{ name: "Saira Stencil One", category: "Display", local: false },
|
|
2889
|
+
{ name: "Audiowide", category: "Display", local: false },
|
|
2890
|
+
{ name: "Orbitron", category: "Display", local: false },
|
|
2891
|
+
{ name: "Plaster", category: "Display", local: false },
|
|
2892
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2893
|
+
// HANDWRITING / SCRIPT — fluid, personal, calligraphic
|
|
2894
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2895
|
+
{ name: "Dancing Script", category: "Handwriting", local: true, popular: true },
|
|
2896
|
+
{ name: "Pacifico", category: "Handwriting", local: true, popular: true },
|
|
2897
|
+
{ name: "Great Vibes", category: "Handwriting", local: true, popular: true },
|
|
2898
|
+
{ name: "Sacramento", category: "Handwriting", local: true },
|
|
2899
|
+
{ name: "Alex Brush", category: "Handwriting", local: true },
|
|
2900
|
+
{ name: "Allura", category: "Handwriting", local: true },
|
|
2901
|
+
{ name: "Caveat", category: "Handwriting", local: true },
|
|
2902
|
+
{ name: "Caveat Brush", category: "Handwriting", local: false },
|
|
2903
|
+
{ name: "Lobster", category: "Handwriting", local: true },
|
|
2904
|
+
{ name: "Lobster Two", category: "Handwriting", local: false },
|
|
2905
|
+
{ name: "Comfortaa", category: "Handwriting", local: true },
|
|
2906
|
+
{ name: "Tangerine", category: "Handwriting", local: false, popular: true },
|
|
2907
|
+
{ name: "Yellowtail", category: "Handwriting", local: false, popular: true },
|
|
2908
|
+
{ name: "Kaushan Script", category: "Handwriting", local: false, popular: true },
|
|
2909
|
+
{ name: "Parisienne", category: "Handwriting", local: false },
|
|
2910
|
+
{ name: "Petit Formal Script", category: "Handwriting", local: false },
|
|
2911
|
+
{ name: "Pinyon Script", category: "Handwriting", local: false },
|
|
2912
|
+
{ name: "Mrs Saint Delafield", category: "Handwriting", local: false },
|
|
2913
|
+
{ name: "Marck Script", category: "Handwriting", local: false },
|
|
2914
|
+
{ name: "Niconne", category: "Handwriting", local: false },
|
|
2915
|
+
{ name: "Homemade Apple", category: "Handwriting", local: false },
|
|
2916
|
+
{ name: "Permanent Marker", category: "Handwriting", local: false },
|
|
2917
|
+
{ name: "Reenie Beanie", category: "Handwriting", local: false },
|
|
2918
|
+
{ name: "Satisfy", category: "Handwriting", local: false },
|
|
2919
|
+
{ name: "Kalam", category: "Handwriting", local: false },
|
|
2920
|
+
{ name: "Indie Flower", category: "Handwriting", local: false },
|
|
2921
|
+
{ name: "Courgette", category: "Handwriting", local: false },
|
|
2922
|
+
{ name: "Cookie", category: "Handwriting", local: false },
|
|
2923
|
+
{ name: "Shadows Into Light", category: "Handwriting", local: false },
|
|
2924
|
+
{ name: "Patrick Hand", category: "Handwriting", local: false },
|
|
2925
|
+
{ name: "Amatic SC", category: "Handwriting", local: false },
|
|
2926
|
+
{ name: "Architects Daughter", category: "Handwriting", local: false },
|
|
2927
|
+
{ name: "Gloria Hallelujah", category: "Handwriting", local: false },
|
|
2928
|
+
{ name: "La Belle Aurore", category: "Handwriting", local: false },
|
|
2929
|
+
{ name: "Mr Dafoe", category: "Handwriting", local: false },
|
|
2930
|
+
{ name: "Italianno", category: "Handwriting", local: false },
|
|
2931
|
+
{ name: "Rouge Script", category: "Handwriting", local: false },
|
|
2932
|
+
{ name: "Grand Hotel", category: "Handwriting", local: false },
|
|
2933
|
+
{ name: "Bilbo Swash Caps", category: "Handwriting", local: false },
|
|
2934
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2935
|
+
// DECORATIVE / FUN — quirky, themed, special-occasion
|
|
2936
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2937
|
+
{ name: "Frijole", category: "Decorative", local: false },
|
|
2938
|
+
{ name: "Creepster", category: "Decorative", local: false },
|
|
2939
|
+
{ name: "Nosifer", category: "Decorative", local: false },
|
|
2940
|
+
{ name: "Ewert", category: "Decorative", local: false },
|
|
2941
|
+
{ name: "Lakki Reddy", category: "Decorative", local: false },
|
|
2942
|
+
{ name: "Henny Penny", category: "Decorative", local: false },
|
|
2943
|
+
{ name: "Special Elite", category: "Decorative", local: false, popular: true },
|
|
2944
|
+
{ name: "Vast Shadow", category: "Decorative", local: false },
|
|
2945
|
+
{ name: "Almendra Display", category: "Decorative", local: false },
|
|
2946
|
+
{ name: "Eater", category: "Decorative", local: false },
|
|
2947
|
+
{ name: "Butcherman", category: "Decorative", local: false },
|
|
2948
|
+
{ name: "Pirata One", category: "Decorative", local: false },
|
|
2949
|
+
{ name: "Metamorphous", category: "Decorative", local: false },
|
|
2950
|
+
{ name: "MedievalSharp", category: "Decorative", local: false },
|
|
2951
|
+
{ name: "Fascinate", category: "Decorative", local: false },
|
|
2952
|
+
{ name: "Fascinate Inline", category: "Decorative", local: false },
|
|
2953
|
+
{ name: "Sancreek", category: "Decorative", local: false },
|
|
2954
|
+
{ name: "Smokum", category: "Decorative", local: false },
|
|
2955
|
+
{ name: "Vampiro One", category: "Decorative", local: false },
|
|
2956
|
+
{ name: "Mountains of Christmas", category: "Decorative", local: false },
|
|
2957
|
+
{ name: "Caesar Dressing", category: "Decorative", local: false },
|
|
2958
|
+
{ name: "Megrim", category: "Decorative", local: false },
|
|
2959
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2960
|
+
// BLACKLETTER — gothic, medieval, formal
|
|
2961
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2962
|
+
{ name: "UnifrakturCook", category: "Blackletter", local: false },
|
|
2963
|
+
{ name: "UnifrakturMaguntia", category: "Blackletter", local: false },
|
|
2964
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2965
|
+
// MONOSPACE — code, technical
|
|
2966
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2967
|
+
{ name: "Roboto Mono", category: "Monospace", local: true },
|
|
2968
|
+
{ name: "Fira Code", category: "Monospace", local: true },
|
|
2969
|
+
{ name: "JetBrains Mono", category: "Monospace", local: true },
|
|
2970
|
+
{ name: "Source Code Pro", category: "Monospace", local: true },
|
|
2971
|
+
{ name: "IBM Plex Mono", category: "Monospace", local: true },
|
|
2972
|
+
{ name: "Space Mono", category: "Monospace", local: true },
|
|
2973
|
+
{ name: "Geist Mono", category: "Monospace", local: false },
|
|
2974
|
+
{ name: "DM Mono", category: "Monospace", local: false },
|
|
2975
|
+
{ name: "Inconsolata", category: "Monospace", local: false },
|
|
2976
|
+
{ name: "Cousine", category: "Monospace", local: false },
|
|
2977
|
+
{ name: "Anonymous Pro", category: "Monospace", local: false },
|
|
2978
|
+
{ name: "Cutive Mono", category: "Monospace", local: false },
|
|
2979
|
+
{ name: "Major Mono Display", category: "Monospace", local: false },
|
|
2980
|
+
{ name: "VT323", category: "Monospace", local: false },
|
|
2981
|
+
{ name: "Share Tech Mono", category: "Monospace", local: false }
|
|
2982
|
+
];
|
|
2983
|
+
const _fontLookupCache = /* @__PURE__ */ new Map();
|
|
2984
|
+
function findFontEntry(name) {
|
|
2985
|
+
const key = name.toLowerCase().replace(/[\s\-_]/g, "");
|
|
2986
|
+
if (_fontLookupCache.has(key)) return _fontLookupCache.get(key);
|
|
2987
|
+
const entry = EXTENDED_FONT_LIST.find(
|
|
2988
|
+
(f) => f.name.toLowerCase().replace(/[\s\-_]/g, "") === key
|
|
2989
|
+
);
|
|
2990
|
+
if (entry) _fontLookupCache.set(key, entry);
|
|
2991
|
+
return entry;
|
|
2992
|
+
}
|
|
2642
2993
|
const getObjectId = (obj) => obj.__docuforgeId;
|
|
2643
2994
|
const setObjectData = (obj, id) => {
|
|
2644
2995
|
obj.__docuforgeId = id;
|
|
@@ -2860,9 +3211,27 @@ const ensureFontLoaded = async (fontFamily) => {
|
|
|
2860
3211
|
return;
|
|
2861
3212
|
}
|
|
2862
3213
|
try {
|
|
2863
|
-
await
|
|
3214
|
+
await loadFont(fontFamily);
|
|
2864
3215
|
} catch (e) {
|
|
2865
|
-
console.warn(`Failed to load
|
|
3216
|
+
console.warn(`Failed to load font: ${fontFamily}`, e);
|
|
3217
|
+
try {
|
|
3218
|
+
await loadGoogleFont(fontFamily);
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
try {
|
|
3223
|
+
if (document.fonts) {
|
|
3224
|
+
await Promise.race([
|
|
3225
|
+
Promise.all([
|
|
3226
|
+
document.fonts.load(`16px "${fontFamily}"`),
|
|
3227
|
+
document.fonts.load(`bold 16px "${fontFamily}"`),
|
|
3228
|
+
document.fonts.load(`italic 16px "${fontFamily}"`),
|
|
3229
|
+
document.fonts.load(`bold italic 16px "${fontFamily}"`)
|
|
3230
|
+
]),
|
|
3231
|
+
new Promise((r) => setTimeout(r, 2500))
|
|
3232
|
+
]);
|
|
3233
|
+
}
|
|
3234
|
+
} catch {
|
|
2866
3235
|
}
|
|
2867
3236
|
};
|
|
2868
3237
|
const setupFontLoadingListener = (canvas, afterRerender) => {
|
|
@@ -5363,6 +5732,172 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
|
|
|
5363
5732
|
];
|
|
5364
5733
|
return parts.join(" ");
|
|
5365
5734
|
}
|
|
5735
|
+
let activeThemeColors = {};
|
|
5736
|
+
function setMarkdownThemeColors(c) {
|
|
5737
|
+
activeThemeColors = { ...c };
|
|
5738
|
+
}
|
|
5739
|
+
function resolveColorToken(token, theme) {
|
|
5740
|
+
const raw = token.trim();
|
|
5741
|
+
const t = raw.toLowerCase();
|
|
5742
|
+
if (t === "primary") return theme.primary;
|
|
5743
|
+
if (t === "secondary") return theme.secondary;
|
|
5744
|
+
if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
|
|
5745
|
+
if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
|
|
5746
|
+
if (/^[a-z]+$/i.test(raw)) return raw;
|
|
5747
|
+
return void 0;
|
|
5748
|
+
}
|
|
5749
|
+
function mergeStyle(a, b) {
|
|
5750
|
+
return { ...a, ...b };
|
|
5751
|
+
}
|
|
5752
|
+
function tokenize(input, theme) {
|
|
5753
|
+
const runs = [];
|
|
5754
|
+
const stack = [];
|
|
5755
|
+
let buf = "";
|
|
5756
|
+
const activeStyle = () => {
|
|
5757
|
+
let s = {};
|
|
5758
|
+
for (const e of stack) s = mergeStyle(s, e.style);
|
|
5759
|
+
return s;
|
|
5760
|
+
};
|
|
5761
|
+
const flush = () => {
|
|
5762
|
+
if (buf.length === 0) return;
|
|
5763
|
+
runs.push({ text: buf, style: activeStyle() });
|
|
5764
|
+
buf = "";
|
|
5765
|
+
};
|
|
5766
|
+
let i = 0;
|
|
5767
|
+
const n = input.length;
|
|
5768
|
+
const peek = (s, at = i) => input.startsWith(s, at);
|
|
5769
|
+
const findUnescaped = (needle, from) => {
|
|
5770
|
+
let p = from;
|
|
5771
|
+
while (p < n) {
|
|
5772
|
+
if (input[p] === "\\" && p + 1 < n) {
|
|
5773
|
+
p += 2;
|
|
5774
|
+
continue;
|
|
5775
|
+
}
|
|
5776
|
+
if (input.startsWith(needle, p)) return p;
|
|
5777
|
+
p++;
|
|
5778
|
+
}
|
|
5779
|
+
return -1;
|
|
5780
|
+
};
|
|
5781
|
+
const tryOpenBracket = () => {
|
|
5782
|
+
if (input[i] !== "[") return -1;
|
|
5783
|
+
const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
|
|
5784
|
+
if (!m) return -1;
|
|
5785
|
+
const kind = m[1];
|
|
5786
|
+
const tokenRaw = m[2];
|
|
5787
|
+
const closer = kind === "c" ? "[/c]" : "[/bg]";
|
|
5788
|
+
if (findUnescaped(closer, i + m[0].length) === -1) return -1;
|
|
5789
|
+
const color = resolveColorToken(tokenRaw, theme);
|
|
5790
|
+
const style = {};
|
|
5791
|
+
if (color) {
|
|
5792
|
+
if (kind === "c") style.fill = color;
|
|
5793
|
+
else style.textBackgroundColor = color;
|
|
5794
|
+
}
|
|
5795
|
+
flush();
|
|
5796
|
+
stack.push({ kind, style, closer });
|
|
5797
|
+
return i + m[0].length;
|
|
5798
|
+
};
|
|
5799
|
+
const tryCloseBracket = () => {
|
|
5800
|
+
for (let s = stack.length - 1; s >= 0; s--) {
|
|
5801
|
+
const top = stack[s];
|
|
5802
|
+
if (top.kind !== "c" && top.kind !== "bg") continue;
|
|
5803
|
+
if (peek(top.closer)) {
|
|
5804
|
+
if (s !== stack.length - 1) stack.length = s + 1;
|
|
5805
|
+
flush();
|
|
5806
|
+
stack.pop();
|
|
5807
|
+
return i + top.closer.length;
|
|
5808
|
+
}
|
|
5809
|
+
break;
|
|
5810
|
+
}
|
|
5811
|
+
return -1;
|
|
5812
|
+
};
|
|
5813
|
+
const toggle = (delim, kind, style) => {
|
|
5814
|
+
if (!peek(delim)) return null;
|
|
5815
|
+
const topIdx = stack.findIndex((e) => e.kind === kind);
|
|
5816
|
+
if (topIdx >= 0) {
|
|
5817
|
+
if (topIdx !== stack.length - 1) return null;
|
|
5818
|
+
flush();
|
|
5819
|
+
stack.length = topIdx;
|
|
5820
|
+
return i + delim.length;
|
|
5821
|
+
}
|
|
5822
|
+
if (findUnescaped(delim, i + delim.length) === -1) return null;
|
|
5823
|
+
flush();
|
|
5824
|
+
stack.push({ kind, style, closer: delim });
|
|
5825
|
+
return i + delim.length;
|
|
5826
|
+
};
|
|
5827
|
+
while (i < n) {
|
|
5828
|
+
const ch = input[i];
|
|
5829
|
+
if (ch === "\\" && i + 1 < n) {
|
|
5830
|
+
buf += input[i + 1];
|
|
5831
|
+
i += 2;
|
|
5832
|
+
continue;
|
|
5833
|
+
}
|
|
5834
|
+
if (ch === "[") {
|
|
5835
|
+
const closed = tryCloseBracket();
|
|
5836
|
+
if (closed > 0) {
|
|
5837
|
+
i = closed;
|
|
5838
|
+
continue;
|
|
5839
|
+
}
|
|
5840
|
+
const opened = tryOpenBracket();
|
|
5841
|
+
if (opened > 0) {
|
|
5842
|
+
i = opened;
|
|
5843
|
+
continue;
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
let next;
|
|
5847
|
+
if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
|
|
5848
|
+
i = next;
|
|
5849
|
+
continue;
|
|
5850
|
+
}
|
|
5851
|
+
if ((next = toggle("__", "under", { underline: true })) !== null) {
|
|
5852
|
+
i = next;
|
|
5853
|
+
continue;
|
|
5854
|
+
}
|
|
5855
|
+
if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
|
|
5856
|
+
i = next;
|
|
5857
|
+
continue;
|
|
5858
|
+
}
|
|
5859
|
+
if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
|
|
5860
|
+
i = next;
|
|
5861
|
+
continue;
|
|
5862
|
+
}
|
|
5863
|
+
if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
|
|
5864
|
+
i = next;
|
|
5865
|
+
continue;
|
|
5866
|
+
}
|
|
5867
|
+
buf += ch;
|
|
5868
|
+
i++;
|
|
5869
|
+
}
|
|
5870
|
+
flush();
|
|
5871
|
+
return runs;
|
|
5872
|
+
}
|
|
5873
|
+
function parseTextMarkdown(input, themeColors) {
|
|
5874
|
+
const theme = activeThemeColors;
|
|
5875
|
+
const runs = tokenize(input ?? "", theme);
|
|
5876
|
+
let plain = "";
|
|
5877
|
+
const styles = {};
|
|
5878
|
+
let lineIdx = 0;
|
|
5879
|
+
let charIdx = 0;
|
|
5880
|
+
let hasFormatting = false;
|
|
5881
|
+
for (const run of runs) {
|
|
5882
|
+
const styleHasContent = Object.keys(run.style).length > 0;
|
|
5883
|
+
if (styleHasContent) hasFormatting = true;
|
|
5884
|
+
for (const ch of run.text) {
|
|
5885
|
+
if (ch === "\n") {
|
|
5886
|
+
plain += "\n";
|
|
5887
|
+
lineIdx++;
|
|
5888
|
+
charIdx = 0;
|
|
5889
|
+
continue;
|
|
5890
|
+
}
|
|
5891
|
+
plain += ch;
|
|
5892
|
+
if (styleHasContent) {
|
|
5893
|
+
if (!styles[lineIdx]) styles[lineIdx] = {};
|
|
5894
|
+
styles[lineIdx][charIdx] = { ...run.style };
|
|
5895
|
+
}
|
|
5896
|
+
charIdx++;
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
return { plainText: plain, styles, hasFormatting };
|
|
5900
|
+
}
|
|
5366
5901
|
const roundDiag = (value) => {
|
|
5367
5902
|
if (typeof value !== "number") return value;
|
|
5368
5903
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -5501,6 +6036,13 @@ function createText(element) {
|
|
|
5501
6036
|
let fontSize = element.fontSize || 16;
|
|
5502
6037
|
const minFontSize = element.minFontSize || 8;
|
|
5503
6038
|
const maxLines = element.maxLines || 3;
|
|
6039
|
+
const formattingEnabled = element.formattingEnabled === true;
|
|
6040
|
+
let parsedStyles = {};
|
|
6041
|
+
if (formattingEnabled) {
|
|
6042
|
+
const parsed = parseTextMarkdown(text);
|
|
6043
|
+
text = parsed.plainText || " ";
|
|
6044
|
+
parsedStyles = parsed.styles;
|
|
6045
|
+
}
|
|
5504
6046
|
const baseWidth = element.width && element.width > 0 ? element.width : 200;
|
|
5505
6047
|
const baseHeight = element.height;
|
|
5506
6048
|
const fixedWidth = Math.max(baseWidth, 1);
|
|
@@ -5521,7 +6063,8 @@ function createText(element) {
|
|
|
5521
6063
|
fontStyle: element.fontStyle || "normal",
|
|
5522
6064
|
lineHeight: element.lineHeight || 1.2,
|
|
5523
6065
|
charSpacing: element.charSpacing || 0,
|
|
5524
|
-
splitByGrapheme: false
|
|
6066
|
+
splitByGrapheme: false,
|
|
6067
|
+
...formattingEnabled ? { styles: parsedStyles } : {}
|
|
5525
6068
|
});
|
|
5526
6069
|
testTextbox.initDimensions();
|
|
5527
6070
|
const textHeight = testTextbox.height || 0;
|
|
@@ -5633,8 +6176,16 @@ function createText(element) {
|
|
|
5633
6176
|
objectCaching: false,
|
|
5634
6177
|
noScaleCache: true,
|
|
5635
6178
|
splitByGrapheme,
|
|
5636
|
-
|
|
6179
|
+
// When inline markdown formatting is enabled, the displayed text is the
|
|
6180
|
+
// PARSED plain text (markdown source lives separately on the element).
|
|
6181
|
+
// Allowing canvas inline editing would let the user edit that plain text
|
|
6182
|
+
// and on commit we'd save it back as the new markdown source — wiping all
|
|
6183
|
+
// formatting tokens (**, __, [c=...], etc). Disable inline edit and steer
|
|
6184
|
+
// users to the right-panel text field which exposes the raw markdown.
|
|
6185
|
+
editable: !formattingEnabled,
|
|
6186
|
+
...formattingEnabled ? { styles: parsedStyles } : element.styles ? { styles: element.styles } : {}
|
|
5637
6187
|
});
|
|
6188
|
+
textbox.__formattingEnabled = formattingEnabled;
|
|
5638
6189
|
textbox.initDimensions();
|
|
5639
6190
|
textbox.set({
|
|
5640
6191
|
width: targetWidth,
|
|
@@ -5655,6 +6206,15 @@ function createText(element) {
|
|
|
5655
6206
|
textbox.setCoords();
|
|
5656
6207
|
}
|
|
5657
6208
|
textbox.dirty = true;
|
|
6209
|
+
if (formattingEnabled) {
|
|
6210
|
+
try {
|
|
6211
|
+
textbox._forceClearCache = true;
|
|
6212
|
+
if (typeof textbox._clearCache === "function") {
|
|
6213
|
+
textbox._clearCache();
|
|
6214
|
+
}
|
|
6215
|
+
} catch {
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
5658
6218
|
if (overflowPolicy === "auto-shrink" && typeof window !== "undefined" && window.__pixldocsDebugAutoShrink === true) {
|
|
5659
6219
|
console.log("[auto-shrink][final-textbox] " + stringifyDiag({
|
|
5660
6220
|
id: element.id,
|
|
@@ -6391,6 +6951,10 @@ const PageCanvas = react.forwardRef(
|
|
|
6391
6951
|
skipFontReadyWait = false,
|
|
6392
6952
|
onReady
|
|
6393
6953
|
}, ref) => {
|
|
6954
|
+
setMarkdownThemeColors({
|
|
6955
|
+
primary: projectSettings.primaryColor || "#7c3aed",
|
|
6956
|
+
secondary: projectSettings.secondaryColor || "#ffe066"
|
|
6957
|
+
});
|
|
6394
6958
|
const isEditorMode = mode === "editor";
|
|
6395
6959
|
const isPreviewMode = mode === "preview";
|
|
6396
6960
|
const allowEditing = isEditorMode;
|
|
@@ -6486,8 +7050,8 @@ const PageCanvas = react.forwardRef(
|
|
|
6486
7050
|
}, [selectedIds]);
|
|
6487
7051
|
react.useEffect(() => {
|
|
6488
7052
|
isActiveRef.current = isActive;
|
|
6489
|
-
if (isActive && fabricRef.current) ;
|
|
6490
|
-
}, [isActive, pageId]);
|
|
7053
|
+
if (isEditorMode && isActive && fabricRef.current) ;
|
|
7054
|
+
}, [isActive, isEditorMode, pageId]);
|
|
6491
7055
|
const getObjId = react.useCallback((obj) => {
|
|
6492
7056
|
return obj.__docuforgeId;
|
|
6493
7057
|
}, []);
|
|
@@ -6625,11 +7189,18 @@ const PageCanvas = react.forwardRef(
|
|
|
6625
7189
|
const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
|
|
6626
7190
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
6627
7191
|
const splitByGrapheme = overflowPolicy === "auto-shrink" ? false : element.splitByGrapheme ?? element.wordWrap === "break-word";
|
|
7192
|
+
let reflowText = element.text || "Text";
|
|
7193
|
+
let reflowParsedStyles = null;
|
|
7194
|
+
if (element.formattingEnabled === true) {
|
|
7195
|
+
const parsed = parseTextMarkdown(reflowText);
|
|
7196
|
+
reflowText = parsed.plainText || " ";
|
|
7197
|
+
reflowParsedStyles = parsed.styles;
|
|
7198
|
+
}
|
|
6628
7199
|
obj.set({
|
|
6629
7200
|
width: targetWidth,
|
|
6630
7201
|
minWidth: 1,
|
|
6631
7202
|
dynamicMinWidth: 0,
|
|
6632
|
-
text:
|
|
7203
|
+
text: reflowText,
|
|
6633
7204
|
fontSize: element.fontSize || 16,
|
|
6634
7205
|
fontFamily: element.fontFamily || "Open Sans",
|
|
6635
7206
|
fontWeight: element.fontWeight || 400,
|
|
@@ -6638,6 +7209,11 @@ const PageCanvas = react.forwardRef(
|
|
|
6638
7209
|
charSpacing: element.charSpacing || 0,
|
|
6639
7210
|
splitByGrapheme
|
|
6640
7211
|
});
|
|
7212
|
+
if (element.formattingEnabled === true) {
|
|
7213
|
+
obj.styles = reflowParsedStyles || {};
|
|
7214
|
+
}
|
|
7215
|
+
obj.editable = element.formattingEnabled !== true;
|
|
7216
|
+
obj.__formattingEnabled = element.formattingEnabled === true;
|
|
6641
7217
|
obj.initDimensions();
|
|
6642
7218
|
if (Math.abs((obj.width ?? 0) - targetWidth) > 0.01) {
|
|
6643
7219
|
obj.width = targetWidth;
|
|
@@ -6645,10 +7221,26 @@ const PageCanvas = react.forwardRef(
|
|
|
6645
7221
|
obj.dynamicMinWidth = 0;
|
|
6646
7222
|
obj.setCoords();
|
|
6647
7223
|
obj.dirty = true;
|
|
7224
|
+
try {
|
|
7225
|
+
obj._forceClearCache = true;
|
|
7226
|
+
if (typeof obj._clearCache === "function") obj._clearCache();
|
|
7227
|
+
} catch {
|
|
7228
|
+
}
|
|
6648
7229
|
didReflow = true;
|
|
6649
7230
|
};
|
|
6650
7231
|
canvas2.getObjects().forEach(reflowObject);
|
|
6651
|
-
if (didReflow)
|
|
7232
|
+
if (didReflow) {
|
|
7233
|
+
canvas2.requestRenderAll();
|
|
7234
|
+
try {
|
|
7235
|
+
requestAnimationFrame(() => {
|
|
7236
|
+
try {
|
|
7237
|
+
canvas2.requestRenderAll();
|
|
7238
|
+
} catch {
|
|
7239
|
+
}
|
|
7240
|
+
});
|
|
7241
|
+
} catch {
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
6652
7244
|
}, []);
|
|
6653
7245
|
react.useEffect(() => {
|
|
6654
7246
|
if (!canvasElRef.current) return;
|
|
@@ -6695,7 +7287,8 @@ const PageCanvas = react.forwardRef(
|
|
|
6695
7287
|
absolutePositioned: true
|
|
6696
7288
|
});
|
|
6697
7289
|
fabricRef.current = fabricCanvas;
|
|
6698
|
-
registerFabricCanvas(pageId, fabricCanvas);
|
|
7290
|
+
const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
|
|
7291
|
+
fabricCanvas.__storeRegistryKey = storeRegistryKey;
|
|
6699
7292
|
const initFonts = async () => {
|
|
6700
7293
|
try {
|
|
6701
7294
|
await preloadAllFonts();
|
|
@@ -6988,7 +7581,10 @@ const PageCanvas = react.forwardRef(
|
|
|
6988
7581
|
if (!window.__fabricCanvasRegistry || !(window.__fabricCanvasRegistry instanceof Map)) {
|
|
6989
7582
|
window.__fabricCanvasRegistry = /* @__PURE__ */ new Map();
|
|
6990
7583
|
}
|
|
6991
|
-
window.__fabricCanvasRegistry
|
|
7584
|
+
const reg = window.__fabricCanvasRegistry;
|
|
7585
|
+
const registryKey = reg.has(pageId) ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
7586
|
+
fabricCanvas.__registryKey = registryKey;
|
|
7587
|
+
reg.set(registryKey, {
|
|
6992
7588
|
canvas: fabricCanvas,
|
|
6993
7589
|
forceUnlockEdits
|
|
6994
7590
|
});
|
|
@@ -7758,9 +8354,6 @@ const PageCanvas = react.forwardRef(
|
|
|
7758
8354
|
scaleY: finalScaleY,
|
|
7759
8355
|
transformMatrix: finalAbsoluteMatrix
|
|
7760
8356
|
};
|
|
7761
|
-
if (obj instanceof fabric__namespace.Textbox) {
|
|
7762
|
-
elementUpdate.text = obj.text || "";
|
|
7763
|
-
}
|
|
7764
8357
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
7765
8358
|
elementUpdate.opacity = sourceElement.opacity;
|
|
7766
8359
|
}
|
|
@@ -7839,6 +8432,12 @@ const PageCanvas = react.forwardRef(
|
|
|
7839
8432
|
}
|
|
7840
8433
|
if (target && target instanceof fabric__namespace.Textbox) {
|
|
7841
8434
|
const elementId = getObjectId(target);
|
|
8435
|
+
if (target.__formattingEnabled === true || target.editable === false) {
|
|
8436
|
+
sonner.toast.info("Inline formatting is on — edit the text in the right panel.", {
|
|
8437
|
+
description: "This protects your **bold**, [c=...] and other formatting tokens."
|
|
8438
|
+
});
|
|
8439
|
+
return;
|
|
8440
|
+
}
|
|
7842
8441
|
editingTextIdRef.current = elementId || null;
|
|
7843
8442
|
target.enterEditing();
|
|
7844
8443
|
target.selectAll();
|
|
@@ -7903,7 +8502,19 @@ const PageCanvas = react.forwardRef(
|
|
|
7903
8502
|
});
|
|
7904
8503
|
return () => {
|
|
7905
8504
|
setReady(false);
|
|
7906
|
-
unregisterFabricCanvas(pageId);
|
|
8505
|
+
unregisterFabricCanvas(fabricCanvas.__storeRegistryKey ?? pageId, fabricCanvas);
|
|
8506
|
+
try {
|
|
8507
|
+
if (typeof window !== "undefined") {
|
|
8508
|
+
const reg = window.__fabricCanvasRegistry;
|
|
8509
|
+
if (reg instanceof Map) {
|
|
8510
|
+
const key = fabricCanvas.__registryKey ?? pageId;
|
|
8511
|
+
const entry = reg.get(key);
|
|
8512
|
+
const entryCanvas = (entry == null ? void 0 : entry.canvas) ?? entry;
|
|
8513
|
+
if (entryCanvas === fabricCanvas) reg.delete(key);
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8516
|
+
} catch {
|
|
8517
|
+
}
|
|
7907
8518
|
if (fabricCanvas.__fontCleanup) {
|
|
7908
8519
|
fabricCanvas.__fontCleanup();
|
|
7909
8520
|
}
|
|
@@ -9265,6 +9876,12 @@ const PageCanvas = react.forwardRef(
|
|
|
9265
9876
|
} else if (obj instanceof fabric__namespace.Textbox) {
|
|
9266
9877
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
9267
9878
|
let text = element.text || "Text";
|
|
9879
|
+
let parsedStyles = null;
|
|
9880
|
+
if (element.formattingEnabled === true) {
|
|
9881
|
+
const parsed = parseTextMarkdown(text);
|
|
9882
|
+
text = parsed.plainText || " ";
|
|
9883
|
+
parsedStyles = parsed.styles;
|
|
9884
|
+
}
|
|
9268
9885
|
let fontSize = element.fontSize || 16;
|
|
9269
9886
|
element.minFontSize || 8;
|
|
9270
9887
|
const maxLines = element.maxLines || 3;
|
|
@@ -9361,6 +9978,11 @@ const PageCanvas = react.forwardRef(
|
|
|
9361
9978
|
splitByGrapheme,
|
|
9362
9979
|
text
|
|
9363
9980
|
});
|
|
9981
|
+
if (element.formattingEnabled === true) {
|
|
9982
|
+
obj.styles = parsedStyles || {};
|
|
9983
|
+
} else {
|
|
9984
|
+
obj.styles = element.styles || {};
|
|
9985
|
+
}
|
|
9364
9986
|
obj.initDimensions();
|
|
9365
9987
|
if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {
|
|
9366
9988
|
obj.width = textboxWidth;
|
|
@@ -10365,6 +10987,7 @@ function PreviewCanvas({
|
|
|
10365
10987
|
zoom = 1,
|
|
10366
10988
|
absoluteZoom = false,
|
|
10367
10989
|
skipFontReadyWait = false,
|
|
10990
|
+
pageIdOverride,
|
|
10368
10991
|
className,
|
|
10369
10992
|
onDynamicFieldClick,
|
|
10370
10993
|
onReady
|
|
@@ -10431,13 +11054,19 @@ function PreviewCanvas({
|
|
|
10431
11054
|
backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
|
|
10432
11055
|
};
|
|
10433
11056
|
}, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
|
|
10434
|
-
const projectSettings = react.useMemo(() =>
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
11057
|
+
const projectSettings = react.useMemo(() => {
|
|
11058
|
+
var _a2, _b2, _c2;
|
|
11059
|
+
const vars = ((_a2 = config.themeConfig) == null ? void 0 : _a2.variables) || {};
|
|
11060
|
+
return {
|
|
11061
|
+
showGrid: false,
|
|
11062
|
+
snapToGrid: false,
|
|
11063
|
+
gridSize: 10,
|
|
11064
|
+
snapToGuides: false,
|
|
11065
|
+
snapThreshold: 5,
|
|
11066
|
+
primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
|
|
11067
|
+
secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
|
|
11068
|
+
};
|
|
11069
|
+
}, [config.themeConfig]);
|
|
10441
11070
|
const handleDynamicFieldClick = react.useCallback((elementId) => {
|
|
10442
11071
|
const fieldInfo = elementToFieldMap.get(elementId);
|
|
10443
11072
|
if (fieldInfo && onDynamicFieldClick) {
|
|
@@ -10507,7 +11136,7 @@ function PreviewCanvas({
|
|
|
10507
11136
|
PageCanvas,
|
|
10508
11137
|
{
|
|
10509
11138
|
ref: canvasRef,
|
|
10510
|
-
pageId: page.id || `page-${pageIndex}`,
|
|
11139
|
+
pageId: pageIdOverride || page.id || `page-${pageIndex}`,
|
|
10511
11140
|
elements,
|
|
10512
11141
|
pageChildren: laidOutPageChildren,
|
|
10513
11142
|
pageSettings,
|
|
@@ -12362,38 +12991,6 @@ function applyContentBoundsPagination(config) {
|
|
|
12362
12991
|
if (!mutated) return config;
|
|
12363
12992
|
return { ...config, pages: resultPages };
|
|
12364
12993
|
}
|
|
12365
|
-
const FONTSHARE_SLUGS = {
|
|
12366
|
-
"Satoshi": "satoshi",
|
|
12367
|
-
"Cabinet Grotesk": "cabinet-grotesk",
|
|
12368
|
-
"Clash Display": "clash-display",
|
|
12369
|
-
"Clash Grotesk": "clash-grotesk",
|
|
12370
|
-
"General Sans": "general-sans",
|
|
12371
|
-
"Switzer": "switzer",
|
|
12372
|
-
"Supreme": "supreme",
|
|
12373
|
-
"Author": "author",
|
|
12374
|
-
"Boska": "boska",
|
|
12375
|
-
"Excon": "excon",
|
|
12376
|
-
"Khand": "khand",
|
|
12377
|
-
"Sentient": "sentient",
|
|
12378
|
-
"Synonym": "synonym",
|
|
12379
|
-
"Erode": "erode",
|
|
12380
|
-
"Ranade": "ranade",
|
|
12381
|
-
"Tanker": "tanker",
|
|
12382
|
-
"Zodiak": "zodiak",
|
|
12383
|
-
"Gambarino": "gambarino",
|
|
12384
|
-
"Melodrama": "melodrama",
|
|
12385
|
-
"Bespoke Serif": "bespoke-serif",
|
|
12386
|
-
"Bespoke Stencil": "bespoke-stencil",
|
|
12387
|
-
"Panchang": "panchang",
|
|
12388
|
-
"Quincy CF": "quincy-cf",
|
|
12389
|
-
"Pally": "pally",
|
|
12390
|
-
"Tabular": "tabular",
|
|
12391
|
-
"Sharpie": "sharpie",
|
|
12392
|
-
"Stardom": "stardom",
|
|
12393
|
-
"Rebond Grotesque": "rebond-grotesque",
|
|
12394
|
-
"Telma": "telma",
|
|
12395
|
-
"Nippo": "nippo"
|
|
12396
|
-
};
|
|
12397
12994
|
function normalizeFontFamily(fontStack) {
|
|
12398
12995
|
const first = fontStack.split(",")[0].trim();
|
|
12399
12996
|
return first.replace(/^['"]|['"]$/g, "");
|
|
@@ -12420,31 +13017,11 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12420
13017
|
if (existing) return existing;
|
|
12421
13018
|
const promise = (async () => {
|
|
12422
13019
|
try {
|
|
12423
|
-
|
|
12424
|
-
if (fontshareSlug) {
|
|
12425
|
-
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12426
|
-
const link2 = document.createElement("link");
|
|
12427
|
-
link2.rel = "stylesheet";
|
|
12428
|
-
link2.href = url2;
|
|
12429
|
-
await new Promise((resolve, reject) => {
|
|
12430
|
-
link2.onload = () => resolve();
|
|
12431
|
-
link2.onerror = () => reject(new Error(`Failed to load Fontshare font: ${fontFamily}`));
|
|
12432
|
-
document.head.appendChild(link2);
|
|
12433
|
-
});
|
|
13020
|
+
if (LOCAL_FONTS.has(fontFamily)) {
|
|
12434
13021
|
loadedFonts.add(fontFamily);
|
|
12435
13022
|
return;
|
|
12436
13023
|
}
|
|
12437
|
-
|
|
12438
|
-
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12439
|
-
const link = document.createElement("link");
|
|
12440
|
-
link.rel = "stylesheet";
|
|
12441
|
-
link.href = url;
|
|
12442
|
-
link.crossOrigin = "anonymous";
|
|
12443
|
-
await new Promise((resolve, reject) => {
|
|
12444
|
-
link.onload = () => resolve();
|
|
12445
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12446
|
-
document.head.appendChild(link);
|
|
12447
|
-
});
|
|
13024
|
+
await withTimeout(loadFont(fontFamily), 4e3);
|
|
12448
13025
|
loadedFonts.add(fontFamily);
|
|
12449
13026
|
} catch (e) {
|
|
12450
13027
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12514,6 +13091,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12514
13091
|
if (node.type === "text") {
|
|
12515
13092
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12516
13093
|
add(node.fontFamily, w, node.fontStyle);
|
|
13094
|
+
add(node.fontFamily, w, "italic");
|
|
13095
|
+
}
|
|
13096
|
+
}
|
|
13097
|
+
}
|
|
13098
|
+
if (node.formattingEnabled === true && node.fontFamily) {
|
|
13099
|
+
const parsed = parseTextMarkdown(String(node.text ?? ""));
|
|
13100
|
+
const parsedStyleEntries = Object.values(parsed.styles || {});
|
|
13101
|
+
for (const lineStyle of parsedStyleEntries) {
|
|
13102
|
+
if (lineStyle && typeof lineStyle === "object") {
|
|
13103
|
+
for (const charStyle of Object.values(lineStyle)) {
|
|
13104
|
+
if (!charStyle || typeof charStyle !== "object") continue;
|
|
13105
|
+
add(
|
|
13106
|
+
charStyle.fontFamily || node.fontFamily,
|
|
13107
|
+
charStyle.fontWeight ?? node.fontWeight,
|
|
13108
|
+
charStyle.fontStyle ?? node.fontStyle
|
|
13109
|
+
);
|
|
13110
|
+
}
|
|
12517
13111
|
}
|
|
12518
13112
|
}
|
|
12519
13113
|
}
|
|
@@ -13423,11 +14017,20 @@ function PixldocsPreview(props) {
|
|
|
13423
14017
|
!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..." }) })
|
|
13424
14018
|
] });
|
|
13425
14019
|
}
|
|
13426
|
-
const PACKAGE_VERSION = "0.5.
|
|
14020
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13427
14021
|
const roundParityValue = (value) => {
|
|
13428
14022
|
if (typeof value !== "number") return value;
|
|
13429
14023
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13430
14024
|
};
|
|
14025
|
+
function isolatePageForCapture(config, pageIndex) {
|
|
14026
|
+
var _a;
|
|
14027
|
+
const capturePageId = `__pixldocs_capture_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}_${pageIndex}`;
|
|
14028
|
+
const cloned = JSON.parse(JSON.stringify(config));
|
|
14029
|
+
if ((_a = cloned.pages) == null ? void 0 : _a[pageIndex]) {
|
|
14030
|
+
cloned.pages[pageIndex].id = capturePageId;
|
|
14031
|
+
}
|
|
14032
|
+
return { config: cloned, pageId: capturePageId };
|
|
14033
|
+
}
|
|
13431
14034
|
function logJsonLine(tag, payload) {
|
|
13432
14035
|
try {
|
|
13433
14036
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13465,6 +14068,9 @@ function installUnderlineFix(fab) {
|
|
|
13465
14068
|
const hasOwn = !!this[type];
|
|
13466
14069
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13467
14070
|
if (!hasOwn && !hasStyled) return;
|
|
14071
|
+
if (!hasOwn && hasStyled) {
|
|
14072
|
+
return original.call(this, ctx, type);
|
|
14073
|
+
}
|
|
13468
14074
|
const lines = this._textLines;
|
|
13469
14075
|
const offsets = this.offsets;
|
|
13470
14076
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13476,7 +14082,7 @@ function installUnderlineFix(fab) {
|
|
|
13476
14082
|
let topOffset = this._getTopOffset();
|
|
13477
14083
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13478
14084
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13479
|
-
const lineHas = !!this[type]
|
|
14085
|
+
const lineHas = !!this[type];
|
|
13480
14086
|
if (!lineHas) {
|
|
13481
14087
|
topOffset += heightOfLine;
|
|
13482
14088
|
continue;
|
|
@@ -14006,9 +14612,11 @@ class PixldocsRenderer {
|
|
|
14006
14612
|
}
|
|
14007
14613
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
14008
14614
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14009
|
-
const
|
|
14010
|
-
const
|
|
14011
|
-
const
|
|
14615
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14616
|
+
const renderConfig = capture.config;
|
|
14617
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
14618
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
14619
|
+
const hasAutoShrink = configHasAutoShrinkText(renderConfig);
|
|
14012
14620
|
let firstMountSettled = false;
|
|
14013
14621
|
let lateFontSettleDetected = false;
|
|
14014
14622
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14056,12 +14664,12 @@ class PixldocsRenderer {
|
|
|
14056
14664
|
root = client.createRoot(container);
|
|
14057
14665
|
await new Promise((settle) => {
|
|
14058
14666
|
const onReadyOnce = () => {
|
|
14059
|
-
this.waitForCanvasScene(container,
|
|
14667
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14060
14668
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14061
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14669
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14062
14670
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14063
|
-
await this.waitForStableTextMetrics(container,
|
|
14064
|
-
await this.waitForCanvasScene(container,
|
|
14671
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14672
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14065
14673
|
if (!fabricInstance) return settle();
|
|
14066
14674
|
settle();
|
|
14067
14675
|
}).catch(() => settle());
|
|
@@ -14069,8 +14677,9 @@ class PixldocsRenderer {
|
|
|
14069
14677
|
root.render(
|
|
14070
14678
|
react.createElement(PreviewCanvas2, {
|
|
14071
14679
|
key: `remount-${mountKey}`,
|
|
14072
|
-
config,
|
|
14680
|
+
config: renderConfig,
|
|
14073
14681
|
pageIndex,
|
|
14682
|
+
pageIdOverride: capture.pageId,
|
|
14074
14683
|
zoom: pixelRatio,
|
|
14075
14684
|
absoluteZoom: true,
|
|
14076
14685
|
skipFontReadyWait: false,
|
|
@@ -14080,13 +14689,13 @@ class PixldocsRenderer {
|
|
|
14080
14689
|
});
|
|
14081
14690
|
};
|
|
14082
14691
|
const onReady = () => {
|
|
14083
|
-
this.waitForCanvasScene(container,
|
|
14692
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14084
14693
|
try {
|
|
14085
14694
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14086
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14695
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14087
14696
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14088
|
-
await this.waitForStableTextMetrics(container,
|
|
14089
|
-
await this.waitForCanvasScene(container,
|
|
14697
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14698
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14090
14699
|
firstMountSettled = true;
|
|
14091
14700
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14092
14701
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14112,7 +14721,7 @@ class PixldocsRenderer {
|
|
|
14112
14721
|
}
|
|
14113
14722
|
exportCtx.save();
|
|
14114
14723
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14115
|
-
this.paintPageBackground(exportCtx,
|
|
14724
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14116
14725
|
exportCtx.restore();
|
|
14117
14726
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14118
14727
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14128,8 +14737,9 @@ class PixldocsRenderer {
|
|
|
14128
14737
|
root = client.createRoot(container);
|
|
14129
14738
|
root.render(
|
|
14130
14739
|
react.createElement(PreviewCanvas2, {
|
|
14131
|
-
config,
|
|
14740
|
+
config: renderConfig,
|
|
14132
14741
|
pageIndex,
|
|
14742
|
+
pageIdOverride: capture.pageId,
|
|
14133
14743
|
zoom: pixelRatio,
|
|
14134
14744
|
absoluteZoom: true,
|
|
14135
14745
|
skipFontReadyWait: false,
|
|
@@ -14150,6 +14760,8 @@ class PixldocsRenderer {
|
|
|
14150
14760
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14151
14761
|
return new Promise(async (resolve, reject) => {
|
|
14152
14762
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14763
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14764
|
+
const renderConfig = capture.config;
|
|
14153
14765
|
const container = document.createElement("div");
|
|
14154
14766
|
container.style.cssText = `
|
|
14155
14767
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14176,8 +14788,9 @@ class PixldocsRenderer {
|
|
|
14176
14788
|
root.render(
|
|
14177
14789
|
react.createElement(PreviewCanvas2, {
|
|
14178
14790
|
key: `svg-capture-${mountKey}`,
|
|
14179
|
-
config,
|
|
14791
|
+
config: renderConfig,
|
|
14180
14792
|
pageIndex,
|
|
14793
|
+
pageIdOverride: capture.pageId,
|
|
14181
14794
|
zoom: 1,
|
|
14182
14795
|
absoluteZoom: true,
|
|
14183
14796
|
skipFontReadyWait: false,
|
|
@@ -14186,13 +14799,13 @@ class PixldocsRenderer {
|
|
|
14186
14799
|
);
|
|
14187
14800
|
};
|
|
14188
14801
|
const onReady = () => {
|
|
14189
|
-
this.waitForCanvasScene(container,
|
|
14802
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14190
14803
|
var _a, _b;
|
|
14191
14804
|
try {
|
|
14192
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14805
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14193
14806
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14194
|
-
await this.waitForStableTextMetrics(container,
|
|
14195
|
-
await this.waitForCanvasScene(container,
|
|
14807
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14808
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14196
14809
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14197
14810
|
if (!fabricInstance) {
|
|
14198
14811
|
cleanup();
|
|
@@ -14281,7 +14894,7 @@ class PixldocsRenderer {
|
|
|
14281
14894
|
);
|
|
14282
14895
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14283
14896
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14284
|
-
const page =
|
|
14897
|
+
const page = renderConfig.pages[pageIndex];
|
|
14285
14898
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14286
14899
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14287
14900
|
cleanup();
|
|
@@ -14963,8 +15576,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14963
15576
|
}
|
|
14964
15577
|
return files.regular;
|
|
14965
15578
|
}
|
|
14966
|
-
function
|
|
14967
|
-
|
|
15579
|
+
function isExactWeightItalicMatch(files, weight, isItalic, path) {
|
|
15580
|
+
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";
|
|
15581
|
+
return files[exactKey] === path;
|
|
14968
15582
|
}
|
|
14969
15583
|
function getJsPDFFontName(fontName) {
|
|
14970
15584
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15037,10 +15651,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15037
15651
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15038
15652
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15039
15653
|
if (!fontPath) return false;
|
|
15040
|
-
|
|
15041
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15654
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15655
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15042
15656
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15043
|
-
const italicSuffix =
|
|
15657
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15044
15658
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15045
15659
|
const url = baseUrl + fontPath;
|
|
15046
15660
|
try {
|
|
@@ -15055,6 +15669,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15055
15669
|
}
|
|
15056
15670
|
}
|
|
15057
15671
|
registeredFamilies.add(fontName);
|
|
15672
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15058
15673
|
return true;
|
|
15059
15674
|
} catch (e) {
|
|
15060
15675
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15063,6 +15678,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15063
15678
|
}
|
|
15064
15679
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15065
15680
|
const fontshareNotFound = /* @__PURE__ */ new Set();
|
|
15681
|
+
const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontWeight(weight)}|${isItalic ? "i" : "n"}`;
|
|
15682
|
+
const registeredVariants = /* @__PURE__ */ new Set();
|
|
15683
|
+
const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
|
|
15684
|
+
const resolveBestRegisteredVariant = (family, weight, italic) => {
|
|
15685
|
+
const want = resolveFontWeight(weight);
|
|
15686
|
+
const weights = [300, 400, 500, 600, 700];
|
|
15687
|
+
if (registeredVariants.has(variantKey(family, want, italic))) return { weight: want, italic };
|
|
15688
|
+
const nearest = [...weights].sort((a, b) => Math.abs(a - want) - Math.abs(b - want) || (want >= 500 ? b - a : a - b));
|
|
15689
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, italic))) return { weight: w, italic };
|
|
15690
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, !italic))) return { weight: w, italic: !italic };
|
|
15691
|
+
return null;
|
|
15692
|
+
};
|
|
15066
15693
|
function bytesToBase64(bytes) {
|
|
15067
15694
|
let binary = "";
|
|
15068
15695
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15091,7 +15718,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15091
15718
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15092
15719
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15093
15720
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15094
|
-
|
|
15721
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15722
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15095
15723
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15096
15724
|
if (proxyBytes) {
|
|
15097
15725
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15109,7 +15737,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15109
15737
|
}
|
|
15110
15738
|
});
|
|
15111
15739
|
if (!cssRes.ok) {
|
|
15112
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15740
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15113
15741
|
return null;
|
|
15114
15742
|
}
|
|
15115
15743
|
const css = await cssRes.text();
|
|
@@ -15187,6 +15815,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15187
15815
|
}
|
|
15188
15816
|
}
|
|
15189
15817
|
registeredFamilies.add(fontName);
|
|
15818
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15190
15819
|
return true;
|
|
15191
15820
|
} catch (err) {
|
|
15192
15821
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15201,16 +15830,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15201
15830
|
const resolved = resolveFontWeight(weight);
|
|
15202
15831
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15203
15832
|
if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
|
|
15204
|
-
if (isItalic) {
|
|
15205
|
-
const fsUpright = await fetchFontshareTTF(fontName, resolved, false);
|
|
15206
|
-
if (fsUpright) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsUpright);
|
|
15207
|
-
}
|
|
15208
15833
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15209
15834
|
if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
|
|
15210
|
-
if (isItalic) {
|
|
15211
|
-
const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);
|
|
15212
|
-
if (uprightB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);
|
|
15213
|
-
}
|
|
15214
15835
|
return false;
|
|
15215
15836
|
}
|
|
15216
15837
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15230,9 +15851,32 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15230
15851
|
};
|
|
15231
15852
|
const walkElements = (elements) => {
|
|
15232
15853
|
for (const el of elements) {
|
|
15854
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15855
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15856
|
+
};
|
|
15233
15857
|
if (el.fontFamily) {
|
|
15234
15858
|
const w = normalizeWeight(el.fontWeight);
|
|
15235
|
-
|
|
15859
|
+
addFontVariant(el.fontFamily, w, /italic|oblique/i.test(String(el.fontStyle ?? "")));
|
|
15860
|
+
addFontVariant(el.fontFamily, 700, false);
|
|
15861
|
+
addFontVariant(el.fontFamily, 400, true);
|
|
15862
|
+
addFontVariant(el.fontFamily, 700, true);
|
|
15863
|
+
if (el.formattingEnabled === true && typeof el.text === "string") {
|
|
15864
|
+
try {
|
|
15865
|
+
const parsed = parseTextMarkdown(el.text);
|
|
15866
|
+
for (const lineStyles of Object.values(parsed.styles || {})) {
|
|
15867
|
+
if (!lineStyles || typeof lineStyles !== "object") continue;
|
|
15868
|
+
for (const s of Object.values(lineStyles)) {
|
|
15869
|
+
if (!s) continue;
|
|
15870
|
+
const family = s.fontFamily || el.fontFamily;
|
|
15871
|
+
if (!family) continue;
|
|
15872
|
+
const ww = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15873
|
+
const it = /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? ""));
|
|
15874
|
+
addFontVariant(family, ww, it);
|
|
15875
|
+
}
|
|
15876
|
+
}
|
|
15877
|
+
} catch {
|
|
15878
|
+
}
|
|
15879
|
+
}
|
|
15236
15880
|
}
|
|
15237
15881
|
if (el.styles && typeof el.styles === "object") {
|
|
15238
15882
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15240,9 +15884,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15240
15884
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15241
15885
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15242
15886
|
const s = lineStyles[charKey];
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15887
|
+
const styledFamily = (s == null ? void 0 : s.fontFamily) || el.fontFamily;
|
|
15888
|
+
if (styledFamily) {
|
|
15889
|
+
const w = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15890
|
+
addFontVariant(styledFamily, w, /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? "")));
|
|
15246
15891
|
}
|
|
15247
15892
|
}
|
|
15248
15893
|
}
|
|
@@ -15256,19 +15901,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15256
15901
|
if (page.children) walkElements(page.children);
|
|
15257
15902
|
if (page.elements) walkElements(page.elements);
|
|
15258
15903
|
}
|
|
15259
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15904
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15260
15905
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15261
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15906
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15262
15907
|
}
|
|
15263
15908
|
const embedded = /* @__PURE__ */ new Set();
|
|
15264
15909
|
const tasks = [];
|
|
15265
15910
|
for (const key of fontKeys) {
|
|
15266
15911
|
const sep = key.indexOf(SEP);
|
|
15912
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15267
15913
|
const fontName = key.slice(0, sep);
|
|
15268
|
-
const weight = parseInt(key.slice(sep + 1), 10);
|
|
15914
|
+
const weight = parseInt(key.slice(sep + 1, secondSep), 10);
|
|
15915
|
+
const italic = key.slice(secondSep + 1) === "italic";
|
|
15269
15916
|
if (!isFontAvailable(fontName)) continue;
|
|
15270
15917
|
tasks.push(
|
|
15271
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15918
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15272
15919
|
if (ok) embedded.add(key);
|
|
15273
15920
|
})
|
|
15274
15921
|
);
|
|
@@ -15324,7 +15971,7 @@ function splitIntoRuns(text) {
|
|
|
15324
15971
|
return runs;
|
|
15325
15972
|
}
|
|
15326
15973
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15327
|
-
var _a, _b;
|
|
15974
|
+
var _a, _b, _c;
|
|
15328
15975
|
const parser = new DOMParser();
|
|
15329
15976
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15330
15977
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15356,6 +16003,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15356
16003
|
stylePairs.push(`font-style: normal`);
|
|
15357
16004
|
return stylePairs.join("; ");
|
|
15358
16005
|
};
|
|
16006
|
+
const applySyntheticItalicTransform = (el) => {
|
|
16007
|
+
const x = Number.parseFloat(resolveInheritedValue(el, "x") || "0") || 0;
|
|
16008
|
+
const y = Number.parseFloat(resolveInheritedValue(el, "y") || "0") || 0;
|
|
16009
|
+
const existing = el.getAttribute("transform") || "";
|
|
16010
|
+
const synth = `translate(${x} ${y}) skewX(-12) translate(${-x} ${-y})`;
|
|
16011
|
+
el.setAttribute("transform", existing ? `${existing} ${synth}` : synth);
|
|
16012
|
+
};
|
|
15359
16013
|
const getDepth = (el) => {
|
|
15360
16014
|
let depth = 0;
|
|
15361
16015
|
let current = el.parentElement;
|
|
@@ -15375,20 +16029,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15375
16029
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15376
16030
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15377
16031
|
const weight = resolveWeightNum(weightRaw);
|
|
15378
|
-
const
|
|
15379
|
-
const
|
|
15380
|
-
const
|
|
15381
|
-
|
|
16032
|
+
const requested = resolveFontWeight(weight);
|
|
16033
|
+
const requestedItalic = /italic|oblique/i.test(styleRaw);
|
|
16034
|
+
const best = resolveBestRegisteredVariant(clean, requested, requestedItalic);
|
|
16035
|
+
if (!best) continue;
|
|
16036
|
+
const jsPdfName = getEmbeddedJsPDFFontName(clean, best.weight, best.italic);
|
|
16037
|
+
sourceMeta.set(el, { clean, requested, resolved: best.weight, isItalic: requestedItalic, actualItalic: best.italic, jsPdfName, inlineStyle, weight });
|
|
15382
16038
|
}
|
|
15383
16039
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15384
16040
|
for (const el of textEls) {
|
|
15385
16041
|
if (!el.isConnected) continue;
|
|
15386
16042
|
const meta = sourceMeta.get(el);
|
|
15387
16043
|
if (!meta) continue;
|
|
15388
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16044
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15389
16045
|
el.setAttribute("data-source-font-family", clean);
|
|
15390
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16046
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15391
16047
|
el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
|
|
16048
|
+
if (requested >= 600 && resolved < 600) {
|
|
16049
|
+
const fill = resolveInheritedValue(el, "fill") || readStyleToken(inlineStyle, "fill") || "#000000";
|
|
16050
|
+
el.setAttribute("stroke", fill);
|
|
16051
|
+
el.setAttribute("stroke-width", String(requested === 700 ? 0.7 : 0.5));
|
|
16052
|
+
el.setAttribute("stroke-linejoin", "round");
|
|
16053
|
+
}
|
|
16054
|
+
if (isItalic && !actualItalic) {
|
|
16055
|
+
applySyntheticItalicTransform(el);
|
|
16056
|
+
try {
|
|
16057
|
+
console.log("[Vector PDF][synthetic-italic]", {
|
|
16058
|
+
tag: el.tagName,
|
|
16059
|
+
family: clean,
|
|
16060
|
+
weight: requested,
|
|
16061
|
+
textPreview: (el.textContent || "").slice(0, 40)
|
|
16062
|
+
});
|
|
16063
|
+
} catch {
|
|
16064
|
+
}
|
|
16065
|
+
}
|
|
15392
16066
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15393
16067
|
const hasDevanagari = containsDevanagari(directText);
|
|
15394
16068
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15431,6 +16105,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15431
16105
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15432
16106
|
}
|
|
15433
16107
|
}
|
|
16108
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
16109
|
+
const transformedTspans = Array.from(doc.querySelectorAll("tspan[transform]"));
|
|
16110
|
+
for (const tspan of transformedTspans) {
|
|
16111
|
+
const parentText = tspan.parentElement;
|
|
16112
|
+
if (!parentText || parentText.tagName.toLowerCase() !== "text") continue;
|
|
16113
|
+
const transformVal = tspan.getAttribute("transform") || "";
|
|
16114
|
+
if (!/skewX/i.test(transformVal)) continue;
|
|
16115
|
+
const wrapper = doc.createElementNS(SVG_NS, "text");
|
|
16116
|
+
let parentTransform = "";
|
|
16117
|
+
for (const attr of Array.from(parentText.attributes)) {
|
|
16118
|
+
if (attr.name === "transform") {
|
|
16119
|
+
parentTransform = attr.value;
|
|
16120
|
+
continue;
|
|
16121
|
+
}
|
|
16122
|
+
wrapper.setAttribute(attr.name, attr.value);
|
|
16123
|
+
}
|
|
16124
|
+
const combined = parentTransform ? `${parentTransform} ${transformVal}` : transformVal;
|
|
16125
|
+
wrapper.setAttribute("transform", combined);
|
|
16126
|
+
tspan.removeAttribute("transform");
|
|
16127
|
+
wrapper.appendChild(tspan);
|
|
16128
|
+
(_c = parentText.parentNode) == null ? void 0 : _c.insertBefore(wrapper, parentText.nextSibling);
|
|
16129
|
+
}
|
|
15434
16130
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15435
16131
|
}
|
|
15436
16132
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15461,14 +16157,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15461
16157
|
if (ok) embedded.add(`${family}${w}`);
|
|
15462
16158
|
})
|
|
15463
16159
|
);
|
|
16160
|
+
tasks.push(
|
|
16161
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16162
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16163
|
+
})
|
|
16164
|
+
);
|
|
15464
16165
|
}
|
|
15465
16166
|
} else {
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
}
|
|
15471
|
-
|
|
16167
|
+
const variants = [
|
|
16168
|
+
{ w: 400, italic: false },
|
|
16169
|
+
{ w: 700, italic: false },
|
|
16170
|
+
{ w: 400, italic: true },
|
|
16171
|
+
{ w: 700, italic: true }
|
|
16172
|
+
];
|
|
16173
|
+
for (const v of variants) {
|
|
16174
|
+
tasks.push(
|
|
16175
|
+
embedFontWithGoogleFallback(pdf, family, v.w, fontBaseUrl, v.italic).then((ok) => {
|
|
16176
|
+
if (ok) embedded.add(`${family}${v.w}${v.italic ? "i" : ""}`);
|
|
16177
|
+
else if (v.w === 400 && !v.italic) {
|
|
16178
|
+
console.warn(`[pdf-fonts] No TTF (local/Google/Fontshare) for "${family}" — will use Helvetica fallback`);
|
|
16179
|
+
}
|
|
16180
|
+
})
|
|
16181
|
+
);
|
|
16182
|
+
}
|
|
15472
16183
|
}
|
|
15473
16184
|
}
|
|
15474
16185
|
await Promise.all(tasks);
|
|
@@ -15489,6 +16200,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15489
16200
|
getEmbeddedJsPDFFontName,
|
|
15490
16201
|
getFontPathForWeight,
|
|
15491
16202
|
isFontAvailable,
|
|
16203
|
+
resolveBestRegisteredVariant,
|
|
15492
16204
|
resolveFontWeight,
|
|
15493
16205
|
rewriteSvgFontsForJsPDF
|
|
15494
16206
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15528,9 +16240,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15528
16240
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15529
16241
|
var _a, _b;
|
|
15530
16242
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15531
|
-
const tspanInfo = tspans.slice(0,
|
|
16243
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15532
16244
|
x: s.getAttribute("x"),
|
|
15533
16245
|
y: s.getAttribute("y"),
|
|
16246
|
+
fs: s.getAttribute("font-style"),
|
|
16247
|
+
fw: s.getAttribute("font-weight"),
|
|
16248
|
+
ff: s.getAttribute("font-family"),
|
|
16249
|
+
td: s.getAttribute("text-decoration"),
|
|
16250
|
+
style: (s.getAttribute("style") || "").slice(0, 120),
|
|
15534
16251
|
text: (s.textContent || "").slice(0, 40)
|
|
15535
16252
|
}));
|
|
15536
16253
|
let containerWidth = null;
|
|
@@ -16565,13 +17282,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16565
17282
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16566
17283
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16567
17284
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17285
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16568
17286
|
for (let si = 0; si < tspans.length; si++) {
|
|
16569
17287
|
const tspan = tspans[si];
|
|
16570
17288
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16571
17289
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16572
17290
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16573
17291
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16574
|
-
|
|
17292
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17293
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16575
17294
|
const content = tspan.textContent || "";
|
|
16576
17295
|
if (!content.trim()) continue;
|
|
16577
17296
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16626,23 +17345,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16626
17345
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16627
17346
|
lineEndX = lineStartX + textWidth;
|
|
16628
17347
|
}
|
|
16629
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16630
17348
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
16631
|
-
const
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
textEl.parentElement
|
|
16643
|
-
|
|
17349
|
+
const makeLine = (yPos) => {
|
|
17350
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
17351
|
+
line.setAttribute("x1", String(lineStartX));
|
|
17352
|
+
line.setAttribute("y1", String(yPos));
|
|
17353
|
+
line.setAttribute("x2", String(lineEndX));
|
|
17354
|
+
line.setAttribute("y2", String(yPos));
|
|
17355
|
+
line.setAttribute("stroke", fill.startsWith("url(") ? "#000000" : fill);
|
|
17356
|
+
line.setAttribute("stroke-width", String(thickness));
|
|
17357
|
+
line.setAttribute("stroke-linecap", "butt");
|
|
17358
|
+
line.setAttribute("fill", "none");
|
|
17359
|
+
if (fillOpacity) line.setAttribute("stroke-opacity", fillOpacity);
|
|
17360
|
+
if (textEl.parentElement) {
|
|
17361
|
+
textEl.parentElement.insertBefore(line, textEl.nextSibling);
|
|
17362
|
+
}
|
|
17363
|
+
};
|
|
17364
|
+
if (hasUnderline) makeLine(baselineY + fontSize * 0.15);
|
|
17365
|
+
if (hasLinethrough) makeLine(baselineY - fontSize * 0.3);
|
|
16644
17366
|
stripTextDecoration(tspan);
|
|
16645
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17367
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16646
17368
|
}
|
|
16647
17369
|
}
|
|
16648
17370
|
if (tempContainer) {
|
|
@@ -16656,7 +17378,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16656
17378
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16657
17379
|
return svgStr;
|
|
16658
17380
|
}
|
|
16659
|
-
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr)) {
|
|
17381
|
+
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr) && !/line-through/i.test(svgStr)) {
|
|
16660
17382
|
return svgStr;
|
|
16661
17383
|
}
|
|
16662
17384
|
try {
|
|
@@ -16948,7 +17670,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16948
17670
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16949
17671
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16950
17672
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16951
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17673
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16952
17674
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16953
17675
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16954
17676
|
let pageSvg = page.svg;
|
|
@@ -16962,7 +17684,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16962
17684
|
}
|
|
16963
17685
|
if (shouldOutlineText) {
|
|
16964
17686
|
try {
|
|
16965
|
-
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-
|
|
17687
|
+
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-DTKsddnS.cjs"));
|
|
16966
17688
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16967
17689
|
try {
|
|
16968
17690
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|