@pixldocs/canvas-renderer 0.5.103 → 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 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +879 -140
- 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,
|
|
@@ -6269,7 +6829,6 @@ function bakeEdgeFade(source, fade) {
|
|
|
6269
6829
|
let x = 0, y = 0, rectW = mask.width, rectH = mask.height;
|
|
6270
6830
|
const STOPS = 64;
|
|
6271
6831
|
const gamma = 1 / hardness;
|
|
6272
|
-
const seamGuardPx = 2;
|
|
6273
6832
|
const innerAlpha = (t) => {
|
|
6274
6833
|
const keepProgress = Math.pow(t, gamma);
|
|
6275
6834
|
const erase = (1 - amount) * (1 - keepProgress);
|
|
@@ -6307,16 +6866,6 @@ function bakeEdgeFade(source, fade) {
|
|
|
6307
6866
|
}
|
|
6308
6867
|
mctx.fillStyle = g;
|
|
6309
6868
|
mctx.fillRect(x, y, rectW, rectH);
|
|
6310
|
-
{
|
|
6311
|
-
const edgePx = Math.min(seamGuardPx, side === "top" || side === "bottom" ? rectH : rectW);
|
|
6312
|
-
mctx.fillStyle = "rgba(0,0,0,0)";
|
|
6313
|
-
mctx.globalCompositeOperation = "copy";
|
|
6314
|
-
if (side === "top") mctx.fillRect(0, 0, mask.width, edgePx);
|
|
6315
|
-
if (side === "bottom") mctx.fillRect(0, mask.height - edgePx, mask.width, edgePx);
|
|
6316
|
-
if (side === "left") mctx.fillRect(0, 0, edgePx, mask.height);
|
|
6317
|
-
if (side === "right") mctx.fillRect(mask.width - edgePx, 0, edgePx, mask.height);
|
|
6318
|
-
mctx.globalCompositeOperation = "source-over";
|
|
6319
|
-
}
|
|
6320
6869
|
ctx.globalCompositeOperation = "destination-in";
|
|
6321
6870
|
ctx.drawImage(mask, 0, 0);
|
|
6322
6871
|
ctx.globalCompositeOperation = "source-over";
|
|
@@ -6402,6 +6951,10 @@ const PageCanvas = react.forwardRef(
|
|
|
6402
6951
|
skipFontReadyWait = false,
|
|
6403
6952
|
onReady
|
|
6404
6953
|
}, ref) => {
|
|
6954
|
+
setMarkdownThemeColors({
|
|
6955
|
+
primary: projectSettings.primaryColor || "#7c3aed",
|
|
6956
|
+
secondary: projectSettings.secondaryColor || "#ffe066"
|
|
6957
|
+
});
|
|
6405
6958
|
const isEditorMode = mode === "editor";
|
|
6406
6959
|
const isPreviewMode = mode === "preview";
|
|
6407
6960
|
const allowEditing = isEditorMode;
|
|
@@ -6497,8 +7050,8 @@ const PageCanvas = react.forwardRef(
|
|
|
6497
7050
|
}, [selectedIds]);
|
|
6498
7051
|
react.useEffect(() => {
|
|
6499
7052
|
isActiveRef.current = isActive;
|
|
6500
|
-
if (isActive && fabricRef.current) ;
|
|
6501
|
-
}, [isActive, pageId]);
|
|
7053
|
+
if (isEditorMode && isActive && fabricRef.current) ;
|
|
7054
|
+
}, [isActive, isEditorMode, pageId]);
|
|
6502
7055
|
const getObjId = react.useCallback((obj) => {
|
|
6503
7056
|
return obj.__docuforgeId;
|
|
6504
7057
|
}, []);
|
|
@@ -6636,11 +7189,18 @@ const PageCanvas = react.forwardRef(
|
|
|
6636
7189
|
const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
|
|
6637
7190
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
6638
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
|
+
}
|
|
6639
7199
|
obj.set({
|
|
6640
7200
|
width: targetWidth,
|
|
6641
7201
|
minWidth: 1,
|
|
6642
7202
|
dynamicMinWidth: 0,
|
|
6643
|
-
text:
|
|
7203
|
+
text: reflowText,
|
|
6644
7204
|
fontSize: element.fontSize || 16,
|
|
6645
7205
|
fontFamily: element.fontFamily || "Open Sans",
|
|
6646
7206
|
fontWeight: element.fontWeight || 400,
|
|
@@ -6649,6 +7209,11 @@ const PageCanvas = react.forwardRef(
|
|
|
6649
7209
|
charSpacing: element.charSpacing || 0,
|
|
6650
7210
|
splitByGrapheme
|
|
6651
7211
|
});
|
|
7212
|
+
if (element.formattingEnabled === true) {
|
|
7213
|
+
obj.styles = reflowParsedStyles || {};
|
|
7214
|
+
}
|
|
7215
|
+
obj.editable = element.formattingEnabled !== true;
|
|
7216
|
+
obj.__formattingEnabled = element.formattingEnabled === true;
|
|
6652
7217
|
obj.initDimensions();
|
|
6653
7218
|
if (Math.abs((obj.width ?? 0) - targetWidth) > 0.01) {
|
|
6654
7219
|
obj.width = targetWidth;
|
|
@@ -6656,10 +7221,26 @@ const PageCanvas = react.forwardRef(
|
|
|
6656
7221
|
obj.dynamicMinWidth = 0;
|
|
6657
7222
|
obj.setCoords();
|
|
6658
7223
|
obj.dirty = true;
|
|
7224
|
+
try {
|
|
7225
|
+
obj._forceClearCache = true;
|
|
7226
|
+
if (typeof obj._clearCache === "function") obj._clearCache();
|
|
7227
|
+
} catch {
|
|
7228
|
+
}
|
|
6659
7229
|
didReflow = true;
|
|
6660
7230
|
};
|
|
6661
7231
|
canvas2.getObjects().forEach(reflowObject);
|
|
6662
|
-
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
|
+
}
|
|
6663
7244
|
}, []);
|
|
6664
7245
|
react.useEffect(() => {
|
|
6665
7246
|
if (!canvasElRef.current) return;
|
|
@@ -6706,7 +7287,8 @@ const PageCanvas = react.forwardRef(
|
|
|
6706
7287
|
absolutePositioned: true
|
|
6707
7288
|
});
|
|
6708
7289
|
fabricRef.current = fabricCanvas;
|
|
6709
|
-
registerFabricCanvas(pageId, fabricCanvas);
|
|
7290
|
+
const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
|
|
7291
|
+
fabricCanvas.__storeRegistryKey = storeRegistryKey;
|
|
6710
7292
|
const initFonts = async () => {
|
|
6711
7293
|
try {
|
|
6712
7294
|
await preloadAllFonts();
|
|
@@ -6999,7 +7581,10 @@ const PageCanvas = react.forwardRef(
|
|
|
6999
7581
|
if (!window.__fabricCanvasRegistry || !(window.__fabricCanvasRegistry instanceof Map)) {
|
|
7000
7582
|
window.__fabricCanvasRegistry = /* @__PURE__ */ new Map();
|
|
7001
7583
|
}
|
|
7002
|
-
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, {
|
|
7003
7588
|
canvas: fabricCanvas,
|
|
7004
7589
|
forceUnlockEdits
|
|
7005
7590
|
});
|
|
@@ -7769,9 +8354,6 @@ const PageCanvas = react.forwardRef(
|
|
|
7769
8354
|
scaleY: finalScaleY,
|
|
7770
8355
|
transformMatrix: finalAbsoluteMatrix
|
|
7771
8356
|
};
|
|
7772
|
-
if (obj instanceof fabric__namespace.Textbox) {
|
|
7773
|
-
elementUpdate.text = obj.text || "";
|
|
7774
|
-
}
|
|
7775
8357
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
7776
8358
|
elementUpdate.opacity = sourceElement.opacity;
|
|
7777
8359
|
}
|
|
@@ -7850,6 +8432,12 @@ const PageCanvas = react.forwardRef(
|
|
|
7850
8432
|
}
|
|
7851
8433
|
if (target && target instanceof fabric__namespace.Textbox) {
|
|
7852
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
|
+
}
|
|
7853
8441
|
editingTextIdRef.current = elementId || null;
|
|
7854
8442
|
target.enterEditing();
|
|
7855
8443
|
target.selectAll();
|
|
@@ -7914,7 +8502,19 @@ const PageCanvas = react.forwardRef(
|
|
|
7914
8502
|
});
|
|
7915
8503
|
return () => {
|
|
7916
8504
|
setReady(false);
|
|
7917
|
-
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
|
+
}
|
|
7918
8518
|
if (fabricCanvas.__fontCleanup) {
|
|
7919
8519
|
fabricCanvas.__fontCleanup();
|
|
7920
8520
|
}
|
|
@@ -9276,6 +9876,12 @@ const PageCanvas = react.forwardRef(
|
|
|
9276
9876
|
} else if (obj instanceof fabric__namespace.Textbox) {
|
|
9277
9877
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
9278
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
|
+
}
|
|
9279
9885
|
let fontSize = element.fontSize || 16;
|
|
9280
9886
|
element.minFontSize || 8;
|
|
9281
9887
|
const maxLines = element.maxLines || 3;
|
|
@@ -9372,6 +9978,11 @@ const PageCanvas = react.forwardRef(
|
|
|
9372
9978
|
splitByGrapheme,
|
|
9373
9979
|
text
|
|
9374
9980
|
});
|
|
9981
|
+
if (element.formattingEnabled === true) {
|
|
9982
|
+
obj.styles = parsedStyles || {};
|
|
9983
|
+
} else {
|
|
9984
|
+
obj.styles = element.styles || {};
|
|
9985
|
+
}
|
|
9375
9986
|
obj.initDimensions();
|
|
9376
9987
|
if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {
|
|
9377
9988
|
obj.width = textboxWidth;
|
|
@@ -9733,7 +10344,6 @@ const PageCanvas = react.forwardRef(
|
|
|
9733
10344
|
const eraseAtEdge = 1 - amount;
|
|
9734
10345
|
const STOPS = 64;
|
|
9735
10346
|
const gamma = 1 / hardness;
|
|
9736
|
-
const seamGuardPx = 2;
|
|
9737
10347
|
const addStops = (g) => {
|
|
9738
10348
|
for (let i = 0; i <= STOPS; i++) {
|
|
9739
10349
|
const t = i / STOPS;
|
|
@@ -9748,32 +10358,24 @@ const PageCanvas = react.forwardRef(
|
|
|
9748
10358
|
addStops(gradient);
|
|
9749
10359
|
ctx.fillStyle = gradient;
|
|
9750
10360
|
ctx.fillRect(x, y, w, band);
|
|
9751
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9752
|
-
ctx.fillRect(x, y, w, Math.min(seamGuardPx, band));
|
|
9753
10361
|
} else if (side === "bottom") {
|
|
9754
10362
|
const band = Math.max(1, h * size);
|
|
9755
10363
|
gradient = ctx.createLinearGradient(0, y + h, 0, y + h - band);
|
|
9756
10364
|
addStops(gradient);
|
|
9757
10365
|
ctx.fillStyle = gradient;
|
|
9758
10366
|
ctx.fillRect(x, y + h - band, w, band);
|
|
9759
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9760
|
-
ctx.fillRect(x, y + h - Math.min(seamGuardPx, band), w, Math.min(seamGuardPx, band));
|
|
9761
10367
|
} else if (side === "left") {
|
|
9762
10368
|
const band = Math.max(1, w * size);
|
|
9763
10369
|
gradient = ctx.createLinearGradient(x, 0, x + band, 0);
|
|
9764
10370
|
addStops(gradient);
|
|
9765
10371
|
ctx.fillStyle = gradient;
|
|
9766
10372
|
ctx.fillRect(x, y, band, h);
|
|
9767
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9768
|
-
ctx.fillRect(x, y, Math.min(seamGuardPx, band), h);
|
|
9769
10373
|
} else {
|
|
9770
10374
|
const band = Math.max(1, w * size);
|
|
9771
10375
|
gradient = ctx.createLinearGradient(x + w, 0, x + w - band, 0);
|
|
9772
10376
|
addStops(gradient);
|
|
9773
10377
|
ctx.fillStyle = gradient;
|
|
9774
10378
|
ctx.fillRect(x + w - band, y, band, h);
|
|
9775
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9776
|
-
ctx.fillRect(x + w - Math.min(seamGuardPx, band), y, Math.min(seamGuardPx, band), h);
|
|
9777
10379
|
}
|
|
9778
10380
|
ctx.restore();
|
|
9779
10381
|
};
|
|
@@ -10385,6 +10987,7 @@ function PreviewCanvas({
|
|
|
10385
10987
|
zoom = 1,
|
|
10386
10988
|
absoluteZoom = false,
|
|
10387
10989
|
skipFontReadyWait = false,
|
|
10990
|
+
pageIdOverride,
|
|
10388
10991
|
className,
|
|
10389
10992
|
onDynamicFieldClick,
|
|
10390
10993
|
onReady
|
|
@@ -10451,13 +11054,19 @@ function PreviewCanvas({
|
|
|
10451
11054
|
backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
|
|
10452
11055
|
};
|
|
10453
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]);
|
|
10454
|
-
const projectSettings = react.useMemo(() =>
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
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]);
|
|
10461
11070
|
const handleDynamicFieldClick = react.useCallback((elementId) => {
|
|
10462
11071
|
const fieldInfo = elementToFieldMap.get(elementId);
|
|
10463
11072
|
if (fieldInfo && onDynamicFieldClick) {
|
|
@@ -10527,7 +11136,7 @@ function PreviewCanvas({
|
|
|
10527
11136
|
PageCanvas,
|
|
10528
11137
|
{
|
|
10529
11138
|
ref: canvasRef,
|
|
10530
|
-
pageId: page.id || `page-${pageIndex}`,
|
|
11139
|
+
pageId: pageIdOverride || page.id || `page-${pageIndex}`,
|
|
10531
11140
|
elements,
|
|
10532
11141
|
pageChildren: laidOutPageChildren,
|
|
10533
11142
|
pageSettings,
|
|
@@ -12418,6 +13027,17 @@ function normalizeFontFamily(fontStack) {
|
|
|
12418
13027
|
const first = fontStack.split(",")[0].trim();
|
|
12419
13028
|
return first.replace(/^['"]|['"]$/g, "");
|
|
12420
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
|
+
}
|
|
12421
13041
|
const loadedFonts = /* @__PURE__ */ new Set();
|
|
12422
13042
|
const loadingPromises = /* @__PURE__ */ new Map();
|
|
12423
13043
|
function withTimeout(promise, timeoutMs = 4e3) {
|
|
@@ -12443,28 +13063,19 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12443
13063
|
const fontshareSlug = FONTSHARE_SLUGS[fontFamily];
|
|
12444
13064
|
if (fontshareSlug) {
|
|
12445
13065
|
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
await new Promise((resolve, reject) => {
|
|
12450
|
-
link2.onload = () => resolve();
|
|
12451
|
-
link2.onerror = () => reject(new Error(`Failed to load Fontshare font: ${fontFamily}`));
|
|
12452
|
-
document.head.appendChild(link2);
|
|
12453
|
-
});
|
|
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);
|
|
12454
13069
|
loadedFonts.add(fontFamily);
|
|
12455
13070
|
return;
|
|
12456
13071
|
}
|
|
12457
13072
|
const encoded = encodeURIComponent(fontFamily);
|
|
12458
13073
|
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
link.onload = () => resolve();
|
|
12465
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12466
|
-
document.head.appendChild(link);
|
|
12467
|
-
});
|
|
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);
|
|
12468
13079
|
loadedFonts.add(fontFamily);
|
|
12469
13080
|
} catch (e) {
|
|
12470
13081
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12534,6 +13145,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12534
13145
|
if (node.type === "text") {
|
|
12535
13146
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12536
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
|
+
}
|
|
12537
13165
|
}
|
|
12538
13166
|
}
|
|
12539
13167
|
}
|
|
@@ -13443,11 +14071,20 @@ function PixldocsPreview(props) {
|
|
|
13443
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..." }) })
|
|
13444
14072
|
] });
|
|
13445
14073
|
}
|
|
13446
|
-
const PACKAGE_VERSION = "0.5.
|
|
14074
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13447
14075
|
const roundParityValue = (value) => {
|
|
13448
14076
|
if (typeof value !== "number") return value;
|
|
13449
14077
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13450
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
|
+
}
|
|
13451
14088
|
function logJsonLine(tag, payload) {
|
|
13452
14089
|
try {
|
|
13453
14090
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13485,6 +14122,9 @@ function installUnderlineFix(fab) {
|
|
|
13485
14122
|
const hasOwn = !!this[type];
|
|
13486
14123
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13487
14124
|
if (!hasOwn && !hasStyled) return;
|
|
14125
|
+
if (!hasOwn && hasStyled) {
|
|
14126
|
+
return original.call(this, ctx, type);
|
|
14127
|
+
}
|
|
13488
14128
|
const lines = this._textLines;
|
|
13489
14129
|
const offsets = this.offsets;
|
|
13490
14130
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13496,7 +14136,7 @@ function installUnderlineFix(fab) {
|
|
|
13496
14136
|
let topOffset = this._getTopOffset();
|
|
13497
14137
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13498
14138
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13499
|
-
const lineHas = !!this[type]
|
|
14139
|
+
const lineHas = !!this[type];
|
|
13500
14140
|
if (!lineHas) {
|
|
13501
14141
|
topOffset += heightOfLine;
|
|
13502
14142
|
continue;
|
|
@@ -14026,9 +14666,11 @@ class PixldocsRenderer {
|
|
|
14026
14666
|
}
|
|
14027
14667
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
14028
14668
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14029
|
-
const
|
|
14030
|
-
const
|
|
14031
|
-
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);
|
|
14032
14674
|
let firstMountSettled = false;
|
|
14033
14675
|
let lateFontSettleDetected = false;
|
|
14034
14676
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14076,12 +14718,12 @@ class PixldocsRenderer {
|
|
|
14076
14718
|
root = client.createRoot(container);
|
|
14077
14719
|
await new Promise((settle) => {
|
|
14078
14720
|
const onReadyOnce = () => {
|
|
14079
|
-
this.waitForCanvasScene(container,
|
|
14721
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14080
14722
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14081
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14723
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14082
14724
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14083
|
-
await this.waitForStableTextMetrics(container,
|
|
14084
|
-
await this.waitForCanvasScene(container,
|
|
14725
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14726
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14085
14727
|
if (!fabricInstance) return settle();
|
|
14086
14728
|
settle();
|
|
14087
14729
|
}).catch(() => settle());
|
|
@@ -14089,8 +14731,9 @@ class PixldocsRenderer {
|
|
|
14089
14731
|
root.render(
|
|
14090
14732
|
react.createElement(PreviewCanvas2, {
|
|
14091
14733
|
key: `remount-${mountKey}`,
|
|
14092
|
-
config,
|
|
14734
|
+
config: renderConfig,
|
|
14093
14735
|
pageIndex,
|
|
14736
|
+
pageIdOverride: capture.pageId,
|
|
14094
14737
|
zoom: pixelRatio,
|
|
14095
14738
|
absoluteZoom: true,
|
|
14096
14739
|
skipFontReadyWait: false,
|
|
@@ -14100,13 +14743,13 @@ class PixldocsRenderer {
|
|
|
14100
14743
|
});
|
|
14101
14744
|
};
|
|
14102
14745
|
const onReady = () => {
|
|
14103
|
-
this.waitForCanvasScene(container,
|
|
14746
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14104
14747
|
try {
|
|
14105
14748
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14106
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14749
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14107
14750
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14108
|
-
await this.waitForStableTextMetrics(container,
|
|
14109
|
-
await this.waitForCanvasScene(container,
|
|
14751
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14752
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14110
14753
|
firstMountSettled = true;
|
|
14111
14754
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14112
14755
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14132,7 +14775,7 @@ class PixldocsRenderer {
|
|
|
14132
14775
|
}
|
|
14133
14776
|
exportCtx.save();
|
|
14134
14777
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14135
|
-
this.paintPageBackground(exportCtx,
|
|
14778
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14136
14779
|
exportCtx.restore();
|
|
14137
14780
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14138
14781
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14148,8 +14791,9 @@ class PixldocsRenderer {
|
|
|
14148
14791
|
root = client.createRoot(container);
|
|
14149
14792
|
root.render(
|
|
14150
14793
|
react.createElement(PreviewCanvas2, {
|
|
14151
|
-
config,
|
|
14794
|
+
config: renderConfig,
|
|
14152
14795
|
pageIndex,
|
|
14796
|
+
pageIdOverride: capture.pageId,
|
|
14153
14797
|
zoom: pixelRatio,
|
|
14154
14798
|
absoluteZoom: true,
|
|
14155
14799
|
skipFontReadyWait: false,
|
|
@@ -14170,6 +14814,8 @@ class PixldocsRenderer {
|
|
|
14170
14814
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14171
14815
|
return new Promise(async (resolve, reject) => {
|
|
14172
14816
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14817
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14818
|
+
const renderConfig = capture.config;
|
|
14173
14819
|
const container = document.createElement("div");
|
|
14174
14820
|
container.style.cssText = `
|
|
14175
14821
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14196,8 +14842,9 @@ class PixldocsRenderer {
|
|
|
14196
14842
|
root.render(
|
|
14197
14843
|
react.createElement(PreviewCanvas2, {
|
|
14198
14844
|
key: `svg-capture-${mountKey}`,
|
|
14199
|
-
config,
|
|
14845
|
+
config: renderConfig,
|
|
14200
14846
|
pageIndex,
|
|
14847
|
+
pageIdOverride: capture.pageId,
|
|
14201
14848
|
zoom: 1,
|
|
14202
14849
|
absoluteZoom: true,
|
|
14203
14850
|
skipFontReadyWait: false,
|
|
@@ -14206,13 +14853,13 @@ class PixldocsRenderer {
|
|
|
14206
14853
|
);
|
|
14207
14854
|
};
|
|
14208
14855
|
const onReady = () => {
|
|
14209
|
-
this.waitForCanvasScene(container,
|
|
14856
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14210
14857
|
var _a, _b;
|
|
14211
14858
|
try {
|
|
14212
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14859
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14213
14860
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14214
|
-
await this.waitForStableTextMetrics(container,
|
|
14215
|
-
await this.waitForCanvasScene(container,
|
|
14861
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14862
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14216
14863
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14217
14864
|
if (!fabricInstance) {
|
|
14218
14865
|
cleanup();
|
|
@@ -14301,7 +14948,7 @@ class PixldocsRenderer {
|
|
|
14301
14948
|
);
|
|
14302
14949
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14303
14950
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14304
|
-
const page =
|
|
14951
|
+
const page = renderConfig.pages[pageIndex];
|
|
14305
14952
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14306
14953
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14307
14954
|
cleanup();
|
|
@@ -14983,8 +15630,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14983
15630
|
}
|
|
14984
15631
|
return files.regular;
|
|
14985
15632
|
}
|
|
14986
|
-
function
|
|
14987
|
-
|
|
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;
|
|
14988
15636
|
}
|
|
14989
15637
|
function getJsPDFFontName(fontName) {
|
|
14990
15638
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15057,10 +15705,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15057
15705
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15058
15706
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15059
15707
|
if (!fontPath) return false;
|
|
15060
|
-
|
|
15061
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15708
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15709
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15062
15710
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15063
|
-
const italicSuffix =
|
|
15711
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15064
15712
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15065
15713
|
const url = baseUrl + fontPath;
|
|
15066
15714
|
try {
|
|
@@ -15075,6 +15723,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15075
15723
|
}
|
|
15076
15724
|
}
|
|
15077
15725
|
registeredFamilies.add(fontName);
|
|
15726
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15078
15727
|
return true;
|
|
15079
15728
|
} catch (e) {
|
|
15080
15729
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15083,6 +15732,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15083
15732
|
}
|
|
15084
15733
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15085
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
|
+
};
|
|
15086
15747
|
function bytesToBase64(bytes) {
|
|
15087
15748
|
let binary = "";
|
|
15088
15749
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15111,7 +15772,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15111
15772
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15112
15773
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15113
15774
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15114
|
-
|
|
15775
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15776
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15115
15777
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15116
15778
|
if (proxyBytes) {
|
|
15117
15779
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15129,7 +15791,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15129
15791
|
}
|
|
15130
15792
|
});
|
|
15131
15793
|
if (!cssRes.ok) {
|
|
15132
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15794
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15133
15795
|
return null;
|
|
15134
15796
|
}
|
|
15135
15797
|
const css = await cssRes.text();
|
|
@@ -15207,6 +15869,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15207
15869
|
}
|
|
15208
15870
|
}
|
|
15209
15871
|
registeredFamilies.add(fontName);
|
|
15872
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15210
15873
|
return true;
|
|
15211
15874
|
} catch (err) {
|
|
15212
15875
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15221,16 +15884,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15221
15884
|
const resolved = resolveFontWeight(weight);
|
|
15222
15885
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15223
15886
|
if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
|
|
15224
|
-
if (isItalic) {
|
|
15225
|
-
const fsUpright = await fetchFontshareTTF(fontName, resolved, false);
|
|
15226
|
-
if (fsUpright) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsUpright);
|
|
15227
|
-
}
|
|
15228
15887
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15229
15888
|
if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
|
|
15230
|
-
if (isItalic) {
|
|
15231
|
-
const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);
|
|
15232
|
-
if (uprightB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);
|
|
15233
|
-
}
|
|
15234
15889
|
return false;
|
|
15235
15890
|
}
|
|
15236
15891
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15250,9 +15905,15 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15250
15905
|
};
|
|
15251
15906
|
const walkElements = (elements) => {
|
|
15252
15907
|
for (const el of elements) {
|
|
15908
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15909
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15910
|
+
};
|
|
15253
15911
|
if (el.fontFamily) {
|
|
15254
15912
|
const w = normalizeWeight(el.fontWeight);
|
|
15255
|
-
|
|
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);
|
|
15256
15917
|
}
|
|
15257
15918
|
if (el.styles && typeof el.styles === "object") {
|
|
15258
15919
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15260,9 +15921,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15260
15921
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15261
15922
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15262
15923
|
const s = lineStyles[charKey];
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
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 ?? "")));
|
|
15266
15928
|
}
|
|
15267
15929
|
}
|
|
15268
15930
|
}
|
|
@@ -15276,19 +15938,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15276
15938
|
if (page.children) walkElements(page.children);
|
|
15277
15939
|
if (page.elements) walkElements(page.elements);
|
|
15278
15940
|
}
|
|
15279
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15941
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15280
15942
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15281
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15943
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15282
15944
|
}
|
|
15283
15945
|
const embedded = /* @__PURE__ */ new Set();
|
|
15284
15946
|
const tasks = [];
|
|
15285
15947
|
for (const key of fontKeys) {
|
|
15286
15948
|
const sep = key.indexOf(SEP);
|
|
15949
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15287
15950
|
const fontName = key.slice(0, sep);
|
|
15288
|
-
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";
|
|
15289
15953
|
if (!isFontAvailable(fontName)) continue;
|
|
15290
15954
|
tasks.push(
|
|
15291
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15955
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15292
15956
|
if (ok) embedded.add(key);
|
|
15293
15957
|
})
|
|
15294
15958
|
);
|
|
@@ -15344,7 +16008,7 @@ function splitIntoRuns(text) {
|
|
|
15344
16008
|
return runs;
|
|
15345
16009
|
}
|
|
15346
16010
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15347
|
-
var _a, _b;
|
|
16011
|
+
var _a, _b, _c;
|
|
15348
16012
|
const parser = new DOMParser();
|
|
15349
16013
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15350
16014
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15376,6 +16040,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15376
16040
|
stylePairs.push(`font-style: normal`);
|
|
15377
16041
|
return stylePairs.join("; ");
|
|
15378
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
|
+
};
|
|
15379
16050
|
const getDepth = (el) => {
|
|
15380
16051
|
let depth = 0;
|
|
15381
16052
|
let current = el.parentElement;
|
|
@@ -15395,20 +16066,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15395
16066
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15396
16067
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15397
16068
|
const weight = resolveWeightNum(weightRaw);
|
|
15398
|
-
const
|
|
15399
|
-
const
|
|
15400
|
-
const
|
|
15401
|
-
|
|
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 });
|
|
15402
16075
|
}
|
|
15403
16076
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15404
16077
|
for (const el of textEls) {
|
|
15405
16078
|
if (!el.isConnected) continue;
|
|
15406
16079
|
const meta = sourceMeta.get(el);
|
|
15407
16080
|
if (!meta) continue;
|
|
15408
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16081
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15409
16082
|
el.setAttribute("data-source-font-family", clean);
|
|
15410
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16083
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15411
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
|
+
}
|
|
15412
16103
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15413
16104
|
const hasDevanagari = containsDevanagari(directText);
|
|
15414
16105
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15451,6 +16142,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15451
16142
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15452
16143
|
}
|
|
15453
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
|
+
}
|
|
15454
16167
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15455
16168
|
}
|
|
15456
16169
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15481,14 +16194,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15481
16194
|
if (ok) embedded.add(`${family}${w}`);
|
|
15482
16195
|
})
|
|
15483
16196
|
);
|
|
16197
|
+
tasks.push(
|
|
16198
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16199
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16200
|
+
})
|
|
16201
|
+
);
|
|
15484
16202
|
}
|
|
15485
16203
|
} else {
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
}
|
|
15491
|
-
|
|
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
|
+
}
|
|
15492
16220
|
}
|
|
15493
16221
|
}
|
|
15494
16222
|
await Promise.all(tasks);
|
|
@@ -15509,6 +16237,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15509
16237
|
getEmbeddedJsPDFFontName,
|
|
15510
16238
|
getFontPathForWeight,
|
|
15511
16239
|
isFontAvailable,
|
|
16240
|
+
resolveBestRegisteredVariant,
|
|
15512
16241
|
resolveFontWeight,
|
|
15513
16242
|
rewriteSvgFontsForJsPDF
|
|
15514
16243
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15548,9 +16277,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15548
16277
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15549
16278
|
var _a, _b;
|
|
15550
16279
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15551
|
-
const tspanInfo = tspans.slice(0,
|
|
16280
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15552
16281
|
x: s.getAttribute("x"),
|
|
15553
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),
|
|
15554
16288
|
text: (s.textContent || "").slice(0, 40)
|
|
15555
16289
|
}));
|
|
15556
16290
|
let containerWidth = null;
|
|
@@ -16585,13 +17319,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16585
17319
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16586
17320
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16587
17321
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17322
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16588
17323
|
for (let si = 0; si < tspans.length; si++) {
|
|
16589
17324
|
const tspan = tspans[si];
|
|
16590
17325
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16591
17326
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16592
17327
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16593
17328
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16594
|
-
|
|
17329
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17330
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16595
17331
|
const content = tspan.textContent || "";
|
|
16596
17332
|
if (!content.trim()) continue;
|
|
16597
17333
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16646,23 +17382,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16646
17382
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16647
17383
|
lineEndX = lineStartX + textWidth;
|
|
16648
17384
|
}
|
|
16649
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16650
17385
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
16651
|
-
const
|
|
16652
|
-
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
textEl.parentElement
|
|
16663
|
-
|
|
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);
|
|
16664
17403
|
stripTextDecoration(tspan);
|
|
16665
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17404
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16666
17405
|
}
|
|
16667
17406
|
}
|
|
16668
17407
|
if (tempContainer) {
|
|
@@ -16676,7 +17415,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16676
17415
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16677
17416
|
return svgStr;
|
|
16678
17417
|
}
|
|
16679
|
-
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)) {
|
|
16680
17419
|
return svgStr;
|
|
16681
17420
|
}
|
|
16682
17421
|
try {
|
|
@@ -16968,7 +17707,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16968
17707
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16969
17708
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16970
17709
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16971
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17710
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16972
17711
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16973
17712
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16974
17713
|
let pageSvg = page.svg;
|
|
@@ -16982,7 +17721,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16982
17721
|
}
|
|
16983
17722
|
if (shouldOutlineText) {
|
|
16984
17723
|
try {
|
|
16985
|
-
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-
|
|
17724
|
+
const { convertAllTextToPath } = await Promise.resolve().then(() => require("./svgTextToPath-DTKsddnS.cjs"));
|
|
16986
17725
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16987
17726
|
try {
|
|
16988
17727
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|