@pixldocs/canvas-renderer 0.5.104 → 0.5.105
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 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +879 -120
- 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,
|
|
@@ -12398,6 +13027,17 @@ function normalizeFontFamily(fontStack) {
|
|
|
12398
13027
|
const first = fontStack.split(",")[0].trim();
|
|
12399
13028
|
return first.replace(/^['"]|['"]$/g, "");
|
|
12400
13029
|
}
|
|
13030
|
+
function appendStylesheet(url, rejectOnError = true) {
|
|
13031
|
+
return new Promise((resolve, reject) => {
|
|
13032
|
+
const link = document.createElement("link");
|
|
13033
|
+
link.rel = "stylesheet";
|
|
13034
|
+
link.href = url;
|
|
13035
|
+
link.crossOrigin = "anonymous";
|
|
13036
|
+
link.onload = () => resolve();
|
|
13037
|
+
link.onerror = () => rejectOnError ? reject(new Error(`Failed to load stylesheet: ${url}`)) : resolve();
|
|
13038
|
+
document.head.appendChild(link);
|
|
13039
|
+
});
|
|
13040
|
+
}
|
|
12401
13041
|
const loadedFonts = /* @__PURE__ */ new Set();
|
|
12402
13042
|
const loadingPromises = /* @__PURE__ */ new Map();
|
|
12403
13043
|
function withTimeout(promise, timeoutMs = 4e3) {
|
|
@@ -12423,28 +13063,19 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12423
13063
|
const fontshareSlug = FONTSHARE_SLUGS[fontFamily];
|
|
12424
13064
|
if (fontshareSlug) {
|
|
12425
13065
|
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
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
|
-
});
|
|
13066
|
+
await appendStylesheet(url2);
|
|
13067
|
+
const italicUrl = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300i,400i,500i,700i&display=swap`;
|
|
13068
|
+
await withTimeout(appendStylesheet(italicUrl, false), 1500);
|
|
12434
13069
|
loadedFonts.add(fontFamily);
|
|
12435
13070
|
return;
|
|
12436
13071
|
}
|
|
12437
13072
|
const encoded = encodeURIComponent(fontFamily);
|
|
12438
13073
|
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
link.onload = () => resolve();
|
|
12445
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12446
|
-
document.head.appendChild(link);
|
|
12447
|
-
});
|
|
13074
|
+
await appendStylesheet(url);
|
|
13075
|
+
await withTimeout(Promise.all([300, 400, 500, 600, 700].map((weight) => {
|
|
13076
|
+
const italicUrl = `https://fonts.googleapis.com/css2?family=${encoded}:ital,wght@1,${weight}&display=swap`;
|
|
13077
|
+
return appendStylesheet(italicUrl, false);
|
|
13078
|
+
})), 1800);
|
|
12448
13079
|
loadedFonts.add(fontFamily);
|
|
12449
13080
|
} catch (e) {
|
|
12450
13081
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12514,6 +13145,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12514
13145
|
if (node.type === "text") {
|
|
12515
13146
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12516
13147
|
add(node.fontFamily, w, node.fontStyle);
|
|
13148
|
+
add(node.fontFamily, w, "italic");
|
|
13149
|
+
}
|
|
13150
|
+
}
|
|
13151
|
+
}
|
|
13152
|
+
if (node.formattingEnabled === true && node.fontFamily) {
|
|
13153
|
+
const parsed = parseTextMarkdown(String(node.text ?? ""));
|
|
13154
|
+
const parsedStyleEntries = Object.values(parsed.styles || {});
|
|
13155
|
+
for (const lineStyle of parsedStyleEntries) {
|
|
13156
|
+
if (lineStyle && typeof lineStyle === "object") {
|
|
13157
|
+
for (const charStyle of Object.values(lineStyle)) {
|
|
13158
|
+
if (!charStyle || typeof charStyle !== "object") continue;
|
|
13159
|
+
add(
|
|
13160
|
+
charStyle.fontFamily || node.fontFamily,
|
|
13161
|
+
charStyle.fontWeight ?? node.fontWeight,
|
|
13162
|
+
charStyle.fontStyle ?? node.fontStyle
|
|
13163
|
+
);
|
|
13164
|
+
}
|
|
12517
13165
|
}
|
|
12518
13166
|
}
|
|
12519
13167
|
}
|
|
@@ -13423,11 +14071,20 @@ function PixldocsPreview(props) {
|
|
|
13423
14071
|
!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
14072
|
] });
|
|
13425
14073
|
}
|
|
13426
|
-
const PACKAGE_VERSION = "0.5.
|
|
14074
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13427
14075
|
const roundParityValue = (value) => {
|
|
13428
14076
|
if (typeof value !== "number") return value;
|
|
13429
14077
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13430
14078
|
};
|
|
14079
|
+
function isolatePageForCapture(config, pageIndex) {
|
|
14080
|
+
var _a;
|
|
14081
|
+
const capturePageId = `__pixldocs_capture_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}_${pageIndex}`;
|
|
14082
|
+
const cloned = JSON.parse(JSON.stringify(config));
|
|
14083
|
+
if ((_a = cloned.pages) == null ? void 0 : _a[pageIndex]) {
|
|
14084
|
+
cloned.pages[pageIndex].id = capturePageId;
|
|
14085
|
+
}
|
|
14086
|
+
return { config: cloned, pageId: capturePageId };
|
|
14087
|
+
}
|
|
13431
14088
|
function logJsonLine(tag, payload) {
|
|
13432
14089
|
try {
|
|
13433
14090
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13465,6 +14122,9 @@ function installUnderlineFix(fab) {
|
|
|
13465
14122
|
const hasOwn = !!this[type];
|
|
13466
14123
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13467
14124
|
if (!hasOwn && !hasStyled) return;
|
|
14125
|
+
if (!hasOwn && hasStyled) {
|
|
14126
|
+
return original.call(this, ctx, type);
|
|
14127
|
+
}
|
|
13468
14128
|
const lines = this._textLines;
|
|
13469
14129
|
const offsets = this.offsets;
|
|
13470
14130
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13476,7 +14136,7 @@ function installUnderlineFix(fab) {
|
|
|
13476
14136
|
let topOffset = this._getTopOffset();
|
|
13477
14137
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13478
14138
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13479
|
-
const lineHas = !!this[type]
|
|
14139
|
+
const lineHas = !!this[type];
|
|
13480
14140
|
if (!lineHas) {
|
|
13481
14141
|
topOffset += heightOfLine;
|
|
13482
14142
|
continue;
|
|
@@ -14006,9 +14666,11 @@ class PixldocsRenderer {
|
|
|
14006
14666
|
}
|
|
14007
14667
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
14008
14668
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14009
|
-
const
|
|
14010
|
-
const
|
|
14011
|
-
const
|
|
14669
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14670
|
+
const renderConfig = capture.config;
|
|
14671
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
14672
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
14673
|
+
const hasAutoShrink = configHasAutoShrinkText(renderConfig);
|
|
14012
14674
|
let firstMountSettled = false;
|
|
14013
14675
|
let lateFontSettleDetected = false;
|
|
14014
14676
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14056,12 +14718,12 @@ class PixldocsRenderer {
|
|
|
14056
14718
|
root = client.createRoot(container);
|
|
14057
14719
|
await new Promise((settle) => {
|
|
14058
14720
|
const onReadyOnce = () => {
|
|
14059
|
-
this.waitForCanvasScene(container,
|
|
14721
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14060
14722
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14061
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14723
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14062
14724
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14063
|
-
await this.waitForStableTextMetrics(container,
|
|
14064
|
-
await this.waitForCanvasScene(container,
|
|
14725
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14726
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14065
14727
|
if (!fabricInstance) return settle();
|
|
14066
14728
|
settle();
|
|
14067
14729
|
}).catch(() => settle());
|
|
@@ -14069,8 +14731,9 @@ class PixldocsRenderer {
|
|
|
14069
14731
|
root.render(
|
|
14070
14732
|
react.createElement(PreviewCanvas2, {
|
|
14071
14733
|
key: `remount-${mountKey}`,
|
|
14072
|
-
config,
|
|
14734
|
+
config: renderConfig,
|
|
14073
14735
|
pageIndex,
|
|
14736
|
+
pageIdOverride: capture.pageId,
|
|
14074
14737
|
zoom: pixelRatio,
|
|
14075
14738
|
absoluteZoom: true,
|
|
14076
14739
|
skipFontReadyWait: false,
|
|
@@ -14080,13 +14743,13 @@ class PixldocsRenderer {
|
|
|
14080
14743
|
});
|
|
14081
14744
|
};
|
|
14082
14745
|
const onReady = () => {
|
|
14083
|
-
this.waitForCanvasScene(container,
|
|
14746
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14084
14747
|
try {
|
|
14085
14748
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14086
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14749
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14087
14750
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14088
|
-
await this.waitForStableTextMetrics(container,
|
|
14089
|
-
await this.waitForCanvasScene(container,
|
|
14751
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14752
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14090
14753
|
firstMountSettled = true;
|
|
14091
14754
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14092
14755
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14112,7 +14775,7 @@ class PixldocsRenderer {
|
|
|
14112
14775
|
}
|
|
14113
14776
|
exportCtx.save();
|
|
14114
14777
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14115
|
-
this.paintPageBackground(exportCtx,
|
|
14778
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14116
14779
|
exportCtx.restore();
|
|
14117
14780
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14118
14781
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14128,8 +14791,9 @@ class PixldocsRenderer {
|
|
|
14128
14791
|
root = client.createRoot(container);
|
|
14129
14792
|
root.render(
|
|
14130
14793
|
react.createElement(PreviewCanvas2, {
|
|
14131
|
-
config,
|
|
14794
|
+
config: renderConfig,
|
|
14132
14795
|
pageIndex,
|
|
14796
|
+
pageIdOverride: capture.pageId,
|
|
14133
14797
|
zoom: pixelRatio,
|
|
14134
14798
|
absoluteZoom: true,
|
|
14135
14799
|
skipFontReadyWait: false,
|
|
@@ -14150,6 +14814,8 @@ class PixldocsRenderer {
|
|
|
14150
14814
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14151
14815
|
return new Promise(async (resolve, reject) => {
|
|
14152
14816
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14817
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14818
|
+
const renderConfig = capture.config;
|
|
14153
14819
|
const container = document.createElement("div");
|
|
14154
14820
|
container.style.cssText = `
|
|
14155
14821
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14176,8 +14842,9 @@ class PixldocsRenderer {
|
|
|
14176
14842
|
root.render(
|
|
14177
14843
|
react.createElement(PreviewCanvas2, {
|
|
14178
14844
|
key: `svg-capture-${mountKey}`,
|
|
14179
|
-
config,
|
|
14845
|
+
config: renderConfig,
|
|
14180
14846
|
pageIndex,
|
|
14847
|
+
pageIdOverride: capture.pageId,
|
|
14181
14848
|
zoom: 1,
|
|
14182
14849
|
absoluteZoom: true,
|
|
14183
14850
|
skipFontReadyWait: false,
|
|
@@ -14186,13 +14853,13 @@ class PixldocsRenderer {
|
|
|
14186
14853
|
);
|
|
14187
14854
|
};
|
|
14188
14855
|
const onReady = () => {
|
|
14189
|
-
this.waitForCanvasScene(container,
|
|
14856
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14190
14857
|
var _a, _b;
|
|
14191
14858
|
try {
|
|
14192
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14859
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14193
14860
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14194
|
-
await this.waitForStableTextMetrics(container,
|
|
14195
|
-
await this.waitForCanvasScene(container,
|
|
14861
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14862
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14196
14863
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14197
14864
|
if (!fabricInstance) {
|
|
14198
14865
|
cleanup();
|
|
@@ -14281,7 +14948,7 @@ class PixldocsRenderer {
|
|
|
14281
14948
|
);
|
|
14282
14949
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14283
14950
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14284
|
-
const page =
|
|
14951
|
+
const page = renderConfig.pages[pageIndex];
|
|
14285
14952
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14286
14953
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14287
14954
|
cleanup();
|
|
@@ -14963,8 +15630,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14963
15630
|
}
|
|
14964
15631
|
return files.regular;
|
|
14965
15632
|
}
|
|
14966
|
-
function
|
|
14967
|
-
|
|
15633
|
+
function isExactWeightItalicMatch(files, weight, isItalic, path) {
|
|
15634
|
+
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";
|
|
15635
|
+
return files[exactKey] === path;
|
|
14968
15636
|
}
|
|
14969
15637
|
function getJsPDFFontName(fontName) {
|
|
14970
15638
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15037,10 +15705,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15037
15705
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15038
15706
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15039
15707
|
if (!fontPath) return false;
|
|
15040
|
-
|
|
15041
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15708
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15709
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15042
15710
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15043
|
-
const italicSuffix =
|
|
15711
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15044
15712
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15045
15713
|
const url = baseUrl + fontPath;
|
|
15046
15714
|
try {
|
|
@@ -15055,6 +15723,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15055
15723
|
}
|
|
15056
15724
|
}
|
|
15057
15725
|
registeredFamilies.add(fontName);
|
|
15726
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15058
15727
|
return true;
|
|
15059
15728
|
} catch (e) {
|
|
15060
15729
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15063,6 +15732,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15063
15732
|
}
|
|
15064
15733
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15065
15734
|
const fontshareNotFound = /* @__PURE__ */ new Set();
|
|
15735
|
+
const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontWeight(weight)}|${isItalic ? "i" : "n"}`;
|
|
15736
|
+
const registeredVariants = /* @__PURE__ */ new Set();
|
|
15737
|
+
const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
|
|
15738
|
+
const resolveBestRegisteredVariant = (family, weight, italic) => {
|
|
15739
|
+
const want = resolveFontWeight(weight);
|
|
15740
|
+
const weights = [300, 400, 500, 600, 700];
|
|
15741
|
+
if (registeredVariants.has(variantKey(family, want, italic))) return { weight: want, italic };
|
|
15742
|
+
const nearest = [...weights].sort((a, b) => Math.abs(a - want) - Math.abs(b - want) || (want >= 500 ? b - a : a - b));
|
|
15743
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, italic))) return { weight: w, italic };
|
|
15744
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, !italic))) return { weight: w, italic: !italic };
|
|
15745
|
+
return null;
|
|
15746
|
+
};
|
|
15066
15747
|
function bytesToBase64(bytes) {
|
|
15067
15748
|
let binary = "";
|
|
15068
15749
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15091,7 +15772,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15091
15772
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15092
15773
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15093
15774
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15094
|
-
|
|
15775
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15776
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15095
15777
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15096
15778
|
if (proxyBytes) {
|
|
15097
15779
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15109,7 +15791,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15109
15791
|
}
|
|
15110
15792
|
});
|
|
15111
15793
|
if (!cssRes.ok) {
|
|
15112
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15794
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15113
15795
|
return null;
|
|
15114
15796
|
}
|
|
15115
15797
|
const css = await cssRes.text();
|
|
@@ -15187,6 +15869,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15187
15869
|
}
|
|
15188
15870
|
}
|
|
15189
15871
|
registeredFamilies.add(fontName);
|
|
15872
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15190
15873
|
return true;
|
|
15191
15874
|
} catch (err) {
|
|
15192
15875
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15201,16 +15884,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15201
15884
|
const resolved = resolveFontWeight(weight);
|
|
15202
15885
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15203
15886
|
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
15887
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15209
15888
|
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
15889
|
return false;
|
|
15215
15890
|
}
|
|
15216
15891
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15230,9 +15905,15 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15230
15905
|
};
|
|
15231
15906
|
const walkElements = (elements) => {
|
|
15232
15907
|
for (const el of elements) {
|
|
15908
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15909
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15910
|
+
};
|
|
15233
15911
|
if (el.fontFamily) {
|
|
15234
15912
|
const w = normalizeWeight(el.fontWeight);
|
|
15235
|
-
|
|
15913
|
+
addFontVariant(el.fontFamily, w, /italic|oblique/i.test(String(el.fontStyle ?? "")));
|
|
15914
|
+
addFontVariant(el.fontFamily, 700, false);
|
|
15915
|
+
addFontVariant(el.fontFamily, 400, true);
|
|
15916
|
+
addFontVariant(el.fontFamily, 700, true);
|
|
15236
15917
|
}
|
|
15237
15918
|
if (el.styles && typeof el.styles === "object") {
|
|
15238
15919
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15240,9 +15921,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15240
15921
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15241
15922
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15242
15923
|
const s = lineStyles[charKey];
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15924
|
+
const styledFamily = (s == null ? void 0 : s.fontFamily) || el.fontFamily;
|
|
15925
|
+
if (styledFamily) {
|
|
15926
|
+
const w = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15927
|
+
addFontVariant(styledFamily, w, /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? "")));
|
|
15246
15928
|
}
|
|
15247
15929
|
}
|
|
15248
15930
|
}
|
|
@@ -15256,19 +15938,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15256
15938
|
if (page.children) walkElements(page.children);
|
|
15257
15939
|
if (page.elements) walkElements(page.elements);
|
|
15258
15940
|
}
|
|
15259
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15941
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15260
15942
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15261
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15943
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15262
15944
|
}
|
|
15263
15945
|
const embedded = /* @__PURE__ */ new Set();
|
|
15264
15946
|
const tasks = [];
|
|
15265
15947
|
for (const key of fontKeys) {
|
|
15266
15948
|
const sep = key.indexOf(SEP);
|
|
15949
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15267
15950
|
const fontName = key.slice(0, sep);
|
|
15268
|
-
const weight = parseInt(key.slice(sep + 1), 10);
|
|
15951
|
+
const weight = parseInt(key.slice(sep + 1, secondSep), 10);
|
|
15952
|
+
const italic = key.slice(secondSep + 1) === "italic";
|
|
15269
15953
|
if (!isFontAvailable(fontName)) continue;
|
|
15270
15954
|
tasks.push(
|
|
15271
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15955
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15272
15956
|
if (ok) embedded.add(key);
|
|
15273
15957
|
})
|
|
15274
15958
|
);
|
|
@@ -15324,7 +16008,7 @@ function splitIntoRuns(text) {
|
|
|
15324
16008
|
return runs;
|
|
15325
16009
|
}
|
|
15326
16010
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15327
|
-
var _a, _b;
|
|
16011
|
+
var _a, _b, _c;
|
|
15328
16012
|
const parser = new DOMParser();
|
|
15329
16013
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15330
16014
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15356,6 +16040,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15356
16040
|
stylePairs.push(`font-style: normal`);
|
|
15357
16041
|
return stylePairs.join("; ");
|
|
15358
16042
|
};
|
|
16043
|
+
const applySyntheticItalicTransform = (el) => {
|
|
16044
|
+
const x = Number.parseFloat(resolveInheritedValue(el, "x") || "0") || 0;
|
|
16045
|
+
const y = Number.parseFloat(resolveInheritedValue(el, "y") || "0") || 0;
|
|
16046
|
+
const existing = el.getAttribute("transform") || "";
|
|
16047
|
+
const synth = `translate(${x} ${y}) skewX(-12) translate(${-x} ${-y})`;
|
|
16048
|
+
el.setAttribute("transform", existing ? `${existing} ${synth}` : synth);
|
|
16049
|
+
};
|
|
15359
16050
|
const getDepth = (el) => {
|
|
15360
16051
|
let depth = 0;
|
|
15361
16052
|
let current = el.parentElement;
|
|
@@ -15375,20 +16066,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15375
16066
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15376
16067
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15377
16068
|
const weight = resolveWeightNum(weightRaw);
|
|
15378
|
-
const
|
|
15379
|
-
const
|
|
15380
|
-
const
|
|
15381
|
-
|
|
16069
|
+
const requested = resolveFontWeight(weight);
|
|
16070
|
+
const requestedItalic = /italic|oblique/i.test(styleRaw);
|
|
16071
|
+
const best = resolveBestRegisteredVariant(clean, requested, requestedItalic);
|
|
16072
|
+
if (!best) continue;
|
|
16073
|
+
const jsPdfName = getEmbeddedJsPDFFontName(clean, best.weight, best.italic);
|
|
16074
|
+
sourceMeta.set(el, { clean, requested, resolved: best.weight, isItalic: requestedItalic, actualItalic: best.italic, jsPdfName, inlineStyle, weight });
|
|
15382
16075
|
}
|
|
15383
16076
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15384
16077
|
for (const el of textEls) {
|
|
15385
16078
|
if (!el.isConnected) continue;
|
|
15386
16079
|
const meta = sourceMeta.get(el);
|
|
15387
16080
|
if (!meta) continue;
|
|
15388
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16081
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15389
16082
|
el.setAttribute("data-source-font-family", clean);
|
|
15390
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16083
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15391
16084
|
el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
|
|
16085
|
+
if (requested >= 600 && resolved < 600) {
|
|
16086
|
+
const fill = resolveInheritedValue(el, "fill") || readStyleToken(inlineStyle, "fill") || "#000000";
|
|
16087
|
+
el.setAttribute("stroke", fill);
|
|
16088
|
+
el.setAttribute("stroke-width", String(requested === 700 ? 0.7 : 0.5));
|
|
16089
|
+
el.setAttribute("stroke-linejoin", "round");
|
|
16090
|
+
}
|
|
16091
|
+
if (isItalic && !actualItalic) {
|
|
16092
|
+
applySyntheticItalicTransform(el);
|
|
16093
|
+
try {
|
|
16094
|
+
console.log("[Vector PDF][synthetic-italic]", {
|
|
16095
|
+
tag: el.tagName,
|
|
16096
|
+
family: clean,
|
|
16097
|
+
weight: requested,
|
|
16098
|
+
textPreview: (el.textContent || "").slice(0, 40)
|
|
16099
|
+
});
|
|
16100
|
+
} catch {
|
|
16101
|
+
}
|
|
16102
|
+
}
|
|
15392
16103
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15393
16104
|
const hasDevanagari = containsDevanagari(directText);
|
|
15394
16105
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15431,6 +16142,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15431
16142
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15432
16143
|
}
|
|
15433
16144
|
}
|
|
16145
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
16146
|
+
const transformedTspans = Array.from(doc.querySelectorAll("tspan[transform]"));
|
|
16147
|
+
for (const tspan of transformedTspans) {
|
|
16148
|
+
const parentText = tspan.parentElement;
|
|
16149
|
+
if (!parentText || parentText.tagName.toLowerCase() !== "text") continue;
|
|
16150
|
+
const transformVal = tspan.getAttribute("transform") || "";
|
|
16151
|
+
if (!/skewX/i.test(transformVal)) continue;
|
|
16152
|
+
const wrapper = doc.createElementNS(SVG_NS, "text");
|
|
16153
|
+
let parentTransform = "";
|
|
16154
|
+
for (const attr of Array.from(parentText.attributes)) {
|
|
16155
|
+
if (attr.name === "transform") {
|
|
16156
|
+
parentTransform = attr.value;
|
|
16157
|
+
continue;
|
|
16158
|
+
}
|
|
16159
|
+
wrapper.setAttribute(attr.name, attr.value);
|
|
16160
|
+
}
|
|
16161
|
+
const combined = parentTransform ? `${parentTransform} ${transformVal}` : transformVal;
|
|
16162
|
+
wrapper.setAttribute("transform", combined);
|
|
16163
|
+
tspan.removeAttribute("transform");
|
|
16164
|
+
wrapper.appendChild(tspan);
|
|
16165
|
+
(_c = parentText.parentNode) == null ? void 0 : _c.insertBefore(wrapper, parentText.nextSibling);
|
|
16166
|
+
}
|
|
15434
16167
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15435
16168
|
}
|
|
15436
16169
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15461,14 +16194,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15461
16194
|
if (ok) embedded.add(`${family}${w}`);
|
|
15462
16195
|
})
|
|
15463
16196
|
);
|
|
16197
|
+
tasks.push(
|
|
16198
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16199
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16200
|
+
})
|
|
16201
|
+
);
|
|
15464
16202
|
}
|
|
15465
16203
|
} else {
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
}
|
|
15471
|
-
|
|
16204
|
+
const variants = [
|
|
16205
|
+
{ w: 400, italic: false },
|
|
16206
|
+
{ w: 700, italic: false },
|
|
16207
|
+
{ w: 400, italic: true },
|
|
16208
|
+
{ w: 700, italic: true }
|
|
16209
|
+
];
|
|
16210
|
+
for (const v of variants) {
|
|
16211
|
+
tasks.push(
|
|
16212
|
+
embedFontWithGoogleFallback(pdf, family, v.w, fontBaseUrl, v.italic).then((ok) => {
|
|
16213
|
+
if (ok) embedded.add(`${family}${v.w}${v.italic ? "i" : ""}`);
|
|
16214
|
+
else if (v.w === 400 && !v.italic) {
|
|
16215
|
+
console.warn(`[pdf-fonts] No TTF (local/Google/Fontshare) for "${family}" — will use Helvetica fallback`);
|
|
16216
|
+
}
|
|
16217
|
+
})
|
|
16218
|
+
);
|
|
16219
|
+
}
|
|
15472
16220
|
}
|
|
15473
16221
|
}
|
|
15474
16222
|
await Promise.all(tasks);
|
|
@@ -15489,6 +16237,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15489
16237
|
getEmbeddedJsPDFFontName,
|
|
15490
16238
|
getFontPathForWeight,
|
|
15491
16239
|
isFontAvailable,
|
|
16240
|
+
resolveBestRegisteredVariant,
|
|
15492
16241
|
resolveFontWeight,
|
|
15493
16242
|
rewriteSvgFontsForJsPDF
|
|
15494
16243
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15528,9 +16277,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15528
16277
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15529
16278
|
var _a, _b;
|
|
15530
16279
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15531
|
-
const tspanInfo = tspans.slice(0,
|
|
16280
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15532
16281
|
x: s.getAttribute("x"),
|
|
15533
16282
|
y: s.getAttribute("y"),
|
|
16283
|
+
fs: s.getAttribute("font-style"),
|
|
16284
|
+
fw: s.getAttribute("font-weight"),
|
|
16285
|
+
ff: s.getAttribute("font-family"),
|
|
16286
|
+
td: s.getAttribute("text-decoration"),
|
|
16287
|
+
style: (s.getAttribute("style") || "").slice(0, 120),
|
|
15534
16288
|
text: (s.textContent || "").slice(0, 40)
|
|
15535
16289
|
}));
|
|
15536
16290
|
let containerWidth = null;
|
|
@@ -16565,13 +17319,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16565
17319
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16566
17320
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16567
17321
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17322
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16568
17323
|
for (let si = 0; si < tspans.length; si++) {
|
|
16569
17324
|
const tspan = tspans[si];
|
|
16570
17325
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16571
17326
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16572
17327
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16573
17328
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16574
|
-
|
|
17329
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17330
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16575
17331
|
const content = tspan.textContent || "";
|
|
16576
17332
|
if (!content.trim()) continue;
|
|
16577
17333
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16626,23 +17382,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16626
17382
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16627
17383
|
lineEndX = lineStartX + textWidth;
|
|
16628
17384
|
}
|
|
16629
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16630
17385
|
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
|
-
|
|
17386
|
+
const makeLine = (yPos) => {
|
|
17387
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
17388
|
+
line.setAttribute("x1", String(lineStartX));
|
|
17389
|
+
line.setAttribute("y1", String(yPos));
|
|
17390
|
+
line.setAttribute("x2", String(lineEndX));
|
|
17391
|
+
line.setAttribute("y2", String(yPos));
|
|
17392
|
+
line.setAttribute("stroke", fill.startsWith("url(") ? "#000000" : fill);
|
|
17393
|
+
line.setAttribute("stroke-width", String(thickness));
|
|
17394
|
+
line.setAttribute("stroke-linecap", "butt");
|
|
17395
|
+
line.setAttribute("fill", "none");
|
|
17396
|
+
if (fillOpacity) line.setAttribute("stroke-opacity", fillOpacity);
|
|
17397
|
+
if (textEl.parentElement) {
|
|
17398
|
+
textEl.parentElement.insertBefore(line, textEl.nextSibling);
|
|
17399
|
+
}
|
|
17400
|
+
};
|
|
17401
|
+
if (hasUnderline) makeLine(baselineY + fontSize * 0.15);
|
|
17402
|
+
if (hasLinethrough) makeLine(baselineY - fontSize * 0.3);
|
|
16644
17403
|
stripTextDecoration(tspan);
|
|
16645
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17404
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16646
17405
|
}
|
|
16647
17406
|
}
|
|
16648
17407
|
if (tempContainer) {
|
|
@@ -16656,7 +17415,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16656
17415
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16657
17416
|
return svgStr;
|
|
16658
17417
|
}
|
|
16659
|
-
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr)) {
|
|
17418
|
+
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr) && !/line-through/i.test(svgStr)) {
|
|
16660
17419
|
return svgStr;
|
|
16661
17420
|
}
|
|
16662
17421
|
try {
|
|
@@ -16948,7 +17707,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16948
17707
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16949
17708
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16950
17709
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16951
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17710
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16952
17711
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16953
17712
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16954
17713
|
let pageSvg = page.svg;
|
|
@@ -16962,7 +17721,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16962
17721
|
}
|
|
16963
17722
|
if (shouldOutlineText) {
|
|
16964
17723
|
try {
|
|
16965
|
-
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-
|
|
17724
|
+
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-DTKsddnS.cjs"));
|
|
16966
17725
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16967
17726
|
try {
|
|
16968
17727
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|