@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.js
CHANGED
|
@@ -4,6 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
5
5
|
import { forwardRef, useRef, useState, useMemo, useEffect, useCallback, useImperativeHandle, createElement } from "react";
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
|
+
import { toast } from "sonner";
|
|
7
8
|
import { create } from "zustand";
|
|
8
9
|
import * as fabric from "fabric";
|
|
9
10
|
import { createRoot } from "react-dom/client";
|
|
@@ -2487,9 +2488,17 @@ const useEditorStore = create((set, get) => ({
|
|
|
2487
2488
|
}));
|
|
2488
2489
|
let canvasRegistry = /* @__PURE__ */ new Map();
|
|
2489
2490
|
function registerFabricCanvas(pageId, canvas) {
|
|
2490
|
-
canvasRegistry.
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2491
|
+
const existing = canvasRegistry.get(pageId);
|
|
2492
|
+
const registryKey = existing && existing !== canvas ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
2493
|
+
canvasRegistry.set(registryKey, canvas);
|
|
2494
|
+
return registryKey;
|
|
2495
|
+
}
|
|
2496
|
+
function unregisterFabricCanvas(pageId, canvas) {
|
|
2497
|
+
if (canvas) {
|
|
2498
|
+
const existing = canvasRegistry.get(pageId);
|
|
2499
|
+
if (existing === canvas) canvasRegistry.delete(pageId);
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2493
2502
|
canvasRegistry.delete(pageId);
|
|
2494
2503
|
}
|
|
2495
2504
|
const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -2561,6 +2570,8 @@ const LOCAL_FONTS = /* @__PURE__ */ new Set([
|
|
|
2561
2570
|
]);
|
|
2562
2571
|
const loadedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2563
2572
|
const failedGoogleFonts = /* @__PURE__ */ new Set();
|
|
2573
|
+
const loadedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2574
|
+
const failedFontshareFonts = /* @__PURE__ */ new Set();
|
|
2564
2575
|
const loadingPromises$1 = /* @__PURE__ */ new Map();
|
|
2565
2576
|
async function loadGoogleFont(fontFamily, weights) {
|
|
2566
2577
|
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
@@ -2620,6 +2631,346 @@ async function _doLoadGoogleFont(fontFamily, weights) {
|
|
|
2620
2631
|
console.warn(`[GoogleFonts] Failed to load: ${fontFamily}`);
|
|
2621
2632
|
return false;
|
|
2622
2633
|
}
|
|
2634
|
+
async function loadFontshareFont(fontFamily, slug, weights) {
|
|
2635
|
+
if (loadedFontshareFonts.has(fontFamily)) return true;
|
|
2636
|
+
if (failedFontshareFonts.has(fontFamily)) return false;
|
|
2637
|
+
const existing = loadingPromises$1.get(`fontshare:${fontFamily}`);
|
|
2638
|
+
if (existing) return existing;
|
|
2639
|
+
const promise = (async () => {
|
|
2640
|
+
try {
|
|
2641
|
+
const weightStr = (weights || [300, 400, 500, 700]).join(",");
|
|
2642
|
+
const url = `https://api.fontshare.com/v2/css?f[]=${slug}@${weightStr}&display=swap`;
|
|
2643
|
+
if (document.querySelector(`link[href="${url}"]`)) return true;
|
|
2644
|
+
const link = document.createElement("link");
|
|
2645
|
+
link.rel = "stylesheet";
|
|
2646
|
+
link.href = url;
|
|
2647
|
+
return await new Promise((resolve) => {
|
|
2648
|
+
link.onload = async () => {
|
|
2649
|
+
try {
|
|
2650
|
+
await document.fonts.load(`16px "${fontFamily}"`);
|
|
2651
|
+
await document.fonts.load(`bold 16px "${fontFamily}"`);
|
|
2652
|
+
} catch {
|
|
2653
|
+
}
|
|
2654
|
+
resolve(true);
|
|
2655
|
+
};
|
|
2656
|
+
link.onerror = () => {
|
|
2657
|
+
console.warn(`[Fontshare] Failed to load: ${fontFamily}`);
|
|
2658
|
+
resolve(false);
|
|
2659
|
+
};
|
|
2660
|
+
document.head.appendChild(link);
|
|
2661
|
+
});
|
|
2662
|
+
} catch (e) {
|
|
2663
|
+
console.warn(`[Fontshare] Error loading ${fontFamily}:`, e);
|
|
2664
|
+
return false;
|
|
2665
|
+
}
|
|
2666
|
+
})();
|
|
2667
|
+
loadingPromises$1.set(`fontshare:${fontFamily}`, promise);
|
|
2668
|
+
try {
|
|
2669
|
+
const result = await promise;
|
|
2670
|
+
if (result) loadedFontshareFonts.add(fontFamily);
|
|
2671
|
+
else failedFontshareFonts.add(fontFamily);
|
|
2672
|
+
return result;
|
|
2673
|
+
} finally {
|
|
2674
|
+
loadingPromises$1.delete(`fontshare:${fontFamily}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
async function loadFont(fontFamily) {
|
|
2678
|
+
if (LOCAL_FONTS.has(fontFamily)) return true;
|
|
2679
|
+
const entry = findFontEntry(fontFamily);
|
|
2680
|
+
if ((entry == null ? void 0 : entry.source) === "fontshare" && entry.fontshareSlug) {
|
|
2681
|
+
return loadFontshareFont(fontFamily, entry.fontshareSlug);
|
|
2682
|
+
}
|
|
2683
|
+
return loadGoogleFont(fontFamily);
|
|
2684
|
+
}
|
|
2685
|
+
const EXTENDED_FONT_LIST = [
|
|
2686
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2687
|
+
// PREMIUM (Fontshare) — modern, professional aesthetic
|
|
2688
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2689
|
+
{ name: "Satoshi", category: "Premium", local: false, source: "fontshare", fontshareSlug: "satoshi", popular: true },
|
|
2690
|
+
{ name: "Cabinet Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "cabinet-grotesk", popular: true },
|
|
2691
|
+
{ name: "Clash Display", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-display", popular: true },
|
|
2692
|
+
{ name: "Clash Grotesk", category: "Premium", local: false, source: "fontshare", fontshareSlug: "clash-grotesk", popular: true },
|
|
2693
|
+
{ name: "General Sans", category: "Premium", local: false, source: "fontshare", fontshareSlug: "general-sans", popular: true },
|
|
2694
|
+
{ name: "Switzer", category: "Premium", local: false, source: "fontshare", fontshareSlug: "switzer" },
|
|
2695
|
+
{ name: "Supreme", category: "Premium", local: false, source: "fontshare", fontshareSlug: "supreme" },
|
|
2696
|
+
{ name: "Author", category: "Premium", local: false, source: "fontshare", fontshareSlug: "author" },
|
|
2697
|
+
{ name: "Boska", category: "Premium", local: false, source: "fontshare", fontshareSlug: "boska" },
|
|
2698
|
+
{ name: "Excon", category: "Premium", local: false, source: "fontshare", fontshareSlug: "excon" },
|
|
2699
|
+
{ name: "Khand", category: "Premium", local: false, source: "fontshare", fontshareSlug: "khand" },
|
|
2700
|
+
{ name: "Sentient", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sentient" },
|
|
2701
|
+
{ name: "Synonym", category: "Premium", local: false, source: "fontshare", fontshareSlug: "synonym" },
|
|
2702
|
+
{ name: "Erode", category: "Premium", local: false, source: "fontshare", fontshareSlug: "erode" },
|
|
2703
|
+
{ name: "Ranade", category: "Premium", local: false, source: "fontshare", fontshareSlug: "ranade" },
|
|
2704
|
+
{ name: "Tanker", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tanker" },
|
|
2705
|
+
{ name: "Zodiak", category: "Premium", local: false, source: "fontshare", fontshareSlug: "zodiak" },
|
|
2706
|
+
{ name: "Gambarino", category: "Premium", local: false, source: "fontshare", fontshareSlug: "gambarino" },
|
|
2707
|
+
{ name: "Melodrama", category: "Premium", local: false, source: "fontshare", fontshareSlug: "melodrama" },
|
|
2708
|
+
{ name: "Bespoke Serif", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-serif" },
|
|
2709
|
+
{ name: "Bespoke Stencil", category: "Premium", local: false, source: "fontshare", fontshareSlug: "bespoke-stencil" },
|
|
2710
|
+
{ name: "Panchang", category: "Premium", local: false, source: "fontshare", fontshareSlug: "panchang" },
|
|
2711
|
+
{ name: "Pally", category: "Premium", local: false, source: "fontshare", fontshareSlug: "pally" },
|
|
2712
|
+
{ name: "Tabular", category: "Premium", local: false, source: "fontshare", fontshareSlug: "tabular" },
|
|
2713
|
+
{ name: "Sharpie", category: "Premium", local: false, source: "fontshare", fontshareSlug: "sharpie" },
|
|
2714
|
+
{ name: "Stardom", category: "Premium", local: false, source: "fontshare", fontshareSlug: "stardom" },
|
|
2715
|
+
{ name: "Telma", category: "Premium", local: false, source: "fontshare", fontshareSlug: "telma" },
|
|
2716
|
+
{ name: "Nippo", category: "Premium", local: false, source: "fontshare", fontshareSlug: "nippo" },
|
|
2717
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2718
|
+
// SERIF — editorial, classical, elegant
|
|
2719
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2720
|
+
{ name: "Playfair Display", category: "Serif", local: true, popular: true },
|
|
2721
|
+
{ name: "Cormorant", category: "Serif", local: false, popular: true },
|
|
2722
|
+
{ name: "Cormorant Garamond", category: "Serif", local: false },
|
|
2723
|
+
{ name: "Cinzel", category: "Serif", local: false, popular: true },
|
|
2724
|
+
{ name: "Cinzel Decorative", category: "Serif", local: false },
|
|
2725
|
+
{ name: "Bodoni Moda", category: "Serif", local: false, popular: true },
|
|
2726
|
+
{ name: "DM Serif Display", category: "Serif", local: true },
|
|
2727
|
+
{ name: "DM Serif Text", category: "Serif", local: false },
|
|
2728
|
+
{ name: "Italiana", category: "Serif", local: false },
|
|
2729
|
+
{ name: "Marcellus", category: "Serif", local: false },
|
|
2730
|
+
{ name: "Marcellus SC", category: "Serif", local: false },
|
|
2731
|
+
{ name: "Yeseva One", category: "Serif", local: false },
|
|
2732
|
+
{ name: "Prata", category: "Serif", local: false },
|
|
2733
|
+
{ name: "Tenor Sans", category: "Serif", local: false },
|
|
2734
|
+
{ name: "Fraunces", category: "Serif", local: false, popular: true },
|
|
2735
|
+
{ name: "Newsreader", category: "Serif", local: false },
|
|
2736
|
+
{ name: "Source Serif Pro", category: "Serif", local: false },
|
|
2737
|
+
{ name: "Merriweather", category: "Serif", local: true },
|
|
2738
|
+
{ name: "Lora", category: "Serif", local: true },
|
|
2739
|
+
{ name: "EB Garamond", category: "Serif", local: true },
|
|
2740
|
+
{ name: "Libre Baskerville", category: "Serif", local: true },
|
|
2741
|
+
{ name: "Libre Caslon Text", category: "Serif", local: false },
|
|
2742
|
+
{ name: "Libre Caslon Display", category: "Serif", local: false },
|
|
2743
|
+
{ name: "Crimson Text", category: "Serif", local: true },
|
|
2744
|
+
{ name: "Crimson Pro", category: "Serif", local: false },
|
|
2745
|
+
{ name: "Noto Serif", category: "Serif", local: false },
|
|
2746
|
+
{ name: "Noto Serif Display", category: "Serif", local: false },
|
|
2747
|
+
{ name: "PT Serif", category: "Serif", local: false },
|
|
2748
|
+
{ name: "Bitter", category: "Serif", local: false },
|
|
2749
|
+
{ name: "Spectral", category: "Serif", local: false },
|
|
2750
|
+
{ name: "Cardo", category: "Serif", local: false },
|
|
2751
|
+
{ name: "Old Standard TT", category: "Serif", local: false },
|
|
2752
|
+
{ name: "Vollkorn", category: "Serif", local: false },
|
|
2753
|
+
{ name: "Cantata One", category: "Serif", local: false },
|
|
2754
|
+
{ name: "Domine", category: "Serif", local: false },
|
|
2755
|
+
{ name: "Gentium Plus", category: "Serif", local: false },
|
|
2756
|
+
{ name: "Tinos", category: "Serif", local: false },
|
|
2757
|
+
{ name: "Trirong", category: "Serif", local: false },
|
|
2758
|
+
{ name: "Sorts Mill Goudy", category: "Serif", local: false },
|
|
2759
|
+
{ name: "IM Fell English", category: "Serif", local: false },
|
|
2760
|
+
{ name: "IM Fell DW Pica", category: "Serif", local: false },
|
|
2761
|
+
{ name: "Petrona", category: "Serif", local: false },
|
|
2762
|
+
{ name: "Rozha One", category: "Serif", local: false },
|
|
2763
|
+
{ name: "Tiro Devanagari Hindi", category: "Serif", local: false },
|
|
2764
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2765
|
+
// SANS-SERIF — clean, modern, workhorse
|
|
2766
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2767
|
+
{ name: "Inter", category: "Sans-Serif", local: true, popular: true },
|
|
2768
|
+
{ name: "Montserrat", category: "Sans-Serif", local: true, popular: true },
|
|
2769
|
+
{ name: "Poppins", category: "Sans-Serif", local: true, popular: true },
|
|
2770
|
+
{ name: "Open Sans", category: "Sans-Serif", local: true },
|
|
2771
|
+
{ name: "Roboto", category: "Sans-Serif", local: true },
|
|
2772
|
+
{ name: "Lato", category: "Sans-Serif", local: true },
|
|
2773
|
+
{ name: "Raleway", category: "Sans-Serif", local: true },
|
|
2774
|
+
{ name: "Nunito", category: "Sans-Serif", local: true },
|
|
2775
|
+
{ name: "Source Sans Pro", category: "Sans-Serif", local: true },
|
|
2776
|
+
{ name: "Work Sans", category: "Sans-Serif", local: true },
|
|
2777
|
+
{ name: "DM Sans", category: "Sans-Serif", local: true, popular: true },
|
|
2778
|
+
{ name: "Outfit", category: "Sans-Serif", local: true, popular: true },
|
|
2779
|
+
{ name: "Figtree", category: "Sans-Serif", local: true },
|
|
2780
|
+
{ name: "Manrope", category: "Sans-Serif", local: true, popular: true },
|
|
2781
|
+
{ name: "Space Grotesk", category: "Sans-Serif", local: true, popular: true },
|
|
2782
|
+
{ name: "Mulish", category: "Sans-Serif", local: true },
|
|
2783
|
+
{ name: "Quicksand", category: "Sans-Serif", local: true },
|
|
2784
|
+
{ name: "Rubik", category: "Sans-Serif", local: true },
|
|
2785
|
+
{ name: "Karla", category: "Sans-Serif", local: true },
|
|
2786
|
+
{ name: "Plus Jakarta Sans", category: "Sans-Serif", local: true },
|
|
2787
|
+
{ name: "Libre Franklin", category: "Sans-Serif", local: true },
|
|
2788
|
+
{ name: "Sora", category: "Sans-Serif", local: true },
|
|
2789
|
+
{ name: "Urbanist", category: "Sans-Serif", local: true },
|
|
2790
|
+
{ name: "Lexend", category: "Sans-Serif", local: true },
|
|
2791
|
+
{ name: "Albert Sans", category: "Sans-Serif", local: true },
|
|
2792
|
+
{ name: "Noto Sans", category: "Sans-Serif", local: true },
|
|
2793
|
+
{ name: "Cabin", category: "Sans-Serif", local: true },
|
|
2794
|
+
{ name: "Barlow", category: "Sans-Serif", local: true },
|
|
2795
|
+
{ name: "Barlow Condensed", category: "Sans-Serif", local: false },
|
|
2796
|
+
{ name: "Josefin Sans", category: "Sans-Serif", local: true },
|
|
2797
|
+
{ name: "Archivo", category: "Sans-Serif", local: true },
|
|
2798
|
+
{ name: "Archivo Narrow", category: "Sans-Serif", local: false },
|
|
2799
|
+
{ name: "Overpass", category: "Sans-Serif", local: true },
|
|
2800
|
+
{ name: "Exo 2", category: "Sans-Serif", local: true },
|
|
2801
|
+
{ name: "Onest", category: "Sans-Serif", local: false, popular: true },
|
|
2802
|
+
{ name: "Be Vietnam Pro", category: "Sans-Serif", local: false },
|
|
2803
|
+
{ name: "Public Sans", category: "Sans-Serif", local: false },
|
|
2804
|
+
{ name: "Red Hat Display", category: "Sans-Serif", local: false },
|
|
2805
|
+
{ name: "Red Hat Text", category: "Sans-Serif", local: false },
|
|
2806
|
+
{ name: "Sen", category: "Sans-Serif", local: false },
|
|
2807
|
+
{ name: "Hanken Grotesk", category: "Sans-Serif", local: false },
|
|
2808
|
+
{ name: "Schibsted Grotesk", category: "Sans-Serif", local: false },
|
|
2809
|
+
{ name: "Reddit Sans", category: "Sans-Serif", local: false },
|
|
2810
|
+
{ name: "Instrument Sans", category: "Sans-Serif", local: false },
|
|
2811
|
+
{ name: "Geist", category: "Sans-Serif", local: false, popular: true },
|
|
2812
|
+
{ name: "Nunito Sans", category: "Sans-Serif", local: false },
|
|
2813
|
+
{ name: "PT Sans", category: "Sans-Serif", local: false },
|
|
2814
|
+
{ name: "PT Sans Narrow", category: "Sans-Serif", local: false },
|
|
2815
|
+
{ name: "Mukta", category: "Sans-Serif", local: false },
|
|
2816
|
+
{ name: "Anek Devanagari", category: "Sans-Serif", local: false },
|
|
2817
|
+
{ name: "Hind", category: "Sans-Serif", local: true },
|
|
2818
|
+
{ name: "Hind Vadodara", category: "Sans-Serif", local: false },
|
|
2819
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2820
|
+
// DISPLAY — bold, attention-grabbing headlines
|
|
2821
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2822
|
+
{ name: "Bebas Neue", category: "Display", local: true, popular: true },
|
|
2823
|
+
{ name: "Anton", category: "Display", local: true, popular: true },
|
|
2824
|
+
{ name: "Oswald", category: "Display", local: true },
|
|
2825
|
+
{ name: "Abril Fatface", category: "Display", local: true, popular: true },
|
|
2826
|
+
{ name: "League Spartan", category: "Display", local: true },
|
|
2827
|
+
{ name: "Teko", category: "Display", local: true },
|
|
2828
|
+
{ name: "Righteous", category: "Display", local: false },
|
|
2829
|
+
{ name: "Alfa Slab One", category: "Display", local: false, popular: true },
|
|
2830
|
+
{ name: "Archivo Black", category: "Display", local: false },
|
|
2831
|
+
{ name: "Fredoka", category: "Display", local: false },
|
|
2832
|
+
{ name: "Passion One", category: "Display", local: false },
|
|
2833
|
+
{ name: "Bowlby One", category: "Display", local: false },
|
|
2834
|
+
{ name: "Bowlby One SC", category: "Display", local: false },
|
|
2835
|
+
{ name: "Secular One", category: "Display", local: false },
|
|
2836
|
+
{ name: "Lilita One", category: "Display", local: false },
|
|
2837
|
+
{ name: "Titan One", category: "Display", local: false },
|
|
2838
|
+
{ name: "Russo One", category: "Display", local: false },
|
|
2839
|
+
{ name: "Staatliches", category: "Display", local: false, popular: true },
|
|
2840
|
+
{ name: "Dela Gothic One", category: "Display", local: false },
|
|
2841
|
+
{ name: "Ultra", category: "Display", local: false },
|
|
2842
|
+
{ name: "Sigmar One", category: "Display", local: false },
|
|
2843
|
+
{ name: "Sigmar", category: "Display", local: false },
|
|
2844
|
+
{ name: "Modak", category: "Display", local: false },
|
|
2845
|
+
{ name: "Bagel Fat One", category: "Display", local: false },
|
|
2846
|
+
{ name: "Climate Crisis", category: "Display", local: false },
|
|
2847
|
+
{ name: "Yatra One", category: "Display", local: false },
|
|
2848
|
+
{ name: "Bungee", category: "Display", local: false },
|
|
2849
|
+
{ name: "Bungee Shade", category: "Display", local: false },
|
|
2850
|
+
{ name: "Bungee Outline", category: "Display", local: false },
|
|
2851
|
+
{ name: "Bungee Inline", category: "Display", local: false },
|
|
2852
|
+
{ name: "Monoton", category: "Display", local: false, popular: true },
|
|
2853
|
+
{ name: "Black Ops One", category: "Display", local: false },
|
|
2854
|
+
{ name: "Faster One", category: "Display", local: false },
|
|
2855
|
+
{ name: "Rubik Glitch", category: "Display", local: false },
|
|
2856
|
+
{ name: "Rubik Mono One", category: "Display", local: false },
|
|
2857
|
+
{ name: "Rubik Wet Paint", category: "Display", local: false },
|
|
2858
|
+
{ name: "Rubik Bubbles", category: "Display", local: false },
|
|
2859
|
+
{ name: "Rubik Beastly", category: "Display", local: false },
|
|
2860
|
+
{ name: "Rubik Burned", category: "Display", local: false },
|
|
2861
|
+
{ name: "Rubik Distressed", category: "Display", local: false },
|
|
2862
|
+
{ name: "Rubik Iso", category: "Display", local: false },
|
|
2863
|
+
{ name: "Rubik Marker Hatch", category: "Display", local: false },
|
|
2864
|
+
{ name: "Rubik Maze", category: "Display", local: false },
|
|
2865
|
+
{ name: "Rubik Pixels", category: "Display", local: false },
|
|
2866
|
+
{ name: "Rubik Puddles", category: "Display", local: false },
|
|
2867
|
+
{ name: "Rubik Spray Paint", category: "Display", local: false },
|
|
2868
|
+
{ name: "Rubik Vinyl", category: "Display", local: false },
|
|
2869
|
+
{ name: "Saira Stencil One", category: "Display", local: false },
|
|
2870
|
+
{ name: "Audiowide", category: "Display", local: false },
|
|
2871
|
+
{ name: "Orbitron", category: "Display", local: false },
|
|
2872
|
+
{ name: "Plaster", category: "Display", local: false },
|
|
2873
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2874
|
+
// HANDWRITING / SCRIPT — fluid, personal, calligraphic
|
|
2875
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2876
|
+
{ name: "Dancing Script", category: "Handwriting", local: true, popular: true },
|
|
2877
|
+
{ name: "Pacifico", category: "Handwriting", local: true, popular: true },
|
|
2878
|
+
{ name: "Great Vibes", category: "Handwriting", local: true, popular: true },
|
|
2879
|
+
{ name: "Sacramento", category: "Handwriting", local: true },
|
|
2880
|
+
{ name: "Alex Brush", category: "Handwriting", local: true },
|
|
2881
|
+
{ name: "Allura", category: "Handwriting", local: true },
|
|
2882
|
+
{ name: "Caveat", category: "Handwriting", local: true },
|
|
2883
|
+
{ name: "Caveat Brush", category: "Handwriting", local: false },
|
|
2884
|
+
{ name: "Lobster", category: "Handwriting", local: true },
|
|
2885
|
+
{ name: "Lobster Two", category: "Handwriting", local: false },
|
|
2886
|
+
{ name: "Comfortaa", category: "Handwriting", local: true },
|
|
2887
|
+
{ name: "Tangerine", category: "Handwriting", local: false, popular: true },
|
|
2888
|
+
{ name: "Yellowtail", category: "Handwriting", local: false, popular: true },
|
|
2889
|
+
{ name: "Kaushan Script", category: "Handwriting", local: false, popular: true },
|
|
2890
|
+
{ name: "Parisienne", category: "Handwriting", local: false },
|
|
2891
|
+
{ name: "Petit Formal Script", category: "Handwriting", local: false },
|
|
2892
|
+
{ name: "Pinyon Script", category: "Handwriting", local: false },
|
|
2893
|
+
{ name: "Mrs Saint Delafield", category: "Handwriting", local: false },
|
|
2894
|
+
{ name: "Marck Script", category: "Handwriting", local: false },
|
|
2895
|
+
{ name: "Niconne", category: "Handwriting", local: false },
|
|
2896
|
+
{ name: "Homemade Apple", category: "Handwriting", local: false },
|
|
2897
|
+
{ name: "Permanent Marker", category: "Handwriting", local: false },
|
|
2898
|
+
{ name: "Reenie Beanie", category: "Handwriting", local: false },
|
|
2899
|
+
{ name: "Satisfy", category: "Handwriting", local: false },
|
|
2900
|
+
{ name: "Kalam", category: "Handwriting", local: false },
|
|
2901
|
+
{ name: "Indie Flower", category: "Handwriting", local: false },
|
|
2902
|
+
{ name: "Courgette", category: "Handwriting", local: false },
|
|
2903
|
+
{ name: "Cookie", category: "Handwriting", local: false },
|
|
2904
|
+
{ name: "Shadows Into Light", category: "Handwriting", local: false },
|
|
2905
|
+
{ name: "Patrick Hand", category: "Handwriting", local: false },
|
|
2906
|
+
{ name: "Amatic SC", category: "Handwriting", local: false },
|
|
2907
|
+
{ name: "Architects Daughter", category: "Handwriting", local: false },
|
|
2908
|
+
{ name: "Gloria Hallelujah", category: "Handwriting", local: false },
|
|
2909
|
+
{ name: "La Belle Aurore", category: "Handwriting", local: false },
|
|
2910
|
+
{ name: "Mr Dafoe", category: "Handwriting", local: false },
|
|
2911
|
+
{ name: "Italianno", category: "Handwriting", local: false },
|
|
2912
|
+
{ name: "Rouge Script", category: "Handwriting", local: false },
|
|
2913
|
+
{ name: "Grand Hotel", category: "Handwriting", local: false },
|
|
2914
|
+
{ name: "Bilbo Swash Caps", category: "Handwriting", local: false },
|
|
2915
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2916
|
+
// DECORATIVE / FUN — quirky, themed, special-occasion
|
|
2917
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2918
|
+
{ name: "Frijole", category: "Decorative", local: false },
|
|
2919
|
+
{ name: "Creepster", category: "Decorative", local: false },
|
|
2920
|
+
{ name: "Nosifer", category: "Decorative", local: false },
|
|
2921
|
+
{ name: "Ewert", category: "Decorative", local: false },
|
|
2922
|
+
{ name: "Lakki Reddy", category: "Decorative", local: false },
|
|
2923
|
+
{ name: "Henny Penny", category: "Decorative", local: false },
|
|
2924
|
+
{ name: "Special Elite", category: "Decorative", local: false, popular: true },
|
|
2925
|
+
{ name: "Vast Shadow", category: "Decorative", local: false },
|
|
2926
|
+
{ name: "Almendra Display", category: "Decorative", local: false },
|
|
2927
|
+
{ name: "Eater", category: "Decorative", local: false },
|
|
2928
|
+
{ name: "Butcherman", category: "Decorative", local: false },
|
|
2929
|
+
{ name: "Pirata One", category: "Decorative", local: false },
|
|
2930
|
+
{ name: "Metamorphous", category: "Decorative", local: false },
|
|
2931
|
+
{ name: "MedievalSharp", category: "Decorative", local: false },
|
|
2932
|
+
{ name: "Fascinate", category: "Decorative", local: false },
|
|
2933
|
+
{ name: "Fascinate Inline", category: "Decorative", local: false },
|
|
2934
|
+
{ name: "Sancreek", category: "Decorative", local: false },
|
|
2935
|
+
{ name: "Smokum", category: "Decorative", local: false },
|
|
2936
|
+
{ name: "Vampiro One", category: "Decorative", local: false },
|
|
2937
|
+
{ name: "Mountains of Christmas", category: "Decorative", local: false },
|
|
2938
|
+
{ name: "Caesar Dressing", category: "Decorative", local: false },
|
|
2939
|
+
{ name: "Megrim", category: "Decorative", local: false },
|
|
2940
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2941
|
+
// BLACKLETTER — gothic, medieval, formal
|
|
2942
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2943
|
+
{ name: "UnifrakturCook", category: "Blackletter", local: false },
|
|
2944
|
+
{ name: "UnifrakturMaguntia", category: "Blackletter", local: false },
|
|
2945
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2946
|
+
// MONOSPACE — code, technical
|
|
2947
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2948
|
+
{ name: "Roboto Mono", category: "Monospace", local: true },
|
|
2949
|
+
{ name: "Fira Code", category: "Monospace", local: true },
|
|
2950
|
+
{ name: "JetBrains Mono", category: "Monospace", local: true },
|
|
2951
|
+
{ name: "Source Code Pro", category: "Monospace", local: true },
|
|
2952
|
+
{ name: "IBM Plex Mono", category: "Monospace", local: true },
|
|
2953
|
+
{ name: "Space Mono", category: "Monospace", local: true },
|
|
2954
|
+
{ name: "Geist Mono", category: "Monospace", local: false },
|
|
2955
|
+
{ name: "DM Mono", category: "Monospace", local: false },
|
|
2956
|
+
{ name: "Inconsolata", category: "Monospace", local: false },
|
|
2957
|
+
{ name: "Cousine", category: "Monospace", local: false },
|
|
2958
|
+
{ name: "Anonymous Pro", category: "Monospace", local: false },
|
|
2959
|
+
{ name: "Cutive Mono", category: "Monospace", local: false },
|
|
2960
|
+
{ name: "Major Mono Display", category: "Monospace", local: false },
|
|
2961
|
+
{ name: "VT323", category: "Monospace", local: false },
|
|
2962
|
+
{ name: "Share Tech Mono", category: "Monospace", local: false }
|
|
2963
|
+
];
|
|
2964
|
+
const _fontLookupCache = /* @__PURE__ */ new Map();
|
|
2965
|
+
function findFontEntry(name) {
|
|
2966
|
+
const key = name.toLowerCase().replace(/[\s\-_]/g, "");
|
|
2967
|
+
if (_fontLookupCache.has(key)) return _fontLookupCache.get(key);
|
|
2968
|
+
const entry = EXTENDED_FONT_LIST.find(
|
|
2969
|
+
(f) => f.name.toLowerCase().replace(/[\s\-_]/g, "") === key
|
|
2970
|
+
);
|
|
2971
|
+
if (entry) _fontLookupCache.set(key, entry);
|
|
2972
|
+
return entry;
|
|
2973
|
+
}
|
|
2623
2974
|
const getObjectId = (obj) => obj.__docuforgeId;
|
|
2624
2975
|
const setObjectData = (obj, id) => {
|
|
2625
2976
|
obj.__docuforgeId = id;
|
|
@@ -2841,9 +3192,27 @@ const ensureFontLoaded = async (fontFamily) => {
|
|
|
2841
3192
|
return;
|
|
2842
3193
|
}
|
|
2843
3194
|
try {
|
|
2844
|
-
await
|
|
3195
|
+
await loadFont(fontFamily);
|
|
2845
3196
|
} catch (e) {
|
|
2846
|
-
console.warn(`Failed to load
|
|
3197
|
+
console.warn(`Failed to load font: ${fontFamily}`, e);
|
|
3198
|
+
try {
|
|
3199
|
+
await loadGoogleFont(fontFamily);
|
|
3200
|
+
} catch {
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
try {
|
|
3204
|
+
if (document.fonts) {
|
|
3205
|
+
await Promise.race([
|
|
3206
|
+
Promise.all([
|
|
3207
|
+
document.fonts.load(`16px "${fontFamily}"`),
|
|
3208
|
+
document.fonts.load(`bold 16px "${fontFamily}"`),
|
|
3209
|
+
document.fonts.load(`italic 16px "${fontFamily}"`),
|
|
3210
|
+
document.fonts.load(`bold italic 16px "${fontFamily}"`)
|
|
3211
|
+
]),
|
|
3212
|
+
new Promise((r) => setTimeout(r, 2500))
|
|
3213
|
+
]);
|
|
3214
|
+
}
|
|
3215
|
+
} catch {
|
|
2847
3216
|
}
|
|
2848
3217
|
};
|
|
2849
3218
|
const setupFontLoadingListener = (canvas, afterRerender) => {
|
|
@@ -5344,6 +5713,172 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
|
|
|
5344
5713
|
];
|
|
5345
5714
|
return parts.join(" ");
|
|
5346
5715
|
}
|
|
5716
|
+
let activeThemeColors = {};
|
|
5717
|
+
function setMarkdownThemeColors(c) {
|
|
5718
|
+
activeThemeColors = { ...c };
|
|
5719
|
+
}
|
|
5720
|
+
function resolveColorToken(token, theme) {
|
|
5721
|
+
const raw = token.trim();
|
|
5722
|
+
const t = raw.toLowerCase();
|
|
5723
|
+
if (t === "primary") return theme.primary;
|
|
5724
|
+
if (t === "secondary") return theme.secondary;
|
|
5725
|
+
if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
|
|
5726
|
+
if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
|
|
5727
|
+
if (/^[a-z]+$/i.test(raw)) return raw;
|
|
5728
|
+
return void 0;
|
|
5729
|
+
}
|
|
5730
|
+
function mergeStyle(a, b) {
|
|
5731
|
+
return { ...a, ...b };
|
|
5732
|
+
}
|
|
5733
|
+
function tokenize(input, theme) {
|
|
5734
|
+
const runs = [];
|
|
5735
|
+
const stack = [];
|
|
5736
|
+
let buf = "";
|
|
5737
|
+
const activeStyle = () => {
|
|
5738
|
+
let s = {};
|
|
5739
|
+
for (const e of stack) s = mergeStyle(s, e.style);
|
|
5740
|
+
return s;
|
|
5741
|
+
};
|
|
5742
|
+
const flush = () => {
|
|
5743
|
+
if (buf.length === 0) return;
|
|
5744
|
+
runs.push({ text: buf, style: activeStyle() });
|
|
5745
|
+
buf = "";
|
|
5746
|
+
};
|
|
5747
|
+
let i = 0;
|
|
5748
|
+
const n = input.length;
|
|
5749
|
+
const peek = (s, at = i) => input.startsWith(s, at);
|
|
5750
|
+
const findUnescaped = (needle, from) => {
|
|
5751
|
+
let p = from;
|
|
5752
|
+
while (p < n) {
|
|
5753
|
+
if (input[p] === "\\" && p + 1 < n) {
|
|
5754
|
+
p += 2;
|
|
5755
|
+
continue;
|
|
5756
|
+
}
|
|
5757
|
+
if (input.startsWith(needle, p)) return p;
|
|
5758
|
+
p++;
|
|
5759
|
+
}
|
|
5760
|
+
return -1;
|
|
5761
|
+
};
|
|
5762
|
+
const tryOpenBracket = () => {
|
|
5763
|
+
if (input[i] !== "[") return -1;
|
|
5764
|
+
const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
|
|
5765
|
+
if (!m) return -1;
|
|
5766
|
+
const kind = m[1];
|
|
5767
|
+
const tokenRaw = m[2];
|
|
5768
|
+
const closer = kind === "c" ? "[/c]" : "[/bg]";
|
|
5769
|
+
if (findUnescaped(closer, i + m[0].length) === -1) return -1;
|
|
5770
|
+
const color = resolveColorToken(tokenRaw, theme);
|
|
5771
|
+
const style = {};
|
|
5772
|
+
if (color) {
|
|
5773
|
+
if (kind === "c") style.fill = color;
|
|
5774
|
+
else style.textBackgroundColor = color;
|
|
5775
|
+
}
|
|
5776
|
+
flush();
|
|
5777
|
+
stack.push({ kind, style, closer });
|
|
5778
|
+
return i + m[0].length;
|
|
5779
|
+
};
|
|
5780
|
+
const tryCloseBracket = () => {
|
|
5781
|
+
for (let s = stack.length - 1; s >= 0; s--) {
|
|
5782
|
+
const top = stack[s];
|
|
5783
|
+
if (top.kind !== "c" && top.kind !== "bg") continue;
|
|
5784
|
+
if (peek(top.closer)) {
|
|
5785
|
+
if (s !== stack.length - 1) stack.length = s + 1;
|
|
5786
|
+
flush();
|
|
5787
|
+
stack.pop();
|
|
5788
|
+
return i + top.closer.length;
|
|
5789
|
+
}
|
|
5790
|
+
break;
|
|
5791
|
+
}
|
|
5792
|
+
return -1;
|
|
5793
|
+
};
|
|
5794
|
+
const toggle = (delim, kind, style) => {
|
|
5795
|
+
if (!peek(delim)) return null;
|
|
5796
|
+
const topIdx = stack.findIndex((e) => e.kind === kind);
|
|
5797
|
+
if (topIdx >= 0) {
|
|
5798
|
+
if (topIdx !== stack.length - 1) return null;
|
|
5799
|
+
flush();
|
|
5800
|
+
stack.length = topIdx;
|
|
5801
|
+
return i + delim.length;
|
|
5802
|
+
}
|
|
5803
|
+
if (findUnescaped(delim, i + delim.length) === -1) return null;
|
|
5804
|
+
flush();
|
|
5805
|
+
stack.push({ kind, style, closer: delim });
|
|
5806
|
+
return i + delim.length;
|
|
5807
|
+
};
|
|
5808
|
+
while (i < n) {
|
|
5809
|
+
const ch = input[i];
|
|
5810
|
+
if (ch === "\\" && i + 1 < n) {
|
|
5811
|
+
buf += input[i + 1];
|
|
5812
|
+
i += 2;
|
|
5813
|
+
continue;
|
|
5814
|
+
}
|
|
5815
|
+
if (ch === "[") {
|
|
5816
|
+
const closed = tryCloseBracket();
|
|
5817
|
+
if (closed > 0) {
|
|
5818
|
+
i = closed;
|
|
5819
|
+
continue;
|
|
5820
|
+
}
|
|
5821
|
+
const opened = tryOpenBracket();
|
|
5822
|
+
if (opened > 0) {
|
|
5823
|
+
i = opened;
|
|
5824
|
+
continue;
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
let next;
|
|
5828
|
+
if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
|
|
5829
|
+
i = next;
|
|
5830
|
+
continue;
|
|
5831
|
+
}
|
|
5832
|
+
if ((next = toggle("__", "under", { underline: true })) !== null) {
|
|
5833
|
+
i = next;
|
|
5834
|
+
continue;
|
|
5835
|
+
}
|
|
5836
|
+
if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
|
|
5837
|
+
i = next;
|
|
5838
|
+
continue;
|
|
5839
|
+
}
|
|
5840
|
+
if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
|
|
5841
|
+
i = next;
|
|
5842
|
+
continue;
|
|
5843
|
+
}
|
|
5844
|
+
if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
|
|
5845
|
+
i = next;
|
|
5846
|
+
continue;
|
|
5847
|
+
}
|
|
5848
|
+
buf += ch;
|
|
5849
|
+
i++;
|
|
5850
|
+
}
|
|
5851
|
+
flush();
|
|
5852
|
+
return runs;
|
|
5853
|
+
}
|
|
5854
|
+
function parseTextMarkdown(input, themeColors) {
|
|
5855
|
+
const theme = activeThemeColors;
|
|
5856
|
+
const runs = tokenize(input ?? "", theme);
|
|
5857
|
+
let plain = "";
|
|
5858
|
+
const styles = {};
|
|
5859
|
+
let lineIdx = 0;
|
|
5860
|
+
let charIdx = 0;
|
|
5861
|
+
let hasFormatting = false;
|
|
5862
|
+
for (const run of runs) {
|
|
5863
|
+
const styleHasContent = Object.keys(run.style).length > 0;
|
|
5864
|
+
if (styleHasContent) hasFormatting = true;
|
|
5865
|
+
for (const ch of run.text) {
|
|
5866
|
+
if (ch === "\n") {
|
|
5867
|
+
plain += "\n";
|
|
5868
|
+
lineIdx++;
|
|
5869
|
+
charIdx = 0;
|
|
5870
|
+
continue;
|
|
5871
|
+
}
|
|
5872
|
+
plain += ch;
|
|
5873
|
+
if (styleHasContent) {
|
|
5874
|
+
if (!styles[lineIdx]) styles[lineIdx] = {};
|
|
5875
|
+
styles[lineIdx][charIdx] = { ...run.style };
|
|
5876
|
+
}
|
|
5877
|
+
charIdx++;
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
return { plainText: plain, styles, hasFormatting };
|
|
5881
|
+
}
|
|
5347
5882
|
const roundDiag = (value) => {
|
|
5348
5883
|
if (typeof value !== "number") return value;
|
|
5349
5884
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -5482,6 +6017,13 @@ function createText(element) {
|
|
|
5482
6017
|
let fontSize = element.fontSize || 16;
|
|
5483
6018
|
const minFontSize = element.minFontSize || 8;
|
|
5484
6019
|
const maxLines = element.maxLines || 3;
|
|
6020
|
+
const formattingEnabled = element.formattingEnabled === true;
|
|
6021
|
+
let parsedStyles = {};
|
|
6022
|
+
if (formattingEnabled) {
|
|
6023
|
+
const parsed = parseTextMarkdown(text);
|
|
6024
|
+
text = parsed.plainText || " ";
|
|
6025
|
+
parsedStyles = parsed.styles;
|
|
6026
|
+
}
|
|
5485
6027
|
const baseWidth = element.width && element.width > 0 ? element.width : 200;
|
|
5486
6028
|
const baseHeight = element.height;
|
|
5487
6029
|
const fixedWidth = Math.max(baseWidth, 1);
|
|
@@ -5502,7 +6044,8 @@ function createText(element) {
|
|
|
5502
6044
|
fontStyle: element.fontStyle || "normal",
|
|
5503
6045
|
lineHeight: element.lineHeight || 1.2,
|
|
5504
6046
|
charSpacing: element.charSpacing || 0,
|
|
5505
|
-
splitByGrapheme: false
|
|
6047
|
+
splitByGrapheme: false,
|
|
6048
|
+
...formattingEnabled ? { styles: parsedStyles } : {}
|
|
5506
6049
|
});
|
|
5507
6050
|
testTextbox.initDimensions();
|
|
5508
6051
|
const textHeight = testTextbox.height || 0;
|
|
@@ -5614,8 +6157,16 @@ function createText(element) {
|
|
|
5614
6157
|
objectCaching: false,
|
|
5615
6158
|
noScaleCache: true,
|
|
5616
6159
|
splitByGrapheme,
|
|
5617
|
-
|
|
6160
|
+
// When inline markdown formatting is enabled, the displayed text is the
|
|
6161
|
+
// PARSED plain text (markdown source lives separately on the element).
|
|
6162
|
+
// Allowing canvas inline editing would let the user edit that plain text
|
|
6163
|
+
// and on commit we'd save it back as the new markdown source — wiping all
|
|
6164
|
+
// formatting tokens (**, __, [c=...], etc). Disable inline edit and steer
|
|
6165
|
+
// users to the right-panel text field which exposes the raw markdown.
|
|
6166
|
+
editable: !formattingEnabled,
|
|
6167
|
+
...formattingEnabled ? { styles: parsedStyles } : element.styles ? { styles: element.styles } : {}
|
|
5618
6168
|
});
|
|
6169
|
+
textbox.__formattingEnabled = formattingEnabled;
|
|
5619
6170
|
textbox.initDimensions();
|
|
5620
6171
|
textbox.set({
|
|
5621
6172
|
width: targetWidth,
|
|
@@ -5636,6 +6187,15 @@ function createText(element) {
|
|
|
5636
6187
|
textbox.setCoords();
|
|
5637
6188
|
}
|
|
5638
6189
|
textbox.dirty = true;
|
|
6190
|
+
if (formattingEnabled) {
|
|
6191
|
+
try {
|
|
6192
|
+
textbox._forceClearCache = true;
|
|
6193
|
+
if (typeof textbox._clearCache === "function") {
|
|
6194
|
+
textbox._clearCache();
|
|
6195
|
+
}
|
|
6196
|
+
} catch {
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
5639
6199
|
if (overflowPolicy === "auto-shrink" && typeof window !== "undefined" && window.__pixldocsDebugAutoShrink === true) {
|
|
5640
6200
|
console.log("[auto-shrink][final-textbox] " + stringifyDiag({
|
|
5641
6201
|
id: element.id,
|
|
@@ -6372,6 +6932,10 @@ const PageCanvas = forwardRef(
|
|
|
6372
6932
|
skipFontReadyWait = false,
|
|
6373
6933
|
onReady
|
|
6374
6934
|
}, ref) => {
|
|
6935
|
+
setMarkdownThemeColors({
|
|
6936
|
+
primary: projectSettings.primaryColor || "#7c3aed",
|
|
6937
|
+
secondary: projectSettings.secondaryColor || "#ffe066"
|
|
6938
|
+
});
|
|
6375
6939
|
const isEditorMode = mode === "editor";
|
|
6376
6940
|
const isPreviewMode = mode === "preview";
|
|
6377
6941
|
const allowEditing = isEditorMode;
|
|
@@ -6467,8 +7031,8 @@ const PageCanvas = forwardRef(
|
|
|
6467
7031
|
}, [selectedIds]);
|
|
6468
7032
|
useEffect(() => {
|
|
6469
7033
|
isActiveRef.current = isActive;
|
|
6470
|
-
if (isActive && fabricRef.current) ;
|
|
6471
|
-
}, [isActive, pageId]);
|
|
7034
|
+
if (isEditorMode && isActive && fabricRef.current) ;
|
|
7035
|
+
}, [isActive, isEditorMode, pageId]);
|
|
6472
7036
|
const getObjId = useCallback((obj) => {
|
|
6473
7037
|
return obj.__docuforgeId;
|
|
6474
7038
|
}, []);
|
|
@@ -6606,11 +7170,18 @@ const PageCanvas = forwardRef(
|
|
|
6606
7170
|
const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
|
|
6607
7171
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
6608
7172
|
const splitByGrapheme = overflowPolicy === "auto-shrink" ? false : element.splitByGrapheme ?? element.wordWrap === "break-word";
|
|
7173
|
+
let reflowText = element.text || "Text";
|
|
7174
|
+
let reflowParsedStyles = null;
|
|
7175
|
+
if (element.formattingEnabled === true) {
|
|
7176
|
+
const parsed = parseTextMarkdown(reflowText);
|
|
7177
|
+
reflowText = parsed.plainText || " ";
|
|
7178
|
+
reflowParsedStyles = parsed.styles;
|
|
7179
|
+
}
|
|
6609
7180
|
obj.set({
|
|
6610
7181
|
width: targetWidth,
|
|
6611
7182
|
minWidth: 1,
|
|
6612
7183
|
dynamicMinWidth: 0,
|
|
6613
|
-
text:
|
|
7184
|
+
text: reflowText,
|
|
6614
7185
|
fontSize: element.fontSize || 16,
|
|
6615
7186
|
fontFamily: element.fontFamily || "Open Sans",
|
|
6616
7187
|
fontWeight: element.fontWeight || 400,
|
|
@@ -6619,6 +7190,11 @@ const PageCanvas = forwardRef(
|
|
|
6619
7190
|
charSpacing: element.charSpacing || 0,
|
|
6620
7191
|
splitByGrapheme
|
|
6621
7192
|
});
|
|
7193
|
+
if (element.formattingEnabled === true) {
|
|
7194
|
+
obj.styles = reflowParsedStyles || {};
|
|
7195
|
+
}
|
|
7196
|
+
obj.editable = element.formattingEnabled !== true;
|
|
7197
|
+
obj.__formattingEnabled = element.formattingEnabled === true;
|
|
6622
7198
|
obj.initDimensions();
|
|
6623
7199
|
if (Math.abs((obj.width ?? 0) - targetWidth) > 0.01) {
|
|
6624
7200
|
obj.width = targetWidth;
|
|
@@ -6626,10 +7202,26 @@ const PageCanvas = forwardRef(
|
|
|
6626
7202
|
obj.dynamicMinWidth = 0;
|
|
6627
7203
|
obj.setCoords();
|
|
6628
7204
|
obj.dirty = true;
|
|
7205
|
+
try {
|
|
7206
|
+
obj._forceClearCache = true;
|
|
7207
|
+
if (typeof obj._clearCache === "function") obj._clearCache();
|
|
7208
|
+
} catch {
|
|
7209
|
+
}
|
|
6629
7210
|
didReflow = true;
|
|
6630
7211
|
};
|
|
6631
7212
|
canvas2.getObjects().forEach(reflowObject);
|
|
6632
|
-
if (didReflow)
|
|
7213
|
+
if (didReflow) {
|
|
7214
|
+
canvas2.requestRenderAll();
|
|
7215
|
+
try {
|
|
7216
|
+
requestAnimationFrame(() => {
|
|
7217
|
+
try {
|
|
7218
|
+
canvas2.requestRenderAll();
|
|
7219
|
+
} catch {
|
|
7220
|
+
}
|
|
7221
|
+
});
|
|
7222
|
+
} catch {
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
6633
7225
|
}, []);
|
|
6634
7226
|
useEffect(() => {
|
|
6635
7227
|
if (!canvasElRef.current) return;
|
|
@@ -6676,7 +7268,8 @@ const PageCanvas = forwardRef(
|
|
|
6676
7268
|
absolutePositioned: true
|
|
6677
7269
|
});
|
|
6678
7270
|
fabricRef.current = fabricCanvas;
|
|
6679
|
-
registerFabricCanvas(pageId, fabricCanvas);
|
|
7271
|
+
const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
|
|
7272
|
+
fabricCanvas.__storeRegistryKey = storeRegistryKey;
|
|
6680
7273
|
const initFonts = async () => {
|
|
6681
7274
|
try {
|
|
6682
7275
|
await preloadAllFonts();
|
|
@@ -6969,7 +7562,10 @@ const PageCanvas = forwardRef(
|
|
|
6969
7562
|
if (!window.__fabricCanvasRegistry || !(window.__fabricCanvasRegistry instanceof Map)) {
|
|
6970
7563
|
window.__fabricCanvasRegistry = /* @__PURE__ */ new Map();
|
|
6971
7564
|
}
|
|
6972
|
-
window.__fabricCanvasRegistry
|
|
7565
|
+
const reg = window.__fabricCanvasRegistry;
|
|
7566
|
+
const registryKey = reg.has(pageId) ? `${pageId}#${Math.random().toString(36).slice(2, 10)}` : pageId;
|
|
7567
|
+
fabricCanvas.__registryKey = registryKey;
|
|
7568
|
+
reg.set(registryKey, {
|
|
6973
7569
|
canvas: fabricCanvas,
|
|
6974
7570
|
forceUnlockEdits
|
|
6975
7571
|
});
|
|
@@ -7739,9 +8335,6 @@ const PageCanvas = forwardRef(
|
|
|
7739
8335
|
scaleY: finalScaleY,
|
|
7740
8336
|
transformMatrix: finalAbsoluteMatrix
|
|
7741
8337
|
};
|
|
7742
|
-
if (obj instanceof fabric.Textbox) {
|
|
7743
|
-
elementUpdate.text = obj.text || "";
|
|
7744
|
-
}
|
|
7745
8338
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
7746
8339
|
elementUpdate.opacity = sourceElement.opacity;
|
|
7747
8340
|
}
|
|
@@ -7820,6 +8413,12 @@ const PageCanvas = forwardRef(
|
|
|
7820
8413
|
}
|
|
7821
8414
|
if (target && target instanceof fabric.Textbox) {
|
|
7822
8415
|
const elementId = getObjectId(target);
|
|
8416
|
+
if (target.__formattingEnabled === true || target.editable === false) {
|
|
8417
|
+
toast.info("Inline formatting is on — edit the text in the right panel.", {
|
|
8418
|
+
description: "This protects your **bold**, [c=...] and other formatting tokens."
|
|
8419
|
+
});
|
|
8420
|
+
return;
|
|
8421
|
+
}
|
|
7823
8422
|
editingTextIdRef.current = elementId || null;
|
|
7824
8423
|
target.enterEditing();
|
|
7825
8424
|
target.selectAll();
|
|
@@ -7884,7 +8483,19 @@ const PageCanvas = forwardRef(
|
|
|
7884
8483
|
});
|
|
7885
8484
|
return () => {
|
|
7886
8485
|
setReady(false);
|
|
7887
|
-
unregisterFabricCanvas(pageId);
|
|
8486
|
+
unregisterFabricCanvas(fabricCanvas.__storeRegistryKey ?? pageId, fabricCanvas);
|
|
8487
|
+
try {
|
|
8488
|
+
if (typeof window !== "undefined") {
|
|
8489
|
+
const reg = window.__fabricCanvasRegistry;
|
|
8490
|
+
if (reg instanceof Map) {
|
|
8491
|
+
const key = fabricCanvas.__registryKey ?? pageId;
|
|
8492
|
+
const entry = reg.get(key);
|
|
8493
|
+
const entryCanvas = (entry == null ? void 0 : entry.canvas) ?? entry;
|
|
8494
|
+
if (entryCanvas === fabricCanvas) reg.delete(key);
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
} catch {
|
|
8498
|
+
}
|
|
7888
8499
|
if (fabricCanvas.__fontCleanup) {
|
|
7889
8500
|
fabricCanvas.__fontCleanup();
|
|
7890
8501
|
}
|
|
@@ -9246,6 +9857,12 @@ const PageCanvas = forwardRef(
|
|
|
9246
9857
|
} else if (obj instanceof fabric.Textbox) {
|
|
9247
9858
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
9248
9859
|
let text = element.text || "Text";
|
|
9860
|
+
let parsedStyles = null;
|
|
9861
|
+
if (element.formattingEnabled === true) {
|
|
9862
|
+
const parsed = parseTextMarkdown(text);
|
|
9863
|
+
text = parsed.plainText || " ";
|
|
9864
|
+
parsedStyles = parsed.styles;
|
|
9865
|
+
}
|
|
9249
9866
|
let fontSize = element.fontSize || 16;
|
|
9250
9867
|
element.minFontSize || 8;
|
|
9251
9868
|
const maxLines = element.maxLines || 3;
|
|
@@ -9342,6 +9959,11 @@ const PageCanvas = forwardRef(
|
|
|
9342
9959
|
splitByGrapheme,
|
|
9343
9960
|
text
|
|
9344
9961
|
});
|
|
9962
|
+
if (element.formattingEnabled === true) {
|
|
9963
|
+
obj.styles = parsedStyles || {};
|
|
9964
|
+
} else {
|
|
9965
|
+
obj.styles = element.styles || {};
|
|
9966
|
+
}
|
|
9345
9967
|
obj.initDimensions();
|
|
9346
9968
|
if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {
|
|
9347
9969
|
obj.width = textboxWidth;
|
|
@@ -10346,6 +10968,7 @@ function PreviewCanvas({
|
|
|
10346
10968
|
zoom = 1,
|
|
10347
10969
|
absoluteZoom = false,
|
|
10348
10970
|
skipFontReadyWait = false,
|
|
10971
|
+
pageIdOverride,
|
|
10349
10972
|
className,
|
|
10350
10973
|
onDynamicFieldClick,
|
|
10351
10974
|
onReady
|
|
@@ -10412,13 +11035,19 @@ function PreviewCanvas({
|
|
|
10412
11035
|
backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
|
|
10413
11036
|
};
|
|
10414
11037
|
}, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
|
|
10415
|
-
const projectSettings = useMemo(() =>
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
11038
|
+
const projectSettings = useMemo(() => {
|
|
11039
|
+
var _a2, _b2, _c2;
|
|
11040
|
+
const vars = ((_a2 = config.themeConfig) == null ? void 0 : _a2.variables) || {};
|
|
11041
|
+
return {
|
|
11042
|
+
showGrid: false,
|
|
11043
|
+
snapToGrid: false,
|
|
11044
|
+
gridSize: 10,
|
|
11045
|
+
snapToGuides: false,
|
|
11046
|
+
snapThreshold: 5,
|
|
11047
|
+
primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
|
|
11048
|
+
secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
|
|
11049
|
+
};
|
|
11050
|
+
}, [config.themeConfig]);
|
|
10422
11051
|
const handleDynamicFieldClick = useCallback((elementId) => {
|
|
10423
11052
|
const fieldInfo = elementToFieldMap.get(elementId);
|
|
10424
11053
|
if (fieldInfo && onDynamicFieldClick) {
|
|
@@ -10488,7 +11117,7 @@ function PreviewCanvas({
|
|
|
10488
11117
|
PageCanvas,
|
|
10489
11118
|
{
|
|
10490
11119
|
ref: canvasRef,
|
|
10491
|
-
pageId: page.id || `page-${pageIndex}`,
|
|
11120
|
+
pageId: pageIdOverride || page.id || `page-${pageIndex}`,
|
|
10492
11121
|
elements,
|
|
10493
11122
|
pageChildren: laidOutPageChildren,
|
|
10494
11123
|
pageSettings,
|
|
@@ -12379,6 +13008,17 @@ function normalizeFontFamily(fontStack) {
|
|
|
12379
13008
|
const first = fontStack.split(",")[0].trim();
|
|
12380
13009
|
return first.replace(/^['"]|['"]$/g, "");
|
|
12381
13010
|
}
|
|
13011
|
+
function appendStylesheet(url, rejectOnError = true) {
|
|
13012
|
+
return new Promise((resolve, reject) => {
|
|
13013
|
+
const link = document.createElement("link");
|
|
13014
|
+
link.rel = "stylesheet";
|
|
13015
|
+
link.href = url;
|
|
13016
|
+
link.crossOrigin = "anonymous";
|
|
13017
|
+
link.onload = () => resolve();
|
|
13018
|
+
link.onerror = () => rejectOnError ? reject(new Error(`Failed to load stylesheet: ${url}`)) : resolve();
|
|
13019
|
+
document.head.appendChild(link);
|
|
13020
|
+
});
|
|
13021
|
+
}
|
|
12382
13022
|
const loadedFonts = /* @__PURE__ */ new Set();
|
|
12383
13023
|
const loadingPromises = /* @__PURE__ */ new Map();
|
|
12384
13024
|
function withTimeout(promise, timeoutMs = 4e3) {
|
|
@@ -12404,28 +13044,19 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12404
13044
|
const fontshareSlug = FONTSHARE_SLUGS[fontFamily];
|
|
12405
13045
|
if (fontshareSlug) {
|
|
12406
13046
|
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
await new Promise((resolve, reject) => {
|
|
12411
|
-
link2.onload = () => resolve();
|
|
12412
|
-
link2.onerror = () => reject(new Error(`Failed to load Fontshare font: ${fontFamily}`));
|
|
12413
|
-
document.head.appendChild(link2);
|
|
12414
|
-
});
|
|
13047
|
+
await appendStylesheet(url2);
|
|
13048
|
+
const italicUrl = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300i,400i,500i,700i&display=swap`;
|
|
13049
|
+
await withTimeout(appendStylesheet(italicUrl, false), 1500);
|
|
12415
13050
|
loadedFonts.add(fontFamily);
|
|
12416
13051
|
return;
|
|
12417
13052
|
}
|
|
12418
13053
|
const encoded = encodeURIComponent(fontFamily);
|
|
12419
13054
|
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
link.onload = () => resolve();
|
|
12426
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12427
|
-
document.head.appendChild(link);
|
|
12428
|
-
});
|
|
13055
|
+
await appendStylesheet(url);
|
|
13056
|
+
await withTimeout(Promise.all([300, 400, 500, 600, 700].map((weight) => {
|
|
13057
|
+
const italicUrl = `https://fonts.googleapis.com/css2?family=${encoded}:ital,wght@1,${weight}&display=swap`;
|
|
13058
|
+
return appendStylesheet(italicUrl, false);
|
|
13059
|
+
})), 1800);
|
|
12429
13060
|
loadedFonts.add(fontFamily);
|
|
12430
13061
|
} catch (e) {
|
|
12431
13062
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12495,6 +13126,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12495
13126
|
if (node.type === "text") {
|
|
12496
13127
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12497
13128
|
add(node.fontFamily, w, node.fontStyle);
|
|
13129
|
+
add(node.fontFamily, w, "italic");
|
|
13130
|
+
}
|
|
13131
|
+
}
|
|
13132
|
+
}
|
|
13133
|
+
if (node.formattingEnabled === true && node.fontFamily) {
|
|
13134
|
+
const parsed = parseTextMarkdown(String(node.text ?? ""));
|
|
13135
|
+
const parsedStyleEntries = Object.values(parsed.styles || {});
|
|
13136
|
+
for (const lineStyle of parsedStyleEntries) {
|
|
13137
|
+
if (lineStyle && typeof lineStyle === "object") {
|
|
13138
|
+
for (const charStyle of Object.values(lineStyle)) {
|
|
13139
|
+
if (!charStyle || typeof charStyle !== "object") continue;
|
|
13140
|
+
add(
|
|
13141
|
+
charStyle.fontFamily || node.fontFamily,
|
|
13142
|
+
charStyle.fontWeight ?? node.fontWeight,
|
|
13143
|
+
charStyle.fontStyle ?? node.fontStyle
|
|
13144
|
+
);
|
|
13145
|
+
}
|
|
12498
13146
|
}
|
|
12499
13147
|
}
|
|
12500
13148
|
}
|
|
@@ -13404,11 +14052,20 @@ function PixldocsPreview(props) {
|
|
|
13404
14052
|
!canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
|
|
13405
14053
|
] });
|
|
13406
14054
|
}
|
|
13407
|
-
const PACKAGE_VERSION = "0.5.
|
|
14055
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13408
14056
|
const roundParityValue = (value) => {
|
|
13409
14057
|
if (typeof value !== "number") return value;
|
|
13410
14058
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13411
14059
|
};
|
|
14060
|
+
function isolatePageForCapture(config, pageIndex) {
|
|
14061
|
+
var _a;
|
|
14062
|
+
const capturePageId = `__pixldocs_capture_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}_${pageIndex}`;
|
|
14063
|
+
const cloned = JSON.parse(JSON.stringify(config));
|
|
14064
|
+
if ((_a = cloned.pages) == null ? void 0 : _a[pageIndex]) {
|
|
14065
|
+
cloned.pages[pageIndex].id = capturePageId;
|
|
14066
|
+
}
|
|
14067
|
+
return { config: cloned, pageId: capturePageId };
|
|
14068
|
+
}
|
|
13412
14069
|
function logJsonLine(tag, payload) {
|
|
13413
14070
|
try {
|
|
13414
14071
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13446,6 +14103,9 @@ function installUnderlineFix(fab) {
|
|
|
13446
14103
|
const hasOwn = !!this[type];
|
|
13447
14104
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13448
14105
|
if (!hasOwn && !hasStyled) return;
|
|
14106
|
+
if (!hasOwn && hasStyled) {
|
|
14107
|
+
return original.call(this, ctx, type);
|
|
14108
|
+
}
|
|
13449
14109
|
const lines = this._textLines;
|
|
13450
14110
|
const offsets = this.offsets;
|
|
13451
14111
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13457,7 +14117,7 @@ function installUnderlineFix(fab) {
|
|
|
13457
14117
|
let topOffset = this._getTopOffset();
|
|
13458
14118
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13459
14119
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13460
|
-
const lineHas = !!this[type]
|
|
14120
|
+
const lineHas = !!this[type];
|
|
13461
14121
|
if (!lineHas) {
|
|
13462
14122
|
topOffset += heightOfLine;
|
|
13463
14123
|
continue;
|
|
@@ -13987,9 +14647,11 @@ class PixldocsRenderer {
|
|
|
13987
14647
|
}
|
|
13988
14648
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
13989
14649
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
13990
|
-
const
|
|
13991
|
-
const
|
|
13992
|
-
const
|
|
14650
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14651
|
+
const renderConfig = capture.config;
|
|
14652
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
14653
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
14654
|
+
const hasAutoShrink = configHasAutoShrinkText(renderConfig);
|
|
13993
14655
|
let firstMountSettled = false;
|
|
13994
14656
|
let lateFontSettleDetected = false;
|
|
13995
14657
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14037,12 +14699,12 @@ class PixldocsRenderer {
|
|
|
14037
14699
|
root = createRoot(container);
|
|
14038
14700
|
await new Promise((settle) => {
|
|
14039
14701
|
const onReadyOnce = () => {
|
|
14040
|
-
this.waitForCanvasScene(container,
|
|
14702
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14041
14703
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14042
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14704
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14043
14705
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14044
|
-
await this.waitForStableTextMetrics(container,
|
|
14045
|
-
await this.waitForCanvasScene(container,
|
|
14706
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14707
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14046
14708
|
if (!fabricInstance) return settle();
|
|
14047
14709
|
settle();
|
|
14048
14710
|
}).catch(() => settle());
|
|
@@ -14050,8 +14712,9 @@ class PixldocsRenderer {
|
|
|
14050
14712
|
root.render(
|
|
14051
14713
|
createElement(PreviewCanvas2, {
|
|
14052
14714
|
key: `remount-${mountKey}`,
|
|
14053
|
-
config,
|
|
14715
|
+
config: renderConfig,
|
|
14054
14716
|
pageIndex,
|
|
14717
|
+
pageIdOverride: capture.pageId,
|
|
14055
14718
|
zoom: pixelRatio,
|
|
14056
14719
|
absoluteZoom: true,
|
|
14057
14720
|
skipFontReadyWait: false,
|
|
@@ -14061,13 +14724,13 @@ class PixldocsRenderer {
|
|
|
14061
14724
|
});
|
|
14062
14725
|
};
|
|
14063
14726
|
const onReady = () => {
|
|
14064
|
-
this.waitForCanvasScene(container,
|
|
14727
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14065
14728
|
try {
|
|
14066
14729
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14067
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14730
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14068
14731
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14069
|
-
await this.waitForStableTextMetrics(container,
|
|
14070
|
-
await this.waitForCanvasScene(container,
|
|
14732
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14733
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14071
14734
|
firstMountSettled = true;
|
|
14072
14735
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14073
14736
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14093,7 +14756,7 @@ class PixldocsRenderer {
|
|
|
14093
14756
|
}
|
|
14094
14757
|
exportCtx.save();
|
|
14095
14758
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14096
|
-
this.paintPageBackground(exportCtx,
|
|
14759
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14097
14760
|
exportCtx.restore();
|
|
14098
14761
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14099
14762
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14109,8 +14772,9 @@ class PixldocsRenderer {
|
|
|
14109
14772
|
root = createRoot(container);
|
|
14110
14773
|
root.render(
|
|
14111
14774
|
createElement(PreviewCanvas2, {
|
|
14112
|
-
config,
|
|
14775
|
+
config: renderConfig,
|
|
14113
14776
|
pageIndex,
|
|
14777
|
+
pageIdOverride: capture.pageId,
|
|
14114
14778
|
zoom: pixelRatio,
|
|
14115
14779
|
absoluteZoom: true,
|
|
14116
14780
|
skipFontReadyWait: false,
|
|
@@ -14131,6 +14795,8 @@ class PixldocsRenderer {
|
|
|
14131
14795
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14132
14796
|
return new Promise(async (resolve, reject) => {
|
|
14133
14797
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14798
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14799
|
+
const renderConfig = capture.config;
|
|
14134
14800
|
const container = document.createElement("div");
|
|
14135
14801
|
container.style.cssText = `
|
|
14136
14802
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14157,8 +14823,9 @@ class PixldocsRenderer {
|
|
|
14157
14823
|
root.render(
|
|
14158
14824
|
createElement(PreviewCanvas2, {
|
|
14159
14825
|
key: `svg-capture-${mountKey}`,
|
|
14160
|
-
config,
|
|
14826
|
+
config: renderConfig,
|
|
14161
14827
|
pageIndex,
|
|
14828
|
+
pageIdOverride: capture.pageId,
|
|
14162
14829
|
zoom: 1,
|
|
14163
14830
|
absoluteZoom: true,
|
|
14164
14831
|
skipFontReadyWait: false,
|
|
@@ -14167,13 +14834,13 @@ class PixldocsRenderer {
|
|
|
14167
14834
|
);
|
|
14168
14835
|
};
|
|
14169
14836
|
const onReady = () => {
|
|
14170
|
-
this.waitForCanvasScene(container,
|
|
14837
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14171
14838
|
var _a, _b;
|
|
14172
14839
|
try {
|
|
14173
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14840
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14174
14841
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14175
|
-
await this.waitForStableTextMetrics(container,
|
|
14176
|
-
await this.waitForCanvasScene(container,
|
|
14842
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14843
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14177
14844
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14178
14845
|
if (!fabricInstance) {
|
|
14179
14846
|
cleanup();
|
|
@@ -14262,7 +14929,7 @@ class PixldocsRenderer {
|
|
|
14262
14929
|
);
|
|
14263
14930
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14264
14931
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14265
|
-
const page =
|
|
14932
|
+
const page = renderConfig.pages[pageIndex];
|
|
14266
14933
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14267
14934
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14268
14935
|
cleanup();
|
|
@@ -14944,8 +15611,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14944
15611
|
}
|
|
14945
15612
|
return files.regular;
|
|
14946
15613
|
}
|
|
14947
|
-
function
|
|
14948
|
-
|
|
15614
|
+
function isExactWeightItalicMatch(files, weight, isItalic, path) {
|
|
15615
|
+
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";
|
|
15616
|
+
return files[exactKey] === path;
|
|
14949
15617
|
}
|
|
14950
15618
|
function getJsPDFFontName(fontName) {
|
|
14951
15619
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15018,10 +15686,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15018
15686
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15019
15687
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15020
15688
|
if (!fontPath) return false;
|
|
15021
|
-
|
|
15022
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15689
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15690
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15023
15691
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15024
|
-
const italicSuffix =
|
|
15692
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15025
15693
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15026
15694
|
const url = baseUrl + fontPath;
|
|
15027
15695
|
try {
|
|
@@ -15036,6 +15704,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15036
15704
|
}
|
|
15037
15705
|
}
|
|
15038
15706
|
registeredFamilies.add(fontName);
|
|
15707
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15039
15708
|
return true;
|
|
15040
15709
|
} catch (e) {
|
|
15041
15710
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15044,6 +15713,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15044
15713
|
}
|
|
15045
15714
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15046
15715
|
const fontshareNotFound = /* @__PURE__ */ new Set();
|
|
15716
|
+
const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontWeight(weight)}|${isItalic ? "i" : "n"}`;
|
|
15717
|
+
const registeredVariants = /* @__PURE__ */ new Set();
|
|
15718
|
+
const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
|
|
15719
|
+
const resolveBestRegisteredVariant = (family, weight, italic) => {
|
|
15720
|
+
const want = resolveFontWeight(weight);
|
|
15721
|
+
const weights = [300, 400, 500, 600, 700];
|
|
15722
|
+
if (registeredVariants.has(variantKey(family, want, italic))) return { weight: want, italic };
|
|
15723
|
+
const nearest = [...weights].sort((a, b) => Math.abs(a - want) - Math.abs(b - want) || (want >= 500 ? b - a : a - b));
|
|
15724
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, italic))) return { weight: w, italic };
|
|
15725
|
+
for (const w of nearest) if (registeredVariants.has(variantKey(family, w, !italic))) return { weight: w, italic: !italic };
|
|
15726
|
+
return null;
|
|
15727
|
+
};
|
|
15047
15728
|
function bytesToBase64(bytes) {
|
|
15048
15729
|
let binary = "";
|
|
15049
15730
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15072,7 +15753,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15072
15753
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15073
15754
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15074
15755
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15075
|
-
|
|
15756
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15757
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15076
15758
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15077
15759
|
if (proxyBytes) {
|
|
15078
15760
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15090,7 +15772,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15090
15772
|
}
|
|
15091
15773
|
});
|
|
15092
15774
|
if (!cssRes.ok) {
|
|
15093
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15775
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15094
15776
|
return null;
|
|
15095
15777
|
}
|
|
15096
15778
|
const css = await cssRes.text();
|
|
@@ -15168,6 +15850,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15168
15850
|
}
|
|
15169
15851
|
}
|
|
15170
15852
|
registeredFamilies.add(fontName);
|
|
15853
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15171
15854
|
return true;
|
|
15172
15855
|
} catch (err) {
|
|
15173
15856
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15182,16 +15865,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15182
15865
|
const resolved = resolveFontWeight(weight);
|
|
15183
15866
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15184
15867
|
if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
|
|
15185
|
-
if (isItalic) {
|
|
15186
|
-
const fsUpright = await fetchFontshareTTF(fontName, resolved, false);
|
|
15187
|
-
if (fsUpright) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsUpright);
|
|
15188
|
-
}
|
|
15189
15868
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15190
15869
|
if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
|
|
15191
|
-
if (isItalic) {
|
|
15192
|
-
const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);
|
|
15193
|
-
if (uprightB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);
|
|
15194
|
-
}
|
|
15195
15870
|
return false;
|
|
15196
15871
|
}
|
|
15197
15872
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15211,9 +15886,15 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15211
15886
|
};
|
|
15212
15887
|
const walkElements = (elements) => {
|
|
15213
15888
|
for (const el of elements) {
|
|
15889
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15890
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15891
|
+
};
|
|
15214
15892
|
if (el.fontFamily) {
|
|
15215
15893
|
const w = normalizeWeight(el.fontWeight);
|
|
15216
|
-
|
|
15894
|
+
addFontVariant(el.fontFamily, w, /italic|oblique/i.test(String(el.fontStyle ?? "")));
|
|
15895
|
+
addFontVariant(el.fontFamily, 700, false);
|
|
15896
|
+
addFontVariant(el.fontFamily, 400, true);
|
|
15897
|
+
addFontVariant(el.fontFamily, 700, true);
|
|
15217
15898
|
}
|
|
15218
15899
|
if (el.styles && typeof el.styles === "object") {
|
|
15219
15900
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15221,9 +15902,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15221
15902
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15222
15903
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15223
15904
|
const s = lineStyles[charKey];
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15905
|
+
const styledFamily = (s == null ? void 0 : s.fontFamily) || el.fontFamily;
|
|
15906
|
+
if (styledFamily) {
|
|
15907
|
+
const w = normalizeWeight(s.fontWeight ?? el.fontWeight);
|
|
15908
|
+
addFontVariant(styledFamily, w, /italic|oblique/i.test(String(s.fontStyle ?? el.fontStyle ?? "")));
|
|
15227
15909
|
}
|
|
15228
15910
|
}
|
|
15229
15911
|
}
|
|
@@ -15237,19 +15919,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15237
15919
|
if (page.children) walkElements(page.children);
|
|
15238
15920
|
if (page.elements) walkElements(page.elements);
|
|
15239
15921
|
}
|
|
15240
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15922
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15241
15923
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15242
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15924
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15243
15925
|
}
|
|
15244
15926
|
const embedded = /* @__PURE__ */ new Set();
|
|
15245
15927
|
const tasks = [];
|
|
15246
15928
|
for (const key of fontKeys) {
|
|
15247
15929
|
const sep = key.indexOf(SEP);
|
|
15930
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15248
15931
|
const fontName = key.slice(0, sep);
|
|
15249
|
-
const weight = parseInt(key.slice(sep + 1), 10);
|
|
15932
|
+
const weight = parseInt(key.slice(sep + 1, secondSep), 10);
|
|
15933
|
+
const italic = key.slice(secondSep + 1) === "italic";
|
|
15250
15934
|
if (!isFontAvailable(fontName)) continue;
|
|
15251
15935
|
tasks.push(
|
|
15252
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15936
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15253
15937
|
if (ok) embedded.add(key);
|
|
15254
15938
|
})
|
|
15255
15939
|
);
|
|
@@ -15305,7 +15989,7 @@ function splitIntoRuns(text) {
|
|
|
15305
15989
|
return runs;
|
|
15306
15990
|
}
|
|
15307
15991
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15308
|
-
var _a, _b;
|
|
15992
|
+
var _a, _b, _c;
|
|
15309
15993
|
const parser = new DOMParser();
|
|
15310
15994
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15311
15995
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15337,6 +16021,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15337
16021
|
stylePairs.push(`font-style: normal`);
|
|
15338
16022
|
return stylePairs.join("; ");
|
|
15339
16023
|
};
|
|
16024
|
+
const applySyntheticItalicTransform = (el) => {
|
|
16025
|
+
const x = Number.parseFloat(resolveInheritedValue(el, "x") || "0") || 0;
|
|
16026
|
+
const y = Number.parseFloat(resolveInheritedValue(el, "y") || "0") || 0;
|
|
16027
|
+
const existing = el.getAttribute("transform") || "";
|
|
16028
|
+
const synth = `translate(${x} ${y}) skewX(-12) translate(${-x} ${-y})`;
|
|
16029
|
+
el.setAttribute("transform", existing ? `${existing} ${synth}` : synth);
|
|
16030
|
+
};
|
|
15340
16031
|
const getDepth = (el) => {
|
|
15341
16032
|
let depth = 0;
|
|
15342
16033
|
let current = el.parentElement;
|
|
@@ -15356,20 +16047,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15356
16047
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15357
16048
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15358
16049
|
const weight = resolveWeightNum(weightRaw);
|
|
15359
|
-
const
|
|
15360
|
-
const
|
|
15361
|
-
const
|
|
15362
|
-
|
|
16050
|
+
const requested = resolveFontWeight(weight);
|
|
16051
|
+
const requestedItalic = /italic|oblique/i.test(styleRaw);
|
|
16052
|
+
const best = resolveBestRegisteredVariant(clean, requested, requestedItalic);
|
|
16053
|
+
if (!best) continue;
|
|
16054
|
+
const jsPdfName = getEmbeddedJsPDFFontName(clean, best.weight, best.italic);
|
|
16055
|
+
sourceMeta.set(el, { clean, requested, resolved: best.weight, isItalic: requestedItalic, actualItalic: best.italic, jsPdfName, inlineStyle, weight });
|
|
15363
16056
|
}
|
|
15364
16057
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15365
16058
|
for (const el of textEls) {
|
|
15366
16059
|
if (!el.isConnected) continue;
|
|
15367
16060
|
const meta = sourceMeta.get(el);
|
|
15368
16061
|
if (!meta) continue;
|
|
15369
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16062
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15370
16063
|
el.setAttribute("data-source-font-family", clean);
|
|
15371
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16064
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15372
16065
|
el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
|
|
16066
|
+
if (requested >= 600 && resolved < 600) {
|
|
16067
|
+
const fill = resolveInheritedValue(el, "fill") || readStyleToken(inlineStyle, "fill") || "#000000";
|
|
16068
|
+
el.setAttribute("stroke", fill);
|
|
16069
|
+
el.setAttribute("stroke-width", String(requested === 700 ? 0.7 : 0.5));
|
|
16070
|
+
el.setAttribute("stroke-linejoin", "round");
|
|
16071
|
+
}
|
|
16072
|
+
if (isItalic && !actualItalic) {
|
|
16073
|
+
applySyntheticItalicTransform(el);
|
|
16074
|
+
try {
|
|
16075
|
+
console.log("[Vector PDF][synthetic-italic]", {
|
|
16076
|
+
tag: el.tagName,
|
|
16077
|
+
family: clean,
|
|
16078
|
+
weight: requested,
|
|
16079
|
+
textPreview: (el.textContent || "").slice(0, 40)
|
|
16080
|
+
});
|
|
16081
|
+
} catch {
|
|
16082
|
+
}
|
|
16083
|
+
}
|
|
15373
16084
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15374
16085
|
const hasDevanagari = containsDevanagari(directText);
|
|
15375
16086
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15412,6 +16123,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15412
16123
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15413
16124
|
}
|
|
15414
16125
|
}
|
|
16126
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
16127
|
+
const transformedTspans = Array.from(doc.querySelectorAll("tspan[transform]"));
|
|
16128
|
+
for (const tspan of transformedTspans) {
|
|
16129
|
+
const parentText = tspan.parentElement;
|
|
16130
|
+
if (!parentText || parentText.tagName.toLowerCase() !== "text") continue;
|
|
16131
|
+
const transformVal = tspan.getAttribute("transform") || "";
|
|
16132
|
+
if (!/skewX/i.test(transformVal)) continue;
|
|
16133
|
+
const wrapper = doc.createElementNS(SVG_NS, "text");
|
|
16134
|
+
let parentTransform = "";
|
|
16135
|
+
for (const attr of Array.from(parentText.attributes)) {
|
|
16136
|
+
if (attr.name === "transform") {
|
|
16137
|
+
parentTransform = attr.value;
|
|
16138
|
+
continue;
|
|
16139
|
+
}
|
|
16140
|
+
wrapper.setAttribute(attr.name, attr.value);
|
|
16141
|
+
}
|
|
16142
|
+
const combined = parentTransform ? `${parentTransform} ${transformVal}` : transformVal;
|
|
16143
|
+
wrapper.setAttribute("transform", combined);
|
|
16144
|
+
tspan.removeAttribute("transform");
|
|
16145
|
+
wrapper.appendChild(tspan);
|
|
16146
|
+
(_c = parentText.parentNode) == null ? void 0 : _c.insertBefore(wrapper, parentText.nextSibling);
|
|
16147
|
+
}
|
|
15415
16148
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15416
16149
|
}
|
|
15417
16150
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15442,14 +16175,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15442
16175
|
if (ok) embedded.add(`${family}${w}`);
|
|
15443
16176
|
})
|
|
15444
16177
|
);
|
|
16178
|
+
tasks.push(
|
|
16179
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16180
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16181
|
+
})
|
|
16182
|
+
);
|
|
15445
16183
|
}
|
|
15446
16184
|
} else {
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
}
|
|
15452
|
-
|
|
16185
|
+
const variants = [
|
|
16186
|
+
{ w: 400, italic: false },
|
|
16187
|
+
{ w: 700, italic: false },
|
|
16188
|
+
{ w: 400, italic: true },
|
|
16189
|
+
{ w: 700, italic: true }
|
|
16190
|
+
];
|
|
16191
|
+
for (const v of variants) {
|
|
16192
|
+
tasks.push(
|
|
16193
|
+
embedFontWithGoogleFallback(pdf, family, v.w, fontBaseUrl, v.italic).then((ok) => {
|
|
16194
|
+
if (ok) embedded.add(`${family}${v.w}${v.italic ? "i" : ""}`);
|
|
16195
|
+
else if (v.w === 400 && !v.italic) {
|
|
16196
|
+
console.warn(`[pdf-fonts] No TTF (local/Google/Fontshare) for "${family}" — will use Helvetica fallback`);
|
|
16197
|
+
}
|
|
16198
|
+
})
|
|
16199
|
+
);
|
|
16200
|
+
}
|
|
15453
16201
|
}
|
|
15454
16202
|
}
|
|
15455
16203
|
await Promise.all(tasks);
|
|
@@ -15470,6 +16218,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15470
16218
|
getEmbeddedJsPDFFontName,
|
|
15471
16219
|
getFontPathForWeight,
|
|
15472
16220
|
isFontAvailable,
|
|
16221
|
+
resolveBestRegisteredVariant,
|
|
15473
16222
|
resolveFontWeight,
|
|
15474
16223
|
rewriteSvgFontsForJsPDF
|
|
15475
16224
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15509,9 +16258,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15509
16258
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15510
16259
|
var _a, _b;
|
|
15511
16260
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15512
|
-
const tspanInfo = tspans.slice(0,
|
|
16261
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15513
16262
|
x: s.getAttribute("x"),
|
|
15514
16263
|
y: s.getAttribute("y"),
|
|
16264
|
+
fs: s.getAttribute("font-style"),
|
|
16265
|
+
fw: s.getAttribute("font-weight"),
|
|
16266
|
+
ff: s.getAttribute("font-family"),
|
|
16267
|
+
td: s.getAttribute("text-decoration"),
|
|
16268
|
+
style: (s.getAttribute("style") || "").slice(0, 120),
|
|
15515
16269
|
text: (s.textContent || "").slice(0, 40)
|
|
15516
16270
|
}));
|
|
15517
16271
|
let containerWidth = null;
|
|
@@ -16546,13 +17300,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16546
17300
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16547
17301
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16548
17302
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17303
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16549
17304
|
for (let si = 0; si < tspans.length; si++) {
|
|
16550
17305
|
const tspan = tspans[si];
|
|
16551
17306
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16552
17307
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16553
17308
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16554
17309
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16555
|
-
|
|
17310
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17311
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16556
17312
|
const content = tspan.textContent || "";
|
|
16557
17313
|
if (!content.trim()) continue;
|
|
16558
17314
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16607,23 +17363,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16607
17363
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16608
17364
|
lineEndX = lineStartX + textWidth;
|
|
16609
17365
|
}
|
|
16610
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16611
17366
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
16612
|
-
const
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
textEl.parentElement
|
|
16624
|
-
|
|
17367
|
+
const makeLine = (yPos) => {
|
|
17368
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
17369
|
+
line.setAttribute("x1", String(lineStartX));
|
|
17370
|
+
line.setAttribute("y1", String(yPos));
|
|
17371
|
+
line.setAttribute("x2", String(lineEndX));
|
|
17372
|
+
line.setAttribute("y2", String(yPos));
|
|
17373
|
+
line.setAttribute("stroke", fill.startsWith("url(") ? "#000000" : fill);
|
|
17374
|
+
line.setAttribute("stroke-width", String(thickness));
|
|
17375
|
+
line.setAttribute("stroke-linecap", "butt");
|
|
17376
|
+
line.setAttribute("fill", "none");
|
|
17377
|
+
if (fillOpacity) line.setAttribute("stroke-opacity", fillOpacity);
|
|
17378
|
+
if (textEl.parentElement) {
|
|
17379
|
+
textEl.parentElement.insertBefore(line, textEl.nextSibling);
|
|
17380
|
+
}
|
|
17381
|
+
};
|
|
17382
|
+
if (hasUnderline) makeLine(baselineY + fontSize * 0.15);
|
|
17383
|
+
if (hasLinethrough) makeLine(baselineY - fontSize * 0.3);
|
|
16625
17384
|
stripTextDecoration(tspan);
|
|
16626
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17385
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16627
17386
|
}
|
|
16628
17387
|
}
|
|
16629
17388
|
if (tempContainer) {
|
|
@@ -16637,7 +17396,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16637
17396
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16638
17397
|
return svgStr;
|
|
16639
17398
|
}
|
|
16640
|
-
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr)) {
|
|
17399
|
+
if (!/text-decoration/i.test(svgStr) && !/underline/i.test(svgStr) && !/line-through/i.test(svgStr)) {
|
|
16641
17400
|
return svgStr;
|
|
16642
17401
|
}
|
|
16643
17402
|
try {
|
|
@@ -16929,7 +17688,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16929
17688
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16930
17689
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16931
17690
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16932
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17691
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16933
17692
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16934
17693
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16935
17694
|
let pageSvg = page.svg;
|
|
@@ -16943,7 +17702,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16943
17702
|
}
|
|
16944
17703
|
if (shouldOutlineText) {
|
|
16945
17704
|
try {
|
|
16946
|
-
const { convertAllTextToPath } = await import("./svgTextToPath-
|
|
17705
|
+
const { convertAllTextToPath } = await import("./svgTextToPath-C20Obtt2.js");
|
|
16947
17706
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16948
17707
|
try {
|
|
16949
17708
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|