@pixldocs/canvas-renderer 0.5.65 → 0.5.66

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svgTextToPath-BTHnqJpM.cjs","sources":["../../../src/lib/pdfFonts.ts","../../../src/lib/harfbuzzShaper.ts","../../../src/lib/svgTextToPath.ts"],"sourcesContent":["// Utility for loading local TTF fonts into jsPDF for vector PDF rendering\n\nconst fontCache: Map<string, string> = new Map();\n/** Cache for raw font bytes (used by HarfBuzz / opentype.js for path conversion) */\nconst fontBytesCache: Map<string, Uint8Array> = new Map();\n/** Cache for Google Fonts CSS-parsed TTF URLs (so we don't re-fetch CSS for every weight) */\nconst googleFontUrlCache: Map<string, string> = new Map();\n/** Negative cache — fonts known not to exist on Google Fonts; don't keep retrying */\nconst googleFontNotFound: Set<string> = new Set();\n\n// Weight labels for jsPDF font names (editor uses 300, 400, 500, 600, 700)\nexport const FONT_WEIGHT_LABELS: Record<number, string> = {\n 300: 'Light',\n 400: 'Regular',\n 500: 'Medium',\n 600: 'SemiBold',\n 700: 'Bold',\n};\n\n// Resolve numeric weight to the closest supported key (300, 400, 500, 600, 700)\nexport function resolveFontWeight(weight: number): number {\n if (weight <= 350) return 300;\n if (weight <= 450) return 400;\n if (weight <= 550) return 500;\n if (weight <= 650) return 600;\n return 700;\n}\n\n// Font file mapping - maps font names to optional weight-specific TTF paths\nexport type FontWeightFiles = {\n regular: string;\n bold?: string;\n light?: string;\n medium?: string;\n semibold?: string;\n // Italic variants\n italic?: string;\n boldItalic?: string;\n lightItalic?: string;\n mediumItalic?: string;\n semiboldItalic?: string;\n};\n\n/** Font used for symbols (● ◆ ★ etc.) when the main font lacks the glyph. Must be in FONT_FILES. */\nexport const FONT_FALLBACK_SYMBOLS = 'Noto Sans';\n\n/** Font used for Devanagari / Hindi script when the main font lacks the glyphs. Must be in FONT_FILES. */\nexport const FONT_FALLBACK_DEVANAGARI = 'Hind';\n\n// List paths under public/fonts.\n// All entries now use static (per-weight) TTF files for reliable PDF export.\n// Variable font files are kept in fonts.css for browser rendering only.\nexport const FONT_FILES: Record<string, FontWeightFiles> = {\n 'Playfair Display': {\n regular: '/fonts/PlayfairDisplay-Regular.ttf',\n bold: '/fonts/PlayfairDisplay-Bold.ttf',\n italic: '/fonts/PlayfairDisplay-Italic.ttf',\n boldItalic: '/fonts/PlayfairDisplay-BoldItalic.ttf',\n },\n 'Merriweather': {\n regular: '/fonts/Merriweather-Regular.ttf',\n bold: '/fonts/Merriweather-Bold.ttf',\n light: '/fonts/Merriweather-Light.ttf',\n italic: '/fonts/Merriweather-Italic.ttf',\n boldItalic: '/fonts/Merriweather-BoldItalic.ttf',\n lightItalic: '/fonts/Merriweather-LightItalic.ttf',\n },\n 'Lora': {\n regular: '/fonts/Lora-Regular.ttf',\n bold: '/fonts/Lora-Bold.ttf',\n italic: '/fonts/Lora-Italic.ttf',\n boldItalic: '/fonts/Lora-BoldItalic.ttf',\n },\n 'Montserrat': {\n regular: '/fonts/Montserrat-Regular.ttf',\n bold: '/fonts/Montserrat-Bold.ttf',\n light: '/fonts/Montserrat-Light.ttf',\n medium: '/fonts/Montserrat-Medium.ttf',\n semibold: '/fonts/Montserrat-SemiBold.ttf',\n italic: '/fonts/Montserrat-Italic.ttf',\n boldItalic: '/fonts/Montserrat-BoldItalic.ttf',\n lightItalic: '/fonts/Montserrat-LightItalic.ttf',\n mediumItalic: '/fonts/Montserrat-MediumItalic.ttf',\n semiboldItalic: '/fonts/Montserrat-SemiBoldItalic.ttf',\n },\n 'Open Sans': {\n regular: '/fonts/OpenSans-Regular.ttf',\n bold: '/fonts/OpenSans-Bold.ttf',\n light: '/fonts/OpenSans-Light.ttf',\n semibold: '/fonts/OpenSans-SemiBold.ttf',\n italic: '/fonts/OpenSans-Italic.ttf',\n boldItalic: '/fonts/OpenSans-BoldItalic.ttf',\n lightItalic: '/fonts/OpenSans-LightItalic.ttf',\n semiboldItalic: '/fonts/OpenSans-SemiBoldItalic.ttf',\n },\n 'Roboto': {\n regular: '/fonts/Roboto-Regular.ttf',\n bold: '/fonts/Roboto-Bold.ttf',\n light: '/fonts/Roboto-Light.ttf',\n medium: '/fonts/Roboto-Medium.ttf',\n italic: '/fonts/Roboto-Italic.ttf',\n boldItalic: '/fonts/Roboto-BoldItalic.ttf',\n lightItalic: '/fonts/Roboto-LightItalic.ttf',\n mediumItalic: '/fonts/Roboto-MediumItalic.ttf',\n },\n 'Lato': {\n regular: '/fonts/Lato-Regular.ttf',\n bold: '/fonts/Lato-Bold.ttf',\n light: '/fonts/Lato-Light.ttf',\n italic: '/fonts/Lato-Italic.ttf',\n boldItalic: '/fonts/Lato-BoldItalic.ttf',\n lightItalic: '/fonts/Lato-LightItalic.ttf',\n },\n 'Raleway': {\n regular: '/fonts/Raleway-Regular.ttf',\n bold: '/fonts/Raleway-Bold.ttf',\n light: '/fonts/Raleway-Light.ttf',\n medium: '/fonts/Raleway-Medium.ttf',\n semibold: '/fonts/Raleway-SemiBold.ttf',\n italic: '/fonts/Raleway-Italic.ttf',\n boldItalic: '/fonts/Raleway-BoldItalic.ttf',\n lightItalic: '/fonts/Raleway-LightItalic.ttf',\n },\n 'Poppins': {\n regular: '/fonts/Poppins-Regular.ttf',\n bold: '/fonts/Poppins-Bold.ttf',\n light: '/fonts/Poppins-Light.ttf',\n medium: '/fonts/Poppins-Medium.ttf',\n semibold: '/fonts/Poppins-SemiBold.ttf',\n italic: '/fonts/Poppins-Italic.ttf',\n boldItalic: '/fonts/Poppins-BoldItalic.ttf',\n lightItalic: '/fonts/Poppins-LightItalic.ttf',\n mediumItalic: '/fonts/Poppins-MediumItalic.ttf',\n semiboldItalic: '/fonts/Poppins-SemiBoldItalic.ttf',\n },\n 'Inter': {\n regular: '/fonts/Inter-Regular.ttf',\n bold: '/fonts/Inter-Bold.ttf',\n italic: '/fonts/Inter-Italic.ttf',\n boldItalic: '/fonts/Inter-BoldItalic.ttf',\n },\n 'Nunito': {\n regular: '/fonts/Nunito-Regular.ttf',\n bold: '/fonts/Nunito-Bold.ttf',\n light: '/fonts/Nunito-Light.ttf',\n medium: '/fonts/Nunito-Medium.ttf',\n semibold: '/fonts/Nunito-SemiBold.ttf',\n italic: '/fonts/Nunito-Italic.ttf',\n boldItalic: '/fonts/Nunito-BoldItalic.ttf',\n },\n 'Source Sans Pro': {\n regular: '/fonts/SourceSansPro-Regular.ttf',\n bold: '/fonts/SourceSansPro-Bold.ttf',\n light: '/fonts/SourceSansPro-Light.ttf',\n italic: '/fonts/SourceSansPro-Italic.ttf',\n boldItalic: '/fonts/SourceSansPro-BoldItalic.ttf',\n },\n 'Work Sans': {\n regular: '/fonts/WorkSans-Regular.ttf',\n bold: '/fonts/WorkSans-Bold.ttf',\n italic: '/fonts/WorkSans-Italic.ttf',\n boldItalic: '/fonts/WorkSans-BoldItalic.ttf',\n },\n 'Oswald': { regular: '/fonts/Oswald-Regular.ttf', bold: '/fonts/Oswald-Bold.ttf' },\n 'Bebas Neue': { regular: '/fonts/BebasNeue-Regular.ttf' },\n 'Abril Fatface': { regular: '/fonts/AbrilFatface-Regular.ttf' },\n 'Dancing Script': { regular: '/fonts/DancingScript-Regular.ttf', bold: '/fonts/DancingScript-Bold.ttf' },\n 'Pacifico': { regular: '/fonts/Pacifico-Regular.ttf' },\n 'Great Vibes': { regular: '/fonts/GreatVibes-Regular.ttf' },\n\n // ── Previously variable-only fonts — now with static per-weight TTFs for PDF export ──\n 'DM Sans': {\n regular: '/fonts/DMSans-Regular.ttf',\n bold: '/fonts/DMSans-Bold.ttf',\n light: '/fonts/DMSans-Light.ttf',\n medium: '/fonts/DMSans-Medium.ttf',\n semibold: '/fonts/DMSans-SemiBold.ttf',\n italic: '/fonts/DMSans-RegularItalic.ttf',\n boldItalic: '/fonts/DMSans-BoldItalic.ttf',\n lightItalic: '/fonts/DMSans-LightItalic.ttf',\n mediumItalic: '/fonts/DMSans-MediumItalic.ttf',\n semiboldItalic: '/fonts/DMSans-SemiBoldItalic.ttf',\n },\n 'Outfit': {\n regular: '/fonts/Outfit-Regular.ttf',\n bold: '/fonts/Outfit-Bold.ttf',\n light: '/fonts/Outfit-Light.ttf',\n medium: '/fonts/Outfit-Medium.ttf',\n semibold: '/fonts/Outfit-SemiBold.ttf',\n },\n 'Figtree': {\n regular: '/fonts/Figtree-Regular.ttf',\n bold: '/fonts/Figtree-Bold.ttf',\n light: '/fonts/Figtree-Light.ttf',\n medium: '/fonts/Figtree-Medium.ttf',\n semibold: '/fonts/Figtree-SemiBold.ttf',\n italic: '/fonts/Figtree-RegularItalic.ttf',\n boldItalic: '/fonts/Figtree-BoldItalic.ttf',\n lightItalic: '/fonts/Figtree-LightItalic.ttf',\n mediumItalic: '/fonts/Figtree-MediumItalic.ttf',\n semiboldItalic: '/fonts/Figtree-SemiBoldItalic.ttf',\n },\n 'Manrope': {\n regular: '/fonts/Manrope-Regular.ttf',\n bold: '/fonts/Manrope-Bold.ttf',\n light: '/fonts/Manrope-Light.ttf',\n medium: '/fonts/Manrope-Medium.ttf',\n semibold: '/fonts/Manrope-SemiBold.ttf',\n },\n 'Space Grotesk': {\n regular: '/fonts/SpaceGrotesk-Regular.ttf',\n bold: '/fonts/SpaceGrotesk-Bold.ttf',\n light: '/fonts/SpaceGrotesk-Light.ttf',\n medium: '/fonts/SpaceGrotesk-Medium.ttf',\n semibold: '/fonts/SpaceGrotesk-SemiBold.ttf',\n },\n 'League Spartan': {\n regular: '/fonts/LeagueSpartan-Regular.ttf',\n bold: '/fonts/LeagueSpartan-Bold.ttf',\n light: '/fonts/LeagueSpartan-Light.ttf',\n medium: '/fonts/LeagueSpartan-Medium.ttf',\n semibold: '/fonts/LeagueSpartan-SemiBold.ttf',\n },\n 'EB Garamond': {\n regular: '/fonts/EBGaramond-Regular.ttf',\n bold: '/fonts/EBGaramond-Bold.ttf',\n medium: '/fonts/EBGaramond-Medium.ttf',\n semibold: '/fonts/EBGaramond-SemiBold.ttf',\n italic: '/fonts/EBGaramond-RegularItalic.ttf',\n boldItalic: '/fonts/EBGaramond-BoldItalic.ttf',\n mediumItalic: '/fonts/EBGaramond-MediumItalic.ttf',\n semiboldItalic: '/fonts/EBGaramond-SemiBoldItalic.ttf',\n },\n 'Libre Baskerville': {\n regular: '/fonts/LibreBaskerville-Regular.ttf',\n bold: '/fonts/LibreBaskerville-Bold.ttf',\n italic: '/fonts/LibreBaskerville-RegularItalic.ttf',\n boldItalic: '/fonts/LibreBaskerville-BoldItalic.ttf',\n },\n 'Crimson Text': {\n regular: '/fonts/CrimsonText-Regular.ttf',\n bold: '/fonts/CrimsonText-Bold.ttf',\n italic: '/fonts/CrimsonText-Italic.ttf',\n boldItalic: '/fonts/CrimsonText-BoldItalic.ttf',\n },\n 'DM Serif Display': {\n regular: '/fonts/DMSerifDisplay-Regular.ttf',\n italic: '/fonts/DMSerifDisplay-Italic.ttf',\n },\n 'Libre Franklin': {\n regular: '/fonts/LibreFranklin-Regular.ttf',\n bold: '/fonts/LibreFranklin-Bold.ttf',\n light: '/fonts/LibreFranklin-Light.ttf',\n medium: '/fonts/LibreFranklin-Medium.ttf',\n semibold: '/fonts/LibreFranklin-SemiBold.ttf',\n italic: '/fonts/LibreFranklin-RegularItalic.ttf',\n boldItalic: '/fonts/LibreFranklin-BoldItalic.ttf',\n lightItalic: '/fonts/LibreFranklin-LightItalic.ttf',\n mediumItalic: '/fonts/LibreFranklin-MediumItalic.ttf',\n semiboldItalic: '/fonts/LibreFranklin-SemiBoldItalic.ttf',\n },\n 'Mulish': {\n regular: '/fonts/Mulish-Regular.ttf',\n bold: '/fonts/Mulish-Bold.ttf',\n light: '/fonts/Mulish-Light.ttf',\n medium: '/fonts/Mulish-Medium.ttf',\n semibold: '/fonts/Mulish-SemiBold.ttf',\n italic: '/fonts/Mulish-RegularItalic.ttf',\n boldItalic: '/fonts/Mulish-BoldItalic.ttf',\n lightItalic: '/fonts/Mulish-LightItalic.ttf',\n mediumItalic: '/fonts/Mulish-MediumItalic.ttf',\n semiboldItalic: '/fonts/Mulish-SemiBoldItalic.ttf',\n },\n 'Quicksand': {\n regular: '/fonts/Quicksand-Regular.ttf',\n bold: '/fonts/Quicksand-Bold.ttf',\n light: '/fonts/Quicksand-Light.ttf',\n medium: '/fonts/Quicksand-Medium.ttf',\n semibold: '/fonts/Quicksand-SemiBold.ttf',\n },\n 'Rubik': {\n regular: '/fonts/Rubik-Regular.ttf',\n bold: '/fonts/Rubik-Bold.ttf',\n light: '/fonts/Rubik-Light.ttf',\n medium: '/fonts/Rubik-Medium.ttf',\n semibold: '/fonts/Rubik-SemiBold.ttf',\n italic: '/fonts/Rubik-RegularItalic.ttf',\n boldItalic: '/fonts/Rubik-BoldItalic.ttf',\n lightItalic: '/fonts/Rubik-LightItalic.ttf',\n mediumItalic: '/fonts/Rubik-MediumItalic.ttf',\n semiboldItalic: '/fonts/Rubik-SemiBoldItalic.ttf',\n },\n 'Karla': {\n regular: '/fonts/Karla-Regular.ttf',\n bold: '/fonts/Karla-Bold.ttf',\n light: '/fonts/Karla-Light.ttf',\n medium: '/fonts/Karla-Medium.ttf',\n semibold: '/fonts/Karla-SemiBold.ttf',\n italic: '/fonts/Karla-RegularItalic.ttf',\n boldItalic: '/fonts/Karla-BoldItalic.ttf',\n lightItalic: '/fonts/Karla-LightItalic.ttf',\n mediumItalic: '/fonts/Karla-MediumItalic.ttf',\n semiboldItalic: '/fonts/Karla-SemiBoldItalic.ttf',\n },\n 'Plus Jakarta Sans': {\n regular: '/fonts/PlusJakartaSans-Regular.ttf',\n bold: '/fonts/PlusJakartaSans-Bold.ttf',\n light: '/fonts/PlusJakartaSans-Light.ttf',\n medium: '/fonts/PlusJakartaSans-Medium.ttf',\n semibold: '/fonts/PlusJakartaSans-SemiBold.ttf',\n italic: '/fonts/PlusJakartaSans-RegularItalic.ttf',\n boldItalic: '/fonts/PlusJakartaSans-BoldItalic.ttf',\n lightItalic: '/fonts/PlusJakartaSans-LightItalic.ttf',\n mediumItalic: '/fonts/PlusJakartaSans-MediumItalic.ttf',\n semiboldItalic: '/fonts/PlusJakartaSans-SemiBoldItalic.ttf',\n },\n 'Sora': {\n regular: '/fonts/Sora-Regular.ttf',\n bold: '/fonts/Sora-Bold.ttf',\n light: '/fonts/Sora-Light.ttf',\n medium: '/fonts/Sora-Medium.ttf',\n semibold: '/fonts/Sora-SemiBold.ttf',\n },\n 'Urbanist': {\n regular: '/fonts/Urbanist-Regular.ttf',\n bold: '/fonts/Urbanist-Bold.ttf',\n light: '/fonts/Urbanist-Light.ttf',\n medium: '/fonts/Urbanist-Medium.ttf',\n semibold: '/fonts/Urbanist-SemiBold.ttf',\n italic: '/fonts/Urbanist-RegularItalic.ttf',\n boldItalic: '/fonts/Urbanist-BoldItalic.ttf',\n lightItalic: '/fonts/Urbanist-LightItalic.ttf',\n mediumItalic: '/fonts/Urbanist-MediumItalic.ttf',\n semiboldItalic: '/fonts/Urbanist-SemiBoldItalic.ttf',\n },\n 'Lexend': {\n regular: '/fonts/Lexend-Regular.ttf',\n bold: '/fonts/Lexend-Bold.ttf',\n light: '/fonts/Lexend-Light.ttf',\n medium: '/fonts/Lexend-Medium.ttf',\n semibold: '/fonts/Lexend-SemiBold.ttf',\n },\n 'Albert Sans': {\n regular: '/fonts/AlbertSans-Regular.ttf',\n bold: '/fonts/AlbertSans-Bold.ttf',\n light: '/fonts/AlbertSans-Light.ttf',\n medium: '/fonts/AlbertSans-Medium.ttf',\n semibold: '/fonts/AlbertSans-SemiBold.ttf',\n italic: '/fonts/AlbertSans-RegularItalic.ttf',\n boldItalic: '/fonts/AlbertSans-BoldItalic.ttf',\n lightItalic: '/fonts/AlbertSans-LightItalic.ttf',\n mediumItalic: '/fonts/AlbertSans-MediumItalic.ttf',\n semiboldItalic: '/fonts/AlbertSans-SemiBoldItalic.ttf',\n },\n 'Noto Sans': {\n regular: '/fonts/NotoSans-Regular.ttf',\n bold: '/fonts/NotoSans-Bold.ttf',\n light: '/fonts/NotoSans-Light.ttf',\n medium: '/fonts/NotoSans-Medium.ttf',\n semibold: '/fonts/NotoSans-SemiBold.ttf',\n italic: '/fonts/NotoSans-RegularItalic.ttf',\n boldItalic: '/fonts/NotoSans-BoldItalic.ttf',\n lightItalic: '/fonts/NotoSans-LightItalic.ttf',\n mediumItalic: '/fonts/NotoSans-MediumItalic.ttf',\n semiboldItalic: '/fonts/NotoSans-SemiBoldItalic.ttf',\n },\n 'Cabin': {\n regular: '/fonts/Cabin-Regular.ttf',\n bold: '/fonts/Cabin-Bold.ttf',\n medium: '/fonts/Cabin-Medium.ttf',\n semibold: '/fonts/Cabin-SemiBold.ttf',\n italic: '/fonts/Cabin-RegularItalic.ttf',\n boldItalic: '/fonts/Cabin-BoldItalic.ttf',\n mediumItalic: '/fonts/Cabin-MediumItalic.ttf',\n semiboldItalic: '/fonts/Cabin-SemiBoldItalic.ttf',\n },\n 'Barlow': {\n regular: '/fonts/Barlow-Regular.ttf',\n bold: '/fonts/Barlow-Bold.ttf',\n light: '/fonts/Barlow-Light.ttf',\n medium: '/fonts/Barlow-Medium.ttf',\n semibold: '/fonts/Barlow-SemiBold.ttf',\n italic: '/fonts/Barlow-Italic.ttf',\n boldItalic: '/fonts/Barlow-BoldItalic.ttf',\n },\n 'Josefin Sans': {\n regular: '/fonts/JosefinSans-Regular.ttf',\n bold: '/fonts/JosefinSans-Bold.ttf',\n light: '/fonts/JosefinSans-Light.ttf',\n medium: '/fonts/JosefinSans-Medium.ttf',\n semibold: '/fonts/JosefinSans-SemiBold.ttf',\n italic: '/fonts/JosefinSans-RegularItalic.ttf',\n boldItalic: '/fonts/JosefinSans-BoldItalic.ttf',\n lightItalic: '/fonts/JosefinSans-LightItalic.ttf',\n mediumItalic: '/fonts/JosefinSans-MediumItalic.ttf',\n semiboldItalic: '/fonts/JosefinSans-SemiBoldItalic.ttf',\n },\n 'Archivo': {\n regular: '/fonts/Archivo-Regular.ttf',\n bold: '/fonts/Archivo-Bold.ttf',\n light: '/fonts/Archivo-Light.ttf',\n medium: '/fonts/Archivo-Medium.ttf',\n semibold: '/fonts/Archivo-SemiBold.ttf',\n italic: '/fonts/Archivo-RegularItalic.ttf',\n boldItalic: '/fonts/Archivo-BoldItalic.ttf',\n lightItalic: '/fonts/Archivo-LightItalic.ttf',\n mediumItalic: '/fonts/Archivo-MediumItalic.ttf',\n semiboldItalic: '/fonts/Archivo-SemiBoldItalic.ttf',\n },\n 'Overpass': {\n regular: '/fonts/Overpass-Regular.ttf',\n bold: '/fonts/Overpass-Bold.ttf',\n light: '/fonts/Overpass-Light.ttf',\n medium: '/fonts/Overpass-Medium.ttf',\n semibold: '/fonts/Overpass-SemiBold.ttf',\n italic: '/fonts/Overpass-RegularItalic.ttf',\n boldItalic: '/fonts/Overpass-BoldItalic.ttf',\n lightItalic: '/fonts/Overpass-LightItalic.ttf',\n mediumItalic: '/fonts/Overpass-MediumItalic.ttf',\n semiboldItalic: '/fonts/Overpass-SemiBoldItalic.ttf',\n },\n 'Exo 2': {\n regular: '/fonts/Exo2-Regular.ttf',\n bold: '/fonts/Exo2-Bold.ttf',\n light: '/fonts/Exo2-Light.ttf',\n medium: '/fonts/Exo2-Medium.ttf',\n semibold: '/fonts/Exo2-SemiBold.ttf',\n italic: '/fonts/Exo2-RegularItalic.ttf',\n boldItalic: '/fonts/Exo2-BoldItalic.ttf',\n lightItalic: '/fonts/Exo2-LightItalic.ttf',\n mediumItalic: '/fonts/Exo2-MediumItalic.ttf',\n semiboldItalic: '/fonts/Exo2-SemiBoldItalic.ttf',\n },\n 'Roboto Mono': {\n regular: '/fonts/RobotoMono-Regular.ttf',\n bold: '/fonts/RobotoMono-Bold.ttf',\n light: '/fonts/RobotoMono-Light.ttf',\n medium: '/fonts/RobotoMono-Medium.ttf',\n semibold: '/fonts/RobotoMono-SemiBold.ttf',\n italic: '/fonts/RobotoMono-RegularItalic.ttf',\n boldItalic: '/fonts/RobotoMono-BoldItalic.ttf',\n lightItalic: '/fonts/RobotoMono-LightItalic.ttf',\n mediumItalic: '/fonts/RobotoMono-MediumItalic.ttf',\n semiboldItalic: '/fonts/RobotoMono-SemiBoldItalic.ttf',\n },\n 'Fira Code': {\n regular: '/fonts/FiraCode-Regular.ttf',\n bold: '/fonts/FiraCode-Bold.ttf',\n light: '/fonts/FiraCode-Light.ttf',\n medium: '/fonts/FiraCode-Medium.ttf',\n semibold: '/fonts/FiraCode-SemiBold.ttf',\n },\n 'JetBrains Mono': {\n regular: '/fonts/JetBrainsMono-Regular.ttf',\n bold: '/fonts/JetBrainsMono-Bold.ttf',\n light: '/fonts/JetBrainsMono-Light.ttf',\n medium: '/fonts/JetBrainsMono-Medium.ttf',\n semibold: '/fonts/JetBrainsMono-SemiBold.ttf',\n italic: '/fonts/JetBrainsMono-RegularItalic.ttf',\n boldItalic: '/fonts/JetBrainsMono-BoldItalic.ttf',\n lightItalic: '/fonts/JetBrainsMono-LightItalic.ttf',\n mediumItalic: '/fonts/JetBrainsMono-MediumItalic.ttf',\n semiboldItalic: '/fonts/JetBrainsMono-SemiBoldItalic.ttf',\n },\n 'Source Code Pro': {\n regular: '/fonts/SourceCodePro-Regular.ttf',\n bold: '/fonts/SourceCodePro-Bold.ttf',\n light: '/fonts/SourceCodePro-Light.ttf',\n medium: '/fonts/SourceCodePro-Medium.ttf',\n semibold: '/fonts/SourceCodePro-SemiBold.ttf',\n italic: '/fonts/SourceCodePro-RegularItalic.ttf',\n boldItalic: '/fonts/SourceCodePro-BoldItalic.ttf',\n lightItalic: '/fonts/SourceCodePro-LightItalic.ttf',\n mediumItalic: '/fonts/SourceCodePro-MediumItalic.ttf',\n semiboldItalic: '/fonts/SourceCodePro-SemiBoldItalic.ttf',\n },\n 'IBM Plex Mono': {\n regular: '/fonts/IBMPlexMono-Regular.ttf',\n bold: '/fonts/IBMPlexMono-Bold.ttf',\n },\n 'Space Mono': {\n regular: '/fonts/SpaceMono-Regular.ttf',\n bold: '/fonts/SpaceMono-Bold.ttf',\n italic: '/fonts/SpaceMono-Italic.ttf',\n boldItalic: '/fonts/SpaceMono-BoldItalic.ttf',\n },\n 'Sacramento': { regular: '/fonts/Sacramento-Regular.ttf' },\n 'Alex Brush': { regular: '/fonts/AlexBrush-Regular.ttf' },\n 'Allura': { regular: '/fonts/Allura-Regular.ttf' },\n 'Caveat': {\n regular: '/fonts/Caveat-Regular.ttf',\n bold: '/fonts/Caveat-Bold.ttf',\n medium: '/fonts/Caveat-Medium.ttf',\n semibold: '/fonts/Caveat-SemiBold.ttf',\n },\n 'Lobster': { regular: '/fonts/Lobster-Regular.ttf' },\n 'Comfortaa': {\n regular: '/fonts/Comfortaa-Regular.ttf',\n bold: '/fonts/Comfortaa-Bold.ttf',\n light: '/fonts/Comfortaa-Light.ttf',\n medium: '/fonts/Comfortaa-Medium.ttf',\n semibold: '/fonts/Comfortaa-SemiBold.ttf',\n },\n 'Anton': { regular: '/fonts/Anton-Regular.ttf' },\n 'Teko': {\n regular: '/fonts/Teko-Regular.ttf',\n bold: '/fonts/Teko-Bold.ttf',\n light: '/fonts/Teko-Light.ttf',\n medium: '/fonts/Teko-Medium.ttf',\n semibold: '/fonts/Teko-SemiBold.ttf',\n },\n // ── Indic Script Fallback Fonts ──\n 'Noto Sans Devanagari': {\n regular: '/fonts/NotoSansDevanagari-Regular.ttf',\n bold: '/fonts/NotoSansDevanagari-Bold.ttf',\n light: '/fonts/NotoSansDevanagari-Light.ttf',\n medium: '/fonts/NotoSansDevanagari-Medium.ttf',\n semibold: '/fonts/NotoSansDevanagari-SemiBold.ttf',\n },\n 'Hind': {\n regular: '/fonts/Hind-Regular.ttf',\n bold: '/fonts/Hind-Bold.ttf',\n light: '/fonts/Hind-Light.ttf',\n medium: '/fonts/Hind-Medium.ttf',\n semibold: '/fonts/Hind-SemiBold.ttf',\n },\n};\n\n// Convert font file to Base64\nconst fontToBase64 = async (url: string): Promise<string> => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch font: ${response.statusText}`);\n }\n const arrayBuffer = await response.arrayBuffer();\n \n // Convert ArrayBuffer to Base64\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n};\n\n// Get jsPDF-compatible font name (sanitized - no spaces)\nexport const getJsPDFFontName = (fontName: string): string => {\n return fontName.replace(/\\s+/g, '');\n};\n\n// Weight → file key and fallback order for resolving TTF path (normal style)\nconst WEIGHT_TO_KEYS: Record<number, (keyof FontWeightFiles)[]> = {\n 300: ['light', 'regular'],\n 400: ['regular'],\n 500: ['medium', 'regular'],\n 600: ['semibold', 'bold', 'regular'],\n 700: ['bold', 'regular'],\n};\n\n// Weight → file key and fallback order for resolving TTF path (italic style)\nconst WEIGHT_TO_ITALIC_KEYS: Record<number, (keyof FontWeightFiles)[]> = {\n 300: ['lightItalic', 'italic', 'light', 'regular'],\n 400: ['italic', 'regular'],\n 500: ['mediumItalic', 'italic', 'medium', 'regular'],\n 600: ['semiboldItalic', 'boldItalic', 'italic', 'semibold', 'bold', 'regular'],\n 700: ['boldItalic', 'italic', 'bold', 'regular'],\n};\n\nfunction getFontPathForWeight(fontFiles: FontWeightFiles, resolvedWeight: number, isItalic = false): string {\n const keys = isItalic ? WEIGHT_TO_ITALIC_KEYS[resolvedWeight] : WEIGHT_TO_KEYS[resolvedWeight];\n if (!keys) return fontFiles.regular;\n for (const k of keys) {\n const path = fontFiles[k];\n if (path) return path;\n }\n return fontFiles.regular;\n}\n\n/** Check if the resolved path is actually an italic-specific file */\nfunction isItalicPath(fontFiles: FontWeightFiles, path: string): boolean {\n return path === fontFiles.italic ||\n path === fontFiles.boldItalic ||\n path === fontFiles.lightItalic ||\n path === fontFiles.mediumItalic ||\n path === fontFiles.semiboldItalic;\n}\n\n// Returns the jsPDF font name used when embedding; includes italic suffix when applicable\nexport const getEmbeddedJsPDFFontName = (fontName: string, weight: number, isItalic = false): string => {\n const resolved = resolveFontWeight(weight);\n const label = FONT_WEIGHT_LABELS[resolved];\n const italicSuffix = isItalic ? 'Italic' : '';\n return `${getJsPDFFontName(fontName)}-${label}${italicSuffix}`;\n};\n\n// Load and embed a font into jsPDF from local TTF files (one jsPDF font per weight+style, style always \"normal\")\nexport const embedFont = async (\n pdf: any,\n fontName: string,\n weight: number = 400,\n isItalic = false,\n): Promise<boolean> => {\n const fontFiles = FONT_FILES[fontName];\n if (!fontFiles) {\n console.warn(`Font ${fontName} not found in local fonts`);\n return false;\n }\n\n const resolvedWeight = resolveFontWeight(weight);\n const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);\n if (!fontPath) return false;\n\n const label = FONT_WEIGHT_LABELS[resolvedWeight];\n const hasItalicFile = isItalic && isItalicPath(fontFiles, fontPath);\n const italicSuffix = hasItalicFile ? 'Italic' : '';\n const cacheKey = `${fontName}-${resolvedWeight}${isItalic ? '-italic' : ''}`;\n const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, hasItalicFile);\n\n try {\n let base64Font = fontCache.get(cacheKey);\n if (!base64Font) {\n base64Font = await fontToBase64(fontPath);\n fontCache.set(cacheKey, base64Font);\n }\n\n const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;\n pdf.addFileToVFS(fileName, base64Font);\n pdf.addFont(fileName, jsPdfFontName, 'normal');\n if (fontName !== jsPdfFontName) {\n try {\n pdf.addFont(fileName, fontName, 'normal');\n } catch {\n // Ignore duplicate alias registration; svg2pdf only needs one successful alias.\n }\n }\n return true;\n } catch (error) {\n console.error(`Failed to embed font ${fontName} (weight ${weight}, italic=${isItalic}):`, error);\n return false;\n }\n};\n\n// Pre-load all font weights into cache (can be called at app start for faster PDF export)\nexport const preloadFonts = async (): Promise<void> => {\n const loadPromises: Promise<void>[] = [];\n const weights = [300, 400, 500, 600, 700] as const;\n\n for (const [fontName, files] of Object.entries(FONT_FILES)) {\n for (const w of weights) {\n // Normal\n const path = getFontPathForWeight(files as FontWeightFiles, w);\n if (path) {\n const cacheKey = `${fontName}-${w}`;\n loadPromises.push(\n fontToBase64(path)\n .then(base64 => { fontCache.set(cacheKey, base64); })\n .catch(err => console.warn(`Failed to preload ${fontName} weight ${w}:`, err))\n );\n }\n // Italic\n const italicPath = getFontPathForWeight(files as FontWeightFiles, w, true);\n if (italicPath && italicPath !== path) {\n const cacheKeyItalic = `${fontName}-${w}-italic`;\n loadPromises.push(\n fontToBase64(italicPath)\n .then(base64 => { fontCache.set(cacheKeyItalic, base64); })\n .catch(err => console.warn(`Failed to preload ${fontName} weight ${w} italic:`, err))\n );\n }\n }\n }\n\n await Promise.all(loadPromises);\n};\n\n// Check if a font is available locally\nexport const isFontAvailable = (fontName: string): boolean => {\n return fontName in FONT_FILES;\n};\n\n// Check if a font has an italic variant for the given weight\nexport const hasItalicVariant = (fontName: string, weight: number = 400): boolean => {\n const files = FONT_FILES[fontName];\n if (!files) return false;\n const resolvedWeight = resolveFontWeight(weight);\n const normalPath = getFontPathForWeight(files, resolvedWeight, false);\n const italicPath = getFontPathForWeight(files, resolvedWeight, true);\n return italicPath !== normalPath && isItalicPath(files, italicPath);\n};\n\n// Get all available font names\nexport const getAvailableFonts = (): string[] => {\n return Object.keys(FONT_FILES);\n};\n\n/**\n * Get available font weights for a given font family.\n * Returns only weights that have a dedicated TTF file (not fallbacks).\n */\nexport const getAvailableWeights = (fontFamily: string): number[] => {\n const files = FONT_FILES[fontFamily];\n if (!files) return [400]; // Fallback: at least regular\n\n const weights: number[] = [];\n if (files.light) weights.push(300);\n // regular always exists\n weights.push(400);\n if (files.medium) weights.push(500);\n if (files.semibold) weights.push(600);\n if (files.bold) weights.push(700);\n\n return weights;\n};\n\n/**\n * Check if a font has any italic variant at all.\n */\nexport const hasAnyItalicVariant = (fontFamily: string): boolean => {\n const files = FONT_FILES[fontFamily];\n if (!files) return false;\n return !!(files.italic || files.boldItalic || files.lightItalic || files.mediumItalic || files.semiboldItalic);\n};\n\n// ═══════════════════════════════════════════════════════════════\n// Google Fonts dynamic TTF fetching (parity with EC2 server)\n// ═══════════════════════════════════════════════════════════════\n\n/** Fetch a TTF (as base64) for any Google Font. Caches per (family, weight, style). */\nexport async function fetchGoogleFontTTF(\n fontFamily: string,\n weight: number = 400,\n isItalic = false,\n): Promise<string | null> {\n const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? 'i' : 'n'}`;\n if (fontCache.has(cacheKey)) return fontCache.get(cacheKey)!;\n if (googleFontNotFound.has(fontFamily)) return null;\n try {\n const ital = isItalic ? '1' : '0';\n const cssUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(\n fontFamily,\n )}:ital,wght@${ital},${weight}&display=swap`;\n // The User-Agent below tricks Google Fonts into returning legacy TTF (not WOFF2),\n // matching what the EC2 server does so jsPDF can consume it.\n const cssRes = await fetch(cssUrl, {\n headers: {\n 'User-Agent':\n 'Mozilla/5.0 (Linux; U; Android 2.2; en-us) AppleWebKit/533.1 (KHTML, like Gecko)',\n },\n });\n if (!cssRes.ok) {\n if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(fontFamily);\n return null;\n }\n const css = await cssRes.text();\n const urlMatch = css.match(/url\\(([^)]+)\\)\\s+format\\(['\"]?truetype['\"]?\\)/i)\n || css.match(/url\\(([^)]+)\\)/);\n if (!urlMatch) return null;\n const ttfUrl = urlMatch[1].replace(/['\"]/g, '');\n googleFontUrlCache.set(cacheKey, ttfUrl);\n const ttfRes = await fetch(ttfUrl);\n if (!ttfRes.ok) return null;\n const buf = await ttfRes.arrayBuffer();\n const bytes = new Uint8Array(buf);\n fontBytesCache.set(cacheKey, bytes);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);\n const b64 = btoa(binary);\n fontCache.set(cacheKey, b64);\n return b64;\n } catch (err) {\n console.warn(`[pdfFonts] fetchGoogleFontTTF failed for ${fontFamily} (${weight}):`, err);\n return null;\n }\n}\n\n/** Get raw TTF bytes for a Google Font (used by opentype.js / HarfBuzz). */\nexport async function getGoogleFontBytes(\n fontFamily: string,\n weight: number = 400,\n isItalic = false,\n): Promise<Uint8Array | null> {\n const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? 'i' : 'n'}`;\n if (fontBytesCache.has(cacheKey)) return fontBytesCache.get(cacheKey)!;\n await fetchGoogleFontTTF(fontFamily, weight, isItalic);\n return fontBytesCache.get(cacheKey) || null;\n}\n\n/**\n * Embed a font into jsPDF — local TTF if available, otherwise Google Fonts.\n * Returns true on success. Registers under the SAME jsPDF font name as\n * `getEmbeddedJsPDFFontName(...)` so SVG rewriting matches.\n */\nexport const embedFontWithGoogleFallback = async (\n pdf: any,\n fontName: string,\n weight: number = 400,\n isItalic = false,\n): Promise<boolean> => {\n // 1. Local first\n if (FONT_FILES[fontName]) {\n const ok = await embedFont(pdf, fontName, weight, isItalic);\n if (ok) return true;\n }\n\n // 2. Google Fonts fallback\n const resolved = resolveFontWeight(weight);\n const b64 = await fetchGoogleFontTTF(fontName, resolved, isItalic);\n if (!b64) {\n // If italic was requested but Google has no italic, fall back to upright so we\n // still get the right family rather than Helvetica.\n if (isItalic) {\n const uprightB64 = await fetchGoogleFontTTF(fontName, resolved, false);\n if (uprightB64) {\n return registerJsPdfFont(pdf, fontName, resolved, isItalic, uprightB64);\n }\n }\n return false;\n }\n return registerJsPdfFont(pdf, fontName, resolved, isItalic, b64);\n};\n\n/** Register a base64 TTF inside jsPDF under the canonical embedded name + alias. */\nfunction registerJsPdfFont(\n pdf: any,\n fontName: string,\n resolvedWeight: number,\n isItalic: boolean,\n base64: string,\n): boolean {\n const label = FONT_WEIGHT_LABELS[resolvedWeight];\n const italicSuffix = isItalic ? 'Italic' : '';\n const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, resolvedWeight, isItalic);\n const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;\n try {\n pdf.addFileToVFS(fileName, base64);\n pdf.addFont(fileName, jsPdfFontName, 'normal');\n if (fontName !== jsPdfFontName) {\n try {\n pdf.addFont(fileName, fontName, 'normal');\n } catch {\n /* duplicate alias; ignore */\n }\n }\n return true;\n } catch (err) {\n console.warn(`[pdfFonts] registerJsPdfFont failed for ${fontName}:`, err);\n return false;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Latin → Devanagari font sibling mapping\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Map of Latin Google Fonts → their Devanagari sibling family on Google Fonts.\n * When a user picks a Latin font and types Hindi, we use the matching Devanagari\n * family for path conversion so the visual style is preserved (instead of always\n * falling back to Hind). Sourced from Google's own family pairings.\n */\nexport const LATIN_TO_DEVANAGARI: Record<string, string> = {\n // Direct Devanagari subsets — same family carries both scripts\n 'Poppins': 'Poppins',\n 'Noto Sans': 'Noto Sans Devanagari',\n 'Noto Serif': 'Noto Serif Devanagari',\n 'Hind': 'Hind',\n 'Mukta': 'Mukta',\n\n // Curated visual matches (Google's own recommendations)\n 'Montserrat': 'Mukta',\n 'Open Sans': 'Mukta',\n 'Lato': 'Mukta',\n 'Roboto': 'Mukta',\n 'Inter': 'Mukta',\n 'Nunito': 'Mukta',\n 'Source Sans Pro': 'Mukta',\n 'Work Sans': 'Mukta',\n 'DM Sans': 'Mukta',\n 'Outfit': 'Mukta',\n 'Figtree': 'Mukta',\n 'Manrope': 'Mukta',\n 'Plus Jakarta Sans': 'Mukta',\n 'Sora': 'Mukta',\n 'Urbanist': 'Mukta',\n 'Lexend': 'Mukta',\n 'Albert Sans': 'Mukta',\n 'Cabin': 'Mukta',\n 'Karla': 'Mukta',\n 'Mulish': 'Mukta',\n 'Rubik': 'Mukta',\n 'Quicksand': 'Mukta',\n 'Libre Franklin': 'Mukta',\n 'Barlow': 'Mukta',\n 'Archivo': 'Mukta',\n 'Overpass': 'Mukta',\n 'Josefin Sans': 'Mukta',\n 'Exo 2': 'Mukta',\n 'Space Grotesk': 'Mukta',\n\n // Display / heavy\n 'Oswald': 'Teko',\n 'Bebas Neue': 'Teko',\n 'Anton': 'Teko',\n 'Teko': 'Teko',\n 'League Spartan': 'Teko',\n\n // Serifs → Tiro Devanagari Hindi (a refined Devanagari serif)\n 'Playfair Display': 'Tiro Devanagari Hindi',\n 'Merriweather': 'Tiro Devanagari Hindi',\n 'Lora': 'Tiro Devanagari Hindi',\n 'EB Garamond': 'Tiro Devanagari Hindi',\n 'Libre Baskerville': 'Tiro Devanagari Hindi',\n 'Crimson Text': 'Tiro Devanagari Hindi',\n 'DM Serif Display': 'Tiro Devanagari Sanskrit',\n 'Abril Fatface': 'Tiro Devanagari Sanskrit',\n\n // Handwriting → Kalam (the only serious Devanagari handwriting family on GF)\n 'Dancing Script': 'Kalam',\n 'Pacifico': 'Kalam',\n 'Great Vibes': 'Kalam',\n 'Sacramento': 'Kalam',\n 'Alex Brush': 'Kalam',\n 'Allura': 'Kalam',\n 'Caveat': 'Kalam',\n 'Lobster': 'Kalam',\n 'Comfortaa': 'Mukta',\n\n // Monospace → there's no proper Devanagari mono; fall back to Mukta\n 'Roboto Mono': 'Mukta',\n 'Fira Code': 'Mukta',\n 'JetBrains Mono': 'Mukta',\n 'Source Code Pro': 'Mukta',\n 'IBM Plex Mono': 'Mukta',\n 'Space Mono': 'Mukta',\n};\n\n/** Pick the best Devanagari family for a given Latin font (returns FONT_FALLBACK_DEVANAGARI as last resort). */\nexport function resolveDevanagariSibling(latinFont: string | null | undefined): string {\n if (!latinFont) return FONT_FALLBACK_DEVANAGARI;\n return LATIN_TO_DEVANAGARI[latinFont] || FONT_FALLBACK_DEVANAGARI;\n}\n","/**\n * HarfBuzz Shaper — Browser-side complex-script text shaping for vector PDF export.\n *\n * `opentype.js`'s `getPath()` only does naive codepoint→glyph mapping with very\n * limited GSUB support. That breaks complex scripts like Devanagari (Hindi /\n * Marathi / Sanskrit) where glyphs require:\n * - Conjuncts (क्ष, ज्ञ, त्र — multi-consonant ligatures)\n * - Reph (र above following consonant)\n * - Half-forms before virama\n * - Matra reordering (कि — इ matra rendered before क but typed after)\n * - Indic-specific GSUB lookups (nukt, akhn, rphf, blwf, half, pstf, vatu,\n * cjct, pres, abvs, blws, psts, haln, rkrf)\n *\n * The browser canvas preview works because the browser's text renderer uses\n * HarfBuzz internally. Our EC2 PDF works because Puppeteer also uses HarfBuzz.\n * The client-side vector PDF was the only path missing a real shaper.\n *\n * This module wraps `harfbuzzjs` (HarfBuzz compiled to WASM, ~400KB) and\n * returns a single shaped SVG path string per text run, ready to drop into\n * the SVG that we hand to svg2pdf.js.\n *\n * Lazy-loaded — the WASM is only fetched the first time a Devanagari (or other\n * complex-script) string is encountered during PDF export.\n */\n\n// hb.wasm is served from /public/wasm/hb.wasm\nconst HB_WASM_URL = '/wasm/hb.wasm';\n\ntype HBFont = {\n ptr: number;\n setScale: (x: number, y: number) => void;\n glyphToPath: (glyphId: number) => string;\n destroy: () => void;\n};\n\ntype HBBuffer = {\n addText: (text: string) => void;\n guessSegmentProperties: () => void;\n setDirection: (dir: 'ltr' | 'rtl' | 'ttb' | 'btt') => void;\n setScript: (script: string) => void;\n setLanguage: (lang: string) => void;\n json: () => Array<{\n g: number;\n cl: number;\n ax: number;\n ay: number;\n dx: number;\n dy: number;\n flags?: number;\n }>;\n destroy: () => void;\n};\n\ntype HBInstance = {\n createBlob: (data: Uint8Array | ArrayBuffer) => { destroy: () => void };\n createFace: (blob: any, index: number) => { upem?: number; destroy: () => void; getUpem?: () => number };\n createFont: (face: any) => HBFont;\n createBuffer: () => HBBuffer;\n shape: (font: HBFont, buffer: HBBuffer, features?: string) => void;\n};\n\nlet hbInstancePromise: Promise<HBInstance> | null = null;\n\n/** Lazy-load and initialize HarfBuzz WASM. Idempotent. */\nasync function getHB(): Promise<HBInstance> {\n if (hbInstancePromise) return hbInstancePromise;\n\n hbInstancePromise = (async () => {\n // Dynamically import to keep HarfBuzz out of the main bundle.\n // hb.js is an Emscripten module that exports a `createHarfBuzz` factory.\n // It looks up the .wasm file via Module.locateFile.\n const [{ default: createHarfBuzz }, { default: hbjs }] = await Promise.all([\n import('harfbuzzjs/hb.js'),\n import('harfbuzzjs/hbjs.js'),\n ]);\n\n const moduleInstance = await createHarfBuzz({\n locateFile: (path: string) => {\n if (path.endsWith('.wasm')) return HB_WASM_URL;\n return path;\n },\n });\n\n return hbjs(moduleInstance) as HBInstance;\n })();\n\n return hbInstancePromise;\n}\n\n/** Per-font cache: parsed face/font ready for repeated shaping calls. */\nconst hbFontCache = new Map<string, { face: any; font: HBFont; upem: number }>();\n\n/**\n * Register (or fetch from cache) a font with HarfBuzz.\n * `cacheKey` should uniquely identify the font binary (e.g. `family|weight`).\n * `fontDataLoader` is only invoked on cache miss.\n */\nasync function getHBFont(\n cacheKey: string,\n fontDataLoader: () => Promise<Uint8Array>,\n): Promise<{ font: HBFont; upem: number }> {\n const cached = hbFontCache.get(cacheKey);\n if (cached) return { font: cached.font, upem: cached.upem };\n\n const hb = await getHB();\n const fontData = await fontDataLoader();\n const blob = hb.createBlob(fontData);\n const face = hb.createFace(blob, 0);\n const font = hb.createFont(face);\n\n // Use the font's native upem so glyphToPath coordinates are in font units.\n // `setScale(upem, upem)` keeps the natural EM-square coordinates.\n const upem = (face.getUpem ? face.getUpem() : (face.upem ?? 1000)) || 1000;\n font.setScale(upem, upem);\n\n // Blob can be destroyed; face/font hold their own references.\n blob.destroy();\n\n hbFontCache.set(cacheKey, { face, font, upem });\n return { font, upem };\n}\n\nexport interface ShapedRun {\n /** SVG path data ready to drop into a <path d=\"...\"> element. */\n pathData: string;\n /** Total advance width of the run, in user-space units (matching `fontSize`). */\n width: number;\n}\n\n/**\n * Shape `text` with `fontData` at `fontSize` and return a single SVG path\n * positioned starting at (`x`, `y`) on the SVG baseline.\n *\n * `fontSize` is the rendered point size (matches CSS `font-size`).\n * `cacheKey` must uniquely identify `fontData` so subsequent calls hit cache.\n */\nexport async function shapeRunToSvgPath(\n fontData: Uint8Array | (() => Promise<Uint8Array>),\n cacheKey: string,\n text: string,\n x: number,\n y: number,\n fontSize: number,\n opts?: { script?: string; language?: string; direction?: 'ltr' | 'rtl' },\n): Promise<ShapedRun> {\n const hb = await getHB();\n const loader = typeof fontData === 'function'\n ? (fontData as () => Promise<Uint8Array>)\n : async () => fontData as Uint8Array;\n\n const { font, upem } = await getHBFont(cacheKey, loader);\n\n const buffer = hb.createBuffer();\n buffer.addText(text);\n // Setting script + direction explicitly is more reliable than guess for\n // Indic mixed-content runs.\n if (opts?.direction) buffer.setDirection(opts.direction);\n if (opts?.script) buffer.setScript(opts.script);\n if (opts?.language) buffer.setLanguage(opts.language);\n buffer.guessSegmentProperties();\n\n hb.shape(font, buffer);\n const glyphs = buffer.json();\n\n // HarfBuzz coordinate system: Y is UP, units are font units (we set scale\n // to upem so 1 unit == 1 font-design-unit). Convert to SVG user-space:\n // svgUnits = fontUnits * (fontSize / upem)\n // and flip Y.\n const scale = fontSize / upem;\n\n // Pen position, in font units.\n let penX = 0;\n let penY = 0;\n const pieces: string[] = [];\n\n for (const g of glyphs) {\n const rawPath = font.glyphToPath(g.g);\n if (rawPath) {\n // Transform glyph path:\n // 1. Translate to (penX + dx, penY + dy) in font units\n // 2. Flip Y (since HB Y-up, SVG Y-down)\n // 3. Scale by `scale`\n // 4. Translate by (x, y) in user units\n const ox = (penX + g.dx) * scale + x;\n const oy = (penY + g.dy) * -scale + y;\n pieces.push(transformPathData(rawPath, scale, -scale, ox, oy));\n }\n penX += g.ax;\n penY += g.ay;\n }\n\n buffer.destroy();\n\n return {\n pathData: pieces.join(''),\n width: penX * scale,\n };\n}\n\n/**\n * Apply an affine transform (sx, sy, tx, ty) to an SVG path's absolute\n * coordinates. The path emitted by HarfBuzz uses only absolute commands\n * (M, L, C, Q, Z), so we only need to transform numeric coordinate pairs.\n */\nfunction transformPathData(d: string, sx: number, sy: number, tx: number, ty: number): string {\n // Tokenize into commands and number pairs.\n // HB output format: \"M x,y\", \"L x,y\", \"C cx1,cy1 cx2,cy2 x,y\", \"Q cx,cy x,y\", \"Z\"\n // Numbers may be negative or fractional.\n let out = '';\n let i = 0;\n const n = d.length;\n while (i < n) {\n const c = d[i];\n if (c === 'M' || c === 'L' || c === 'C' || c === 'Q') {\n out += c;\n i++;\n // Read remaining coordinate pairs until next command letter or end.\n // Pairs are separated by space or comma.\n let pairsBuf = '';\n while (i < n && d[i] !== 'M' && d[i] !== 'L' && d[i] !== 'C' && d[i] !== 'Q' && d[i] !== 'Z') {\n pairsBuf += d[i];\n i++;\n }\n // pairsBuf now holds something like \"12.5,30.2 -4,5 0,0\"\n const nums = pairsBuf.trim().split(/[ ,]+/).filter(Boolean).map(Number);\n for (let k = 0; k < nums.length; k += 2) {\n const px = nums[k] * sx + tx;\n const py = nums[k + 1] * sy + ty;\n if (k > 0) out += ' ';\n out += `${round(px)},${round(py)}`;\n }\n } else if (c === 'Z') {\n out += 'Z';\n i++;\n } else {\n // Skip unexpected character (e.g. stray whitespace)\n i++;\n }\n }\n return out;\n}\n\nfunction round(n: number): string {\n // 2 decimal places — same precision as opentype.js fallback.\n return (Math.round(n * 100) / 100).toString();\n}\n\n/**\n * Pre-warm HarfBuzz WASM so the first PDF export doesn't pay the load cost.\n * Safe to call multiple times.\n */\nexport function preloadHarfBuzz(): Promise<HBInstance> {\n return getHB();\n}\n","/**\n * SVG Text-to-Path Conversion (universal outlining)\n *\n * Converts EVERY <text> element to <path> elements using the actual font the\n * browser is rendering. This guarantees 100% visual parity between the canvas\n * preview and the downloaded vector PDF — what you see is what you get, for\n * any font (Google Fonts, local TTFs) and any script (Latin, Devanagari, etc.).\n *\n * Tradeoffs vs. leaving text as <text>:\n * + Pixel-perfect parity for any font, including unknown weights, custom kerns,\n * ligatures, and complex Indic shaping.\n * - Text is no longer selectable/searchable in the PDF.\n * - PDF file size grows ~30-50% (paths are bigger than text + embedded TTF).\n *\n * Mechanics:\n * - Latin runs → opentype.js getPath() (uses font's own metrics)\n * - Devanagari → HarfBuzz shaping for proper conjuncts/matra reordering\n * - Mixed runs → split by script, advance cursor between runs\n * - text-anchor → run width measured via the font; offset applied\n * - Failure → keep original <text> so the user still sees something\n */\n\nimport opentype from 'opentype.js';\nimport {\n FONT_FILES,\n FONT_FALLBACK_DEVANAGARI,\n resolveFontWeight,\n resolveDevanagariSibling,\n getGoogleFontBytes,\n} from '@/lib/pdfFonts';\nimport type { FontWeightFiles } from '@/lib/pdfFonts';\nimport { shapeRunToSvgPath } from '@/lib/harfbuzzShaper';\n\n// Cache parsed fonts to avoid re-fetching\nconst fontCache = new Map<string, opentype.Font>();\n/** Raw font bytes cache — shared between opentype.js and HarfBuzz */\nconst fontBytesCache = new Map<string, Uint8Array>();\n\n/** True if the character is in the Devanagari Unicode block */\nfunction isDevanagari(char: string): boolean {\n const c = char.codePointAt(0) ?? 0;\n return (c >= 0x0900 && c <= 0x097F) || (c >= 0xA8E0 && c <= 0xA8FF) || (c >= 0x1CD0 && c <= 0x1CFF);\n}\n\n/** Check if a string contains any Devanagari characters */\nfunction containsDevanagari(text: string): boolean {\n if (!text) return false;\n for (const char of text) {\n if (isDevanagari(char)) return true;\n }\n return false;\n}\n\n/** Neutral marks/punctuation that don't need their own glyph coverage decision. */\nfunction isIgnorableForCoverage(char: string): boolean {\n return /\\s/.test(char) || /[\\u0964\\u0965\\u200c\\u200d]/u.test(char);\n}\n\n/** True when the parsed font can actually draw every Devanagari base glyph in this run. */\nfunction fontSupportsText(font: opentype.Font | null, text: string): boolean {\n if (!font) return false;\n for (const char of text) {\n if (!isDevanagari(char) || isIgnorableForCoverage(char)) continue;\n const glyph = font.charToGlyph(char);\n if (!glyph || glyph.index === 0) return false;\n }\n return true;\n}\n\nconst browserMeasureCanvas: HTMLCanvasElement | null = typeof document !== 'undefined'\n ? document.createElement('canvas')\n : null;\n\n/** Measure with the browser's real canvas fallback stack — this is our preview truth source. */\nfunction measureBrowserWidth(fontFamily: string, weight: number, fontSize: number, text: string): number | null {\n const ctx = browserMeasureCanvas?.getContext('2d');\n if (!ctx) return null;\n ctx.font = `normal normal ${weight} ${fontSize}px \"${fontFamily}\"`;\n return ctx.measureText(text).width;\n}\n\nfunction uniqueFamilies(families: Array<string | null | undefined>): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const family of families) {\n const clean = family?.trim();\n if (!clean || seen.has(clean)) continue;\n seen.add(clean);\n out.push(clean);\n }\n return out;\n}\n\n/** Get the font file path for a given weight */\nfunction getFontPath(fontFiles: FontWeightFiles, weight: number): string | null {\n const resolved = resolveFontWeight(weight);\n const keyMap: Record<number, (keyof FontWeightFiles)[]> = {\n 300: ['light', 'regular'],\n 400: ['regular'],\n 500: ['medium', 'regular'],\n 600: ['semibold', 'bold'],\n 700: ['bold', 'semibold', 'regular'],\n };\n for (const key of keyMap[resolved] || ['regular']) {\n if (fontFiles[key]) return fontFiles[key]!;\n }\n return fontFiles.regular || null;\n}\n\n/** Build a fetchable font URL without accidentally producing /fonts/fonts/*. */\nfunction resolveFontUrl(fileName: string, fontBaseUrl: string): string {\n if (/^https?:\\/\\//i.test(fileName) || fileName.startsWith('data:')) return fileName;\n if (fileName.startsWith('/')) {\n return typeof window !== 'undefined' ? new URL(fileName, window.location.origin).toString() : fileName;\n }\n const baseUrl = fontBaseUrl.endsWith('/') ? fontBaseUrl : fontBaseUrl + '/';\n return new URL(fileName, baseUrl).toString();\n}\n\n/** Load and cache a font via opentype.js */\nasync function loadFont(fontFamily: string, weight: number, fontBaseUrl: string): Promise<opentype.Font | null> {\n const cacheKey = `${fontFamily}__${weight}`;\n if (fontCache.has(cacheKey)) return fontCache.get(cacheKey)!;\n\n const fontFiles = FONT_FILES[fontFamily];\n\n // ── Local TTF path ──\n if (fontFiles) {\n const fileName = getFontPath(fontFiles, weight);\n if (!fileName) return null;\n const url = resolveFontUrl(fileName, fontBaseUrl);\n try {\n const response = await fetch(url);\n if (!response.ok) {\n console.warn(`[text-to-path] Failed to fetch font ${url}: ${response.status}`);\n return null;\n }\n const buffer = await response.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n fontBytesCache.set(cacheKey, bytes);\n const font = opentype.parse(buffer);\n fontCache.set(cacheKey, font);\n return font;\n } catch (err) {\n console.warn(`[text-to-path] Failed to load local font ${fontFamily}:`, err);\n return null;\n }\n }\n\n // ── Google Fonts fallback (e.g. Mukta, Tiro Devanagari Hindi, Kalam) ──\n try {\n const resolvedWeight = resolveFontWeight(weight);\n const bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, false);\n if (!bytes) {\n console.warn(`[text-to-path] Google Fonts has no TTF for ${fontFamily} (${weight})`);\n return null;\n }\n fontBytesCache.set(cacheKey, bytes);\n // opentype.parse needs an ArrayBuffer (not a typed-array view).\n const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n const font = opentype.parse(ab);\n fontCache.set(cacheKey, font);\n return font;\n } catch (err) {\n console.warn(`[text-to-path] Failed to load Google font ${fontFamily}:`, err);\n return null;\n }\n}\n\n/** Get raw font bytes (loads via opentype path if not yet cached). */\nasync function getFontBytes(\n fontFamily: string,\n weight: number,\n fontBaseUrl: string,\n): Promise<{ bytes: Uint8Array; cacheKey: string } | null> {\n const cacheKey = `${fontFamily}__${weight}`;\n if (!fontBytesCache.has(cacheKey)) {\n // Triggers fetch + populates fontBytesCache as a side-effect.\n await loadFont(fontFamily, weight, fontBaseUrl);\n }\n const bytes = fontBytesCache.get(cacheKey);\n return bytes ? { bytes, cacheKey } : null;\n}\n\n/** Measure run advance width using opentype's own metrics (kerning enabled). */\nfunction measureRunWidth(font: opentype.Font, text: string, fontSize: number): number {\n try {\n return font.getAdvanceWidth(text, fontSize, { kerning: true });\n } catch {\n try { return font.getAdvanceWidth(text, fontSize); } catch { return text.length * fontSize * 0.5; }\n }\n}\n\n/**\n * Split a string into consecutive runs by script (Devanagari vs other),\n * preserving order. Whitespace stays with its preceding run.\n */\nfunction splitByScript(text: string): Array<{ text: string; isDevanagari: boolean }> {\n if (!text) return [];\n const runs: Array<{ text: string; isDevanagari: boolean }> = [];\n let buf = '';\n let bufIsDeva = isDevanagari(text[0]);\n for (const ch of text) {\n const chDeva = isDevanagari(ch);\n // Whitespace and punctuation stick to the current run to avoid breaking shaping.\n const isNeutral = /\\s/.test(ch);\n if (chDeva === bufIsDeva || isNeutral) {\n buf += ch;\n } else {\n if (buf) runs.push({ text: buf, isDevanagari: bufIsDeva });\n buf = ch;\n bufIsDeva = chDeva;\n }\n }\n if (buf) runs.push({ text: buf, isDevanagari: bufIsDeva });\n return runs;\n}\n\n/**\n * Generate an SVG path `d` string for a text run using the appropriate engine.\n * Returns { pathData, advance } or null on failure.\n */\nasync function shapeRunToPath(\n run: string,\n isDeva: boolean,\n primaryFont: opentype.Font,\n primaryBytes: { bytes: Uint8Array; cacheKey: string } | null,\n devaFont: opentype.Font | null,\n devaBytes: { bytes: Uint8Array; cacheKey: string } | null,\n x: number,\n y: number,\n fontSize: number,\n): Promise<{ pathData: string; advance: number } | null> {\n // Devanagari → HarfBuzz with the resolved Devanagari font (sibling of the chosen Latin font)\n if (isDeva && devaBytes && devaFont) {\n try {\n const shaped = await shapeRunToSvgPath(\n devaBytes.bytes,\n devaBytes.cacheKey,\n run,\n x,\n y,\n fontSize,\n { script: 'deva', language: 'hin', direction: 'ltr' },\n );\n if (shaped.pathData) {\n const advance = measureRunWidth(devaFont, run, fontSize);\n return { pathData: shaped.pathData, advance };\n }\n } catch (err) {\n console.warn(`[text-to-path] HarfBuzz failed for \"${run}\":`, err);\n }\n // Fall through to opentype.js fallback below using devaFont.\n try {\n const path = devaFont.getPath(run, x, y, fontSize);\n const d = path.toPathData(2);\n if (d && d !== 'M0 0') return { pathData: d, advance: measureRunWidth(devaFont, run, fontSize) };\n } catch { /* noop */ }\n return null;\n }\n\n // Latin / non-Indic → opentype.js getPath with the actual chosen font\n try {\n const path = primaryFont.getPath(run, x, y, fontSize, { kerning: true });\n const d = path.toPathData(2);\n if (d && d !== 'M0 0') return { pathData: d, advance: measureRunWidth(primaryFont, run, fontSize) };\n return { pathData: '', advance: measureRunWidth(primaryFont, run, fontSize) };\n } catch (err) {\n console.warn(`[text-to-path] opentype getPath failed for \"${run}\":`, err);\n return null;\n }\n}\n\n/** Parse a CSS-style transform attribute to extract translate values */\nfunction parseTranslate(transform: string | null): { tx: number; ty: number } {\n if (!transform) return { tx: 0, ty: 0 };\n const m = transform.match(/translate\\(\\s*([-\\d.]+)[ \\s,]+([-\\d.]+)\\s*\\)/);\n if (m) return { tx: parseFloat(m[1]), ty: parseFloat(m[2]) };\n // matrix(a,b,c,d,e,f) — e=tx, f=ty\n const mm = transform.match(/matrix\\(\\s*([-\\d.]+)[ \\s,]+([-\\d.]+)[ \\s,]+([-\\d.]+)[ \\s,]+([-\\d.]+)[ \\s,]+([-\\d.]+)[ \\s,]+([-\\d.]+)\\s*\\)/);\n if (mm) return { tx: parseFloat(mm[5]), ty: parseFloat(mm[6]) };\n return { tx: 0, ty: 0 };\n}\n\n/**\n * Convert EVERY <text> element in the SVG to <path> elements using the actual\n * font (Latin → opentype, Devanagari → HarfBuzz). Guarantees 100% font parity\n * between the canvas preview and the downloaded PDF.\n *\n * Name kept as `convertDevanagariTextToPath` for backwards compatibility with\n * existing callers (UsePackage.tsx). The behaviour is now universal — name is\n * a misnomer.\n *\n * @param svgStr - The SVG string from Fabric's toSVG()\n * @param fontBaseUrl - Base URL where TTF font files are served (e.g., '/fonts/')\n * @returns Modified SVG string with text converted to paths\n */\nexport async function convertDevanagariTextToPath(\n svgStr: string,\n fontBaseUrl?: string\n): Promise<string> {\n const baseUrl = fontBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin + '/fonts/' : '/fonts/');\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(svgStr, 'image/svg+xml');\n\n // Find all top-level <text> elements (not nested tspans)\n const textEls = doc.querySelectorAll('text');\n let convertedCount = 0;\n let skippedCount = 0;\n\n for (const textEl of textEls) {\n // Universal mode: process every <text>, not just Devanagari ones.\n const fullText = textEl.textContent || '';\n if (!fullText.trim()) continue;\n\n // Resolve font properties\n const readStyleToken = (style: string, prop: string): string | null => {\n const m = style.match(new RegExp(`${prop}\\\\s*:\\\\s*([^;]+)`, 'i'));\n return m?.[1]?.trim() || null;\n };\n\n const getAttrOrStyle = (el: Element, attr: string): string | null => {\n let current: Element | null = el;\n while (current) {\n const attrVal = current.getAttribute(attr)?.trim();\n if (attrVal) return attrVal;\n const styleVal = readStyleToken(current.getAttribute('style') || '', attr);\n if (styleVal) return styleVal;\n current = current.parentElement;\n }\n return null;\n };\n\n const fontFamily = getAttrOrStyle(textEl, 'font-family')\n ?.split(',')[0]\n ?.replace(/['\"]/g, '')\n .trim();\n\n const fontSizeStr = getAttrOrStyle(textEl, 'font-size') || '16';\n const fontSize = parseFloat(fontSizeStr);\n\n const fontWeightStr = getAttrOrStyle(textEl, 'font-weight') || '400';\n const fontWeight = Number.parseInt(fontWeightStr, 10) || 400;\n\n const fillColor = getAttrOrStyle(textEl, 'fill') || '#000000';\n const fillOpacity = getAttrOrStyle(textEl, 'fill-opacity') || '1';\n\n if (!fontFamily) {\n skippedCount++;\n continue;\n }\n\n // ── Load the PRIMARY font (the exact font shown in the preview) ──\n const primaryFont = await loadFont(fontFamily, fontWeight, baseUrl);\n const primaryBytes = await getFontBytes(fontFamily, fontWeight, baseUrl);\n\n // ── Devanagari font resolution ──\n // The browser canvas does NOT use curated siblings when a Latin font lacks\n // Hindi glyphs. It uses the CSS/font fallback stack; in our preview path\n // that fallback is Hind. For parity, pick the candidate whose shaped width\n // matches browser canvas measurement, with the chosen family itself first\n // for fonts like Poppins that genuinely include Devanagari glyphs.\n const hasDeva = containsDevanagari(fullText);\n const devaCandidateFamilies = hasDeva\n ? uniqueFamilies([fontFamily, FONT_FALLBACK_DEVANAGARI, resolveDevanagariSibling(fontFamily)])\n : [];\n type ResolvedRunFont = { family: string; font: opentype.Font; bytes: { bytes: Uint8Array; cacheKey: string } };\n const devaRunFontCache = new Map<string, Promise<ResolvedRunFont | null>>();\n const resolveDevaFontForRun = (runText: string, runFontSize: number): Promise<ResolvedRunFont | null> => {\n const cacheKey = `${runText}__${runFontSize}`;\n const cached = devaRunFontCache.get(cacheKey);\n if (cached) return cached;\n\n const promise = (async () => {\n const browserWidth = measureBrowserWidth(fontFamily, fontWeight, runFontSize, runText);\n let best: (ResolvedRunFont & { diff: number }) | null = null;\n\n for (const family of devaCandidateFamilies) {\n const font = family === fontFamily ? primaryFont : await loadFont(family, fontWeight, baseUrl);\n const bytes = family === fontFamily ? primaryBytes : await getFontBytes(family, fontWeight, baseUrl);\n if (!font || !bytes || !fontSupportsText(font, runText)) continue;\n\n const width = measureRunWidth(font, runText, runFontSize);\n const diff = browserWidth == null ? 0 : Math.abs(width - browserWidth);\n if (!best || diff < best.diff) best = { family, font, bytes, diff };\n\n // Exact/near-exact match to canvas measurement — no need to keep trying.\n if (browserWidth != null && diff <= 0.25) break;\n }\n\n if (best) {\n console.log('[text-to-path] Devanagari font resolved', {\n requestedFamily: fontFamily,\n resolvedFamily: best.family,\n browserWidth,\n diff: Math.round(best.diff * 100) / 100,\n });\n }\n return best;\n })();\n\n devaRunFontCache.set(cacheKey, promise);\n return promise;\n };\n\n // If we have NEITHER a primary font nor (for Devanagari) a fallback font,\n // we can't outline this element — leave the original <text> in place so\n // the user still sees something rather than nothing.\n if (!primaryFont && !hasDeva) {\n console.warn(`[text-to-path] No font available for \"${fontFamily}\", leaving as <text>`);\n skippedCount++;\n continue;\n }\n\n // Resolve text-anchor (Fabric uses 'middle' for centered text). We measure the\n // run width and apply a leftward shift so the anchor point matches.\n const textAnchorRaw = (getAttrOrStyle(textEl, 'text-anchor') || 'start').toLowerCase();\n const textAnchor: 'start' | 'middle' | 'end' =\n textAnchorRaw === 'middle' ? 'middle' : textAnchorRaw === 'end' ? 'end' : 'start';\n\n // Process tspan children or direct text\n const tspans = textEl.querySelectorAll('tspan');\n const elementsToProcess = tspans.length > 0 ? Array.from(tspans) : [textEl];\n\n // Create a <g> group to replace the <text> element\n const group = doc.createElementNS('http://www.w3.org/2000/svg', 'g');\n\n // Copy transform from <text> to <g>\n const textTransform = textEl.getAttribute('transform');\n if (textTransform) group.setAttribute('transform', textTransform);\n\n // Get base x/y from the <text> element\n const baseX = parseFloat(textEl.getAttribute('x') || '0');\n const baseY = parseFloat(textEl.getAttribute('y') || '0');\n\n for (const elem of elementsToProcess) {\n const text = elem.textContent || '';\n if (!text.trim()) continue;\n\n // Get element-specific position\n const elemX = parseFloat(elem.getAttribute('x') || String(baseX));\n const elemY = parseFloat(elem.getAttribute('y') || String(baseY));\n\n // Get element-specific transform\n const elemTransform = elem !== textEl ? parseTranslate(elem.getAttribute('transform')) : { tx: 0, ty: 0 };\n let x = elemX + elemTransform.tx;\n const y = elemY + elemTransform.ty;\n\n // Get element-specific fill\n const elemFill = getAttrOrStyle(elem, 'fill') || fillColor;\n const elemFillOpacity = getAttrOrStyle(elem, 'fill-opacity') || fillOpacity;\n\n // Get element-specific font size\n const elemFontSizeStr = getAttrOrStyle(elem, 'font-size') || String(fontSize);\n const elemFontSize = parseFloat(elemFontSizeStr);\n\n // Split into script runs so each run uses the appropriate font/shaper.\n const runs = splitByScript(text);\n\n // First pass — measure total advance for text-anchor alignment.\n // We can only compute this if every run has a font; otherwise skip and\n // assume start-anchor (Fabric defaults to start for left-aligned text).\n let totalAdvance = 0;\n let canMeasureAll = true;\n for (const r of runs) {\n if (r.isDevanagari) {\n const resolved = await resolveDevaFontForRun(r.text, elemFontSize);\n if (resolved) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else {\n if (primaryFont) totalAdvance += measureRunWidth(primaryFont, r.text, elemFontSize);\n else canMeasureAll = false;\n }\n }\n\n if (canMeasureAll) {\n if (textAnchor === 'middle') x -= totalAdvance / 2;\n else if (textAnchor === 'end') x -= totalAdvance;\n }\n\n // Second pass — emit a <path> per run.\n let cursor = x;\n let elemConverted = false;\n for (const r of runs) {\n // Pick the font that matches the browser-rendered canvas for this run.\n const resolvedDeva = r.isDevanagari ? await resolveDevaFontForRun(r.text, elemFontSize) : null;\n const useDeva = !!resolvedDeva;\n const fontForRun = resolvedDeva?.font ?? primaryFont;\n const bytesForRun = resolvedDeva?.bytes ?? primaryBytes;\n if (!fontForRun) continue;\n\n const result = await shapeRunToPath(\n r.text,\n !!useDeva,\n fontForRun,\n bytesForRun,\n resolvedDeva?.font ?? null,\n resolvedDeva?.bytes ?? null,\n cursor,\n y,\n elemFontSize,\n );\n\n if (result && result.pathData) {\n const pathEl = doc.createElementNS('http://www.w3.org/2000/svg', 'path');\n pathEl.setAttribute('d', result.pathData);\n pathEl.setAttribute('fill', elemFill);\n if (elemFillOpacity !== '1') {\n pathEl.setAttribute('fill-opacity', elemFillOpacity);\n }\n group.appendChild(pathEl);\n elemConverted = true;\n }\n if (result) cursor += result.advance;\n }\n\n if (elemConverted) {\n convertedCount++;\n } else {\n // Couldn't outline — keep original element so something renders.\n if (elem === textEl) {\n const clone = elem.cloneNode(true) as Element;\n if (textTransform) clone.removeAttribute('transform');\n group.appendChild(clone);\n } else {\n const newText = doc.createElementNS('http://www.w3.org/2000/svg', 'text');\n newText.setAttribute('x', String(elemX));\n newText.setAttribute('y', String(elemY));\n if (elem.getAttribute('style')) newText.setAttribute('style', elem.getAttribute('style')!);\n if (elem.getAttribute('font-family')) newText.setAttribute('font-family', elem.getAttribute('font-family')!);\n if (elem.getAttribute('font-size')) newText.setAttribute('font-size', elem.getAttribute('font-size')!);\n if (elem.getAttribute('font-weight')) newText.setAttribute('font-weight', elem.getAttribute('font-weight')!);\n newText.textContent = text;\n group.appendChild(newText);\n }\n }\n }\n\n // Replace <text> with <g> containing paths\n if (group.childNodes.length > 0) {\n textEl.parentNode?.replaceChild(group, textEl);\n }\n }\n\n console.log(\n `[text-to-path] Universal outline complete: converted=${convertedCount} skipped=${skippedCount}`,\n );\n\n return new XMLSerializer().serializeToString(doc.documentElement);\n}\n\n/**\n * Alias for `convertDevanagariTextToPath` with a clearer name. Both call the\n * same universal outliner — the older name is kept for backwards compatibility.\n */\nexport const convertAllTextToPath = convertDevanagariTextToPath;\n\n/**\n * Pre-warm the font cache for Devanagari fonts.\n * Call this early to avoid latency during PDF export.\n */\nexport async function preloadDevanagariFont(fontBaseUrl?: string): Promise<void> {\n const baseUrl = fontBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin + '/fonts/' : '/fonts/');\n for (const weight of [300, 400, 500, 600, 700]) {\n await loadFont(FONT_FALLBACK_DEVANAGARI, weight, baseUrl);\n }\n}\n"],"names":["fontCache","fontBytesCache","_a"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixldocs/canvas-renderer",
3
- "version": "0.5.65",
3
+ "version": "0.5.66",
4
4
  "description": "Client-side template renderer for Pixldocs — React component + imperative API for rendering templates in any web app",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -21,7 +21,9 @@
21
21
  "dev": "vite build --watch"
22
22
  },
23
23
  "dependencies": {
24
+ "harfbuzzjs": "^0.10.3",
24
25
  "jspdf": "^2.5.2",
26
+ "opentype.js": "^1.3.4",
25
27
  "svg2pdf.js": "^2.2.3"
26
28
  },
27
29
  "peerDependencies": {