@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.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,
|
|
@@ -6250,7 +6810,6 @@ function bakeEdgeFade(source, fade) {
|
|
|
6250
6810
|
let x = 0, y = 0, rectW = mask.width, rectH = mask.height;
|
|
6251
6811
|
const STOPS = 64;
|
|
6252
6812
|
const gamma = 1 / hardness;
|
|
6253
|
-
const seamGuardPx = 2;
|
|
6254
6813
|
const innerAlpha = (t) => {
|
|
6255
6814
|
const keepProgress = Math.pow(t, gamma);
|
|
6256
6815
|
const erase = (1 - amount) * (1 - keepProgress);
|
|
@@ -6288,16 +6847,6 @@ function bakeEdgeFade(source, fade) {
|
|
|
6288
6847
|
}
|
|
6289
6848
|
mctx.fillStyle = g;
|
|
6290
6849
|
mctx.fillRect(x, y, rectW, rectH);
|
|
6291
|
-
{
|
|
6292
|
-
const edgePx = Math.min(seamGuardPx, side === "top" || side === "bottom" ? rectH : rectW);
|
|
6293
|
-
mctx.fillStyle = "rgba(0,0,0,0)";
|
|
6294
|
-
mctx.globalCompositeOperation = "copy";
|
|
6295
|
-
if (side === "top") mctx.fillRect(0, 0, mask.width, edgePx);
|
|
6296
|
-
if (side === "bottom") mctx.fillRect(0, mask.height - edgePx, mask.width, edgePx);
|
|
6297
|
-
if (side === "left") mctx.fillRect(0, 0, edgePx, mask.height);
|
|
6298
|
-
if (side === "right") mctx.fillRect(mask.width - edgePx, 0, edgePx, mask.height);
|
|
6299
|
-
mctx.globalCompositeOperation = "source-over";
|
|
6300
|
-
}
|
|
6301
6850
|
ctx.globalCompositeOperation = "destination-in";
|
|
6302
6851
|
ctx.drawImage(mask, 0, 0);
|
|
6303
6852
|
ctx.globalCompositeOperation = "source-over";
|
|
@@ -6383,6 +6932,10 @@ const PageCanvas = forwardRef(
|
|
|
6383
6932
|
skipFontReadyWait = false,
|
|
6384
6933
|
onReady
|
|
6385
6934
|
}, ref) => {
|
|
6935
|
+
setMarkdownThemeColors({
|
|
6936
|
+
primary: projectSettings.primaryColor || "#7c3aed",
|
|
6937
|
+
secondary: projectSettings.secondaryColor || "#ffe066"
|
|
6938
|
+
});
|
|
6386
6939
|
const isEditorMode = mode === "editor";
|
|
6387
6940
|
const isPreviewMode = mode === "preview";
|
|
6388
6941
|
const allowEditing = isEditorMode;
|
|
@@ -6478,8 +7031,8 @@ const PageCanvas = forwardRef(
|
|
|
6478
7031
|
}, [selectedIds]);
|
|
6479
7032
|
useEffect(() => {
|
|
6480
7033
|
isActiveRef.current = isActive;
|
|
6481
|
-
if (isActive && fabricRef.current) ;
|
|
6482
|
-
}, [isActive, pageId]);
|
|
7034
|
+
if (isEditorMode && isActive && fabricRef.current) ;
|
|
7035
|
+
}, [isActive, isEditorMode, pageId]);
|
|
6483
7036
|
const getObjId = useCallback((obj) => {
|
|
6484
7037
|
return obj.__docuforgeId;
|
|
6485
7038
|
}, []);
|
|
@@ -6617,11 +7170,18 @@ const PageCanvas = forwardRef(
|
|
|
6617
7170
|
const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
|
|
6618
7171
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
6619
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
|
+
}
|
|
6620
7180
|
obj.set({
|
|
6621
7181
|
width: targetWidth,
|
|
6622
7182
|
minWidth: 1,
|
|
6623
7183
|
dynamicMinWidth: 0,
|
|
6624
|
-
text:
|
|
7184
|
+
text: reflowText,
|
|
6625
7185
|
fontSize: element.fontSize || 16,
|
|
6626
7186
|
fontFamily: element.fontFamily || "Open Sans",
|
|
6627
7187
|
fontWeight: element.fontWeight || 400,
|
|
@@ -6630,6 +7190,11 @@ const PageCanvas = forwardRef(
|
|
|
6630
7190
|
charSpacing: element.charSpacing || 0,
|
|
6631
7191
|
splitByGrapheme
|
|
6632
7192
|
});
|
|
7193
|
+
if (element.formattingEnabled === true) {
|
|
7194
|
+
obj.styles = reflowParsedStyles || {};
|
|
7195
|
+
}
|
|
7196
|
+
obj.editable = element.formattingEnabled !== true;
|
|
7197
|
+
obj.__formattingEnabled = element.formattingEnabled === true;
|
|
6633
7198
|
obj.initDimensions();
|
|
6634
7199
|
if (Math.abs((obj.width ?? 0) - targetWidth) > 0.01) {
|
|
6635
7200
|
obj.width = targetWidth;
|
|
@@ -6637,10 +7202,26 @@ const PageCanvas = forwardRef(
|
|
|
6637
7202
|
obj.dynamicMinWidth = 0;
|
|
6638
7203
|
obj.setCoords();
|
|
6639
7204
|
obj.dirty = true;
|
|
7205
|
+
try {
|
|
7206
|
+
obj._forceClearCache = true;
|
|
7207
|
+
if (typeof obj._clearCache === "function") obj._clearCache();
|
|
7208
|
+
} catch {
|
|
7209
|
+
}
|
|
6640
7210
|
didReflow = true;
|
|
6641
7211
|
};
|
|
6642
7212
|
canvas2.getObjects().forEach(reflowObject);
|
|
6643
|
-
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
|
+
}
|
|
6644
7225
|
}, []);
|
|
6645
7226
|
useEffect(() => {
|
|
6646
7227
|
if (!canvasElRef.current) return;
|
|
@@ -6687,7 +7268,8 @@ const PageCanvas = forwardRef(
|
|
|
6687
7268
|
absolutePositioned: true
|
|
6688
7269
|
});
|
|
6689
7270
|
fabricRef.current = fabricCanvas;
|
|
6690
|
-
registerFabricCanvas(pageId, fabricCanvas);
|
|
7271
|
+
const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
|
|
7272
|
+
fabricCanvas.__storeRegistryKey = storeRegistryKey;
|
|
6691
7273
|
const initFonts = async () => {
|
|
6692
7274
|
try {
|
|
6693
7275
|
await preloadAllFonts();
|
|
@@ -6980,7 +7562,10 @@ const PageCanvas = forwardRef(
|
|
|
6980
7562
|
if (!window.__fabricCanvasRegistry || !(window.__fabricCanvasRegistry instanceof Map)) {
|
|
6981
7563
|
window.__fabricCanvasRegistry = /* @__PURE__ */ new Map();
|
|
6982
7564
|
}
|
|
6983
|
-
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, {
|
|
6984
7569
|
canvas: fabricCanvas,
|
|
6985
7570
|
forceUnlockEdits
|
|
6986
7571
|
});
|
|
@@ -7750,9 +8335,6 @@ const PageCanvas = forwardRef(
|
|
|
7750
8335
|
scaleY: finalScaleY,
|
|
7751
8336
|
transformMatrix: finalAbsoluteMatrix
|
|
7752
8337
|
};
|
|
7753
|
-
if (obj instanceof fabric.Textbox) {
|
|
7754
|
-
elementUpdate.text = obj.text || "";
|
|
7755
|
-
}
|
|
7756
8338
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
7757
8339
|
elementUpdate.opacity = sourceElement.opacity;
|
|
7758
8340
|
}
|
|
@@ -7831,6 +8413,12 @@ const PageCanvas = forwardRef(
|
|
|
7831
8413
|
}
|
|
7832
8414
|
if (target && target instanceof fabric.Textbox) {
|
|
7833
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
|
+
}
|
|
7834
8422
|
editingTextIdRef.current = elementId || null;
|
|
7835
8423
|
target.enterEditing();
|
|
7836
8424
|
target.selectAll();
|
|
@@ -7895,7 +8483,19 @@ const PageCanvas = forwardRef(
|
|
|
7895
8483
|
});
|
|
7896
8484
|
return () => {
|
|
7897
8485
|
setReady(false);
|
|
7898
|
-
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
|
+
}
|
|
7899
8499
|
if (fabricCanvas.__fontCleanup) {
|
|
7900
8500
|
fabricCanvas.__fontCleanup();
|
|
7901
8501
|
}
|
|
@@ -9257,6 +9857,12 @@ const PageCanvas = forwardRef(
|
|
|
9257
9857
|
} else if (obj instanceof fabric.Textbox) {
|
|
9258
9858
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
9259
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
|
+
}
|
|
9260
9866
|
let fontSize = element.fontSize || 16;
|
|
9261
9867
|
element.minFontSize || 8;
|
|
9262
9868
|
const maxLines = element.maxLines || 3;
|
|
@@ -9353,6 +9959,11 @@ const PageCanvas = forwardRef(
|
|
|
9353
9959
|
splitByGrapheme,
|
|
9354
9960
|
text
|
|
9355
9961
|
});
|
|
9962
|
+
if (element.formattingEnabled === true) {
|
|
9963
|
+
obj.styles = parsedStyles || {};
|
|
9964
|
+
} else {
|
|
9965
|
+
obj.styles = element.styles || {};
|
|
9966
|
+
}
|
|
9356
9967
|
obj.initDimensions();
|
|
9357
9968
|
if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {
|
|
9358
9969
|
obj.width = textboxWidth;
|
|
@@ -9714,7 +10325,6 @@ const PageCanvas = forwardRef(
|
|
|
9714
10325
|
const eraseAtEdge = 1 - amount;
|
|
9715
10326
|
const STOPS = 64;
|
|
9716
10327
|
const gamma = 1 / hardness;
|
|
9717
|
-
const seamGuardPx = 2;
|
|
9718
10328
|
const addStops = (g) => {
|
|
9719
10329
|
for (let i = 0; i <= STOPS; i++) {
|
|
9720
10330
|
const t = i / STOPS;
|
|
@@ -9729,32 +10339,24 @@ const PageCanvas = forwardRef(
|
|
|
9729
10339
|
addStops(gradient);
|
|
9730
10340
|
ctx.fillStyle = gradient;
|
|
9731
10341
|
ctx.fillRect(x, y, w, band);
|
|
9732
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9733
|
-
ctx.fillRect(x, y, w, Math.min(seamGuardPx, band));
|
|
9734
10342
|
} else if (side === "bottom") {
|
|
9735
10343
|
const band = Math.max(1, h * size);
|
|
9736
10344
|
gradient = ctx.createLinearGradient(0, y + h, 0, y + h - band);
|
|
9737
10345
|
addStops(gradient);
|
|
9738
10346
|
ctx.fillStyle = gradient;
|
|
9739
10347
|
ctx.fillRect(x, y + h - band, w, band);
|
|
9740
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9741
|
-
ctx.fillRect(x, y + h - Math.min(seamGuardPx, band), w, Math.min(seamGuardPx, band));
|
|
9742
10348
|
} else if (side === "left") {
|
|
9743
10349
|
const band = Math.max(1, w * size);
|
|
9744
10350
|
gradient = ctx.createLinearGradient(x, 0, x + band, 0);
|
|
9745
10351
|
addStops(gradient);
|
|
9746
10352
|
ctx.fillStyle = gradient;
|
|
9747
10353
|
ctx.fillRect(x, y, band, h);
|
|
9748
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9749
|
-
ctx.fillRect(x, y, Math.min(seamGuardPx, band), h);
|
|
9750
10354
|
} else {
|
|
9751
10355
|
const band = Math.max(1, w * size);
|
|
9752
10356
|
gradient = ctx.createLinearGradient(x + w, 0, x + w - band, 0);
|
|
9753
10357
|
addStops(gradient);
|
|
9754
10358
|
ctx.fillStyle = gradient;
|
|
9755
10359
|
ctx.fillRect(x + w - band, y, band, h);
|
|
9756
|
-
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
9757
|
-
ctx.fillRect(x + w - Math.min(seamGuardPx, band), y, Math.min(seamGuardPx, band), h);
|
|
9758
10360
|
}
|
|
9759
10361
|
ctx.restore();
|
|
9760
10362
|
};
|
|
@@ -10366,6 +10968,7 @@ function PreviewCanvas({
|
|
|
10366
10968
|
zoom = 1,
|
|
10367
10969
|
absoluteZoom = false,
|
|
10368
10970
|
skipFontReadyWait = false,
|
|
10971
|
+
pageIdOverride,
|
|
10369
10972
|
className,
|
|
10370
10973
|
onDynamicFieldClick,
|
|
10371
10974
|
onReady
|
|
@@ -10432,13 +11035,19 @@ function PreviewCanvas({
|
|
|
10432
11035
|
backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
|
|
10433
11036
|
};
|
|
10434
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]);
|
|
10435
|
-
const projectSettings = useMemo(() =>
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
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]);
|
|
10442
11051
|
const handleDynamicFieldClick = useCallback((elementId) => {
|
|
10443
11052
|
const fieldInfo = elementToFieldMap.get(elementId);
|
|
10444
11053
|
if (fieldInfo && onDynamicFieldClick) {
|
|
@@ -10508,7 +11117,7 @@ function PreviewCanvas({
|
|
|
10508
11117
|
PageCanvas,
|
|
10509
11118
|
{
|
|
10510
11119
|
ref: canvasRef,
|
|
10511
|
-
pageId: page.id || `page-${pageIndex}`,
|
|
11120
|
+
pageId: pageIdOverride || page.id || `page-${pageIndex}`,
|
|
10512
11121
|
elements,
|
|
10513
11122
|
pageChildren: laidOutPageChildren,
|
|
10514
11123
|
pageSettings,
|
|
@@ -12399,6 +13008,17 @@ function normalizeFontFamily(fontStack) {
|
|
|
12399
13008
|
const first = fontStack.split(",")[0].trim();
|
|
12400
13009
|
return first.replace(/^['"]|['"]$/g, "");
|
|
12401
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
|
+
}
|
|
12402
13022
|
const loadedFonts = /* @__PURE__ */ new Set();
|
|
12403
13023
|
const loadingPromises = /* @__PURE__ */ new Map();
|
|
12404
13024
|
function withTimeout(promise, timeoutMs = 4e3) {
|
|
@@ -12424,28 +13044,19 @@ async function loadGoogleFontCSS(rawFontFamily) {
|
|
|
12424
13044
|
const fontshareSlug = FONTSHARE_SLUGS[fontFamily];
|
|
12425
13045
|
if (fontshareSlug) {
|
|
12426
13046
|
const url2 = `https://api.fontshare.com/v2/css?f[]=${fontshareSlug}@300,400,500,700&display=swap`;
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
await new Promise((resolve, reject) => {
|
|
12431
|
-
link2.onload = () => resolve();
|
|
12432
|
-
link2.onerror = () => reject(new Error(`Failed to load Fontshare font: ${fontFamily}`));
|
|
12433
|
-
document.head.appendChild(link2);
|
|
12434
|
-
});
|
|
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);
|
|
12435
13050
|
loadedFonts.add(fontFamily);
|
|
12436
13051
|
return;
|
|
12437
13052
|
}
|
|
12438
13053
|
const encoded = encodeURIComponent(fontFamily);
|
|
12439
13054
|
const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
link.onload = () => resolve();
|
|
12446
|
-
link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));
|
|
12447
|
-
document.head.appendChild(link);
|
|
12448
|
-
});
|
|
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);
|
|
12449
13060
|
loadedFonts.add(fontFamily);
|
|
12450
13061
|
} catch (e) {
|
|
12451
13062
|
console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);
|
|
@@ -12515,6 +13126,23 @@ function collectFontDescriptorsFromConfig(config) {
|
|
|
12515
13126
|
if (node.type === "text") {
|
|
12516
13127
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
12517
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
|
+
}
|
|
12518
13146
|
}
|
|
12519
13147
|
}
|
|
12520
13148
|
}
|
|
@@ -13424,11 +14052,20 @@ function PixldocsPreview(props) {
|
|
|
13424
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..." }) })
|
|
13425
14053
|
] });
|
|
13426
14054
|
}
|
|
13427
|
-
const PACKAGE_VERSION = "0.5.
|
|
14055
|
+
const PACKAGE_VERSION = "0.5.105";
|
|
13428
14056
|
const roundParityValue = (value) => {
|
|
13429
14057
|
if (typeof value !== "number") return value;
|
|
13430
14058
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
13431
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
|
+
}
|
|
13432
14069
|
function logJsonLine(tag, payload) {
|
|
13433
14070
|
try {
|
|
13434
14071
|
console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
|
|
@@ -13466,6 +14103,9 @@ function installUnderlineFix(fab) {
|
|
|
13466
14103
|
const hasOwn = !!this[type];
|
|
13467
14104
|
const hasStyled = typeof this.styleHas === "function" && this.styleHas(type);
|
|
13468
14105
|
if (!hasOwn && !hasStyled) return;
|
|
14106
|
+
if (!hasOwn && hasStyled) {
|
|
14107
|
+
return original.call(this, ctx, type);
|
|
14108
|
+
}
|
|
13469
14109
|
const lines = this._textLines;
|
|
13470
14110
|
const offsets = this.offsets;
|
|
13471
14111
|
if (!Array.isArray(lines) || !offsets) {
|
|
@@ -13477,7 +14117,7 @@ function installUnderlineFix(fab) {
|
|
|
13477
14117
|
let topOffset = this._getTopOffset();
|
|
13478
14118
|
for (let i = 0, len = lines.length; i < len; i++) {
|
|
13479
14119
|
const heightOfLine = this.getHeightOfLine(i);
|
|
13480
|
-
const lineHas = !!this[type]
|
|
14120
|
+
const lineHas = !!this[type];
|
|
13481
14121
|
if (!lineHas) {
|
|
13482
14122
|
topOffset += heightOfLine;
|
|
13483
14123
|
continue;
|
|
@@ -14007,9 +14647,11 @@ class PixldocsRenderer {
|
|
|
14007
14647
|
}
|
|
14008
14648
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
14009
14649
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14010
|
-
const
|
|
14011
|
-
const
|
|
14012
|
-
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);
|
|
14013
14655
|
let firstMountSettled = false;
|
|
14014
14656
|
let lateFontSettleDetected = false;
|
|
14015
14657
|
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
@@ -14057,12 +14699,12 @@ class PixldocsRenderer {
|
|
|
14057
14699
|
root = createRoot(container);
|
|
14058
14700
|
await new Promise((settle) => {
|
|
14059
14701
|
const onReadyOnce = () => {
|
|
14060
|
-
this.waitForCanvasScene(container,
|
|
14702
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14061
14703
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14062
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14704
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14063
14705
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14064
|
-
await this.waitForStableTextMetrics(container,
|
|
14065
|
-
await this.waitForCanvasScene(container,
|
|
14706
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14707
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14066
14708
|
if (!fabricInstance) return settle();
|
|
14067
14709
|
settle();
|
|
14068
14710
|
}).catch(() => settle());
|
|
@@ -14070,8 +14712,9 @@ class PixldocsRenderer {
|
|
|
14070
14712
|
root.render(
|
|
14071
14713
|
createElement(PreviewCanvas2, {
|
|
14072
14714
|
key: `remount-${mountKey}`,
|
|
14073
|
-
config,
|
|
14715
|
+
config: renderConfig,
|
|
14074
14716
|
pageIndex,
|
|
14717
|
+
pageIdOverride: capture.pageId,
|
|
14075
14718
|
zoom: pixelRatio,
|
|
14076
14719
|
absoluteZoom: true,
|
|
14077
14720
|
skipFontReadyWait: false,
|
|
@@ -14081,13 +14724,13 @@ class PixldocsRenderer {
|
|
|
14081
14724
|
});
|
|
14082
14725
|
};
|
|
14083
14726
|
const onReady = () => {
|
|
14084
|
-
this.waitForCanvasScene(container,
|
|
14727
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14085
14728
|
try {
|
|
14086
14729
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14087
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14730
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14088
14731
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14089
|
-
await this.waitForStableTextMetrics(container,
|
|
14090
|
-
await this.waitForCanvasScene(container,
|
|
14732
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14733
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14091
14734
|
firstMountSettled = true;
|
|
14092
14735
|
if (hasAutoShrink && lateFontSettleDetected) {
|
|
14093
14736
|
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
@@ -14113,7 +14756,7 @@ class PixldocsRenderer {
|
|
|
14113
14756
|
}
|
|
14114
14757
|
exportCtx.save();
|
|
14115
14758
|
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
14116
|
-
this.paintPageBackground(exportCtx,
|
|
14759
|
+
this.paintPageBackground(exportCtx, renderConfig.pages[pageIndex], canvasWidth, canvasHeight);
|
|
14117
14760
|
exportCtx.restore();
|
|
14118
14761
|
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
14119
14762
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
@@ -14129,8 +14772,9 @@ class PixldocsRenderer {
|
|
|
14129
14772
|
root = createRoot(container);
|
|
14130
14773
|
root.render(
|
|
14131
14774
|
createElement(PreviewCanvas2, {
|
|
14132
|
-
config,
|
|
14775
|
+
config: renderConfig,
|
|
14133
14776
|
pageIndex,
|
|
14777
|
+
pageIdOverride: capture.pageId,
|
|
14134
14778
|
zoom: pixelRatio,
|
|
14135
14779
|
absoluteZoom: true,
|
|
14136
14780
|
skipFontReadyWait: false,
|
|
@@ -14151,6 +14795,8 @@ class PixldocsRenderer {
|
|
|
14151
14795
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
14152
14796
|
return new Promise(async (resolve, reject) => {
|
|
14153
14797
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
14798
|
+
const capture = isolatePageForCapture(config, pageIndex);
|
|
14799
|
+
const renderConfig = capture.config;
|
|
14154
14800
|
const container = document.createElement("div");
|
|
14155
14801
|
container.style.cssText = `
|
|
14156
14802
|
position: fixed; left: -99999px; top: -99999px;
|
|
@@ -14177,8 +14823,9 @@ class PixldocsRenderer {
|
|
|
14177
14823
|
root.render(
|
|
14178
14824
|
createElement(PreviewCanvas2, {
|
|
14179
14825
|
key: `svg-capture-${mountKey}`,
|
|
14180
|
-
config,
|
|
14826
|
+
config: renderConfig,
|
|
14181
14827
|
pageIndex,
|
|
14828
|
+
pageIdOverride: capture.pageId,
|
|
14182
14829
|
zoom: 1,
|
|
14183
14830
|
absoluteZoom: true,
|
|
14184
14831
|
skipFontReadyWait: false,
|
|
@@ -14187,13 +14834,13 @@ class PixldocsRenderer {
|
|
|
14187
14834
|
);
|
|
14188
14835
|
};
|
|
14189
14836
|
const onReady = () => {
|
|
14190
|
-
this.waitForCanvasScene(container,
|
|
14837
|
+
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
14191
14838
|
var _a, _b;
|
|
14192
14839
|
try {
|
|
14193
|
-
const expectedImageCount = this.getExpectedImageCount(
|
|
14840
|
+
const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
|
|
14194
14841
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
14195
|
-
await this.waitForStableTextMetrics(container,
|
|
14196
|
-
await this.waitForCanvasScene(container,
|
|
14842
|
+
await this.waitForStableTextMetrics(container, renderConfig);
|
|
14843
|
+
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
14197
14844
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
14198
14845
|
if (!fabricInstance) {
|
|
14199
14846
|
cleanup();
|
|
@@ -14282,7 +14929,7 @@ class PixldocsRenderer {
|
|
|
14282
14929
|
);
|
|
14283
14930
|
if (prevVPT) fabricInstance.viewportTransform = prevVPT;
|
|
14284
14931
|
fabricInstance.svgViewportTransformation = prevSvgVPT;
|
|
14285
|
-
const page =
|
|
14932
|
+
const page = renderConfig.pages[pageIndex];
|
|
14286
14933
|
const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
|
|
14287
14934
|
const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
|
|
14288
14935
|
cleanup();
|
|
@@ -14964,8 +15611,9 @@ function getFontPathForWeight(files, weight, isItalic = false) {
|
|
|
14964
15611
|
}
|
|
14965
15612
|
return files.regular;
|
|
14966
15613
|
}
|
|
14967
|
-
function
|
|
14968
|
-
|
|
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;
|
|
14969
15617
|
}
|
|
14970
15618
|
function getJsPDFFontName(fontName) {
|
|
14971
15619
|
return fontName.replace(/\s+/g, "");
|
|
@@ -15038,10 +15686,10 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15038
15686
|
const resolvedWeight = resolveFontWeight(weight);
|
|
15039
15687
|
const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
|
|
15040
15688
|
if (!fontPath) return false;
|
|
15041
|
-
|
|
15042
|
-
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight,
|
|
15689
|
+
if (!isExactWeightItalicMatch(fontFiles, resolvedWeight, isItalic, fontPath)) return false;
|
|
15690
|
+
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
15043
15691
|
const label = FONT_WEIGHT_LABELS[resolvedWeight];
|
|
15044
|
-
const italicSuffix =
|
|
15692
|
+
const italicSuffix = isItalic ? "Italic" : "";
|
|
15045
15693
|
const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
|
|
15046
15694
|
const url = baseUrl + fontPath;
|
|
15047
15695
|
try {
|
|
@@ -15056,6 +15704,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15056
15704
|
}
|
|
15057
15705
|
}
|
|
15058
15706
|
registeredFamilies.add(fontName);
|
|
15707
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15059
15708
|
return true;
|
|
15060
15709
|
} catch (e) {
|
|
15061
15710
|
console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
|
|
@@ -15064,6 +15713,18 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
|
|
|
15064
15713
|
}
|
|
15065
15714
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
15066
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
|
+
};
|
|
15067
15728
|
function bytesToBase64(bytes) {
|
|
15068
15729
|
let binary = "";
|
|
15069
15730
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
@@ -15092,7 +15753,8 @@ async function fetchTtfViaProxy(family, weight, isItalic, source) {
|
|
|
15092
15753
|
async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
15093
15754
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
15094
15755
|
if (ttfCache.has(cacheKey)) return ttfCache.get(cacheKey);
|
|
15095
|
-
|
|
15756
|
+
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
15757
|
+
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
15096
15758
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
15097
15759
|
if (proxyBytes) {
|
|
15098
15760
|
const b64 = bytesToBase64(proxyBytes);
|
|
@@ -15110,7 +15772,7 @@ async function fetchGoogleFontTTF(fontFamily, weight, isItalic = false) {
|
|
|
15110
15772
|
}
|
|
15111
15773
|
});
|
|
15112
15774
|
if (!cssRes.ok) {
|
|
15113
|
-
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(
|
|
15775
|
+
if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(notFoundKey);
|
|
15114
15776
|
return null;
|
|
15115
15777
|
}
|
|
15116
15778
|
const css = await cssRes.text();
|
|
@@ -15188,6 +15850,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
|
|
|
15188
15850
|
}
|
|
15189
15851
|
}
|
|
15190
15852
|
registeredFamilies.add(fontName);
|
|
15853
|
+
registeredVariants.add(variantKey(fontName, resolvedWeight, isItalic));
|
|
15191
15854
|
return true;
|
|
15192
15855
|
} catch (err) {
|
|
15193
15856
|
console.warn(`[pdf-fonts] registerJsPdfFont failed for ${fontName}:`, err);
|
|
@@ -15202,16 +15865,8 @@ async function embedFontWithGoogleFallback(pdf, fontName, weight = 400, fontBase
|
|
|
15202
15865
|
const resolved = resolveFontWeight(weight);
|
|
15203
15866
|
const fsB64 = await fetchFontshareTTF(fontName, resolved, isItalic);
|
|
15204
15867
|
if (fsB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsB64);
|
|
15205
|
-
if (isItalic) {
|
|
15206
|
-
const fsUpright = await fetchFontshareTTF(fontName, resolved, false);
|
|
15207
|
-
if (fsUpright) return registerJsPdfFont(pdf, fontName, resolved, isItalic, fsUpright);
|
|
15208
|
-
}
|
|
15209
15868
|
const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);
|
|
15210
15869
|
if (b64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);
|
|
15211
|
-
if (isItalic) {
|
|
15212
|
-
const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);
|
|
15213
|
-
if (uprightB64) return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);
|
|
15214
|
-
}
|
|
15215
15870
|
return false;
|
|
15216
15871
|
}
|
|
15217
15872
|
async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
@@ -15231,9 +15886,15 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15231
15886
|
};
|
|
15232
15887
|
const walkElements = (elements) => {
|
|
15233
15888
|
for (const el of elements) {
|
|
15889
|
+
const addFontVariant = (family, weight, italic) => {
|
|
15890
|
+
fontKeys.add(`${family}${SEP}${weight}${SEP}${italic ? "italic" : "normal"}`);
|
|
15891
|
+
};
|
|
15234
15892
|
if (el.fontFamily) {
|
|
15235
15893
|
const w = normalizeWeight(el.fontWeight);
|
|
15236
|
-
|
|
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);
|
|
15237
15898
|
}
|
|
15238
15899
|
if (el.styles && typeof el.styles === "object") {
|
|
15239
15900
|
for (const lineKey of Object.keys(el.styles)) {
|
|
@@ -15241,9 +15902,10 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15241
15902
|
if (lineStyles && typeof lineStyles === "object") {
|
|
15242
15903
|
for (const charKey of Object.keys(lineStyles)) {
|
|
15243
15904
|
const s = lineStyles[charKey];
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
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 ?? "")));
|
|
15247
15909
|
}
|
|
15248
15910
|
}
|
|
15249
15911
|
}
|
|
@@ -15257,19 +15919,21 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
15257
15919
|
if (page.children) walkElements(page.children);
|
|
15258
15920
|
if (page.elements) walkElements(page.elements);
|
|
15259
15921
|
}
|
|
15260
|
-
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
|
|
15922
|
+
fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
|
|
15261
15923
|
for (const w of [300, 400, 500, 600, 700]) {
|
|
15262
|
-
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
|
|
15924
|
+
fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
|
|
15263
15925
|
}
|
|
15264
15926
|
const embedded = /* @__PURE__ */ new Set();
|
|
15265
15927
|
const tasks = [];
|
|
15266
15928
|
for (const key of fontKeys) {
|
|
15267
15929
|
const sep = key.indexOf(SEP);
|
|
15930
|
+
const secondSep = key.indexOf(SEP, sep + 1);
|
|
15268
15931
|
const fontName = key.slice(0, sep);
|
|
15269
|
-
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";
|
|
15270
15934
|
if (!isFontAvailable(fontName)) continue;
|
|
15271
15935
|
tasks.push(
|
|
15272
|
-
embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
|
|
15936
|
+
embedFont(pdf, fontName, weight, fontBaseUrl, italic).then((ok) => {
|
|
15273
15937
|
if (ok) embedded.add(key);
|
|
15274
15938
|
})
|
|
15275
15939
|
);
|
|
@@ -15325,7 +15989,7 @@ function splitIntoRuns(text) {
|
|
|
15325
15989
|
return runs;
|
|
15326
15990
|
}
|
|
15327
15991
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
15328
|
-
var _a, _b;
|
|
15992
|
+
var _a, _b, _c;
|
|
15329
15993
|
const parser = new DOMParser();
|
|
15330
15994
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
15331
15995
|
const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
|
|
@@ -15357,6 +16021,13 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15357
16021
|
stylePairs.push(`font-style: normal`);
|
|
15358
16022
|
return stylePairs.join("; ");
|
|
15359
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
|
+
};
|
|
15360
16031
|
const getDepth = (el) => {
|
|
15361
16032
|
let depth = 0;
|
|
15362
16033
|
let current = el.parentElement;
|
|
@@ -15376,20 +16047,40 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15376
16047
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
15377
16048
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
15378
16049
|
const weight = resolveWeightNum(weightRaw);
|
|
15379
|
-
const
|
|
15380
|
-
const
|
|
15381
|
-
const
|
|
15382
|
-
|
|
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 });
|
|
15383
16056
|
}
|
|
15384
16057
|
const textEls = allTextEls.sort((a, b) => getDepth(b) - getDepth(a));
|
|
15385
16058
|
for (const el of textEls) {
|
|
15386
16059
|
if (!el.isConnected) continue;
|
|
15387
16060
|
const meta = sourceMeta.get(el);
|
|
15388
16061
|
if (!meta) continue;
|
|
15389
|
-
const { clean, resolved, isItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
16062
|
+
const { clean, requested, resolved, isItalic, actualItalic, jsPdfName, inlineStyle, weight } = meta;
|
|
15390
16063
|
el.setAttribute("data-source-font-family", clean);
|
|
15391
|
-
el.setAttribute("data-source-font-weight", String(
|
|
16064
|
+
el.setAttribute("data-source-font-weight", String(requested));
|
|
15392
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
|
+
}
|
|
15393
16084
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
15394
16085
|
const hasDevanagari = containsDevanagari(directText);
|
|
15395
16086
|
const hasSymbol = containsSymbol(directText);
|
|
@@ -15432,6 +16123,28 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
15432
16123
|
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
15433
16124
|
}
|
|
15434
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
|
+
}
|
|
15435
16148
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
15436
16149
|
}
|
|
15437
16150
|
function extractFontFamiliesFromSvgs(svgs) {
|
|
@@ -15462,14 +16175,29 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
|
|
|
15462
16175
|
if (ok) embedded.add(`${family}${w}`);
|
|
15463
16176
|
})
|
|
15464
16177
|
);
|
|
16178
|
+
tasks.push(
|
|
16179
|
+
embedFont(pdf, family, w, fontBaseUrl, true).then((ok) => {
|
|
16180
|
+
if (ok) embedded.add(`${family}${w}i`);
|
|
16181
|
+
})
|
|
16182
|
+
);
|
|
15465
16183
|
}
|
|
15466
16184
|
} else {
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
}
|
|
15472
|
-
|
|
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
|
+
}
|
|
15473
16201
|
}
|
|
15474
16202
|
}
|
|
15475
16203
|
await Promise.all(tasks);
|
|
@@ -15490,6 +16218,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
15490
16218
|
getEmbeddedJsPDFFontName,
|
|
15491
16219
|
getFontPathForWeight,
|
|
15492
16220
|
isFontAvailable,
|
|
16221
|
+
resolveBestRegisteredVariant,
|
|
15493
16222
|
resolveFontWeight,
|
|
15494
16223
|
rewriteSvgFontsForJsPDF
|
|
15495
16224
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -15529,9 +16258,14 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
|
|
|
15529
16258
|
const sample = texts.slice(0, maxItems).map((t, idx) => {
|
|
15530
16259
|
var _a, _b;
|
|
15531
16260
|
const tspans = Array.from(t.querySelectorAll("tspan"));
|
|
15532
|
-
const tspanInfo = tspans.slice(0,
|
|
16261
|
+
const tspanInfo = tspans.slice(0, 8).map((s) => ({
|
|
15533
16262
|
x: s.getAttribute("x"),
|
|
15534
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),
|
|
15535
16269
|
text: (s.textContent || "").slice(0, 40)
|
|
15536
16270
|
}));
|
|
15537
16271
|
let containerWidth = null;
|
|
@@ -16566,13 +17300,15 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16566
17300
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
16567
17301
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
16568
17302
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
17303
|
+
const textHasLinethrough = textDecOnText.includes("line-through") || textStyleDec.includes("line-through");
|
|
16569
17304
|
for (let si = 0; si < tspans.length; si++) {
|
|
16570
17305
|
const tspan = tspans[si];
|
|
16571
17306
|
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
16572
17307
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
16573
17308
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
16574
17309
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
16575
|
-
|
|
17310
|
+
const hasLinethrough = tspanDec.includes("line-through") || tspanStyleDec.includes("line-through") || textHasLinethrough;
|
|
17311
|
+
if (!hasUnderline && !hasLinethrough) continue;
|
|
16576
17312
|
const content = tspan.textContent || "";
|
|
16577
17313
|
if (!content.trim()) continue;
|
|
16578
17314
|
const xAttr = tspan.getAttribute("x") ?? textEl.getAttribute("x") ?? "0";
|
|
@@ -16627,23 +17363,26 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
16627
17363
|
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
16628
17364
|
lineEndX = lineStartX + textWidth;
|
|
16629
17365
|
}
|
|
16630
|
-
const underlineY = baselineY + fontSize * 0.15;
|
|
16631
17366
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
16632
|
-
const
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
textEl.parentElement
|
|
16644
|
-
|
|
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);
|
|
16645
17384
|
stripTextDecoration(tspan);
|
|
16646
|
-
if (textHasUnderline) stripTextDecoration(textEl);
|
|
17385
|
+
if (textHasUnderline || textHasLinethrough) stripTextDecoration(textEl);
|
|
16647
17386
|
}
|
|
16648
17387
|
}
|
|
16649
17388
|
if (tempContainer) {
|
|
@@ -16657,7 +17396,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
|
|
|
16657
17396
|
if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") {
|
|
16658
17397
|
return svgStr;
|
|
16659
17398
|
}
|
|
16660
|
-
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)) {
|
|
16661
17400
|
return svgStr;
|
|
16662
17401
|
}
|
|
16663
17402
|
try {
|
|
@@ -16949,7 +17688,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16949
17688
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
16950
17689
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
16951
17690
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
16952
|
-
const textMode = options.textMode ?? (options.outlineText ? "
|
|
17691
|
+
const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "selectable");
|
|
16953
17692
|
const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
|
|
16954
17693
|
const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
|
|
16955
17694
|
let pageSvg = page.svg;
|
|
@@ -16963,7 +17702,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
16963
17702
|
}
|
|
16964
17703
|
if (shouldOutlineText) {
|
|
16965
17704
|
try {
|
|
16966
|
-
const { convertAllTextToPath } = await import("./svgTextToPath-
|
|
17705
|
+
const { convertAllTextToPath } = await import("./svgTextToPath-C20Obtt2.js");
|
|
16967
17706
|
pageSvg = await convertAllTextToPath(pageSvg, fontBaseUrl, { mode: outlineSubMode });
|
|
16968
17707
|
try {
|
|
16969
17708
|
dumpSvgTextDiagnostics(pageSvg, i, PARITY_TAG, "STAGE-1b-after-text-to-path-raw");
|