@pixldocs/canvas-renderer 0.5.120 → 0.5.122
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-DjwTSk_C.cjs → index-BObeaBCm.cjs} +4 -4
- package/dist/{index-DjwTSk_C.cjs.map → index-BObeaBCm.cjs.map} +1 -1
- package/dist/{index-Dm9AEQy0.js → index-vqyuTJLz.js} +4 -4
- package/dist/{index-Dm9AEQy0.js.map → index-vqyuTJLz.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{pdfFonts-DpkEz6IN.cjs → pdfFonts-BPQ8WSXF.cjs} +79 -3
- package/dist/pdfFonts-BPQ8WSXF.cjs.map +1 -0
- package/dist/{pdfFonts-BUnZcHhp.js → pdfFonts-CZpNVjX9.js} +86 -10
- package/dist/pdfFonts-CZpNVjX9.js.map +1 -0
- package/dist/{svgTextToPath-DR7QIEWZ.js → svgTextToPath-Bsxjuz6-.js} +2 -2
- package/dist/{svgTextToPath-DR7QIEWZ.js.map → svgTextToPath-Bsxjuz6-.js.map} +1 -1
- package/dist/{svgTextToPath-DcQKj5Mc.cjs → svgTextToPath-BytLBSI5.cjs} +2 -2
- package/dist/{svgTextToPath-DcQKj5Mc.cjs.map → svgTextToPath-BytLBSI5.cjs.map} +1 -1
- package/dist/{vectorPdfExport-CxWPJs_l.js → vectorPdfExport-CD0Jz7Cc.js} +15 -5
- package/dist/vectorPdfExport-CD0Jz7Cc.js.map +1 -0
- package/dist/{vectorPdfExport-yAT4k3Je.cjs → vectorPdfExport-DIYIL06v.cjs} +15 -5
- package/dist/vectorPdfExport-DIYIL06v.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/pdfFonts-BUnZcHhp.js.map +0 -1
- package/dist/pdfFonts-DpkEz6IN.cjs.map +0 -1
- package/dist/vectorPdfExport-CxWPJs_l.js.map +0 -1
- package/dist/vectorPdfExport-yAT4k3Je.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svgTextToPath-DcQKj5Mc.cjs","sources":["../../../src/lib/harfbuzzShaper.ts","../../../src/lib/svgTextToPath.ts"],"sourcesContent":["/**\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 * as opentype from 'opentype.js';\nimport {\n FONT_FILES,\n FONT_FALLBACK_SYMBOLS,\n FONT_FALLBACK_DEVANAGARI,\n FONT_FALLBACK_MATH,\n resolveFontWeight,\n resolveDevanagariSibling,\n getGoogleFontBytes,\n getFontshareFontBytes,\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/** True if the character is in Basic Latin, Latin-1, or Latin Extended. */\nfunction isBasicLatinOrLatinExtended(char: string): boolean {\n const c = char.codePointAt(0) ?? 0;\n return c <= 0x024F;\n}\n\ntype FontRunType = 'main' | 'devanagari' | 'math' | 'symbol';\n\n/** Math operators / arrows (≠ ≤ ≥ ≈ ∞ → ← ∑ √ ∈ …) — typically missing from\n * Latin fonts AND from the Noto Sans symbol fallback subset, so they need\n * the dedicated Noto Sans Math fallback to render correctly. */\nfunction isMathOperatorChar(char: string): boolean {\n const c = char.codePointAt(0) ?? 0;\n if (c >= 0x2190 && c <= 0x21FF) return true; // Arrows\n if (c >= 0x2200 && c <= 0x22FF) return true; // Mathematical Operators\n if (c >= 0x2300 && c <= 0x23FF) return true; // Misc Technical\n if (c >= 0x27C0 && c <= 0x27EF) return true; // Misc Math Symbols-A\n if (c >= 0x2980 && c <= 0x29FF) return true; // Misc Math Symbols-B\n if (c >= 0x2A00 && c <= 0x2AFF) return true; // Supplemental Math Operators\n if (c >= 0x2B00 && c <= 0x2B59) return true;\n return false;\n}\n\nfunction classifyCharForFontRun(char: string): FontRunType {\n if (isBasicLatinOrLatinExtended(char)) return 'main';\n if (isDevanagari(char)) return 'devanagari';\n if (isMathOperatorChar(char)) return 'math';\n return 'symbol';\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 relevant glyph in this run. */\nfunction fontSupportsRun(font: opentype.Font | null, text: string, runType: FontRunType): boolean {\n if (!font) return false;\n for (const char of text) {\n if (isIgnorableForCoverage(char)) continue;\n if (runType === 'devanagari' && !isDevanagari(char)) continue;\n if (runType === 'symbol' && classifyCharForFontRun(char) !== 'symbol') continue;\n if (runType === 'math' && classifyCharForFontRun(char) !== 'math') 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\nfunction fontLooksItalic(font: opentype.Font | null | undefined): boolean {\n if (!font) return false;\n const names = font.names as unknown as Record<string, Record<string, string> | undefined>;\n const candidates = [\n names.fontSubfamily?.en,\n names.typographicSubfamily?.en,\n names.fullName?.en,\n names.postScriptName?.en,\n ].filter(Boolean).join(' ');\n return /italic|oblique/i.test(candidates);\n}\n\nfunction syntheticItalicTransform(anchorX: number, baselineY: number): string {\n return `translate(${anchorX} ${baselineY}) skewX(-12) translate(${-anchorX} ${-baselineY})`;\n}\n\n/** Get the font file path for a given weight */\nfunction getFontPath(\n fontFiles: FontWeightFiles,\n weight: number,\n isItalic = false,\n): string | null {\n const resolved = resolveFontWeight(weight);\n const uprightMap: 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 // Italic/oblique: prefer the matching italic variant, then fall back through\n // other italic variants, then upright as a last resort. Without this italic\n // tspans (e.g. *italic* markdown) silently rendered as upright in the PDF.\n const italicMap: 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', 'semiboldItalic', 'italic', 'bold', 'semibold', 'regular'],\n };\n const map = isItalic ? italicMap : uprightMap;\n for (const key of map[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, isItalic = false): Promise<opentype.Font | null> {\n const cacheKey = `${fontFamily}__${weight}__${isItalic ? 'i' : 'n'}`;\n if (fontCache.has(cacheKey)) return fontCache.get(cacheKey)!;\n\n const fontFiles = FONT_FILES[fontFamily];\n\n // ── Local TTF path ──\n // Use local files for both upright and italic variants when available.\n // (Previously italic always fell through to Google/Fontshare and silently\n // dropped to the regular weight when those lookups failed, so *italic*\n // markdown text rendered upright in the vector PDF.)\n if (fontFiles) {\n const fileName = getFontPath(fontFiles, weight, isItalic);\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 // Try Google first, then Fontshare. Both populate the same byte cache shape\n // (`gf:${family}:${weight}:n|i`) so downstream HarfBuzz lookups still hit.\n let bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, isItalic);\n if (!bytes) {\n bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, isItalic);\n }\n // For italic, fall back to upright if italic variant is unavailable so\n // the bold/italic combo still renders the right family.\n if (!bytes && isItalic) {\n bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, false);\n if (!bytes) bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, false);\n }\n if (!bytes) {\n console.warn(`[text-to-path] No TTF available for ${fontFamily} (${weight}) on Google Fonts or Fontshare`);\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 isItalic = false,\n): Promise<{ bytes: Uint8Array; cacheKey: string } | null> {\n const cacheKey = `${fontFamily}__${weight}__${isItalic ? 'i' : 'n'}`;\n if (!fontBytesCache.has(cacheKey)) {\n // Triggers fetch + populates fontBytesCache as a side-effect.\n await loadFont(fontFamily, weight, fontBaseUrl, isItalic);\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/fallback font,\n * preserving order. Whitespace stays with its preceding run.\n */\nfunction splitByFontRun(text: string): Array<{ text: string; runType: FontRunType }> {\n if (!text) return [];\n const runs: Array<{ text: string; runType: FontRunType }> = [];\n let buf = '';\n let bufType = classifyCharForFontRun(text[0]);\n for (const ch of text) {\n const chType = classifyCharForFontRun(ch);\n // Whitespace and punctuation stick to the current run to avoid breaking shaping.\n const isNeutral = /\\s/.test(ch);\n if (chType === bufType || isNeutral) {\n buf += ch;\n } else {\n if (buf) runs.push({ text: buf, runType: bufType });\n buf = ch;\n bufType = chType;\n }\n }\n if (buf) runs.push({ text: buf, runType: bufType });\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/** Heuristic: does this text need shaping (complex script) we can't trust the PDF font embedder for? */\nfunction needsComplexShaping(text: string): boolean {\n if (!text) return false;\n for (const ch of text) {\n const c = ch.codePointAt(0) ?? 0;\n // Devanagari + extensions\n if ((c >= 0x0900 && c <= 0x097F) || (c >= 0xA8E0 && c <= 0xA8FF) || (c >= 0x1CD0 && c <= 0x1CFF)) return true;\n // Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala\n if (c >= 0x0980 && c <= 0x0DFF) return true;\n // Thai, Lao, Tibetan, Myanmar, Khmer\n if (c >= 0x0E00 && c <= 0x0FFF) return true;\n if (c >= 0x1000 && c <= 0x109F) return true;\n if (c >= 0x1780 && c <= 0x17FF) return true;\n // Arabic, Hebrew (RTL + shaping)\n if (c >= 0x0590 && c <= 0x06FF) return true;\n if (c >= 0x0700 && c <= 0x074F) return true;\n // CJK\n if (c >= 0x3000 && c <= 0x9FFF) return true;\n if (c >= 0xAC00 && c <= 0xD7AF) return true;\n // Emoji / supplementary\n if (c >= 0x1F000) return true;\n // Math operators / arrows / misc technical — Latin fonts almost never\n // contain these glyphs, so jsPDF renders them as `.notdef` (backtick).\n // Treat as complex-shaping so they get outlined via the math fallback.\n if (c >= 0x2190 && c <= 0x21FF) return true; // Arrows\n if (c >= 0x2200 && c <= 0x22FF) return true; // Mathematical Operators\n if (c >= 0x2300 && c <= 0x23FF) return true; // Misc Technical\n if (c >= 0x27C0 && c <= 0x27EF) return true; // Misc Math Symbols-A\n if (c >= 0x2980 && c <= 0x29FF) return true; // Misc Math Symbols-B\n if (c >= 0x2A00 && c <= 0x2AFF) return true; // Supplemental Math Operators\n if (c >= 0x2B00 && c <= 0x2B59) return true;\n }\n return false;\n}\n\nexport interface OutlineTextOptions {\n /**\n * - 'all' (default): outline every <text> for pixel-perfect parity.\n * - 'complex-only': only outline text that contains complex scripts (Indic, Arabic, CJK, emoji);\n * leave Latin text as <text> so it stays selectable in the PDF.\n * - 'mixed-style-only': only outline <text> elements whose tspans override\n * font-weight / font-style / fill (i.e. inline markdown formatting like\n * **bold**, *italic*, [c=primary]color[/c]). Plain text stays selectable\n * while formatted runs render as paths so bold/italic/colored spans don't\n * silently lose their styling because jsPDF only embedded one font weight.\n */\n mode?: 'all' | 'complex-only' | 'mixed-style-only';\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 * @param options - Outline mode controls (default: outline all)\n * @returns Modified SVG string with text converted to paths\n */\nexport async function convertDevanagariTextToPath(\n svgStr: string,\n fontBaseUrl?: string,\n options: OutlineTextOptions = {},\n): Promise<string> {\n const baseUrl = fontBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin + '/fonts/' : '/fonts/');\n const mode = options.mode ?? 'all';\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 // Helper: does any tspan override formatting (markdown bold/italic/color/decoration)?\n const hasMixedStyleTspans = (): boolean => {\n const tspansAll = textEl.querySelectorAll('tspan');\n const readProp = (el: Element, prop: string): string => {\n const attr = (el.getAttribute(prop) || '').trim();\n if (attr) return attr;\n const style = el.getAttribute('style') || '';\n const m = style.match(new RegExp(`(?:^|;)\\\\s*${prop}\\\\s*:\\\\s*([^;]+)`, 'i'));\n return m ? m[1].trim() : '';\n };\n const baseWeight = readProp(textEl, 'font-weight');\n const baseStyle = readProp(textEl, 'font-style');\n const baseFill = readProp(textEl, 'fill');\n const baseDeco = readProp(textEl, 'text-decoration');\n for (const ts of tspansAll) {\n const tw = readProp(ts, 'font-weight');\n const tst = readProp(ts, 'font-style');\n const tFill = readProp(ts, 'fill');\n const tDeco = readProp(ts, 'text-decoration');\n const tBg = (ts.getAttribute('style') || '').match(/background|text-background/i);\n if (\n (tw && tw !== baseWeight) ||\n (tst && tst !== baseStyle) ||\n (tFill && tFill !== baseFill) ||\n (tDeco && tDeco !== baseDeco) ||\n tBg\n ) {\n return true;\n }\n }\n return false;\n };\n\n // 'complex-only' mode: leave plain Latin text as real selectable text,\n // BUT still outline anything with markdown formatting (mixed-style tspans)\n // since svg2pdf only embeds one font weight per element.\n if (mode === 'complex-only' && !needsComplexShaping(fullText) && !hasMixedStyleTspans()) {\n skippedCount++;\n continue;\n }\n\n // 'mixed-style-only' mode: only outline <text> elements that have any\n // tspan with formatting overrides relative to the parent <text> (different\n // font-weight, font-style, fill, or text-decoration). These come from\n // inline markdown — bold/italic/highlight/color runs that svg2pdf can't\n // render correctly because jsPDF only has the element's base font embedded.\n if (mode === 'mixed-style-only' && !hasMixedStyleTspans()) {\n skippedCount++;\n continue;\n }\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 hasSymbol = [...fullText].some((char) => classifyCharForFontRun(char) === 'symbol');\n const hasMath = [...fullText].some((char) => classifyCharForFontRun(char) === 'math');\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 || !fontSupportsRun(font, runText, 'devanagari')) 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 const symbolRunFontPromise: Promise<ResolvedRunFont | null> | null = hasSymbol\n ? (async () => {\n const symbolFont = await loadFont(FONT_FALLBACK_SYMBOLS, 400, baseUrl);\n const symbolBytes = await getFontBytes(FONT_FALLBACK_SYMBOLS, 400, baseUrl);\n return symbolFont && symbolBytes ? { family: FONT_FALLBACK_SYMBOLS, font: symbolFont, bytes: symbolBytes } : null;\n })()\n : null;\n const mathRunFontPromise: Promise<ResolvedRunFont | null> | null = hasMath\n ? (async () => {\n const mathFont = await loadFont(FONT_FALLBACK_MATH, 400, baseUrl);\n const mathBytes = await getFontBytes(FONT_FALLBACK_MATH, 400, baseUrl);\n return mathFont && mathBytes ? { family: FONT_FALLBACK_MATH, font: mathFont, bytes: mathBytes } : null;\n })()\n : null;\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 && !hasSymbol && !hasMath) {\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 // ── Per-element font weight / style (markdown bold/italic support) ──\n // Each tspan may override font-weight (e.g. **bold**) and font-style\n // (e.g. *italic*). Resolve per-element so we load the matching font\n // variant for outlining instead of always using the parent font.\n const elemWeightStr = getAttrOrStyle(elem, 'font-weight') || String(fontWeight);\n const elemWeight = Number.parseInt(elemWeightStr, 10) || (/bold/i.test(elemWeightStr) ? 700 : fontWeight);\n const elemStyleRaw = (getAttrOrStyle(elem, 'font-style') || 'normal').toLowerCase();\n const elemItalic = /italic|oblique/i.test(elemStyleRaw);\n const sameAsPrimary = elemWeight === fontWeight && !elemItalic;\n const elemFont = sameAsPrimary\n ? primaryFont\n : await loadFont(fontFamily, elemWeight, baseUrl, elemItalic);\n const elemBytes = sameAsPrimary\n ? primaryBytes\n : await getFontBytes(fontFamily, elemWeight, baseUrl, elemItalic);\n // If the variant didn't load, fall back to the primary font so we still\n // render something — better than dropping the tspan entirely.\n const fontForElem = elemFont || primaryFont;\n const bytesForElem = elemBytes || primaryBytes;\n\n // Split into font runs so each run uses the appropriate font/shaper.\n const runs = splitByFontRun(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.runType === 'devanagari') {\n const resolved = await resolveDevaFontForRun(r.text, elemFontSize);\n if (resolved) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else if (r.runType === 'symbol') {\n const resolved = await symbolRunFontPromise;\n if (resolved && fontSupportsRun(resolved.font, r.text, 'symbol')) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else if (r.runType === 'math') {\n const resolved = await mathRunFontPromise;\n if (resolved && fontSupportsRun(resolved.font, r.text, 'math')) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else {\n if (fontForElem) totalAdvance += measureRunWidth(fontForElem, 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.runType === 'devanagari' ? await resolveDevaFontForRun(r.text, elemFontSize) : null;\n const resolvedSymbol = r.runType === 'symbol' ? await symbolRunFontPromise : null;\n const resolvedMath = r.runType === 'math' ? await mathRunFontPromise : null;\n const useDeva = !!resolvedDeva;\n const fontForRun = resolvedDeva?.font ?? resolvedSymbol?.font ?? resolvedMath?.font ?? fontForElem;\n const bytesForRun = resolvedDeva?.bytes ?? resolvedSymbol?.bytes ?? resolvedMath?.bytes ?? bytesForElem;\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 (elemItalic && !fontLooksItalic(fontForRun)) {\n // Families like Excon have no italic file; the browser preview uses\n // synthetic oblique, so skew the outlined glyph paths to match it.\n pathEl.setAttribute('transform', syntheticItalicTransform(cursor, y));\n }\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":["resolveFontWeight","FONT_FILES","opentype","getGoogleFontBytes","getFontshareFontBytes","_a","FONT_FALLBACK_DEVANAGARI","resolveDevanagariSibling","FONT_FALLBACK_SYMBOLS","FONT_FALLBACK_MATH"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"svgTextToPath-BytLBSI5.cjs","sources":["../../../src/lib/harfbuzzShaper.ts","../../../src/lib/svgTextToPath.ts"],"sourcesContent":["/**\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 * as opentype from 'opentype.js';\nimport {\n FONT_FILES,\n FONT_FALLBACK_SYMBOLS,\n FONT_FALLBACK_DEVANAGARI,\n FONT_FALLBACK_MATH,\n resolveFontWeight,\n resolveDevanagariSibling,\n getGoogleFontBytes,\n getFontshareFontBytes,\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/** True if the character is in Basic Latin, Latin-1, or Latin Extended. */\nfunction isBasicLatinOrLatinExtended(char: string): boolean {\n const c = char.codePointAt(0) ?? 0;\n return c <= 0x024F;\n}\n\ntype FontRunType = 'main' | 'devanagari' | 'math' | 'symbol';\n\n/** Math operators / arrows (≠ ≤ ≥ ≈ ∞ → ← ∑ √ ∈ …) — typically missing from\n * Latin fonts AND from the Noto Sans symbol fallback subset, so they need\n * the dedicated Noto Sans Math fallback to render correctly. */\nfunction isMathOperatorChar(char: string): boolean {\n const c = char.codePointAt(0) ?? 0;\n if (c >= 0x2190 && c <= 0x21FF) return true; // Arrows\n if (c >= 0x2200 && c <= 0x22FF) return true; // Mathematical Operators\n if (c >= 0x2300 && c <= 0x23FF) return true; // Misc Technical\n if (c >= 0x27C0 && c <= 0x27EF) return true; // Misc Math Symbols-A\n if (c >= 0x2980 && c <= 0x29FF) return true; // Misc Math Symbols-B\n if (c >= 0x2A00 && c <= 0x2AFF) return true; // Supplemental Math Operators\n if (c >= 0x2B00 && c <= 0x2B59) return true;\n return false;\n}\n\nfunction classifyCharForFontRun(char: string): FontRunType {\n if (isBasicLatinOrLatinExtended(char)) return 'main';\n if (isDevanagari(char)) return 'devanagari';\n if (isMathOperatorChar(char)) return 'math';\n return 'symbol';\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 relevant glyph in this run. */\nfunction fontSupportsRun(font: opentype.Font | null, text: string, runType: FontRunType): boolean {\n if (!font) return false;\n for (const char of text) {\n if (isIgnorableForCoverage(char)) continue;\n if (runType === 'devanagari' && !isDevanagari(char)) continue;\n if (runType === 'symbol' && classifyCharForFontRun(char) !== 'symbol') continue;\n if (runType === 'math' && classifyCharForFontRun(char) !== 'math') 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\nfunction fontLooksItalic(font: opentype.Font | null | undefined): boolean {\n if (!font) return false;\n const names = font.names as unknown as Record<string, Record<string, string> | undefined>;\n const candidates = [\n names.fontSubfamily?.en,\n names.typographicSubfamily?.en,\n names.fullName?.en,\n names.postScriptName?.en,\n ].filter(Boolean).join(' ');\n return /italic|oblique/i.test(candidates);\n}\n\nfunction syntheticItalicTransform(anchorX: number, baselineY: number): string {\n return `translate(${anchorX} ${baselineY}) skewX(-12) translate(${-anchorX} ${-baselineY})`;\n}\n\n/** Get the font file path for a given weight */\nfunction getFontPath(\n fontFiles: FontWeightFiles,\n weight: number,\n isItalic = false,\n): string | null {\n const resolved = resolveFontWeight(weight);\n const uprightMap: 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 // Italic/oblique: prefer the matching italic variant, then fall back through\n // other italic variants, then upright as a last resort. Without this italic\n // tspans (e.g. *italic* markdown) silently rendered as upright in the PDF.\n const italicMap: 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', 'semiboldItalic', 'italic', 'bold', 'semibold', 'regular'],\n };\n const map = isItalic ? italicMap : uprightMap;\n for (const key of map[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, isItalic = false): Promise<opentype.Font | null> {\n const cacheKey = `${fontFamily}__${weight}__${isItalic ? 'i' : 'n'}`;\n if (fontCache.has(cacheKey)) return fontCache.get(cacheKey)!;\n\n const fontFiles = FONT_FILES[fontFamily];\n\n // ── Local TTF path ──\n // Use local files for both upright and italic variants when available.\n // (Previously italic always fell through to Google/Fontshare and silently\n // dropped to the regular weight when those lookups failed, so *italic*\n // markdown text rendered upright in the vector PDF.)\n if (fontFiles) {\n const fileName = getFontPath(fontFiles, weight, isItalic);\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 // Try Google first, then Fontshare. Both populate the same byte cache shape\n // (`gf:${family}:${weight}:n|i`) so downstream HarfBuzz lookups still hit.\n let bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, isItalic);\n if (!bytes) {\n bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, isItalic);\n }\n // For italic, fall back to upright if italic variant is unavailable so\n // the bold/italic combo still renders the right family.\n if (!bytes && isItalic) {\n bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, false);\n if (!bytes) bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, false);\n }\n if (!bytes) {\n console.warn(`[text-to-path] No TTF available for ${fontFamily} (${weight}) on Google Fonts or Fontshare`);\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 isItalic = false,\n): Promise<{ bytes: Uint8Array; cacheKey: string } | null> {\n const cacheKey = `${fontFamily}__${weight}__${isItalic ? 'i' : 'n'}`;\n if (!fontBytesCache.has(cacheKey)) {\n // Triggers fetch + populates fontBytesCache as a side-effect.\n await loadFont(fontFamily, weight, fontBaseUrl, isItalic);\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/fallback font,\n * preserving order. Whitespace stays with its preceding run.\n */\nfunction splitByFontRun(text: string): Array<{ text: string; runType: FontRunType }> {\n if (!text) return [];\n const runs: Array<{ text: string; runType: FontRunType }> = [];\n let buf = '';\n let bufType = classifyCharForFontRun(text[0]);\n for (const ch of text) {\n const chType = classifyCharForFontRun(ch);\n // Whitespace and punctuation stick to the current run to avoid breaking shaping.\n const isNeutral = /\\s/.test(ch);\n if (chType === bufType || isNeutral) {\n buf += ch;\n } else {\n if (buf) runs.push({ text: buf, runType: bufType });\n buf = ch;\n bufType = chType;\n }\n }\n if (buf) runs.push({ text: buf, runType: bufType });\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/** Heuristic: does this text need shaping (complex script) we can't trust the PDF font embedder for? */\nfunction needsComplexShaping(text: string): boolean {\n if (!text) return false;\n for (const ch of text) {\n const c = ch.codePointAt(0) ?? 0;\n // Devanagari + extensions\n if ((c >= 0x0900 && c <= 0x097F) || (c >= 0xA8E0 && c <= 0xA8FF) || (c >= 0x1CD0 && c <= 0x1CFF)) return true;\n // Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala\n if (c >= 0x0980 && c <= 0x0DFF) return true;\n // Thai, Lao, Tibetan, Myanmar, Khmer\n if (c >= 0x0E00 && c <= 0x0FFF) return true;\n if (c >= 0x1000 && c <= 0x109F) return true;\n if (c >= 0x1780 && c <= 0x17FF) return true;\n // Arabic, Hebrew (RTL + shaping)\n if (c >= 0x0590 && c <= 0x06FF) return true;\n if (c >= 0x0700 && c <= 0x074F) return true;\n // CJK\n if (c >= 0x3000 && c <= 0x9FFF) return true;\n if (c >= 0xAC00 && c <= 0xD7AF) return true;\n // Emoji / supplementary\n if (c >= 0x1F000) return true;\n // Math operators / arrows / misc technical — Latin fonts almost never\n // contain these glyphs, so jsPDF renders them as `.notdef` (backtick).\n // Treat as complex-shaping so they get outlined via the math fallback.\n if (c >= 0x2190 && c <= 0x21FF) return true; // Arrows\n if (c >= 0x2200 && c <= 0x22FF) return true; // Mathematical Operators\n if (c >= 0x2300 && c <= 0x23FF) return true; // Misc Technical\n if (c >= 0x27C0 && c <= 0x27EF) return true; // Misc Math Symbols-A\n if (c >= 0x2980 && c <= 0x29FF) return true; // Misc Math Symbols-B\n if (c >= 0x2A00 && c <= 0x2AFF) return true; // Supplemental Math Operators\n if (c >= 0x2B00 && c <= 0x2B59) return true;\n }\n return false;\n}\n\nexport interface OutlineTextOptions {\n /**\n * - 'all' (default): outline every <text> for pixel-perfect parity.\n * - 'complex-only': only outline text that contains complex scripts (Indic, Arabic, CJK, emoji);\n * leave Latin text as <text> so it stays selectable in the PDF.\n * - 'mixed-style-only': only outline <text> elements whose tspans override\n * font-weight / font-style / fill (i.e. inline markdown formatting like\n * **bold**, *italic*, [c=primary]color[/c]). Plain text stays selectable\n * while formatted runs render as paths so bold/italic/colored spans don't\n * silently lose their styling because jsPDF only embedded one font weight.\n */\n mode?: 'all' | 'complex-only' | 'mixed-style-only';\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 * @param options - Outline mode controls (default: outline all)\n * @returns Modified SVG string with text converted to paths\n */\nexport async function convertDevanagariTextToPath(\n svgStr: string,\n fontBaseUrl?: string,\n options: OutlineTextOptions = {},\n): Promise<string> {\n const baseUrl = fontBaseUrl ?? (typeof window !== 'undefined' ? window.location.origin + '/fonts/' : '/fonts/');\n const mode = options.mode ?? 'all';\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 // Helper: does any tspan override formatting (markdown bold/italic/color/decoration)?\n const hasMixedStyleTspans = (): boolean => {\n const tspansAll = textEl.querySelectorAll('tspan');\n const readProp = (el: Element, prop: string): string => {\n const attr = (el.getAttribute(prop) || '').trim();\n if (attr) return attr;\n const style = el.getAttribute('style') || '';\n const m = style.match(new RegExp(`(?:^|;)\\\\s*${prop}\\\\s*:\\\\s*([^;]+)`, 'i'));\n return m ? m[1].trim() : '';\n };\n const baseWeight = readProp(textEl, 'font-weight');\n const baseStyle = readProp(textEl, 'font-style');\n const baseFill = readProp(textEl, 'fill');\n const baseDeco = readProp(textEl, 'text-decoration');\n for (const ts of tspansAll) {\n const tw = readProp(ts, 'font-weight');\n const tst = readProp(ts, 'font-style');\n const tFill = readProp(ts, 'fill');\n const tDeco = readProp(ts, 'text-decoration');\n const tBg = (ts.getAttribute('style') || '').match(/background|text-background/i);\n if (\n (tw && tw !== baseWeight) ||\n (tst && tst !== baseStyle) ||\n (tFill && tFill !== baseFill) ||\n (tDeco && tDeco !== baseDeco) ||\n tBg\n ) {\n return true;\n }\n }\n return false;\n };\n\n // 'complex-only' mode: leave plain Latin text as real selectable text,\n // BUT still outline anything with markdown formatting (mixed-style tspans)\n // since svg2pdf only embeds one font weight per element.\n if (mode === 'complex-only' && !needsComplexShaping(fullText) && !hasMixedStyleTspans()) {\n skippedCount++;\n continue;\n }\n\n // 'mixed-style-only' mode: only outline <text> elements that have any\n // tspan with formatting overrides relative to the parent <text> (different\n // font-weight, font-style, fill, or text-decoration). These come from\n // inline markdown — bold/italic/highlight/color runs that svg2pdf can't\n // render correctly because jsPDF only has the element's base font embedded.\n if (mode === 'mixed-style-only' && !hasMixedStyleTspans()) {\n skippedCount++;\n continue;\n }\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 hasSymbol = [...fullText].some((char) => classifyCharForFontRun(char) === 'symbol');\n const hasMath = [...fullText].some((char) => classifyCharForFontRun(char) === 'math');\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 || !fontSupportsRun(font, runText, 'devanagari')) 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 const symbolRunFontPromise: Promise<ResolvedRunFont | null> | null = hasSymbol\n ? (async () => {\n const symbolFont = await loadFont(FONT_FALLBACK_SYMBOLS, 400, baseUrl);\n const symbolBytes = await getFontBytes(FONT_FALLBACK_SYMBOLS, 400, baseUrl);\n return symbolFont && symbolBytes ? { family: FONT_FALLBACK_SYMBOLS, font: symbolFont, bytes: symbolBytes } : null;\n })()\n : null;\n const mathRunFontPromise: Promise<ResolvedRunFont | null> | null = hasMath\n ? (async () => {\n const mathFont = await loadFont(FONT_FALLBACK_MATH, 400, baseUrl);\n const mathBytes = await getFontBytes(FONT_FALLBACK_MATH, 400, baseUrl);\n return mathFont && mathBytes ? { family: FONT_FALLBACK_MATH, font: mathFont, bytes: mathBytes } : null;\n })()\n : null;\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 && !hasSymbol && !hasMath) {\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 // ── Per-element font weight / style (markdown bold/italic support) ──\n // Each tspan may override font-weight (e.g. **bold**) and font-style\n // (e.g. *italic*). Resolve per-element so we load the matching font\n // variant for outlining instead of always using the parent font.\n const elemWeightStr = getAttrOrStyle(elem, 'font-weight') || String(fontWeight);\n const elemWeight = Number.parseInt(elemWeightStr, 10) || (/bold/i.test(elemWeightStr) ? 700 : fontWeight);\n const elemStyleRaw = (getAttrOrStyle(elem, 'font-style') || 'normal').toLowerCase();\n const elemItalic = /italic|oblique/i.test(elemStyleRaw);\n const sameAsPrimary = elemWeight === fontWeight && !elemItalic;\n const elemFont = sameAsPrimary\n ? primaryFont\n : await loadFont(fontFamily, elemWeight, baseUrl, elemItalic);\n const elemBytes = sameAsPrimary\n ? primaryBytes\n : await getFontBytes(fontFamily, elemWeight, baseUrl, elemItalic);\n // If the variant didn't load, fall back to the primary font so we still\n // render something — better than dropping the tspan entirely.\n const fontForElem = elemFont || primaryFont;\n const bytesForElem = elemBytes || primaryBytes;\n\n // Split into font runs so each run uses the appropriate font/shaper.\n const runs = splitByFontRun(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.runType === 'devanagari') {\n const resolved = await resolveDevaFontForRun(r.text, elemFontSize);\n if (resolved) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else if (r.runType === 'symbol') {\n const resolved = await symbolRunFontPromise;\n if (resolved && fontSupportsRun(resolved.font, r.text, 'symbol')) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else if (r.runType === 'math') {\n const resolved = await mathRunFontPromise;\n if (resolved && fontSupportsRun(resolved.font, r.text, 'math')) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);\n else canMeasureAll = false;\n } else {\n if (fontForElem) totalAdvance += measureRunWidth(fontForElem, 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.runType === 'devanagari' ? await resolveDevaFontForRun(r.text, elemFontSize) : null;\n const resolvedSymbol = r.runType === 'symbol' ? await symbolRunFontPromise : null;\n const resolvedMath = r.runType === 'math' ? await mathRunFontPromise : null;\n const useDeva = !!resolvedDeva;\n const fontForRun = resolvedDeva?.font ?? resolvedSymbol?.font ?? resolvedMath?.font ?? fontForElem;\n const bytesForRun = resolvedDeva?.bytes ?? resolvedSymbol?.bytes ?? resolvedMath?.bytes ?? bytesForElem;\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 (elemItalic && !fontLooksItalic(fontForRun)) {\n // Families like Excon have no italic file; the browser preview uses\n // synthetic oblique, so skew the outlined glyph paths to match it.\n pathEl.setAttribute('transform', syntheticItalicTransform(cursor, y));\n }\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":["resolveFontWeight","FONT_FILES","opentype","getGoogleFontBytes","getFontshareFontBytes","_a","FONT_FALLBACK_DEVANAGARI","resolveDevanagariSibling","FONT_FALLBACK_SYMBOLS","FONT_FALLBACK_MATH"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsPDF, ShadingPattern } from "jspdf";
|
|
2
2
|
import { svg2pdf } from "svg2pdf.js";
|
|
3
3
|
import * as fabric from "fabric";
|
|
4
|
-
import { g as getCanvasForPage, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, c as bakeEdgeFade, i as isElement, d as isGroup, e as buildRoundedTrianglePath, A as API_URL, j as getImageProxyFetchOptions, k as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, l as getTrianglePoints } from "./index-
|
|
5
|
-
import { r as resetPdfFontRegistry, F as FONT_FALLBACK_SYMBOLS, a as FONT_FALLBACK_MATH, b as FONT_FALLBACK_DEVANAGARI, e as embedFontWithGoogleFallback, i as isFontAvailable, c as isFamilyEmbedded, d as resolveBestRegisteredVariant,
|
|
4
|
+
import { g as getCanvasForPage, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, c as bakeEdgeFade, i as isElement, d as isGroup, e as buildRoundedTrianglePath, A as API_URL, j as getImageProxyFetchOptions, k as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, l as getTrianglePoints } from "./index-vqyuTJLz.js";
|
|
5
|
+
import { r as resetPdfFontRegistry, F as FONT_FALLBACK_SYMBOLS, a as FONT_FALLBACK_MATH, b as FONT_FALLBACK_DEVANAGARI, e as embedFontWithGoogleFallback, g as getEmbeddedVariantsList, i as isFontAvailable, c as isFamilyEmbedded, d as resolveBestRegisteredVariant, f as getEmbeddedJsPDFFontName, h as resolveFontWeight, j as doesVariantSupportChar } from "./pdfFonts-CZpNVjX9.js";
|
|
6
6
|
async function bakeEdgeFadeIntoDataUrl(dataUrl, element) {
|
|
7
7
|
const fade = element;
|
|
8
8
|
if (!hasEdgeFade(fade)) return null;
|
|
@@ -2022,7 +2022,7 @@ async function fetchSvgAsElement(imageUrl, colorMap) {
|
|
|
2022
2022
|
async function getRecoloredSvgDataUrl(imageUrl, colorMap) {
|
|
2023
2023
|
if (!colorMap || Object.keys(colorMap).length === 0) return null;
|
|
2024
2024
|
try {
|
|
2025
|
-
const { getNormalizedSvgUrl } = await import("./index-
|
|
2025
|
+
const { getNormalizedSvgUrl } = await import("./index-vqyuTJLz.js").then((n) => n.Z);
|
|
2026
2026
|
return await getNormalizedSvgUrl(imageUrl, colorMap);
|
|
2027
2027
|
} catch {
|
|
2028
2028
|
return null;
|
|
@@ -2803,7 +2803,7 @@ async function fetchImageAsBase64(imageUrl, opts = {}) {
|
|
|
2803
2803
|
}
|
|
2804
2804
|
let fetchUrl = imageUrl;
|
|
2805
2805
|
if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
|
|
2806
|
-
const { isPrivateUrl } = await import("./index-
|
|
2806
|
+
const { isPrivateUrl } = await import("./index-vqyuTJLz.js").then((n) => n.Z);
|
|
2807
2807
|
if (isPrivateUrl(imageUrl)) return null;
|
|
2808
2808
|
const proxyUrl = new URL(`${API_URL}/image-proxy`);
|
|
2809
2809
|
proxyUrl.searchParams.set("url", imageUrl);
|
|
@@ -4342,6 +4342,11 @@ async function exportFabricCanvasToVectorPdf(_fabricCanvas, options) {
|
|
|
4342
4342
|
const success = await embedFontWithGoogleFallback(pdf, fontName, weight, true);
|
|
4343
4343
|
if (success) embeddedFonts.add(key);
|
|
4344
4344
|
}
|
|
4345
|
+
try {
|
|
4346
|
+
const list = getEmbeddedVariantsList();
|
|
4347
|
+
console.log(`[pdfFonts] embedded variants (${list.length}):`, list);
|
|
4348
|
+
} catch {
|
|
4349
|
+
}
|
|
4345
4350
|
debugLog(`single-page export: drawing ${elements.length} elements (order = first=back, last=front)`);
|
|
4346
4351
|
elements.forEach((el, i) => debugLog(` [${i}] id=${el.id} type=${el.type}`));
|
|
4347
4352
|
for (const element of elements) {
|
|
@@ -4454,6 +4459,11 @@ async function exportMultiPagePdf(pages, options) {
|
|
|
4454
4459
|
await yieldToUI();
|
|
4455
4460
|
}
|
|
4456
4461
|
await yieldToUI();
|
|
4462
|
+
try {
|
|
4463
|
+
const list = getEmbeddedVariantsList();
|
|
4464
|
+
console.log(`[pdfFonts] embedded variants (${list.length}):`, list);
|
|
4465
|
+
} catch {
|
|
4466
|
+
}
|
|
4457
4467
|
for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
|
|
4458
4468
|
const page = pages[pageIndex];
|
|
4459
4469
|
if (pageIndex > 0) {
|
|
@@ -4776,4 +4786,4 @@ export {
|
|
|
4776
4786
|
preparePagesForExport,
|
|
4777
4787
|
rewriteSvgFontsForJsPDFWithSourceMeta
|
|
4778
4788
|
};
|
|
4779
|
-
//# sourceMappingURL=vectorPdfExport-
|
|
4789
|
+
//# sourceMappingURL=vectorPdfExport-CD0Jz7Cc.js.map
|