@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,1105 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
+ const opentype = require("opentype.js");
26
+ const fontCache$1 = /* @__PURE__ */ new Map();
27
+ const fontBytesCache$1 = /* @__PURE__ */ new Map();
28
+ const googleFontUrlCache = /* @__PURE__ */ new Map();
29
+ const googleFontNotFound = /* @__PURE__ */ new Set();
30
+ function resolveFontWeight(weight) {
31
+ if (weight <= 350) return 300;
32
+ if (weight <= 450) return 400;
33
+ if (weight <= 550) return 500;
34
+ if (weight <= 650) return 600;
35
+ return 700;
36
+ }
37
+ const FONT_FALLBACK_DEVANAGARI = "Hind";
38
+ const FONT_FILES = {
39
+ "Playfair Display": {
40
+ regular: "/fonts/PlayfairDisplay-Regular.ttf",
41
+ bold: "/fonts/PlayfairDisplay-Bold.ttf",
42
+ italic: "/fonts/PlayfairDisplay-Italic.ttf",
43
+ boldItalic: "/fonts/PlayfairDisplay-BoldItalic.ttf"
44
+ },
45
+ "Merriweather": {
46
+ regular: "/fonts/Merriweather-Regular.ttf",
47
+ bold: "/fonts/Merriweather-Bold.ttf",
48
+ light: "/fonts/Merriweather-Light.ttf",
49
+ italic: "/fonts/Merriweather-Italic.ttf",
50
+ boldItalic: "/fonts/Merriweather-BoldItalic.ttf",
51
+ lightItalic: "/fonts/Merriweather-LightItalic.ttf"
52
+ },
53
+ "Lora": {
54
+ regular: "/fonts/Lora-Regular.ttf",
55
+ bold: "/fonts/Lora-Bold.ttf",
56
+ italic: "/fonts/Lora-Italic.ttf",
57
+ boldItalic: "/fonts/Lora-BoldItalic.ttf"
58
+ },
59
+ "Montserrat": {
60
+ regular: "/fonts/Montserrat-Regular.ttf",
61
+ bold: "/fonts/Montserrat-Bold.ttf",
62
+ light: "/fonts/Montserrat-Light.ttf",
63
+ medium: "/fonts/Montserrat-Medium.ttf",
64
+ semibold: "/fonts/Montserrat-SemiBold.ttf",
65
+ italic: "/fonts/Montserrat-Italic.ttf",
66
+ boldItalic: "/fonts/Montserrat-BoldItalic.ttf",
67
+ lightItalic: "/fonts/Montserrat-LightItalic.ttf",
68
+ mediumItalic: "/fonts/Montserrat-MediumItalic.ttf",
69
+ semiboldItalic: "/fonts/Montserrat-SemiBoldItalic.ttf"
70
+ },
71
+ "Open Sans": {
72
+ regular: "/fonts/OpenSans-Regular.ttf",
73
+ bold: "/fonts/OpenSans-Bold.ttf",
74
+ light: "/fonts/OpenSans-Light.ttf",
75
+ semibold: "/fonts/OpenSans-SemiBold.ttf",
76
+ italic: "/fonts/OpenSans-Italic.ttf",
77
+ boldItalic: "/fonts/OpenSans-BoldItalic.ttf",
78
+ lightItalic: "/fonts/OpenSans-LightItalic.ttf",
79
+ semiboldItalic: "/fonts/OpenSans-SemiBoldItalic.ttf"
80
+ },
81
+ "Roboto": {
82
+ regular: "/fonts/Roboto-Regular.ttf",
83
+ bold: "/fonts/Roboto-Bold.ttf",
84
+ light: "/fonts/Roboto-Light.ttf",
85
+ medium: "/fonts/Roboto-Medium.ttf",
86
+ italic: "/fonts/Roboto-Italic.ttf",
87
+ boldItalic: "/fonts/Roboto-BoldItalic.ttf",
88
+ lightItalic: "/fonts/Roboto-LightItalic.ttf",
89
+ mediumItalic: "/fonts/Roboto-MediumItalic.ttf"
90
+ },
91
+ "Lato": {
92
+ regular: "/fonts/Lato-Regular.ttf",
93
+ bold: "/fonts/Lato-Bold.ttf",
94
+ light: "/fonts/Lato-Light.ttf",
95
+ italic: "/fonts/Lato-Italic.ttf",
96
+ boldItalic: "/fonts/Lato-BoldItalic.ttf",
97
+ lightItalic: "/fonts/Lato-LightItalic.ttf"
98
+ },
99
+ "Raleway": {
100
+ regular: "/fonts/Raleway-Regular.ttf",
101
+ bold: "/fonts/Raleway-Bold.ttf",
102
+ light: "/fonts/Raleway-Light.ttf",
103
+ medium: "/fonts/Raleway-Medium.ttf",
104
+ semibold: "/fonts/Raleway-SemiBold.ttf",
105
+ italic: "/fonts/Raleway-Italic.ttf",
106
+ boldItalic: "/fonts/Raleway-BoldItalic.ttf",
107
+ lightItalic: "/fonts/Raleway-LightItalic.ttf"
108
+ },
109
+ "Poppins": {
110
+ regular: "/fonts/Poppins-Regular.ttf",
111
+ bold: "/fonts/Poppins-Bold.ttf",
112
+ light: "/fonts/Poppins-Light.ttf",
113
+ medium: "/fonts/Poppins-Medium.ttf",
114
+ semibold: "/fonts/Poppins-SemiBold.ttf",
115
+ italic: "/fonts/Poppins-Italic.ttf",
116
+ boldItalic: "/fonts/Poppins-BoldItalic.ttf",
117
+ lightItalic: "/fonts/Poppins-LightItalic.ttf",
118
+ mediumItalic: "/fonts/Poppins-MediumItalic.ttf",
119
+ semiboldItalic: "/fonts/Poppins-SemiBoldItalic.ttf"
120
+ },
121
+ "Inter": {
122
+ regular: "/fonts/Inter-Regular.ttf",
123
+ bold: "/fonts/Inter-Bold.ttf",
124
+ italic: "/fonts/Inter-Italic.ttf",
125
+ boldItalic: "/fonts/Inter-BoldItalic.ttf"
126
+ },
127
+ "Nunito": {
128
+ regular: "/fonts/Nunito-Regular.ttf",
129
+ bold: "/fonts/Nunito-Bold.ttf",
130
+ light: "/fonts/Nunito-Light.ttf",
131
+ medium: "/fonts/Nunito-Medium.ttf",
132
+ semibold: "/fonts/Nunito-SemiBold.ttf",
133
+ italic: "/fonts/Nunito-Italic.ttf",
134
+ boldItalic: "/fonts/Nunito-BoldItalic.ttf"
135
+ },
136
+ "Source Sans Pro": {
137
+ regular: "/fonts/SourceSansPro-Regular.ttf",
138
+ bold: "/fonts/SourceSansPro-Bold.ttf",
139
+ light: "/fonts/SourceSansPro-Light.ttf",
140
+ italic: "/fonts/SourceSansPro-Italic.ttf",
141
+ boldItalic: "/fonts/SourceSansPro-BoldItalic.ttf"
142
+ },
143
+ "Work Sans": {
144
+ regular: "/fonts/WorkSans-Regular.ttf",
145
+ bold: "/fonts/WorkSans-Bold.ttf",
146
+ italic: "/fonts/WorkSans-Italic.ttf",
147
+ boldItalic: "/fonts/WorkSans-BoldItalic.ttf"
148
+ },
149
+ "Oswald": { regular: "/fonts/Oswald-Regular.ttf", bold: "/fonts/Oswald-Bold.ttf" },
150
+ "Bebas Neue": { regular: "/fonts/BebasNeue-Regular.ttf" },
151
+ "Abril Fatface": { regular: "/fonts/AbrilFatface-Regular.ttf" },
152
+ "Dancing Script": { regular: "/fonts/DancingScript-Regular.ttf", bold: "/fonts/DancingScript-Bold.ttf" },
153
+ "Pacifico": { regular: "/fonts/Pacifico-Regular.ttf" },
154
+ "Great Vibes": { regular: "/fonts/GreatVibes-Regular.ttf" },
155
+ // ── Previously variable-only fonts — now with static per-weight TTFs for PDF export ──
156
+ "DM Sans": {
157
+ regular: "/fonts/DMSans-Regular.ttf",
158
+ bold: "/fonts/DMSans-Bold.ttf",
159
+ light: "/fonts/DMSans-Light.ttf",
160
+ medium: "/fonts/DMSans-Medium.ttf",
161
+ semibold: "/fonts/DMSans-SemiBold.ttf",
162
+ italic: "/fonts/DMSans-RegularItalic.ttf",
163
+ boldItalic: "/fonts/DMSans-BoldItalic.ttf",
164
+ lightItalic: "/fonts/DMSans-LightItalic.ttf",
165
+ mediumItalic: "/fonts/DMSans-MediumItalic.ttf",
166
+ semiboldItalic: "/fonts/DMSans-SemiBoldItalic.ttf"
167
+ },
168
+ "Outfit": {
169
+ regular: "/fonts/Outfit-Regular.ttf",
170
+ bold: "/fonts/Outfit-Bold.ttf",
171
+ light: "/fonts/Outfit-Light.ttf",
172
+ medium: "/fonts/Outfit-Medium.ttf",
173
+ semibold: "/fonts/Outfit-SemiBold.ttf"
174
+ },
175
+ "Figtree": {
176
+ regular: "/fonts/Figtree-Regular.ttf",
177
+ bold: "/fonts/Figtree-Bold.ttf",
178
+ light: "/fonts/Figtree-Light.ttf",
179
+ medium: "/fonts/Figtree-Medium.ttf",
180
+ semibold: "/fonts/Figtree-SemiBold.ttf",
181
+ italic: "/fonts/Figtree-RegularItalic.ttf",
182
+ boldItalic: "/fonts/Figtree-BoldItalic.ttf",
183
+ lightItalic: "/fonts/Figtree-LightItalic.ttf",
184
+ mediumItalic: "/fonts/Figtree-MediumItalic.ttf",
185
+ semiboldItalic: "/fonts/Figtree-SemiBoldItalic.ttf"
186
+ },
187
+ "Manrope": {
188
+ regular: "/fonts/Manrope-Regular.ttf",
189
+ bold: "/fonts/Manrope-Bold.ttf",
190
+ light: "/fonts/Manrope-Light.ttf",
191
+ medium: "/fonts/Manrope-Medium.ttf",
192
+ semibold: "/fonts/Manrope-SemiBold.ttf"
193
+ },
194
+ "Space Grotesk": {
195
+ regular: "/fonts/SpaceGrotesk-Regular.ttf",
196
+ bold: "/fonts/SpaceGrotesk-Bold.ttf",
197
+ light: "/fonts/SpaceGrotesk-Light.ttf",
198
+ medium: "/fonts/SpaceGrotesk-Medium.ttf",
199
+ semibold: "/fonts/SpaceGrotesk-SemiBold.ttf"
200
+ },
201
+ "League Spartan": {
202
+ regular: "/fonts/LeagueSpartan-Regular.ttf",
203
+ bold: "/fonts/LeagueSpartan-Bold.ttf",
204
+ light: "/fonts/LeagueSpartan-Light.ttf",
205
+ medium: "/fonts/LeagueSpartan-Medium.ttf",
206
+ semibold: "/fonts/LeagueSpartan-SemiBold.ttf"
207
+ },
208
+ "EB Garamond": {
209
+ regular: "/fonts/EBGaramond-Regular.ttf",
210
+ bold: "/fonts/EBGaramond-Bold.ttf",
211
+ medium: "/fonts/EBGaramond-Medium.ttf",
212
+ semibold: "/fonts/EBGaramond-SemiBold.ttf",
213
+ italic: "/fonts/EBGaramond-RegularItalic.ttf",
214
+ boldItalic: "/fonts/EBGaramond-BoldItalic.ttf",
215
+ mediumItalic: "/fonts/EBGaramond-MediumItalic.ttf",
216
+ semiboldItalic: "/fonts/EBGaramond-SemiBoldItalic.ttf"
217
+ },
218
+ "Libre Baskerville": {
219
+ regular: "/fonts/LibreBaskerville-Regular.ttf",
220
+ bold: "/fonts/LibreBaskerville-Bold.ttf",
221
+ italic: "/fonts/LibreBaskerville-RegularItalic.ttf",
222
+ boldItalic: "/fonts/LibreBaskerville-BoldItalic.ttf"
223
+ },
224
+ "Crimson Text": {
225
+ regular: "/fonts/CrimsonText-Regular.ttf",
226
+ bold: "/fonts/CrimsonText-Bold.ttf",
227
+ italic: "/fonts/CrimsonText-Italic.ttf",
228
+ boldItalic: "/fonts/CrimsonText-BoldItalic.ttf"
229
+ },
230
+ "DM Serif Display": {
231
+ regular: "/fonts/DMSerifDisplay-Regular.ttf",
232
+ italic: "/fonts/DMSerifDisplay-Italic.ttf"
233
+ },
234
+ "Libre Franklin": {
235
+ regular: "/fonts/LibreFranklin-Regular.ttf",
236
+ bold: "/fonts/LibreFranklin-Bold.ttf",
237
+ light: "/fonts/LibreFranklin-Light.ttf",
238
+ medium: "/fonts/LibreFranklin-Medium.ttf",
239
+ semibold: "/fonts/LibreFranklin-SemiBold.ttf",
240
+ italic: "/fonts/LibreFranklin-RegularItalic.ttf",
241
+ boldItalic: "/fonts/LibreFranklin-BoldItalic.ttf",
242
+ lightItalic: "/fonts/LibreFranklin-LightItalic.ttf",
243
+ mediumItalic: "/fonts/LibreFranklin-MediumItalic.ttf",
244
+ semiboldItalic: "/fonts/LibreFranklin-SemiBoldItalic.ttf"
245
+ },
246
+ "Mulish": {
247
+ regular: "/fonts/Mulish-Regular.ttf",
248
+ bold: "/fonts/Mulish-Bold.ttf",
249
+ light: "/fonts/Mulish-Light.ttf",
250
+ medium: "/fonts/Mulish-Medium.ttf",
251
+ semibold: "/fonts/Mulish-SemiBold.ttf",
252
+ italic: "/fonts/Mulish-RegularItalic.ttf",
253
+ boldItalic: "/fonts/Mulish-BoldItalic.ttf",
254
+ lightItalic: "/fonts/Mulish-LightItalic.ttf",
255
+ mediumItalic: "/fonts/Mulish-MediumItalic.ttf",
256
+ semiboldItalic: "/fonts/Mulish-SemiBoldItalic.ttf"
257
+ },
258
+ "Quicksand": {
259
+ regular: "/fonts/Quicksand-Regular.ttf",
260
+ bold: "/fonts/Quicksand-Bold.ttf",
261
+ light: "/fonts/Quicksand-Light.ttf",
262
+ medium: "/fonts/Quicksand-Medium.ttf",
263
+ semibold: "/fonts/Quicksand-SemiBold.ttf"
264
+ },
265
+ "Rubik": {
266
+ regular: "/fonts/Rubik-Regular.ttf",
267
+ bold: "/fonts/Rubik-Bold.ttf",
268
+ light: "/fonts/Rubik-Light.ttf",
269
+ medium: "/fonts/Rubik-Medium.ttf",
270
+ semibold: "/fonts/Rubik-SemiBold.ttf",
271
+ italic: "/fonts/Rubik-RegularItalic.ttf",
272
+ boldItalic: "/fonts/Rubik-BoldItalic.ttf",
273
+ lightItalic: "/fonts/Rubik-LightItalic.ttf",
274
+ mediumItalic: "/fonts/Rubik-MediumItalic.ttf",
275
+ semiboldItalic: "/fonts/Rubik-SemiBoldItalic.ttf"
276
+ },
277
+ "Karla": {
278
+ regular: "/fonts/Karla-Regular.ttf",
279
+ bold: "/fonts/Karla-Bold.ttf",
280
+ light: "/fonts/Karla-Light.ttf",
281
+ medium: "/fonts/Karla-Medium.ttf",
282
+ semibold: "/fonts/Karla-SemiBold.ttf",
283
+ italic: "/fonts/Karla-RegularItalic.ttf",
284
+ boldItalic: "/fonts/Karla-BoldItalic.ttf",
285
+ lightItalic: "/fonts/Karla-LightItalic.ttf",
286
+ mediumItalic: "/fonts/Karla-MediumItalic.ttf",
287
+ semiboldItalic: "/fonts/Karla-SemiBoldItalic.ttf"
288
+ },
289
+ "Plus Jakarta Sans": {
290
+ regular: "/fonts/PlusJakartaSans-Regular.ttf",
291
+ bold: "/fonts/PlusJakartaSans-Bold.ttf",
292
+ light: "/fonts/PlusJakartaSans-Light.ttf",
293
+ medium: "/fonts/PlusJakartaSans-Medium.ttf",
294
+ semibold: "/fonts/PlusJakartaSans-SemiBold.ttf",
295
+ italic: "/fonts/PlusJakartaSans-RegularItalic.ttf",
296
+ boldItalic: "/fonts/PlusJakartaSans-BoldItalic.ttf",
297
+ lightItalic: "/fonts/PlusJakartaSans-LightItalic.ttf",
298
+ mediumItalic: "/fonts/PlusJakartaSans-MediumItalic.ttf",
299
+ semiboldItalic: "/fonts/PlusJakartaSans-SemiBoldItalic.ttf"
300
+ },
301
+ "Sora": {
302
+ regular: "/fonts/Sora-Regular.ttf",
303
+ bold: "/fonts/Sora-Bold.ttf",
304
+ light: "/fonts/Sora-Light.ttf",
305
+ medium: "/fonts/Sora-Medium.ttf",
306
+ semibold: "/fonts/Sora-SemiBold.ttf"
307
+ },
308
+ "Urbanist": {
309
+ regular: "/fonts/Urbanist-Regular.ttf",
310
+ bold: "/fonts/Urbanist-Bold.ttf",
311
+ light: "/fonts/Urbanist-Light.ttf",
312
+ medium: "/fonts/Urbanist-Medium.ttf",
313
+ semibold: "/fonts/Urbanist-SemiBold.ttf",
314
+ italic: "/fonts/Urbanist-RegularItalic.ttf",
315
+ boldItalic: "/fonts/Urbanist-BoldItalic.ttf",
316
+ lightItalic: "/fonts/Urbanist-LightItalic.ttf",
317
+ mediumItalic: "/fonts/Urbanist-MediumItalic.ttf",
318
+ semiboldItalic: "/fonts/Urbanist-SemiBoldItalic.ttf"
319
+ },
320
+ "Lexend": {
321
+ regular: "/fonts/Lexend-Regular.ttf",
322
+ bold: "/fonts/Lexend-Bold.ttf",
323
+ light: "/fonts/Lexend-Light.ttf",
324
+ medium: "/fonts/Lexend-Medium.ttf",
325
+ semibold: "/fonts/Lexend-SemiBold.ttf"
326
+ },
327
+ "Albert Sans": {
328
+ regular: "/fonts/AlbertSans-Regular.ttf",
329
+ bold: "/fonts/AlbertSans-Bold.ttf",
330
+ light: "/fonts/AlbertSans-Light.ttf",
331
+ medium: "/fonts/AlbertSans-Medium.ttf",
332
+ semibold: "/fonts/AlbertSans-SemiBold.ttf",
333
+ italic: "/fonts/AlbertSans-RegularItalic.ttf",
334
+ boldItalic: "/fonts/AlbertSans-BoldItalic.ttf",
335
+ lightItalic: "/fonts/AlbertSans-LightItalic.ttf",
336
+ mediumItalic: "/fonts/AlbertSans-MediumItalic.ttf",
337
+ semiboldItalic: "/fonts/AlbertSans-SemiBoldItalic.ttf"
338
+ },
339
+ "Noto Sans": {
340
+ regular: "/fonts/NotoSans-Regular.ttf",
341
+ bold: "/fonts/NotoSans-Bold.ttf",
342
+ light: "/fonts/NotoSans-Light.ttf",
343
+ medium: "/fonts/NotoSans-Medium.ttf",
344
+ semibold: "/fonts/NotoSans-SemiBold.ttf",
345
+ italic: "/fonts/NotoSans-RegularItalic.ttf",
346
+ boldItalic: "/fonts/NotoSans-BoldItalic.ttf",
347
+ lightItalic: "/fonts/NotoSans-LightItalic.ttf",
348
+ mediumItalic: "/fonts/NotoSans-MediumItalic.ttf",
349
+ semiboldItalic: "/fonts/NotoSans-SemiBoldItalic.ttf"
350
+ },
351
+ "Cabin": {
352
+ regular: "/fonts/Cabin-Regular.ttf",
353
+ bold: "/fonts/Cabin-Bold.ttf",
354
+ medium: "/fonts/Cabin-Medium.ttf",
355
+ semibold: "/fonts/Cabin-SemiBold.ttf",
356
+ italic: "/fonts/Cabin-RegularItalic.ttf",
357
+ boldItalic: "/fonts/Cabin-BoldItalic.ttf",
358
+ mediumItalic: "/fonts/Cabin-MediumItalic.ttf",
359
+ semiboldItalic: "/fonts/Cabin-SemiBoldItalic.ttf"
360
+ },
361
+ "Barlow": {
362
+ regular: "/fonts/Barlow-Regular.ttf",
363
+ bold: "/fonts/Barlow-Bold.ttf",
364
+ light: "/fonts/Barlow-Light.ttf",
365
+ medium: "/fonts/Barlow-Medium.ttf",
366
+ semibold: "/fonts/Barlow-SemiBold.ttf",
367
+ italic: "/fonts/Barlow-Italic.ttf",
368
+ boldItalic: "/fonts/Barlow-BoldItalic.ttf"
369
+ },
370
+ "Josefin Sans": {
371
+ regular: "/fonts/JosefinSans-Regular.ttf",
372
+ bold: "/fonts/JosefinSans-Bold.ttf",
373
+ light: "/fonts/JosefinSans-Light.ttf",
374
+ medium: "/fonts/JosefinSans-Medium.ttf",
375
+ semibold: "/fonts/JosefinSans-SemiBold.ttf",
376
+ italic: "/fonts/JosefinSans-RegularItalic.ttf",
377
+ boldItalic: "/fonts/JosefinSans-BoldItalic.ttf",
378
+ lightItalic: "/fonts/JosefinSans-LightItalic.ttf",
379
+ mediumItalic: "/fonts/JosefinSans-MediumItalic.ttf",
380
+ semiboldItalic: "/fonts/JosefinSans-SemiBoldItalic.ttf"
381
+ },
382
+ "Archivo": {
383
+ regular: "/fonts/Archivo-Regular.ttf",
384
+ bold: "/fonts/Archivo-Bold.ttf",
385
+ light: "/fonts/Archivo-Light.ttf",
386
+ medium: "/fonts/Archivo-Medium.ttf",
387
+ semibold: "/fonts/Archivo-SemiBold.ttf",
388
+ italic: "/fonts/Archivo-RegularItalic.ttf",
389
+ boldItalic: "/fonts/Archivo-BoldItalic.ttf",
390
+ lightItalic: "/fonts/Archivo-LightItalic.ttf",
391
+ mediumItalic: "/fonts/Archivo-MediumItalic.ttf",
392
+ semiboldItalic: "/fonts/Archivo-SemiBoldItalic.ttf"
393
+ },
394
+ "Overpass": {
395
+ regular: "/fonts/Overpass-Regular.ttf",
396
+ bold: "/fonts/Overpass-Bold.ttf",
397
+ light: "/fonts/Overpass-Light.ttf",
398
+ medium: "/fonts/Overpass-Medium.ttf",
399
+ semibold: "/fonts/Overpass-SemiBold.ttf",
400
+ italic: "/fonts/Overpass-RegularItalic.ttf",
401
+ boldItalic: "/fonts/Overpass-BoldItalic.ttf",
402
+ lightItalic: "/fonts/Overpass-LightItalic.ttf",
403
+ mediumItalic: "/fonts/Overpass-MediumItalic.ttf",
404
+ semiboldItalic: "/fonts/Overpass-SemiBoldItalic.ttf"
405
+ },
406
+ "Exo 2": {
407
+ regular: "/fonts/Exo2-Regular.ttf",
408
+ bold: "/fonts/Exo2-Bold.ttf",
409
+ light: "/fonts/Exo2-Light.ttf",
410
+ medium: "/fonts/Exo2-Medium.ttf",
411
+ semibold: "/fonts/Exo2-SemiBold.ttf",
412
+ italic: "/fonts/Exo2-RegularItalic.ttf",
413
+ boldItalic: "/fonts/Exo2-BoldItalic.ttf",
414
+ lightItalic: "/fonts/Exo2-LightItalic.ttf",
415
+ mediumItalic: "/fonts/Exo2-MediumItalic.ttf",
416
+ semiboldItalic: "/fonts/Exo2-SemiBoldItalic.ttf"
417
+ },
418
+ "Roboto Mono": {
419
+ regular: "/fonts/RobotoMono-Regular.ttf",
420
+ bold: "/fonts/RobotoMono-Bold.ttf",
421
+ light: "/fonts/RobotoMono-Light.ttf",
422
+ medium: "/fonts/RobotoMono-Medium.ttf",
423
+ semibold: "/fonts/RobotoMono-SemiBold.ttf",
424
+ italic: "/fonts/RobotoMono-RegularItalic.ttf",
425
+ boldItalic: "/fonts/RobotoMono-BoldItalic.ttf",
426
+ lightItalic: "/fonts/RobotoMono-LightItalic.ttf",
427
+ mediumItalic: "/fonts/RobotoMono-MediumItalic.ttf",
428
+ semiboldItalic: "/fonts/RobotoMono-SemiBoldItalic.ttf"
429
+ },
430
+ "Fira Code": {
431
+ regular: "/fonts/FiraCode-Regular.ttf",
432
+ bold: "/fonts/FiraCode-Bold.ttf",
433
+ light: "/fonts/FiraCode-Light.ttf",
434
+ medium: "/fonts/FiraCode-Medium.ttf",
435
+ semibold: "/fonts/FiraCode-SemiBold.ttf"
436
+ },
437
+ "JetBrains Mono": {
438
+ regular: "/fonts/JetBrainsMono-Regular.ttf",
439
+ bold: "/fonts/JetBrainsMono-Bold.ttf",
440
+ light: "/fonts/JetBrainsMono-Light.ttf",
441
+ medium: "/fonts/JetBrainsMono-Medium.ttf",
442
+ semibold: "/fonts/JetBrainsMono-SemiBold.ttf",
443
+ italic: "/fonts/JetBrainsMono-RegularItalic.ttf",
444
+ boldItalic: "/fonts/JetBrainsMono-BoldItalic.ttf",
445
+ lightItalic: "/fonts/JetBrainsMono-LightItalic.ttf",
446
+ mediumItalic: "/fonts/JetBrainsMono-MediumItalic.ttf",
447
+ semiboldItalic: "/fonts/JetBrainsMono-SemiBoldItalic.ttf"
448
+ },
449
+ "Source Code Pro": {
450
+ regular: "/fonts/SourceCodePro-Regular.ttf",
451
+ bold: "/fonts/SourceCodePro-Bold.ttf",
452
+ light: "/fonts/SourceCodePro-Light.ttf",
453
+ medium: "/fonts/SourceCodePro-Medium.ttf",
454
+ semibold: "/fonts/SourceCodePro-SemiBold.ttf",
455
+ italic: "/fonts/SourceCodePro-RegularItalic.ttf",
456
+ boldItalic: "/fonts/SourceCodePro-BoldItalic.ttf",
457
+ lightItalic: "/fonts/SourceCodePro-LightItalic.ttf",
458
+ mediumItalic: "/fonts/SourceCodePro-MediumItalic.ttf",
459
+ semiboldItalic: "/fonts/SourceCodePro-SemiBoldItalic.ttf"
460
+ },
461
+ "IBM Plex Mono": {
462
+ regular: "/fonts/IBMPlexMono-Regular.ttf",
463
+ bold: "/fonts/IBMPlexMono-Bold.ttf"
464
+ },
465
+ "Space Mono": {
466
+ regular: "/fonts/SpaceMono-Regular.ttf",
467
+ bold: "/fonts/SpaceMono-Bold.ttf",
468
+ italic: "/fonts/SpaceMono-Italic.ttf",
469
+ boldItalic: "/fonts/SpaceMono-BoldItalic.ttf"
470
+ },
471
+ "Sacramento": { regular: "/fonts/Sacramento-Regular.ttf" },
472
+ "Alex Brush": { regular: "/fonts/AlexBrush-Regular.ttf" },
473
+ "Allura": { regular: "/fonts/Allura-Regular.ttf" },
474
+ "Caveat": {
475
+ regular: "/fonts/Caveat-Regular.ttf",
476
+ bold: "/fonts/Caveat-Bold.ttf",
477
+ medium: "/fonts/Caveat-Medium.ttf",
478
+ semibold: "/fonts/Caveat-SemiBold.ttf"
479
+ },
480
+ "Lobster": { regular: "/fonts/Lobster-Regular.ttf" },
481
+ "Comfortaa": {
482
+ regular: "/fonts/Comfortaa-Regular.ttf",
483
+ bold: "/fonts/Comfortaa-Bold.ttf",
484
+ light: "/fonts/Comfortaa-Light.ttf",
485
+ medium: "/fonts/Comfortaa-Medium.ttf",
486
+ semibold: "/fonts/Comfortaa-SemiBold.ttf"
487
+ },
488
+ "Anton": { regular: "/fonts/Anton-Regular.ttf" },
489
+ "Teko": {
490
+ regular: "/fonts/Teko-Regular.ttf",
491
+ bold: "/fonts/Teko-Bold.ttf",
492
+ light: "/fonts/Teko-Light.ttf",
493
+ medium: "/fonts/Teko-Medium.ttf",
494
+ semibold: "/fonts/Teko-SemiBold.ttf"
495
+ },
496
+ // ── Indic Script Fallback Fonts ──
497
+ "Noto Sans Devanagari": {
498
+ regular: "/fonts/NotoSansDevanagari-Regular.ttf",
499
+ bold: "/fonts/NotoSansDevanagari-Bold.ttf",
500
+ light: "/fonts/NotoSansDevanagari-Light.ttf",
501
+ medium: "/fonts/NotoSansDevanagari-Medium.ttf",
502
+ semibold: "/fonts/NotoSansDevanagari-SemiBold.ttf"
503
+ },
504
+ "Hind": {
505
+ regular: "/fonts/Hind-Regular.ttf",
506
+ bold: "/fonts/Hind-Bold.ttf",
507
+ light: "/fonts/Hind-Light.ttf",
508
+ medium: "/fonts/Hind-Medium.ttf",
509
+ semibold: "/fonts/Hind-SemiBold.ttf"
510
+ }
511
+ };
512
+ async function fetchGoogleFontTTF(fontFamily, weight = 400, isItalic = false) {
513
+ const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
514
+ if (fontCache$1.has(cacheKey)) return fontCache$1.get(cacheKey);
515
+ if (googleFontNotFound.has(fontFamily)) return null;
516
+ try {
517
+ const ital = isItalic ? "1" : "0";
518
+ const cssUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(
519
+ fontFamily
520
+ )}:ital,wght@${ital},${weight}&display=swap`;
521
+ const cssRes = await fetch(cssUrl, {
522
+ headers: {
523
+ "User-Agent": "Mozilla/5.0 (Linux; U; Android 2.2; en-us) AppleWebKit/533.1 (KHTML, like Gecko)"
524
+ }
525
+ });
526
+ if (!cssRes.ok) {
527
+ if (cssRes.status === 400 || cssRes.status === 404) googleFontNotFound.add(fontFamily);
528
+ return null;
529
+ }
530
+ const css = await cssRes.text();
531
+ const urlMatch = css.match(/url\(([^)]+)\)\s+format\(['"]?truetype['"]?\)/i) || css.match(/url\(([^)]+)\)/);
532
+ if (!urlMatch) return null;
533
+ const ttfUrl = urlMatch[1].replace(/['"]/g, "");
534
+ googleFontUrlCache.set(cacheKey, ttfUrl);
535
+ const ttfRes = await fetch(ttfUrl);
536
+ if (!ttfRes.ok) return null;
537
+ const buf = await ttfRes.arrayBuffer();
538
+ const bytes = new Uint8Array(buf);
539
+ fontBytesCache$1.set(cacheKey, bytes);
540
+ let binary = "";
541
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
542
+ const b64 = btoa(binary);
543
+ fontCache$1.set(cacheKey, b64);
544
+ return b64;
545
+ } catch (err) {
546
+ console.warn(`[pdfFonts] fetchGoogleFontTTF failed for ${fontFamily} (${weight}):`, err);
547
+ return null;
548
+ }
549
+ }
550
+ async function getGoogleFontBytes(fontFamily, weight = 400, isItalic = false) {
551
+ const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
552
+ if (fontBytesCache$1.has(cacheKey)) return fontBytesCache$1.get(cacheKey);
553
+ await fetchGoogleFontTTF(fontFamily, weight, isItalic);
554
+ return fontBytesCache$1.get(cacheKey) || null;
555
+ }
556
+ const LATIN_TO_DEVANAGARI = {
557
+ // Direct Devanagari subsets — same family carries both scripts
558
+ "Poppins": "Poppins",
559
+ "Noto Sans": "Noto Sans Devanagari",
560
+ "Noto Serif": "Noto Serif Devanagari",
561
+ "Hind": "Hind",
562
+ "Mukta": "Mukta",
563
+ // Curated visual matches (Google's own recommendations)
564
+ "Montserrat": "Mukta",
565
+ "Open Sans": "Mukta",
566
+ "Lato": "Mukta",
567
+ "Roboto": "Mukta",
568
+ "Inter": "Mukta",
569
+ "Nunito": "Mukta",
570
+ "Source Sans Pro": "Mukta",
571
+ "Work Sans": "Mukta",
572
+ "DM Sans": "Mukta",
573
+ "Outfit": "Mukta",
574
+ "Figtree": "Mukta",
575
+ "Manrope": "Mukta",
576
+ "Plus Jakarta Sans": "Mukta",
577
+ "Sora": "Mukta",
578
+ "Urbanist": "Mukta",
579
+ "Lexend": "Mukta",
580
+ "Albert Sans": "Mukta",
581
+ "Cabin": "Mukta",
582
+ "Karla": "Mukta",
583
+ "Mulish": "Mukta",
584
+ "Rubik": "Mukta",
585
+ "Quicksand": "Mukta",
586
+ "Libre Franklin": "Mukta",
587
+ "Barlow": "Mukta",
588
+ "Archivo": "Mukta",
589
+ "Overpass": "Mukta",
590
+ "Josefin Sans": "Mukta",
591
+ "Exo 2": "Mukta",
592
+ "Space Grotesk": "Mukta",
593
+ // Display / heavy
594
+ "Oswald": "Teko",
595
+ "Bebas Neue": "Teko",
596
+ "Anton": "Teko",
597
+ "Teko": "Teko",
598
+ "League Spartan": "Teko",
599
+ // Serifs → Tiro Devanagari Hindi (a refined Devanagari serif)
600
+ "Playfair Display": "Tiro Devanagari Hindi",
601
+ "Merriweather": "Tiro Devanagari Hindi",
602
+ "Lora": "Tiro Devanagari Hindi",
603
+ "EB Garamond": "Tiro Devanagari Hindi",
604
+ "Libre Baskerville": "Tiro Devanagari Hindi",
605
+ "Crimson Text": "Tiro Devanagari Hindi",
606
+ "DM Serif Display": "Tiro Devanagari Sanskrit",
607
+ "Abril Fatface": "Tiro Devanagari Sanskrit",
608
+ // Handwriting → Kalam (the only serious Devanagari handwriting family on GF)
609
+ "Dancing Script": "Kalam",
610
+ "Pacifico": "Kalam",
611
+ "Great Vibes": "Kalam",
612
+ "Sacramento": "Kalam",
613
+ "Alex Brush": "Kalam",
614
+ "Allura": "Kalam",
615
+ "Caveat": "Kalam",
616
+ "Lobster": "Kalam",
617
+ "Comfortaa": "Mukta",
618
+ // Monospace → there's no proper Devanagari mono; fall back to Mukta
619
+ "Roboto Mono": "Mukta",
620
+ "Fira Code": "Mukta",
621
+ "JetBrains Mono": "Mukta",
622
+ "Source Code Pro": "Mukta",
623
+ "IBM Plex Mono": "Mukta",
624
+ "Space Mono": "Mukta"
625
+ };
626
+ function resolveDevanagariSibling(latinFont) {
627
+ if (!latinFont) return FONT_FALLBACK_DEVANAGARI;
628
+ return LATIN_TO_DEVANAGARI[latinFont] || FONT_FALLBACK_DEVANAGARI;
629
+ }
630
+ const HB_WASM_URL = "/wasm/hb.wasm";
631
+ let hbInstancePromise = null;
632
+ async function getHB() {
633
+ if (hbInstancePromise) return hbInstancePromise;
634
+ hbInstancePromise = (async () => {
635
+ const [{ default: createHarfBuzz }, { default: hbjs }] = await Promise.all([
636
+ import("harfbuzzjs/hb.js"),
637
+ import("harfbuzzjs/hbjs.js")
638
+ ]);
639
+ const moduleInstance = await createHarfBuzz({
640
+ locateFile: (path) => {
641
+ if (path.endsWith(".wasm")) return HB_WASM_URL;
642
+ return path;
643
+ }
644
+ });
645
+ return hbjs(moduleInstance);
646
+ })();
647
+ return hbInstancePromise;
648
+ }
649
+ const hbFontCache = /* @__PURE__ */ new Map();
650
+ async function getHBFont(cacheKey, fontDataLoader) {
651
+ const cached = hbFontCache.get(cacheKey);
652
+ if (cached) return { font: cached.font, upem: cached.upem };
653
+ const hb = await getHB();
654
+ const fontData = await fontDataLoader();
655
+ const blob = hb.createBlob(fontData);
656
+ const face = hb.createFace(blob, 0);
657
+ const font = hb.createFont(face);
658
+ const upem = (face.getUpem ? face.getUpem() : face.upem ?? 1e3) || 1e3;
659
+ font.setScale(upem, upem);
660
+ blob.destroy();
661
+ hbFontCache.set(cacheKey, { face, font, upem });
662
+ return { font, upem };
663
+ }
664
+ async function shapeRunToSvgPath(fontData, cacheKey, text, x, y, fontSize, opts) {
665
+ const hb = await getHB();
666
+ const loader = typeof fontData === "function" ? fontData : async () => fontData;
667
+ const { font, upem } = await getHBFont(cacheKey, loader);
668
+ const buffer = hb.createBuffer();
669
+ buffer.addText(text);
670
+ if (opts == null ? void 0 : opts.direction) buffer.setDirection(opts.direction);
671
+ if (opts == null ? void 0 : opts.script) buffer.setScript(opts.script);
672
+ if (opts == null ? void 0 : opts.language) buffer.setLanguage(opts.language);
673
+ buffer.guessSegmentProperties();
674
+ hb.shape(font, buffer);
675
+ const glyphs = buffer.json();
676
+ const scale = fontSize / upem;
677
+ let penX = 0;
678
+ let penY = 0;
679
+ const pieces = [];
680
+ for (const g of glyphs) {
681
+ const rawPath = font.glyphToPath(g.g);
682
+ if (rawPath) {
683
+ const ox = (penX + g.dx) * scale + x;
684
+ const oy = (penY + g.dy) * -scale + y;
685
+ pieces.push(transformPathData(rawPath, scale, -scale, ox, oy));
686
+ }
687
+ penX += g.ax;
688
+ penY += g.ay;
689
+ }
690
+ buffer.destroy();
691
+ return {
692
+ pathData: pieces.join(""),
693
+ width: penX * scale
694
+ };
695
+ }
696
+ function transformPathData(d, sx, sy, tx, ty) {
697
+ let out = "";
698
+ let i = 0;
699
+ const n = d.length;
700
+ while (i < n) {
701
+ const c = d[i];
702
+ if (c === "M" || c === "L" || c === "C" || c === "Q") {
703
+ out += c;
704
+ i++;
705
+ let pairsBuf = "";
706
+ while (i < n && d[i] !== "M" && d[i] !== "L" && d[i] !== "C" && d[i] !== "Q" && d[i] !== "Z") {
707
+ pairsBuf += d[i];
708
+ i++;
709
+ }
710
+ const nums = pairsBuf.trim().split(/[ ,]+/).filter(Boolean).map(Number);
711
+ for (let k = 0; k < nums.length; k += 2) {
712
+ const px = nums[k] * sx + tx;
713
+ const py = nums[k + 1] * sy + ty;
714
+ if (k > 0) out += " ";
715
+ out += `${round(px)},${round(py)}`;
716
+ }
717
+ } else if (c === "Z") {
718
+ out += "Z";
719
+ i++;
720
+ } else {
721
+ i++;
722
+ }
723
+ }
724
+ return out;
725
+ }
726
+ function round(n) {
727
+ return (Math.round(n * 100) / 100).toString();
728
+ }
729
+ const fontCache = /* @__PURE__ */ new Map();
730
+ const fontBytesCache = /* @__PURE__ */ new Map();
731
+ function isDevanagari(char) {
732
+ const c = char.codePointAt(0) ?? 0;
733
+ return c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423;
734
+ }
735
+ function containsDevanagari(text) {
736
+ if (!text) return false;
737
+ for (const char of text) {
738
+ if (isDevanagari(char)) return true;
739
+ }
740
+ return false;
741
+ }
742
+ function isIgnorableForCoverage(char) {
743
+ return /\s/.test(char) || /[\u0964\u0965\u200c\u200d]/u.test(char);
744
+ }
745
+ function fontSupportsText(font, text) {
746
+ if (!font) return false;
747
+ for (const char of text) {
748
+ if (!isDevanagari(char) || isIgnorableForCoverage(char)) continue;
749
+ const glyph = font.charToGlyph(char);
750
+ if (!glyph || glyph.index === 0) return false;
751
+ }
752
+ return true;
753
+ }
754
+ const browserMeasureCanvas = typeof document !== "undefined" ? document.createElement("canvas") : null;
755
+ function measureBrowserWidth(fontFamily, weight, fontSize, text) {
756
+ const ctx = browserMeasureCanvas == null ? void 0 : browserMeasureCanvas.getContext("2d");
757
+ if (!ctx) return null;
758
+ ctx.font = `normal normal ${weight} ${fontSize}px "${fontFamily}"`;
759
+ return ctx.measureText(text).width;
760
+ }
761
+ function uniqueFamilies(families) {
762
+ const seen = /* @__PURE__ */ new Set();
763
+ const out = [];
764
+ for (const family of families) {
765
+ const clean = family == null ? void 0 : family.trim();
766
+ if (!clean || seen.has(clean)) continue;
767
+ seen.add(clean);
768
+ out.push(clean);
769
+ }
770
+ return out;
771
+ }
772
+ function getFontPath(fontFiles, weight) {
773
+ const resolved = resolveFontWeight(weight);
774
+ const keyMap = {
775
+ 300: ["light", "regular"],
776
+ 400: ["regular"],
777
+ 500: ["medium", "regular"],
778
+ 600: ["semibold", "bold"],
779
+ 700: ["bold", "semibold", "regular"]
780
+ };
781
+ for (const key of keyMap[resolved] || ["regular"]) {
782
+ if (fontFiles[key]) return fontFiles[key];
783
+ }
784
+ return fontFiles.regular || null;
785
+ }
786
+ function resolveFontUrl(fileName, fontBaseUrl) {
787
+ if (/^https?:\/\//i.test(fileName) || fileName.startsWith("data:")) return fileName;
788
+ if (fileName.startsWith("/")) {
789
+ return typeof window !== "undefined" ? new URL(fileName, window.location.origin).toString() : fileName;
790
+ }
791
+ const baseUrl = fontBaseUrl.endsWith("/") ? fontBaseUrl : fontBaseUrl + "/";
792
+ return new URL(fileName, baseUrl).toString();
793
+ }
794
+ async function loadFont(fontFamily, weight, fontBaseUrl) {
795
+ const cacheKey = `${fontFamily}__${weight}`;
796
+ if (fontCache.has(cacheKey)) return fontCache.get(cacheKey);
797
+ const fontFiles = FONT_FILES[fontFamily];
798
+ if (fontFiles) {
799
+ const fileName = getFontPath(fontFiles, weight);
800
+ if (!fileName) return null;
801
+ const url = resolveFontUrl(fileName, fontBaseUrl);
802
+ try {
803
+ const response = await fetch(url);
804
+ if (!response.ok) {
805
+ console.warn(`[text-to-path] Failed to fetch font ${url}: ${response.status}`);
806
+ return null;
807
+ }
808
+ const buffer = await response.arrayBuffer();
809
+ const bytes = new Uint8Array(buffer);
810
+ fontBytesCache.set(cacheKey, bytes);
811
+ const font = opentype.parse(buffer);
812
+ fontCache.set(cacheKey, font);
813
+ return font;
814
+ } catch (err) {
815
+ console.warn(`[text-to-path] Failed to load local font ${fontFamily}:`, err);
816
+ return null;
817
+ }
818
+ }
819
+ try {
820
+ const resolvedWeight = resolveFontWeight(weight);
821
+ const bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, false);
822
+ if (!bytes) {
823
+ console.warn(`[text-to-path] Google Fonts has no TTF for ${fontFamily} (${weight})`);
824
+ return null;
825
+ }
826
+ fontBytesCache.set(cacheKey, bytes);
827
+ const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
828
+ const font = opentype.parse(ab);
829
+ fontCache.set(cacheKey, font);
830
+ return font;
831
+ } catch (err) {
832
+ console.warn(`[text-to-path] Failed to load Google font ${fontFamily}:`, err);
833
+ return null;
834
+ }
835
+ }
836
+ async function getFontBytes(fontFamily, weight, fontBaseUrl) {
837
+ const cacheKey = `${fontFamily}__${weight}`;
838
+ if (!fontBytesCache.has(cacheKey)) {
839
+ await loadFont(fontFamily, weight, fontBaseUrl);
840
+ }
841
+ const bytes = fontBytesCache.get(cacheKey);
842
+ return bytes ? { bytes, cacheKey } : null;
843
+ }
844
+ function measureRunWidth(font, text, fontSize) {
845
+ try {
846
+ return font.getAdvanceWidth(text, fontSize, { kerning: true });
847
+ } catch {
848
+ try {
849
+ return font.getAdvanceWidth(text, fontSize);
850
+ } catch {
851
+ return text.length * fontSize * 0.5;
852
+ }
853
+ }
854
+ }
855
+ function splitByScript(text) {
856
+ if (!text) return [];
857
+ const runs = [];
858
+ let buf = "";
859
+ let bufIsDeva = isDevanagari(text[0]);
860
+ for (const ch of text) {
861
+ const chDeva = isDevanagari(ch);
862
+ const isNeutral = /\s/.test(ch);
863
+ if (chDeva === bufIsDeva || isNeutral) {
864
+ buf += ch;
865
+ } else {
866
+ if (buf) runs.push({ text: buf, isDevanagari: bufIsDeva });
867
+ buf = ch;
868
+ bufIsDeva = chDeva;
869
+ }
870
+ }
871
+ if (buf) runs.push({ text: buf, isDevanagari: bufIsDeva });
872
+ return runs;
873
+ }
874
+ async function shapeRunToPath(run, isDeva, primaryFont, primaryBytes, devaFont, devaBytes, x, y, fontSize) {
875
+ if (isDeva && devaBytes && devaFont) {
876
+ try {
877
+ const shaped = await shapeRunToSvgPath(
878
+ devaBytes.bytes,
879
+ devaBytes.cacheKey,
880
+ run,
881
+ x,
882
+ y,
883
+ fontSize,
884
+ { script: "deva", language: "hin", direction: "ltr" }
885
+ );
886
+ if (shaped.pathData) {
887
+ const advance = measureRunWidth(devaFont, run, fontSize);
888
+ return { pathData: shaped.pathData, advance };
889
+ }
890
+ } catch (err) {
891
+ console.warn(`[text-to-path] HarfBuzz failed for "${run}":`, err);
892
+ }
893
+ try {
894
+ const path = devaFont.getPath(run, x, y, fontSize);
895
+ const d = path.toPathData(2);
896
+ if (d && d !== "M0 0") return { pathData: d, advance: measureRunWidth(devaFont, run, fontSize) };
897
+ } catch {
898
+ }
899
+ return null;
900
+ }
901
+ try {
902
+ const path = primaryFont.getPath(run, x, y, fontSize, { kerning: true });
903
+ const d = path.toPathData(2);
904
+ if (d && d !== "M0 0") return { pathData: d, advance: measureRunWidth(primaryFont, run, fontSize) };
905
+ return { pathData: "", advance: measureRunWidth(primaryFont, run, fontSize) };
906
+ } catch (err) {
907
+ console.warn(`[text-to-path] opentype getPath failed for "${run}":`, err);
908
+ return null;
909
+ }
910
+ }
911
+ function parseTranslate(transform) {
912
+ if (!transform) return { tx: 0, ty: 0 };
913
+ const m = transform.match(/translate\(\s*([-\d.]+)[ \s,]+([-\d.]+)\s*\)/);
914
+ if (m) return { tx: parseFloat(m[1]), ty: parseFloat(m[2]) };
915
+ const mm = transform.match(/matrix\(\s*([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)\s*\)/);
916
+ if (mm) return { tx: parseFloat(mm[5]), ty: parseFloat(mm[6]) };
917
+ return { tx: 0, ty: 0 };
918
+ }
919
+ async function convertDevanagariTextToPath(svgStr, fontBaseUrl) {
920
+ var _a, _b, _c;
921
+ const baseUrl = fontBaseUrl ?? (typeof window !== "undefined" ? window.location.origin + "/fonts/" : "/fonts/");
922
+ const parser = new DOMParser();
923
+ const doc = parser.parseFromString(svgStr, "image/svg+xml");
924
+ const textEls = doc.querySelectorAll("text");
925
+ let convertedCount = 0;
926
+ let skippedCount = 0;
927
+ for (const textEl of textEls) {
928
+ const fullText = textEl.textContent || "";
929
+ if (!fullText.trim()) continue;
930
+ const readStyleToken = (style, prop) => {
931
+ var _a2;
932
+ const m = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
933
+ return ((_a2 = m == null ? void 0 : m[1]) == null ? void 0 : _a2.trim()) || null;
934
+ };
935
+ const getAttrOrStyle = (el, attr) => {
936
+ var _a2;
937
+ let current = el;
938
+ while (current) {
939
+ const attrVal = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
940
+ if (attrVal) return attrVal;
941
+ const styleVal = readStyleToken(current.getAttribute("style") || "", attr);
942
+ if (styleVal) return styleVal;
943
+ current = current.parentElement;
944
+ }
945
+ return null;
946
+ };
947
+ const fontFamily = (_b = (_a = getAttrOrStyle(textEl, "font-family")) == null ? void 0 : _a.split(",")[0]) == null ? void 0 : _b.replace(/['"]/g, "").trim();
948
+ const fontSizeStr = getAttrOrStyle(textEl, "font-size") || "16";
949
+ const fontSize = parseFloat(fontSizeStr);
950
+ const fontWeightStr = getAttrOrStyle(textEl, "font-weight") || "400";
951
+ const fontWeight = Number.parseInt(fontWeightStr, 10) || 400;
952
+ const fillColor = getAttrOrStyle(textEl, "fill") || "#000000";
953
+ const fillOpacity = getAttrOrStyle(textEl, "fill-opacity") || "1";
954
+ if (!fontFamily) {
955
+ skippedCount++;
956
+ continue;
957
+ }
958
+ const primaryFont = await loadFont(fontFamily, fontWeight, baseUrl);
959
+ const primaryBytes = await getFontBytes(fontFamily, fontWeight, baseUrl);
960
+ const hasDeva = containsDevanagari(fullText);
961
+ const devaCandidateFamilies = hasDeva ? uniqueFamilies([fontFamily, FONT_FALLBACK_DEVANAGARI, resolveDevanagariSibling(fontFamily)]) : [];
962
+ const devaRunFontCache = /* @__PURE__ */ new Map();
963
+ const resolveDevaFontForRun = (runText, runFontSize) => {
964
+ const cacheKey = `${runText}__${runFontSize}`;
965
+ const cached = devaRunFontCache.get(cacheKey);
966
+ if (cached) return cached;
967
+ const promise = (async () => {
968
+ const browserWidth = measureBrowserWidth(fontFamily, fontWeight, runFontSize, runText);
969
+ let best = null;
970
+ for (const family of devaCandidateFamilies) {
971
+ const font = family === fontFamily ? primaryFont : await loadFont(family, fontWeight, baseUrl);
972
+ const bytes = family === fontFamily ? primaryBytes : await getFontBytes(family, fontWeight, baseUrl);
973
+ if (!font || !bytes || !fontSupportsText(font, runText)) continue;
974
+ const width = measureRunWidth(font, runText, runFontSize);
975
+ const diff = browserWidth == null ? 0 : Math.abs(width - browserWidth);
976
+ if (!best || diff < best.diff) best = { family, font, bytes, diff };
977
+ if (browserWidth != null && diff <= 0.25) break;
978
+ }
979
+ if (best) {
980
+ console.log("[text-to-path] Devanagari font resolved", {
981
+ requestedFamily: fontFamily,
982
+ resolvedFamily: best.family,
983
+ browserWidth,
984
+ diff: Math.round(best.diff * 100) / 100
985
+ });
986
+ }
987
+ return best;
988
+ })();
989
+ devaRunFontCache.set(cacheKey, promise);
990
+ return promise;
991
+ };
992
+ if (!primaryFont && !hasDeva) {
993
+ console.warn(`[text-to-path] No font available for "${fontFamily}", leaving as <text>`);
994
+ skippedCount++;
995
+ continue;
996
+ }
997
+ const textAnchorRaw = (getAttrOrStyle(textEl, "text-anchor") || "start").toLowerCase();
998
+ const textAnchor = textAnchorRaw === "middle" ? "middle" : textAnchorRaw === "end" ? "end" : "start";
999
+ const tspans = textEl.querySelectorAll("tspan");
1000
+ const elementsToProcess = tspans.length > 0 ? Array.from(tspans) : [textEl];
1001
+ const group = doc.createElementNS("http://www.w3.org/2000/svg", "g");
1002
+ const textTransform = textEl.getAttribute("transform");
1003
+ if (textTransform) group.setAttribute("transform", textTransform);
1004
+ const baseX = parseFloat(textEl.getAttribute("x") || "0");
1005
+ const baseY = parseFloat(textEl.getAttribute("y") || "0");
1006
+ for (const elem of elementsToProcess) {
1007
+ const text = elem.textContent || "";
1008
+ if (!text.trim()) continue;
1009
+ const elemX = parseFloat(elem.getAttribute("x") || String(baseX));
1010
+ const elemY = parseFloat(elem.getAttribute("y") || String(baseY));
1011
+ const elemTransform = elem !== textEl ? parseTranslate(elem.getAttribute("transform")) : { tx: 0, ty: 0 };
1012
+ let x = elemX + elemTransform.tx;
1013
+ const y = elemY + elemTransform.ty;
1014
+ const elemFill = getAttrOrStyle(elem, "fill") || fillColor;
1015
+ const elemFillOpacity = getAttrOrStyle(elem, "fill-opacity") || fillOpacity;
1016
+ const elemFontSizeStr = getAttrOrStyle(elem, "font-size") || String(fontSize);
1017
+ const elemFontSize = parseFloat(elemFontSizeStr);
1018
+ const runs = splitByScript(text);
1019
+ let totalAdvance = 0;
1020
+ let canMeasureAll = true;
1021
+ for (const r of runs) {
1022
+ if (r.isDevanagari) {
1023
+ const resolved = await resolveDevaFontForRun(r.text, elemFontSize);
1024
+ if (resolved) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);
1025
+ else canMeasureAll = false;
1026
+ } else {
1027
+ if (primaryFont) totalAdvance += measureRunWidth(primaryFont, r.text, elemFontSize);
1028
+ else canMeasureAll = false;
1029
+ }
1030
+ }
1031
+ if (canMeasureAll) {
1032
+ if (textAnchor === "middle") x -= totalAdvance / 2;
1033
+ else if (textAnchor === "end") x -= totalAdvance;
1034
+ }
1035
+ let cursor = x;
1036
+ let elemConverted = false;
1037
+ for (const r of runs) {
1038
+ const resolvedDeva = r.isDevanagari ? await resolveDevaFontForRun(r.text, elemFontSize) : null;
1039
+ const useDeva = !!resolvedDeva;
1040
+ const fontForRun = (resolvedDeva == null ? void 0 : resolvedDeva.font) ?? primaryFont;
1041
+ const bytesForRun = (resolvedDeva == null ? void 0 : resolvedDeva.bytes) ?? primaryBytes;
1042
+ if (!fontForRun) continue;
1043
+ const result = await shapeRunToPath(
1044
+ r.text,
1045
+ !!useDeva,
1046
+ fontForRun,
1047
+ bytesForRun,
1048
+ (resolvedDeva == null ? void 0 : resolvedDeva.font) ?? null,
1049
+ (resolvedDeva == null ? void 0 : resolvedDeva.bytes) ?? null,
1050
+ cursor,
1051
+ y,
1052
+ elemFontSize
1053
+ );
1054
+ if (result && result.pathData) {
1055
+ const pathEl = doc.createElementNS("http://www.w3.org/2000/svg", "path");
1056
+ pathEl.setAttribute("d", result.pathData);
1057
+ pathEl.setAttribute("fill", elemFill);
1058
+ if (elemFillOpacity !== "1") {
1059
+ pathEl.setAttribute("fill-opacity", elemFillOpacity);
1060
+ }
1061
+ group.appendChild(pathEl);
1062
+ elemConverted = true;
1063
+ }
1064
+ if (result) cursor += result.advance;
1065
+ }
1066
+ if (elemConverted) {
1067
+ convertedCount++;
1068
+ } else {
1069
+ if (elem === textEl) {
1070
+ const clone = elem.cloneNode(true);
1071
+ if (textTransform) clone.removeAttribute("transform");
1072
+ group.appendChild(clone);
1073
+ } else {
1074
+ const newText = doc.createElementNS("http://www.w3.org/2000/svg", "text");
1075
+ newText.setAttribute("x", String(elemX));
1076
+ newText.setAttribute("y", String(elemY));
1077
+ if (elem.getAttribute("style")) newText.setAttribute("style", elem.getAttribute("style"));
1078
+ if (elem.getAttribute("font-family")) newText.setAttribute("font-family", elem.getAttribute("font-family"));
1079
+ if (elem.getAttribute("font-size")) newText.setAttribute("font-size", elem.getAttribute("font-size"));
1080
+ if (elem.getAttribute("font-weight")) newText.setAttribute("font-weight", elem.getAttribute("font-weight"));
1081
+ newText.textContent = text;
1082
+ group.appendChild(newText);
1083
+ }
1084
+ }
1085
+ }
1086
+ if (group.childNodes.length > 0) {
1087
+ (_c = textEl.parentNode) == null ? void 0 : _c.replaceChild(group, textEl);
1088
+ }
1089
+ }
1090
+ console.log(
1091
+ `[text-to-path] Universal outline complete: converted=${convertedCount} skipped=${skippedCount}`
1092
+ );
1093
+ return new XMLSerializer().serializeToString(doc.documentElement);
1094
+ }
1095
+ const convertAllTextToPath = convertDevanagariTextToPath;
1096
+ async function preloadDevanagariFont(fontBaseUrl) {
1097
+ const baseUrl = fontBaseUrl ?? (typeof window !== "undefined" ? window.location.origin + "/fonts/" : "/fonts/");
1098
+ for (const weight of [300, 400, 500, 600, 700]) {
1099
+ await loadFont(FONT_FALLBACK_DEVANAGARI, weight, baseUrl);
1100
+ }
1101
+ }
1102
+ exports.convertAllTextToPath = convertAllTextToPath;
1103
+ exports.convertDevanagariTextToPath = convertDevanagariTextToPath;
1104
+ exports.preloadDevanagariFont = preloadDevanagariFont;
1105
+ //# sourceMappingURL=svgTextToPath-BTHnqJpM.cjs.map