@pixldocs/canvas-renderer 0.5.68 → 0.5.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -12142,865 +12142,12 @@ function paintRepeatableSections(config, repeatableSections) {
12142
12142
  }
12143
12143
  }
12144
12144
  }
12145
- const FONT_WEIGHT_LABELS = {
12146
- 300: "Light",
12147
- 400: "Regular",
12148
- 500: "Medium",
12149
- 600: "SemiBold",
12150
- 700: "Bold"
12151
- };
12152
- function resolveFontWeight(weight) {
12153
- if (weight <= 350) return 300;
12154
- if (weight <= 450) return 400;
12155
- if (weight <= 550) return 500;
12156
- if (weight <= 650) return 600;
12157
- return 700;
12158
- }
12159
- const FONT_FALLBACK_SYMBOLS = "Noto Sans";
12160
- const FONT_FALLBACK_DEVANAGARI = "Hind";
12161
- const FONT_FILES = {
12162
- "Playfair Display": {
12163
- regular: "PlayfairDisplay-Regular.ttf",
12164
- bold: "PlayfairDisplay-Bold.ttf",
12165
- italic: "PlayfairDisplay-Italic.ttf",
12166
- boldItalic: "PlayfairDisplay-BoldItalic.ttf"
12167
- },
12168
- "Merriweather": {
12169
- regular: "Merriweather-Regular.ttf",
12170
- bold: "Merriweather-Bold.ttf",
12171
- light: "Merriweather-Light.ttf",
12172
- italic: "Merriweather-Italic.ttf",
12173
- boldItalic: "Merriweather-BoldItalic.ttf",
12174
- lightItalic: "Merriweather-LightItalic.ttf"
12175
- },
12176
- "Lora": {
12177
- regular: "Lora-Regular.ttf",
12178
- bold: "Lora-Bold.ttf",
12179
- italic: "Lora-Italic.ttf",
12180
- boldItalic: "Lora-BoldItalic.ttf"
12181
- },
12182
- "Montserrat": {
12183
- regular: "Montserrat-Regular.ttf",
12184
- bold: "Montserrat-Bold.ttf",
12185
- light: "Montserrat-Light.ttf",
12186
- medium: "Montserrat-Medium.ttf",
12187
- semibold: "Montserrat-SemiBold.ttf",
12188
- italic: "Montserrat-Italic.ttf",
12189
- boldItalic: "Montserrat-BoldItalic.ttf",
12190
- lightItalic: "Montserrat-LightItalic.ttf",
12191
- mediumItalic: "Montserrat-MediumItalic.ttf",
12192
- semiboldItalic: "Montserrat-SemiBoldItalic.ttf"
12193
- },
12194
- "Open Sans": {
12195
- regular: "OpenSans-Regular.ttf",
12196
- bold: "OpenSans-Bold.ttf",
12197
- light: "OpenSans-Light.ttf",
12198
- semibold: "OpenSans-SemiBold.ttf",
12199
- italic: "OpenSans-Italic.ttf",
12200
- boldItalic: "OpenSans-BoldItalic.ttf",
12201
- lightItalic: "OpenSans-LightItalic.ttf",
12202
- semiboldItalic: "OpenSans-SemiBoldItalic.ttf"
12203
- },
12204
- "Roboto": {
12205
- regular: "Roboto-Regular.ttf",
12206
- bold: "Roboto-Bold.ttf",
12207
- light: "Roboto-Light.ttf",
12208
- medium: "Roboto-Medium.ttf",
12209
- italic: "Roboto-Italic.ttf",
12210
- boldItalic: "Roboto-BoldItalic.ttf",
12211
- lightItalic: "Roboto-LightItalic.ttf",
12212
- mediumItalic: "Roboto-MediumItalic.ttf"
12213
- },
12214
- "Lato": {
12215
- regular: "Lato-Regular.ttf",
12216
- bold: "Lato-Bold.ttf",
12217
- light: "Lato-Light.ttf",
12218
- italic: "Lato-Italic.ttf",
12219
- boldItalic: "Lato-BoldItalic.ttf",
12220
- lightItalic: "Lato-LightItalic.ttf"
12221
- },
12222
- "Raleway": {
12223
- regular: "Raleway-Regular.ttf",
12224
- bold: "Raleway-Bold.ttf",
12225
- light: "Raleway-Light.ttf",
12226
- medium: "Raleway-Medium.ttf",
12227
- semibold: "Raleway-SemiBold.ttf",
12228
- italic: "Raleway-Italic.ttf",
12229
- boldItalic: "Raleway-BoldItalic.ttf",
12230
- lightItalic: "Raleway-LightItalic.ttf"
12231
- },
12232
- "Poppins": {
12233
- regular: "Poppins-Regular.ttf",
12234
- bold: "Poppins-Bold.ttf",
12235
- light: "Poppins-Light.ttf",
12236
- medium: "Poppins-Medium.ttf",
12237
- semibold: "Poppins-SemiBold.ttf",
12238
- italic: "Poppins-Italic.ttf",
12239
- boldItalic: "Poppins-BoldItalic.ttf",
12240
- lightItalic: "Poppins-LightItalic.ttf",
12241
- mediumItalic: "Poppins-MediumItalic.ttf",
12242
- semiboldItalic: "Poppins-SemiBoldItalic.ttf"
12243
- },
12244
- "Inter": {
12245
- regular: "Inter-Regular.ttf",
12246
- bold: "Inter-Bold.ttf",
12247
- italic: "Inter-Italic.ttf",
12248
- boldItalic: "Inter-BoldItalic.ttf"
12249
- },
12250
- "Nunito": {
12251
- regular: "Nunito-Regular.ttf",
12252
- bold: "Nunito-Bold.ttf",
12253
- light: "Nunito-Light.ttf",
12254
- medium: "Nunito-Medium.ttf",
12255
- semibold: "Nunito-SemiBold.ttf",
12256
- italic: "Nunito-Italic.ttf",
12257
- boldItalic: "Nunito-BoldItalic.ttf"
12258
- },
12259
- "Source Sans Pro": {
12260
- regular: "SourceSansPro-Regular.ttf",
12261
- bold: "SourceSansPro-Bold.ttf",
12262
- light: "SourceSansPro-Light.ttf",
12263
- italic: "SourceSansPro-Italic.ttf",
12264
- boldItalic: "SourceSansPro-BoldItalic.ttf"
12265
- },
12266
- "Work Sans": {
12267
- regular: "WorkSans-Regular.ttf",
12268
- bold: "WorkSans-Bold.ttf",
12269
- italic: "WorkSans-Italic.ttf",
12270
- boldItalic: "WorkSans-BoldItalic.ttf"
12271
- },
12272
- "Oswald": { regular: "Oswald-Regular.ttf", bold: "Oswald-Bold.ttf" },
12273
- "Bebas Neue": { regular: "BebasNeue-Regular.ttf" },
12274
- "Abril Fatface": { regular: "AbrilFatface-Regular.ttf" },
12275
- "Dancing Script": { regular: "DancingScript-Regular.ttf", bold: "DancingScript-Bold.ttf" },
12276
- "Pacifico": { regular: "Pacifico-Regular.ttf" },
12277
- "Great Vibes": { regular: "GreatVibes-Regular.ttf" },
12278
- "DM Sans": {
12279
- regular: "DMSans-Regular.ttf",
12280
- bold: "DMSans-Bold.ttf",
12281
- light: "DMSans-Light.ttf",
12282
- medium: "DMSans-Medium.ttf",
12283
- semibold: "DMSans-SemiBold.ttf",
12284
- italic: "DMSans-RegularItalic.ttf",
12285
- boldItalic: "DMSans-BoldItalic.ttf",
12286
- lightItalic: "DMSans-LightItalic.ttf",
12287
- mediumItalic: "DMSans-MediumItalic.ttf",
12288
- semiboldItalic: "DMSans-SemiBoldItalic.ttf"
12289
- },
12290
- "Outfit": {
12291
- regular: "Outfit-Regular.ttf",
12292
- bold: "Outfit-Bold.ttf",
12293
- light: "Outfit-Light.ttf",
12294
- medium: "Outfit-Medium.ttf",
12295
- semibold: "Outfit-SemiBold.ttf"
12296
- },
12297
- "Figtree": {
12298
- regular: "Figtree-Regular.ttf",
12299
- bold: "Figtree-Bold.ttf",
12300
- light: "Figtree-Light.ttf",
12301
- medium: "Figtree-Medium.ttf",
12302
- semibold: "Figtree-SemiBold.ttf",
12303
- italic: "Figtree-RegularItalic.ttf",
12304
- boldItalic: "Figtree-BoldItalic.ttf",
12305
- lightItalic: "Figtree-LightItalic.ttf",
12306
- mediumItalic: "Figtree-MediumItalic.ttf",
12307
- semiboldItalic: "Figtree-SemiBoldItalic.ttf"
12308
- },
12309
- "Manrope": {
12310
- regular: "Manrope-Regular.ttf",
12311
- bold: "Manrope-Bold.ttf",
12312
- light: "Manrope-Light.ttf",
12313
- medium: "Manrope-Medium.ttf",
12314
- semibold: "Manrope-SemiBold.ttf"
12315
- },
12316
- "Space Grotesk": {
12317
- regular: "SpaceGrotesk-Regular.ttf",
12318
- bold: "SpaceGrotesk-Bold.ttf",
12319
- light: "SpaceGrotesk-Light.ttf",
12320
- medium: "SpaceGrotesk-Medium.ttf",
12321
- semibold: "SpaceGrotesk-SemiBold.ttf"
12322
- },
12323
- "League Spartan": {
12324
- regular: "LeagueSpartan-Regular.ttf",
12325
- bold: "LeagueSpartan-Bold.ttf",
12326
- light: "LeagueSpartan-Light.ttf",
12327
- medium: "LeagueSpartan-Medium.ttf",
12328
- semibold: "LeagueSpartan-SemiBold.ttf"
12329
- },
12330
- "EB Garamond": {
12331
- regular: "EBGaramond-Regular.ttf",
12332
- bold: "EBGaramond-Bold.ttf",
12333
- medium: "EBGaramond-Medium.ttf",
12334
- semibold: "EBGaramond-SemiBold.ttf",
12335
- italic: "EBGaramond-RegularItalic.ttf",
12336
- boldItalic: "EBGaramond-BoldItalic.ttf",
12337
- mediumItalic: "EBGaramond-MediumItalic.ttf",
12338
- semiboldItalic: "EBGaramond-SemiBoldItalic.ttf"
12339
- },
12340
- "Libre Baskerville": {
12341
- regular: "LibreBaskerville-Regular.ttf",
12342
- bold: "LibreBaskerville-Bold.ttf",
12343
- italic: "LibreBaskerville-RegularItalic.ttf",
12344
- boldItalic: "LibreBaskerville-BoldItalic.ttf"
12345
- },
12346
- "Crimson Text": {
12347
- regular: "CrimsonText-Regular.ttf",
12348
- bold: "CrimsonText-Bold.ttf",
12349
- italic: "CrimsonText-Italic.ttf",
12350
- boldItalic: "CrimsonText-BoldItalic.ttf"
12351
- },
12352
- "DM Serif Display": {
12353
- regular: "DMSerifDisplay-Regular.ttf",
12354
- italic: "DMSerifDisplay-Italic.ttf"
12355
- },
12356
- "Libre Franklin": {
12357
- regular: "LibreFranklin-Regular.ttf",
12358
- bold: "LibreFranklin-Bold.ttf",
12359
- light: "LibreFranklin-Light.ttf",
12360
- medium: "LibreFranklin-Medium.ttf",
12361
- semibold: "LibreFranklin-SemiBold.ttf",
12362
- italic: "LibreFranklin-RegularItalic.ttf",
12363
- boldItalic: "LibreFranklin-BoldItalic.ttf",
12364
- lightItalic: "LibreFranklin-LightItalic.ttf",
12365
- mediumItalic: "LibreFranklin-MediumItalic.ttf",
12366
- semiboldItalic: "LibreFranklin-SemiBoldItalic.ttf"
12367
- },
12368
- "Mulish": {
12369
- regular: "Mulish-Regular.ttf",
12370
- bold: "Mulish-Bold.ttf",
12371
- light: "Mulish-Light.ttf",
12372
- medium: "Mulish-Medium.ttf",
12373
- semibold: "Mulish-SemiBold.ttf",
12374
- italic: "Mulish-RegularItalic.ttf",
12375
- boldItalic: "Mulish-BoldItalic.ttf",
12376
- lightItalic: "Mulish-LightItalic.ttf",
12377
- mediumItalic: "Mulish-MediumItalic.ttf",
12378
- semiboldItalic: "Mulish-SemiBoldItalic.ttf"
12379
- },
12380
- "Quicksand": {
12381
- regular: "Quicksand-Regular.ttf",
12382
- bold: "Quicksand-Bold.ttf",
12383
- light: "Quicksand-Light.ttf",
12384
- medium: "Quicksand-Medium.ttf",
12385
- semibold: "Quicksand-SemiBold.ttf"
12386
- },
12387
- "Rubik": {
12388
- regular: "Rubik-Regular.ttf",
12389
- bold: "Rubik-Bold.ttf",
12390
- light: "Rubik-Light.ttf",
12391
- medium: "Rubik-Medium.ttf",
12392
- semibold: "Rubik-SemiBold.ttf",
12393
- italic: "Rubik-RegularItalic.ttf",
12394
- boldItalic: "Rubik-BoldItalic.ttf",
12395
- lightItalic: "Rubik-LightItalic.ttf",
12396
- mediumItalic: "Rubik-MediumItalic.ttf",
12397
- semiboldItalic: "Rubik-SemiBoldItalic.ttf"
12398
- },
12399
- "Karla": {
12400
- regular: "Karla-Regular.ttf",
12401
- bold: "Karla-Bold.ttf",
12402
- light: "Karla-Light.ttf",
12403
- medium: "Karla-Medium.ttf",
12404
- semibold: "Karla-SemiBold.ttf",
12405
- italic: "Karla-RegularItalic.ttf",
12406
- boldItalic: "Karla-BoldItalic.ttf",
12407
- lightItalic: "Karla-LightItalic.ttf",
12408
- mediumItalic: "Karla-MediumItalic.ttf",
12409
- semiboldItalic: "Karla-SemiBoldItalic.ttf"
12410
- },
12411
- "Plus Jakarta Sans": {
12412
- regular: "PlusJakartaSans-Regular.ttf",
12413
- bold: "PlusJakartaSans-Bold.ttf",
12414
- light: "PlusJakartaSans-Light.ttf",
12415
- medium: "PlusJakartaSans-Medium.ttf",
12416
- semibold: "PlusJakartaSans-SemiBold.ttf",
12417
- italic: "PlusJakartaSans-RegularItalic.ttf",
12418
- boldItalic: "PlusJakartaSans-BoldItalic.ttf",
12419
- lightItalic: "PlusJakartaSans-LightItalic.ttf",
12420
- mediumItalic: "PlusJakartaSans-MediumItalic.ttf",
12421
- semiboldItalic: "PlusJakartaSans-SemiBoldItalic.ttf"
12422
- },
12423
- "Sora": {
12424
- regular: "Sora-Regular.ttf",
12425
- bold: "Sora-Bold.ttf",
12426
- light: "Sora-Light.ttf",
12427
- medium: "Sora-Medium.ttf",
12428
- semibold: "Sora-SemiBold.ttf"
12429
- },
12430
- "Urbanist": {
12431
- regular: "Urbanist-Regular.ttf",
12432
- bold: "Urbanist-Bold.ttf",
12433
- light: "Urbanist-Light.ttf",
12434
- medium: "Urbanist-Medium.ttf",
12435
- semibold: "Urbanist-SemiBold.ttf",
12436
- italic: "Urbanist-RegularItalic.ttf",
12437
- boldItalic: "Urbanist-BoldItalic.ttf",
12438
- lightItalic: "Urbanist-LightItalic.ttf",
12439
- mediumItalic: "Urbanist-MediumItalic.ttf",
12440
- semiboldItalic: "Urbanist-SemiBoldItalic.ttf"
12441
- },
12442
- "Lexend": {
12443
- regular: "Lexend-Regular.ttf",
12444
- bold: "Lexend-Bold.ttf",
12445
- light: "Lexend-Light.ttf",
12446
- medium: "Lexend-Medium.ttf",
12447
- semibold: "Lexend-SemiBold.ttf"
12448
- },
12449
- "Albert Sans": {
12450
- regular: "AlbertSans-Regular.ttf",
12451
- bold: "AlbertSans-Bold.ttf",
12452
- light: "AlbertSans-Light.ttf",
12453
- medium: "AlbertSans-Medium.ttf",
12454
- semibold: "AlbertSans-SemiBold.ttf",
12455
- italic: "AlbertSans-RegularItalic.ttf",
12456
- boldItalic: "AlbertSans-BoldItalic.ttf",
12457
- lightItalic: "AlbertSans-LightItalic.ttf",
12458
- mediumItalic: "AlbertSans-MediumItalic.ttf",
12459
- semiboldItalic: "AlbertSans-SemiBoldItalic.ttf"
12460
- },
12461
- "Noto Sans": {
12462
- regular: "NotoSans-Regular.ttf",
12463
- bold: "NotoSans-Bold.ttf",
12464
- light: "NotoSans-Light.ttf",
12465
- medium: "NotoSans-Medium.ttf",
12466
- semibold: "NotoSans-SemiBold.ttf",
12467
- italic: "NotoSans-RegularItalic.ttf",
12468
- boldItalic: "NotoSans-BoldItalic.ttf",
12469
- lightItalic: "NotoSans-LightItalic.ttf",
12470
- mediumItalic: "NotoSans-MediumItalic.ttf",
12471
- semiboldItalic: "NotoSans-SemiBoldItalic.ttf"
12472
- },
12473
- "Cabin": {
12474
- regular: "Cabin-Regular.ttf",
12475
- bold: "Cabin-Bold.ttf",
12476
- medium: "Cabin-Medium.ttf",
12477
- semibold: "Cabin-SemiBold.ttf",
12478
- italic: "Cabin-RegularItalic.ttf",
12479
- boldItalic: "Cabin-BoldItalic.ttf",
12480
- mediumItalic: "Cabin-MediumItalic.ttf",
12481
- semiboldItalic: "Cabin-SemiBoldItalic.ttf"
12482
- },
12483
- "Barlow": {
12484
- regular: "Barlow-Regular.ttf",
12485
- bold: "Barlow-Bold.ttf",
12486
- light: "Barlow-Light.ttf",
12487
- medium: "Barlow-Medium.ttf",
12488
- semibold: "Barlow-SemiBold.ttf",
12489
- italic: "Barlow-Italic.ttf",
12490
- boldItalic: "Barlow-BoldItalic.ttf"
12491
- },
12492
- "Josefin Sans": {
12493
- regular: "JosefinSans-Regular.ttf",
12494
- bold: "JosefinSans-Bold.ttf",
12495
- light: "JosefinSans-Light.ttf",
12496
- medium: "JosefinSans-Medium.ttf",
12497
- semibold: "JosefinSans-SemiBold.ttf",
12498
- italic: "JosefinSans-RegularItalic.ttf",
12499
- boldItalic: "JosefinSans-BoldItalic.ttf",
12500
- lightItalic: "JosefinSans-LightItalic.ttf",
12501
- mediumItalic: "JosefinSans-MediumItalic.ttf",
12502
- semiboldItalic: "JosefinSans-SemiBoldItalic.ttf"
12503
- },
12504
- "Archivo": {
12505
- regular: "Archivo-Regular.ttf",
12506
- bold: "Archivo-Bold.ttf",
12507
- light: "Archivo-Light.ttf",
12508
- medium: "Archivo-Medium.ttf",
12509
- semibold: "Archivo-SemiBold.ttf",
12510
- italic: "Archivo-RegularItalic.ttf",
12511
- boldItalic: "Archivo-BoldItalic.ttf",
12512
- lightItalic: "Archivo-LightItalic.ttf",
12513
- mediumItalic: "Archivo-MediumItalic.ttf",
12514
- semiboldItalic: "Archivo-SemiBoldItalic.ttf"
12515
- },
12516
- "Overpass": {
12517
- regular: "Overpass-Regular.ttf",
12518
- bold: "Overpass-Bold.ttf",
12519
- light: "Overpass-Light.ttf",
12520
- medium: "Overpass-Medium.ttf",
12521
- semibold: "Overpass-SemiBold.ttf",
12522
- italic: "Overpass-RegularItalic.ttf",
12523
- boldItalic: "Overpass-BoldItalic.ttf",
12524
- lightItalic: "Overpass-LightItalic.ttf",
12525
- mediumItalic: "Overpass-MediumItalic.ttf",
12526
- semiboldItalic: "Overpass-SemiBoldItalic.ttf"
12527
- },
12528
- "Exo 2": {
12529
- regular: "Exo2-Regular.ttf",
12530
- bold: "Exo2-Bold.ttf",
12531
- light: "Exo2-Light.ttf",
12532
- medium: "Exo2-Medium.ttf",
12533
- semibold: "Exo2-SemiBold.ttf",
12534
- italic: "Exo2-RegularItalic.ttf",
12535
- boldItalic: "Exo2-BoldItalic.ttf",
12536
- lightItalic: "Exo2-LightItalic.ttf",
12537
- mediumItalic: "Exo2-MediumItalic.ttf",
12538
- semiboldItalic: "Exo2-SemiBoldItalic.ttf"
12539
- },
12540
- "Roboto Mono": {
12541
- regular: "RobotoMono-Regular.ttf",
12542
- bold: "RobotoMono-Bold.ttf",
12543
- light: "RobotoMono-Light.ttf",
12544
- medium: "RobotoMono-Medium.ttf",
12545
- semibold: "RobotoMono-SemiBold.ttf",
12546
- italic: "RobotoMono-RegularItalic.ttf",
12547
- boldItalic: "RobotoMono-BoldItalic.ttf",
12548
- lightItalic: "RobotoMono-LightItalic.ttf",
12549
- mediumItalic: "RobotoMono-MediumItalic.ttf",
12550
- semiboldItalic: "RobotoMono-SemiBoldItalic.ttf"
12551
- },
12552
- "Fira Code": {
12553
- regular: "FiraCode-Regular.ttf",
12554
- bold: "FiraCode-Bold.ttf",
12555
- light: "FiraCode-Light.ttf",
12556
- medium: "FiraCode-Medium.ttf",
12557
- semibold: "FiraCode-SemiBold.ttf"
12558
- },
12559
- "JetBrains Mono": {
12560
- regular: "JetBrainsMono-Regular.ttf",
12561
- bold: "JetBrainsMono-Bold.ttf",
12562
- light: "JetBrainsMono-Light.ttf",
12563
- medium: "JetBrainsMono-Medium.ttf",
12564
- semibold: "JetBrainsMono-SemiBold.ttf",
12565
- italic: "JetBrainsMono-RegularItalic.ttf",
12566
- boldItalic: "JetBrainsMono-BoldItalic.ttf",
12567
- lightItalic: "JetBrainsMono-LightItalic.ttf",
12568
- mediumItalic: "JetBrainsMono-MediumItalic.ttf",
12569
- semiboldItalic: "JetBrainsMono-SemiBoldItalic.ttf"
12570
- },
12571
- "Source Code Pro": {
12572
- regular: "SourceCodePro-Regular.ttf",
12573
- bold: "SourceCodePro-Bold.ttf",
12574
- light: "SourceCodePro-Light.ttf",
12575
- medium: "SourceCodePro-Medium.ttf",
12576
- semibold: "SourceCodePro-SemiBold.ttf",
12577
- italic: "SourceCodePro-RegularItalic.ttf",
12578
- boldItalic: "SourceCodePro-BoldItalic.ttf",
12579
- lightItalic: "SourceCodePro-LightItalic.ttf",
12580
- mediumItalic: "SourceCodePro-MediumItalic.ttf",
12581
- semiboldItalic: "SourceCodePro-SemiBoldItalic.ttf"
12582
- },
12583
- "IBM Plex Mono": {
12584
- regular: "IBMPlexMono-Regular.ttf",
12585
- bold: "IBMPlexMono-Bold.ttf"
12586
- },
12587
- "Space Mono": {
12588
- regular: "SpaceMono-Regular.ttf",
12589
- bold: "SpaceMono-Bold.ttf",
12590
- italic: "SpaceMono-Italic.ttf",
12591
- boldItalic: "SpaceMono-BoldItalic.ttf"
12592
- },
12593
- "Sacramento": { regular: "Sacramento-Regular.ttf" },
12594
- "Alex Brush": { regular: "AlexBrush-Regular.ttf" },
12595
- "Allura": { regular: "Allura-Regular.ttf" },
12596
- "Caveat": {
12597
- regular: "Caveat-Regular.ttf",
12598
- bold: "Caveat-Bold.ttf",
12599
- medium: "Caveat-Medium.ttf",
12600
- semibold: "Caveat-SemiBold.ttf"
12601
- },
12602
- "Lobster": { regular: "Lobster-Regular.ttf" },
12603
- "Comfortaa": {
12604
- regular: "Comfortaa-Regular.ttf",
12605
- bold: "Comfortaa-Bold.ttf",
12606
- light: "Comfortaa-Light.ttf",
12607
- medium: "Comfortaa-Medium.ttf",
12608
- semibold: "Comfortaa-SemiBold.ttf"
12609
- },
12610
- "Anton": { regular: "Anton-Regular.ttf" },
12611
- "Teko": {
12612
- regular: "Teko-Regular.ttf",
12613
- bold: "Teko-Bold.ttf",
12614
- light: "Teko-Light.ttf",
12615
- medium: "Teko-Medium.ttf",
12616
- semibold: "Teko-SemiBold.ttf"
12617
- },
12618
- "Noto Sans Devanagari": {
12619
- regular: "NotoSansDevanagari-Regular.ttf",
12620
- bold: "NotoSansDevanagari-Bold.ttf",
12621
- light: "NotoSansDevanagari-Light.ttf",
12622
- medium: "NotoSansDevanagari-Medium.ttf",
12623
- semibold: "NotoSansDevanagari-SemiBold.ttf"
12624
- },
12625
- "Hind": {
12626
- regular: "Hind-Regular.ttf",
12627
- bold: "Hind-Bold.ttf",
12628
- light: "Hind-Light.ttf",
12629
- medium: "Hind-Medium.ttf",
12630
- semibold: "Hind-SemiBold.ttf"
12631
- },
12632
- "Cinzel": { regular: "Cinzel-Regular.ttf", bold: "Cinzel-Bold.ttf" },
12633
- "Cormorant Garamond": {
12634
- regular: "CormorantGaramond-Regular.ttf",
12635
- bold: "CormorantGaramond-Bold.ttf",
12636
- light: "CormorantGaramond-Light.ttf",
12637
- medium: "CormorantGaramond-Medium.ttf",
12638
- semibold: "CormorantGaramond-SemiBold.ttf",
12639
- italic: "CormorantGaramond-Italic.ttf",
12640
- boldItalic: "CormorantGaramond-BoldItalic.ttf"
12641
- }
12642
- };
12643
- const WEIGHT_TO_KEYS = {
12644
- 300: ["light", "regular"],
12645
- 400: ["regular"],
12646
- 500: ["medium", "regular"],
12647
- 600: ["semibold", "bold", "regular"],
12648
- 700: ["bold", "regular"]
12649
- };
12650
- const WEIGHT_TO_ITALIC_KEYS = {
12651
- 300: ["lightItalic", "italic", "light", "regular"],
12652
- 400: ["italic", "regular"],
12653
- 500: ["mediumItalic", "italic", "medium", "regular"],
12654
- 600: ["semiboldItalic", "boldItalic", "italic", "semibold", "bold", "regular"],
12655
- 700: ["boldItalic", "italic", "bold", "regular"]
12656
- };
12657
- function getFontPathForWeight(files, weight, isItalic = false) {
12658
- const keys = isItalic ? WEIGHT_TO_ITALIC_KEYS[weight] : WEIGHT_TO_KEYS[weight];
12659
- if (!keys) return files.regular;
12660
- for (const k of keys) {
12661
- const path = files[k];
12662
- if (path) return path;
12663
- }
12664
- return files.regular;
12665
- }
12666
- function isItalicPath(files, path) {
12667
- return path === files.italic || path === files.boldItalic || path === files.lightItalic || path === files.mediumItalic || path === files.semiboldItalic;
12668
- }
12669
- function getJsPDFFontName(fontName) {
12670
- return fontName.replace(/\s+/g, "");
12671
- }
12672
- function getEmbeddedJsPDFFontName(fontName, weight, isItalic = false) {
12673
- const resolved = resolveFontWeight(weight);
12674
- const label = FONT_WEIGHT_LABELS[resolved];
12675
- const italicSuffix = isItalic ? "Italic" : "";
12676
- return `${getJsPDFFontName(fontName)}-${label}${italicSuffix}`;
12677
- }
12678
- function isFontAvailable(fontName) {
12679
- return fontName in FONT_FILES;
12680
- }
12681
- const ttfCache = /* @__PURE__ */ new Map();
12682
- async function fetchTTFAsBase64(url) {
12683
- const cached = ttfCache.get(url);
12684
- if (cached) return cached;
12685
- try {
12686
- const res = await fetch(url);
12687
- if (!res.ok) return null;
12688
- const buf = await res.arrayBuffer();
12689
- const bytes = new Uint8Array(buf);
12690
- let binary = "";
12691
- for (let i = 0; i < bytes.length; i++) {
12692
- binary += String.fromCharCode(bytes[i]);
12693
- }
12694
- const b64 = btoa(binary);
12695
- ttfCache.set(url, b64);
12696
- return b64;
12697
- } catch {
12698
- return null;
12699
- }
12700
- }
12701
- async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
12702
- const fontFiles = FONT_FILES[fontName];
12703
- if (!fontFiles) return false;
12704
- const baseUrl = fontBaseUrl.endsWith("/") ? fontBaseUrl : fontBaseUrl + "/";
12705
- const resolvedWeight = resolveFontWeight(weight);
12706
- const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
12707
- if (!fontPath) return false;
12708
- const hasItalicFile = isItalic && isItalicPath(fontFiles, fontPath);
12709
- const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, hasItalicFile);
12710
- const label = FONT_WEIGHT_LABELS[resolvedWeight];
12711
- const italicSuffix = hasItalicFile ? "Italic" : "";
12712
- const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
12713
- const url = baseUrl + fontPath;
12714
- try {
12715
- const b64 = await fetchTTFAsBase64(url);
12716
- if (!b64) return false;
12717
- pdf.addFileToVFS(fileName, b64);
12718
- pdf.addFont(fileName, jsPdfFontName, "normal");
12719
- if (fontName !== jsPdfFontName) {
12720
- try {
12721
- pdf.addFont(fileName, fontName, "normal");
12722
- } catch {
12723
- }
12724
- }
12725
- return true;
12726
- } catch (e) {
12727
- console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
12728
- return false;
12729
- }
12730
- }
12731
- async function embedFontsForConfig(pdf, config, fontBaseUrl) {
12732
- const fontKeys = /* @__PURE__ */ new Set();
12733
- const SEP = "";
12734
- const walkElements = (elements) => {
12735
- for (const el of elements) {
12736
- if (el.fontFamily) {
12737
- const w = resolveFontWeight(el.fontWeight ?? 400);
12738
- fontKeys.add(`${el.fontFamily}${SEP}${w}`);
12739
- }
12740
- if (el.styles && typeof el.styles === "object") {
12741
- for (const lineKey of Object.keys(el.styles)) {
12742
- const lineStyles = el.styles[lineKey];
12743
- if (lineStyles && typeof lineStyles === "object") {
12744
- for (const charKey of Object.keys(lineStyles)) {
12745
- const s = lineStyles[charKey];
12746
- if (s == null ? void 0 : s.fontFamily) {
12747
- const w = resolveFontWeight(s.fontWeight ?? 400);
12748
- fontKeys.add(`${s.fontFamily}${SEP}${w}`);
12749
- }
12750
- }
12751
- }
12752
- }
12753
- }
12754
- if (el.children) walkElements(el.children);
12755
- if (el.objects) walkElements(el.objects);
12756
- }
12757
- };
12758
- for (const page of (config == null ? void 0 : config.pages) || []) {
12759
- if (page.children) walkElements(page.children);
12760
- if (page.elements) walkElements(page.elements);
12761
- }
12762
- fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
12763
- for (const w of [300, 400, 500, 600, 700]) {
12764
- fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
12765
- }
12766
- const embedded = /* @__PURE__ */ new Set();
12767
- const tasks = [];
12768
- for (const key of fontKeys) {
12769
- const sep = key.indexOf(SEP);
12770
- const fontName = key.slice(0, sep);
12771
- const weight = parseInt(key.slice(sep + 1), 10);
12772
- if (!isFontAvailable(fontName)) continue;
12773
- tasks.push(
12774
- embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
12775
- if (ok) embedded.add(key);
12776
- })
12777
- );
12778
- }
12779
- await Promise.all(tasks);
12780
- console.log(`[pdf-fonts] Embedded ${embedded.size} font variants from config`);
12781
- return embedded;
12782
- }
12783
- function isDevanagari(char) {
12784
- const c = char.codePointAt(0) ?? 0;
12785
- return c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423;
12786
- }
12787
- function containsDevanagari(text) {
12788
- if (!text) return false;
12789
- for (const char of text) {
12790
- if (isDevanagari(char)) return true;
12791
- }
12792
- return false;
12793
- }
12794
- function isBasicLatinOrLatin1(char) {
12795
- const c = char.codePointAt(0) ?? 0;
12796
- return c <= 591;
12797
- }
12798
- function classifyChar(char) {
12799
- if (isBasicLatinOrLatin1(char)) return "main";
12800
- if (isDevanagari(char)) return "devanagari";
12801
- return "symbol";
12802
- }
12803
- function splitIntoRuns(text) {
12804
- if (!text) return [];
12805
- const runs = [];
12806
- let currentType = null;
12807
- let currentText = "";
12808
- for (const char of text) {
12809
- const type = classifyChar(char);
12810
- if (type !== currentType && currentText) {
12811
- runs.push({ text: currentText, runType: currentType });
12812
- currentText = "";
12813
- }
12814
- currentType = type;
12815
- currentText += char;
12816
- }
12817
- if (currentText && currentType) {
12818
- runs.push({ text: currentText, runType: currentType });
12819
- }
12820
- return runs;
12821
- }
12822
- function rewriteSvgFontsForJsPDF(svgStr) {
12823
- var _a, _b;
12824
- const parser = new DOMParser();
12825
- const doc = parser.parseFromString(svgStr, "image/svg+xml");
12826
- const textEls = doc.querySelectorAll("text, tspan, textPath");
12827
- const readStyleToken = (style, prop) => {
12828
- var _a2;
12829
- const match = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
12830
- return ((_a2 = match == null ? void 0 : match[1]) == null ? void 0 : _a2.trim()) || null;
12831
- };
12832
- const resolveInheritedValue = (el, attr, styleProp = attr) => {
12833
- var _a2;
12834
- let current = el;
12835
- while (current) {
12836
- const attrVal = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
12837
- if (attrVal) return attrVal;
12838
- const styleVal = readStyleToken(current.getAttribute("style") || "", styleProp);
12839
- if (styleVal) return styleVal;
12840
- current = current.parentElement;
12841
- }
12842
- return null;
12843
- };
12844
- const resolveWeightNum = (weightRaw) => {
12845
- const parsedWeight = Number.parseInt(weightRaw, 10);
12846
- return Number.isFinite(parsedWeight) ? parsedWeight : /bold/i.test(weightRaw) ? 700 : /medium/i.test(weightRaw) ? 500 : /semi/i.test(weightRaw) ? 600 : /light/i.test(weightRaw) ? 300 : 400;
12847
- };
12848
- const buildStyleString = (existingStyle, fontName) => {
12849
- const stylePairs = existingStyle.split(";").map((part) => part.trim()).filter(Boolean).filter((part) => !/^font-family\s*:/i.test(part) && !/^font-weight\s*:/i.test(part) && !/^font-style\s*:/i.test(part));
12850
- stylePairs.push(`font-family: ${fontName}`);
12851
- stylePairs.push(`font-weight: normal`);
12852
- stylePairs.push(`font-style: normal`);
12853
- return stylePairs.join("; ");
12854
- };
12855
- for (const el of textEls) {
12856
- const inlineStyle = el.getAttribute("style") || "";
12857
- const rawFf = resolveInheritedValue(el, "font-family");
12858
- if (!rawFf) continue;
12859
- const clean = (_a = rawFf.split(",")[0]) == null ? void 0 : _a.replace(/['"]/g, "").trim();
12860
- if (!isFontAvailable(clean)) continue;
12861
- const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
12862
- const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
12863
- const weight = resolveWeightNum(weightRaw);
12864
- const resolved = resolveFontWeight(weight);
12865
- const isItalic = /italic|oblique/i.test(styleRaw);
12866
- const jsPdfName = getEmbeddedJsPDFFontName(clean, resolved, isItalic);
12867
- el.setAttribute("data-source-font-family", clean);
12868
- el.setAttribute("data-source-font-weight", String(resolved));
12869
- el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
12870
- const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
12871
- const hasDevanagari = containsDevanagari(directText);
12872
- if (hasDevanagari && directText.length > 0) {
12873
- const devanagariWeight = resolveFontWeight(weight);
12874
- const devanagariJsPdfName = getEmbeddedJsPDFFontName(FONT_FALLBACK_DEVANAGARI, devanagariWeight);
12875
- const symbolJsPdfName = isFontAvailable(FONT_FALLBACK_SYMBOLS) ? getEmbeddedJsPDFFontName(FONT_FALLBACK_SYMBOLS, 400) : jsPdfName;
12876
- const childNodes = Array.from(el.childNodes);
12877
- for (const node of childNodes) {
12878
- if (node.nodeType !== 3 || !node.textContent) continue;
12879
- const runs = splitIntoRuns(node.textContent);
12880
- if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) !== "devanagari") continue;
12881
- const fragment = doc.createDocumentFragment();
12882
- for (const run of runs) {
12883
- const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
12884
- let runFont;
12885
- if (run.runType === "devanagari") {
12886
- runFont = devanagariJsPdfName;
12887
- } else if (run.runType === "symbol") {
12888
- runFont = symbolJsPdfName;
12889
- } else {
12890
- runFont = jsPdfName;
12891
- }
12892
- tspan.setAttribute("font-family", runFont);
12893
- tspan.setAttribute("font-weight", "normal");
12894
- tspan.setAttribute("font-style", "normal");
12895
- tspan.textContent = run.text;
12896
- fragment.appendChild(tspan);
12897
- }
12898
- el.replaceChild(fragment, node);
12899
- }
12900
- el.setAttribute("font-family", jsPdfName);
12901
- el.setAttribute("font-weight", "normal");
12902
- el.setAttribute("font-style", "normal");
12903
- el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
12904
- } else {
12905
- el.setAttribute("font-family", jsPdfName);
12906
- el.setAttribute("font-weight", "normal");
12907
- el.setAttribute("font-style", "normal");
12908
- el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
12909
- }
12910
- }
12911
- return new XMLSerializer().serializeToString(doc.documentElement);
12912
- }
12913
- function extractFontFamiliesFromSvgs(svgs) {
12914
- const families = /* @__PURE__ */ new Set();
12915
- const regex = /font-family[=:]\s*["']?([^"';},]+)/gi;
12916
- for (const svg of svgs) {
12917
- let m;
12918
- while ((m = regex.exec(svg)) !== null) {
12919
- const raw = m[1].trim().split(",")[0].trim().replace(/^['"]|['"]$/g, "");
12920
- if (raw && raw !== "serif" && raw !== "sans-serif" && raw !== "monospace") {
12921
- families.add(raw);
12922
- }
12923
- }
12924
- }
12925
- return families;
12926
- }
12927
- async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
12928
- const embedded = /* @__PURE__ */ new Set();
12929
- const weights = [300, 400, 500, 600, 700];
12930
- const tasks = [];
12931
- for (const family of fontFamilies) {
12932
- if (!isFontAvailable(family)) {
12933
- console.warn(`[pdf-fonts] No TTF mapping for "${family}" — will use Helvetica fallback`);
12934
- continue;
12935
- }
12936
- for (const w of weights) {
12937
- tasks.push(
12938
- embedFont(pdf, family, w, fontBaseUrl).then((ok) => {
12939
- if (ok) embedded.add(`${family}${w}`);
12940
- })
12941
- );
12942
- }
12943
- }
12944
- await Promise.all(tasks);
12945
- console.log(`[pdf-fonts] Embedded ${embedded.size} font variants for ${fontFamilies.size} families`);
12946
- return embedded;
12947
- }
12948
- const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12949
- __proto__: null,
12950
- FONT_FALLBACK_DEVANAGARI,
12951
- FONT_FALLBACK_SYMBOLS,
12952
- FONT_FILES,
12953
- FONT_WEIGHT_LABELS,
12954
- embedFont,
12955
- embedFontsForConfig,
12956
- embedFontsInPdf,
12957
- extractFontFamiliesFromSvgs,
12958
- getEmbeddedJsPDFFontName,
12959
- getFontPathForWeight,
12960
- isFontAvailable,
12961
- resolveFontWeight,
12962
- rewriteSvgFontsForJsPDF
12963
- }, Symbol.toStringTag, { value: "Module" }));
12964
- function normalizeFontFamily(fontStack) {
12965
- const first = fontStack.split(",")[0].trim();
12966
- return first.replace(/^['"]|['"]$/g, "");
12145
+ function normalizeFontFamily(fontStack) {
12146
+ const first = fontStack.split(",")[0].trim();
12147
+ return first.replace(/^['"]|['"]$/g, "");
12967
12148
  }
12968
12149
  const loadedFonts = /* @__PURE__ */ new Set();
12969
12150
  const loadingPromises = /* @__PURE__ */ new Map();
12970
- function getFontBaseUrl() {
12971
- if (typeof window === "undefined") return "/fonts/";
12972
- return `${window.location.origin}/fonts/`;
12973
- }
12974
- function injectLocalFontFaces(rawFontFamily) {
12975
- if (typeof document === "undefined") return false;
12976
- const fontFamily = normalizeFontFamily(rawFontFamily);
12977
- const files = FONT_FILES[fontFamily];
12978
- if (!files) return false;
12979
- const id = `pixldocs-local-font-${fontFamily.replace(/[^a-z0-9_-]+/gi, "-")}`;
12980
- if (document.getElementById(id)) return true;
12981
- const baseUrl = getFontBaseUrl();
12982
- const rules = [];
12983
- const seen = /* @__PURE__ */ new Set();
12984
- for (const weight of [300, 400, 500, 600, 700]) {
12985
- for (const italic of [false, true]) {
12986
- const resolvedWeight = resolveFontWeight(weight);
12987
- const file = getFontPathForWeight(files, resolvedWeight, italic);
12988
- if (!file) continue;
12989
- const key = `${file}|${weight}|${italic ? "italic" : "normal"}`;
12990
- if (seen.has(key)) continue;
12991
- seen.add(key);
12992
- rules.push(
12993
- `@font-face{font-family:${JSON.stringify(fontFamily)};src:url(${JSON.stringify(baseUrl + file)}) format("truetype");font-weight:${weight};font-style:${italic ? "italic" : "normal"};font-display:block;}`
12994
- );
12995
- }
12996
- }
12997
- if (rules.length === 0) return false;
12998
- const style = document.createElement("style");
12999
- style.id = id;
13000
- style.textContent = rules.join("\n");
13001
- document.head.appendChild(style);
13002
- return true;
13003
- }
13004
12151
  function withTimeout(promise, timeoutMs = 4e3) {
13005
12152
  let timeoutId;
13006
12153
  return Promise.race([
@@ -13021,10 +12168,6 @@ async function loadGoogleFontCSS(rawFontFamily) {
13021
12168
  if (existing) return existing;
13022
12169
  const promise = (async () => {
13023
12170
  try {
13024
- if (injectLocalFontFaces(fontFamily)) {
13025
- loadedFonts.add(fontFamily);
13026
- return;
13027
- }
13028
12171
  const encoded = encodeURIComponent(fontFamily);
13029
12172
  const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
13030
12173
  const link = document.createElement("link");
@@ -13147,7 +12290,7 @@ async function ensureFontsForResolvedConfig(config) {
13147
12290
  if (typeof document === "undefined") return;
13148
12291
  const descriptors = collectFontDescriptorsFromConfig(config);
13149
12292
  const families = new Set(descriptors.map((d) => d.family));
13150
- void withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 2500);
12293
+ await withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 3500);
13151
12294
  if (document.fonts) {
13152
12295
  descriptors.forEach((d) => {
13153
12296
  const stylePrefix = d.style === "italic" ? "italic " : "";
@@ -13176,7 +12319,7 @@ function configHasAutoShrinkText$1(config) {
13176
12319
  }
13177
12320
  async function awaitFontsForConfig(config, maxWaitMs) {
13178
12321
  if (typeof document === "undefined" || !document.fonts) return;
13179
- void ensureFontsForResolvedConfig(config);
12322
+ await ensureFontsForResolvedConfig(config);
13180
12323
  const descriptors = collectFontDescriptorsFromConfig(config);
13181
12324
  if (descriptors.length === 0) return;
13182
12325
  const loads = Promise.all(
@@ -13418,7 +12561,7 @@ function PixldocsPreview(props) {
13418
12561
  !canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
13419
12562
  ] });
13420
12563
  }
13421
- const PACKAGE_VERSION = "0.5.68";
12564
+ const PACKAGE_VERSION = "0.5.71";
13422
12565
  let __underlineFixInstalled = false;
13423
12566
  function installUnderlineFix(fab) {
13424
12567
  var _a;
@@ -13730,638 +12873,1482 @@ class PixldocsRenderer {
13730
12873
  });
13731
12874
  return this.renderAllPages(resolved.config, options);
13732
12875
  }
13733
- // ─── Internal: render a page using the full PreviewCanvas engine ───
13734
- getExpectedImageCount(config, pageIndex) {
13735
- const page = config.pages[pageIndex];
13736
- if (!(page == null ? void 0 : page.children)) return 0;
13737
- let count = 0;
13738
- const walk = (nodes) => {
13739
- for (const node of nodes) {
13740
- if (!node || node.visible === false) continue;
13741
- const src = typeof node.src === "string" ? node.src.trim() : "";
13742
- const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
13743
- if (node.type === "image" && (src || imageUrl)) count += 1;
13744
- if (Array.isArray(node.children) && node.children.length > 0) {
13745
- walk(node.children);
13746
- }
12876
+ // ─── Internal: render a page using the full PreviewCanvas engine ───
12877
+ getExpectedImageCount(config, pageIndex) {
12878
+ const page = config.pages[pageIndex];
12879
+ if (!(page == null ? void 0 : page.children)) return 0;
12880
+ let count = 0;
12881
+ const walk = (nodes) => {
12882
+ for (const node of nodes) {
12883
+ if (!node || node.visible === false) continue;
12884
+ const src = typeof node.src === "string" ? node.src.trim() : "";
12885
+ const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
12886
+ if (node.type === "image" && (src || imageUrl)) count += 1;
12887
+ if (Array.isArray(node.children) && node.children.length > 0) {
12888
+ walk(node.children);
12889
+ }
12890
+ }
12891
+ };
12892
+ walk(page.children);
12893
+ return count;
12894
+ }
12895
+ waitForCanvasImages(container, expectedImageCount, maxWaitMs = 15e3, pollMs = 120) {
12896
+ return new Promise((resolve) => {
12897
+ const start = Date.now();
12898
+ let stableFrames = 0;
12899
+ let lastSummary = "";
12900
+ const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
12901
+ const collectRenderableImages = (obj, seen) => {
12902
+ if (!obj || typeof obj !== "object") return;
12903
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
12904
+ for (const candidate of candidates) {
12905
+ if (isRenderableImage(candidate)) {
12906
+ seen.add(candidate);
12907
+ } else if (candidate instanceof HTMLImageElement) {
12908
+ return false;
12909
+ }
12910
+ }
12911
+ const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
12912
+ for (const child of nested) {
12913
+ if (collectRenderableImages(child, seen) === false) return false;
12914
+ }
12915
+ return true;
12916
+ };
12917
+ const getFabricCanvas = () => {
12918
+ const registry2 = window.__fabricCanvasRegistry;
12919
+ if (registry2 instanceof Map) {
12920
+ for (const entry of registry2.values()) {
12921
+ const canvas = entry == null ? void 0 : entry.canvas;
12922
+ const el = (canvas == null ? void 0 : canvas.lowerCanvasEl) || (canvas == null ? void 0 : canvas.upperCanvasEl);
12923
+ if (el && container.contains(el)) return canvas;
12924
+ }
12925
+ }
12926
+ return null;
12927
+ };
12928
+ const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
12929
+ const getImageDebugInfo = (obj, bucket) => {
12930
+ if (!obj || typeof obj !== "object") return;
12931
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
12932
+ for (const candidate of candidates) {
12933
+ if (candidate instanceof HTMLImageElement) {
12934
+ bucket.push({
12935
+ id: obj.__docuforgeId || obj.id || "unknown",
12936
+ src: (candidate.currentSrc || candidate.src || "").slice(0, 240),
12937
+ complete: candidate.complete,
12938
+ naturalWidth: candidate.naturalWidth,
12939
+ naturalHeight: candidate.naturalHeight
12940
+ });
12941
+ }
12942
+ }
12943
+ const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
12944
+ for (const child of nested) {
12945
+ getImageDebugInfo(child, bucket);
12946
+ }
12947
+ };
12948
+ const check = () => {
12949
+ const elapsed = Date.now() - start;
12950
+ const domImages = Array.from(container.querySelectorAll("img"));
12951
+ const allDomLoaded = domImages.every((img) => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
12952
+ const fabricCanvas = getFabricCanvas();
12953
+ const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
12954
+ const renderableImages = /* @__PURE__ */ new Set();
12955
+ const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
12956
+ const actualImageCount = Math.max(domImages.length, renderableImages.size);
12957
+ const canvasReady = !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
12958
+ const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
12959
+ const ready = allDomLoaded && fabricReady && hasExpectedAssets;
12960
+ const summary = `expected=${expectedImageCount} actual=${actualImageCount} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}`;
12961
+ if (summary !== lastSummary) {
12962
+ lastSummary = summary;
12963
+ console.log(`[canvas-renderer][asset-wait] ${summary}`);
12964
+ }
12965
+ if (ready) {
12966
+ stableFrames += 1;
12967
+ if (stableFrames >= 2) {
12968
+ console.log(`[canvas-renderer][asset-wait] ready after ${elapsed}ms (${summary})`);
12969
+ settle();
12970
+ return;
12971
+ }
12972
+ } else {
12973
+ stableFrames = 0;
12974
+ }
12975
+ if (elapsed >= maxWaitMs) {
12976
+ const fabricImageDebug = [];
12977
+ for (const obj of fabricObjects) {
12978
+ getImageDebugInfo(obj, fabricImageDebug);
12979
+ }
12980
+ const domImageDebug = domImages.map((img, index) => ({
12981
+ index,
12982
+ src: (img.currentSrc || img.src || "").slice(0, 240),
12983
+ complete: img.complete,
12984
+ naturalWidth: img.naturalWidth,
12985
+ naturalHeight: img.naturalHeight
12986
+ }));
12987
+ console.warn(`[canvas-renderer][asset-wait-timeout] elapsed=${elapsed}ms ${summary}`);
12988
+ console.warn("[canvas-renderer][asset-wait-timeout][dom-images]", domImageDebug);
12989
+ console.warn("[canvas-renderer][asset-wait-timeout][fabric-images]", fabricImageDebug);
12990
+ settle();
12991
+ return;
12992
+ }
12993
+ setTimeout(check, pollMs);
12994
+ };
12995
+ setTimeout(check, 0);
12996
+ });
12997
+ }
12998
+ waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
12999
+ return new Promise((resolve) => {
13000
+ var _a, _b;
13001
+ const start = Date.now();
13002
+ const pageHasContent = (((_b = (_a = config.pages[pageIndex]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) ?? 0) > 0;
13003
+ const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
13004
+ const check = () => {
13005
+ const fabricCanvas = this.getFabricCanvasFromContainer(container);
13006
+ const lowerCanvas = (fabricCanvas == null ? void 0 : fabricCanvas.lowerCanvasEl) || container.querySelector("canvas.lower-canvas, canvas");
13007
+ const objectCount = typeof (fabricCanvas == null ? void 0 : fabricCanvas.getObjects) === "function" ? fabricCanvas.getObjects().length : 0;
13008
+ const ready = !!lowerCanvas && (!pageHasContent || objectCount > 0);
13009
+ if (ready) {
13010
+ console.log(`[canvas-renderer][scene-wait] ready after ${Date.now() - start}ms (objects=${objectCount})`);
13011
+ settle();
13012
+ return;
13013
+ }
13014
+ if (Date.now() - start >= maxWaitMs) {
13015
+ console.warn(`[canvas-renderer][scene-wait-timeout] elapsed=${Date.now() - start}ms objects=${objectCount} pageHasContent=${pageHasContent}`);
13016
+ settle();
13017
+ return;
13018
+ }
13019
+ setTimeout(check, pollMs);
13020
+ };
13021
+ setTimeout(check, 0);
13022
+ });
13023
+ }
13024
+ async waitForRelevantFonts(config, maxWaitMs = 1800) {
13025
+ if (typeof document === "undefined" || !document.fonts) return;
13026
+ const descriptors = collectFontDescriptorsFromConfig(config);
13027
+ if (descriptors.length === 0) return;
13028
+ const loads = Promise.all(
13029
+ descriptors.map((descriptor) => {
13030
+ const stylePrefix = descriptor.style === "italic" ? "italic " : "";
13031
+ const spec = `${stylePrefix}${descriptor.weight} 16px "${descriptor.family}"`;
13032
+ return document.fonts.load(spec).catch(() => []);
13033
+ })
13034
+ ).then(() => void 0);
13035
+ await Promise.race([
13036
+ loads,
13037
+ new Promise((resolve) => setTimeout(resolve, maxWaitMs))
13038
+ ]);
13039
+ await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
13040
+ }
13041
+ /**
13042
+ * Block until the webfonts referenced by `config` have actually loaded
13043
+ * (or `maxWaitMs` elapses). Used by the headless capture path BEFORE
13044
+ * mounting `PreviewCanvas`, so the synchronous `createText` auto-shrink
13045
+ * loop measures against final font metrics instead of fallback ones.
13046
+ *
13047
+ * Stronger than `ensureFontsForResolvedConfig` (which is fire-and-forget)
13048
+ * — this awaits each `document.fonts.load(spec)` AND `document.fonts.ready`,
13049
+ * racing the whole thing against `maxWaitMs` so a slow CDN can't hang the
13050
+ * renderer.
13051
+ */
13052
+ async awaitFontsForConfig(config, maxWaitMs) {
13053
+ if (typeof document === "undefined" || !document.fonts) return;
13054
+ await ensureFontsForResolvedConfig(config);
13055
+ await this.waitForRelevantFonts(config, maxWaitMs);
13056
+ await Promise.race([
13057
+ document.fonts.ready.catch(() => void 0).then(() => void 0),
13058
+ new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
13059
+ ]);
13060
+ }
13061
+ getNormalizedGradientStops(gradient) {
13062
+ const stops = Array.isArray(gradient == null ? void 0 : gradient.stops) ? gradient.stops.map((stop) => ({
13063
+ offset: Math.max(0, Math.min(1, Number((stop == null ? void 0 : stop.offset) ?? 0))),
13064
+ color: String((stop == null ? void 0 : stop.color) ?? "#ffffff")
13065
+ })).filter((stop) => Number.isFinite(stop.offset)).sort((a, b) => a.offset - b.offset) : [];
13066
+ if (stops.length === 0) return [];
13067
+ const normalized = [...stops];
13068
+ if (normalized[0].offset > 0) {
13069
+ normalized.unshift({ offset: 0, color: normalized[0].color });
13070
+ }
13071
+ if (normalized[normalized.length - 1].offset < 1) {
13072
+ normalized.push({ offset: 1, color: normalized[normalized.length - 1].color });
13073
+ }
13074
+ return normalized;
13075
+ }
13076
+ paintPageBackground(ctx, page, width, height) {
13077
+ var _a, _b;
13078
+ const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
13079
+ const gradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
13080
+ ctx.clearRect(0, 0, width, height);
13081
+ ctx.fillStyle = backgroundColor;
13082
+ ctx.fillRect(0, 0, width, height);
13083
+ const stops = this.getNormalizedGradientStops(gradient);
13084
+ if (stops.length < 2) return;
13085
+ try {
13086
+ let canvasGradient = null;
13087
+ if ((gradient == null ? void 0 : gradient.type) === "radial") {
13088
+ const cx = Number.isFinite(gradient == null ? void 0 : gradient.cx) ? gradient.cx : 0.5;
13089
+ const cy = Number.isFinite(gradient == null ? void 0 : gradient.cy) ? gradient.cy : 0.5;
13090
+ const centerX = width * cx;
13091
+ const centerY = height * cy;
13092
+ const radius = Math.max(
13093
+ Math.hypot(centerX, centerY),
13094
+ Math.hypot(width - centerX, centerY),
13095
+ Math.hypot(centerX, height - centerY),
13096
+ Math.hypot(width - centerX, height - centerY)
13097
+ );
13098
+ canvasGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
13099
+ } else if ((gradient == null ? void 0 : gradient.type) === "conic" && typeof ctx.createConicGradient === "function") {
13100
+ const cx = Number.isFinite(gradient == null ? void 0 : gradient.cx) ? gradient.cx : 0.5;
13101
+ const cy = Number.isFinite(gradient == null ? void 0 : gradient.cy) ? gradient.cy : 0.5;
13102
+ const startAngle = (((gradient == null ? void 0 : gradient.angle) ?? 0) - 90) * Math.PI / 180;
13103
+ canvasGradient = ctx.createConicGradient(startAngle, width * cx, height * cy);
13104
+ } else {
13105
+ const angleDeg = (gradient == null ? void 0 : gradient.angle) ?? 90;
13106
+ const angleRad = angleDeg * Math.PI / 180;
13107
+ const sinA = Math.sin(angleRad);
13108
+ const cosA = Math.cos(angleRad);
13109
+ const midX = width / 2;
13110
+ const midY = height / 2;
13111
+ const corners = [
13112
+ [0, 0],
13113
+ [width, 0],
13114
+ [width, height],
13115
+ [0, height]
13116
+ ];
13117
+ const projections = corners.map(([x, y]) => x * sinA - y * cosA);
13118
+ const minProjection = Math.min(...projections);
13119
+ const maxProjection = Math.max(...projections);
13120
+ canvasGradient = ctx.createLinearGradient(
13121
+ midX + minProjection * sinA,
13122
+ midY - minProjection * cosA,
13123
+ midX + maxProjection * sinA,
13124
+ midY - maxProjection * cosA
13125
+ );
13747
13126
  }
13748
- };
13749
- walk(page.children);
13750
- return count;
13127
+ if (!canvasGradient) return;
13128
+ stops.forEach((stop) => canvasGradient.addColorStop(stop.offset, stop.color));
13129
+ ctx.fillStyle = canvasGradient;
13130
+ ctx.fillRect(0, 0, width, height);
13131
+ } catch {
13132
+ }
13751
13133
  }
13752
- waitForCanvasImages(container, expectedImageCount, maxWaitMs = 15e3, pollMs = 120) {
13753
- return new Promise((resolve) => {
13754
- const start = Date.now();
13755
- let stableFrames = 0;
13756
- let lastSummary = "";
13757
- const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
13758
- const collectRenderableImages = (obj, seen) => {
13759
- if (!obj || typeof obj !== "object") return;
13760
- const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
13761
- for (const candidate of candidates) {
13762
- if (isRenderableImage(candidate)) {
13763
- seen.add(candidate);
13764
- } else if (candidate instanceof HTMLImageElement) {
13765
- return false;
13766
- }
13767
- }
13768
- const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
13769
- for (const child of nested) {
13770
- if (collectRenderableImages(child, seen) === false) return false;
13134
+ async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
13135
+ const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
13136
+ const canvasWidth = config.canvas.width;
13137
+ const canvasHeight = config.canvas.height;
13138
+ const hasAutoShrink = configHasAutoShrinkText(config);
13139
+ let firstMountSettled = false;
13140
+ let lateFontSettleDetected = false;
13141
+ if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
13142
+ document.fonts.ready.then(() => {
13143
+ if (firstMountSettled) lateFontSettleDetected = true;
13144
+ }).catch(() => {
13145
+ });
13146
+ }
13147
+ return new Promise((resolve, reject) => {
13148
+ const container = document.createElement("div");
13149
+ container.style.cssText = `
13150
+ position: fixed; left: -99999px; top: -99999px;
13151
+ width: ${canvasWidth}px; height: ${canvasHeight}px;
13152
+ overflow: hidden; pointer-events: none; opacity: 0;
13153
+ `;
13154
+ document.body.appendChild(container);
13155
+ const timeout = setTimeout(() => {
13156
+ cleanup();
13157
+ reject(new Error("Render timeout (30s)"));
13158
+ }, 3e4);
13159
+ let root;
13160
+ let mountKey = 0;
13161
+ const cleanup = () => {
13162
+ clearTimeout(timeout);
13163
+ try {
13164
+ root.unmount();
13165
+ } catch {
13771
13166
  }
13772
- return true;
13167
+ container.remove();
13773
13168
  };
13774
- const getFabricCanvas = () => {
13775
- const registry2 = window.__fabricCanvasRegistry;
13776
- if (registry2 instanceof Map) {
13777
- for (const entry of registry2.values()) {
13778
- const canvas = entry == null ? void 0 : entry.canvas;
13779
- const el = (canvas == null ? void 0 : canvas.lowerCanvasEl) || (canvas == null ? void 0 : canvas.upperCanvasEl);
13780
- if (el && container.contains(el)) return canvas;
13781
- }
13169
+ const remountWithFreshKey = async () => {
13170
+ mountKey += 1;
13171
+ try {
13172
+ clearMeasurementCache();
13173
+ } catch {
13782
13174
  }
13783
- return null;
13784
- };
13785
- const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
13786
- const getImageDebugInfo = (obj, bucket) => {
13787
- if (!obj || typeof obj !== "object") return;
13788
- const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
13789
- for (const candidate of candidates) {
13790
- if (candidate instanceof HTMLImageElement) {
13791
- bucket.push({
13792
- id: obj.__docuforgeId || obj.id || "unknown",
13793
- src: (candidate.currentSrc || candidate.src || "").slice(0, 240),
13794
- complete: candidate.complete,
13795
- naturalWidth: candidate.naturalWidth,
13796
- naturalHeight: candidate.naturalHeight
13797
- });
13798
- }
13175
+ try {
13176
+ clearFabricCharCache();
13177
+ } catch {
13799
13178
  }
13800
- const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
13801
- for (const child of nested) {
13802
- getImageDebugInfo(child, bucket);
13179
+ try {
13180
+ root.unmount();
13181
+ } catch {
13803
13182
  }
13183
+ root = client.createRoot(container);
13184
+ await new Promise((settle) => {
13185
+ const onReadyOnce = () => {
13186
+ this.waitForCanvasScene(container, config, pageIndex).then(async () => {
13187
+ const fabricInstance = this.getFabricCanvasFromContainer(container);
13188
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
13189
+ await this.waitForCanvasImages(container, expectedImageCount);
13190
+ await this.waitForStableTextMetrics(container, config);
13191
+ await this.waitForCanvasScene(container, config, pageIndex);
13192
+ if (!fabricInstance) return settle();
13193
+ settle();
13194
+ }).catch(() => settle());
13195
+ };
13196
+ root.render(
13197
+ react.createElement(PreviewCanvas2, {
13198
+ key: `remount-${mountKey}`,
13199
+ config,
13200
+ pageIndex,
13201
+ zoom: pixelRatio,
13202
+ absoluteZoom: true,
13203
+ skipFontReadyWait: false,
13204
+ onReady: onReadyOnce
13205
+ })
13206
+ );
13207
+ });
13804
13208
  };
13805
- const check = () => {
13806
- const elapsed = Date.now() - start;
13807
- const domImages = Array.from(container.querySelectorAll("img"));
13808
- const allDomLoaded = domImages.every((img) => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
13809
- const fabricCanvas = getFabricCanvas();
13810
- const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
13811
- const renderableImages = /* @__PURE__ */ new Set();
13812
- const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
13813
- const actualImageCount = Math.max(domImages.length, renderableImages.size);
13814
- const canvasReady = !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
13815
- const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
13816
- const ready = allDomLoaded && fabricReady && hasExpectedAssets;
13817
- const summary = `expected=${expectedImageCount} actual=${actualImageCount} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}`;
13818
- if (summary !== lastSummary) {
13819
- lastSummary = summary;
13820
- console.log(`[canvas-renderer][asset-wait] ${summary}`);
13821
- }
13822
- if (ready) {
13823
- stableFrames += 1;
13824
- if (stableFrames >= 2) {
13825
- console.log(`[canvas-renderer][asset-wait] ready after ${elapsed}ms (${summary})`);
13826
- settle();
13827
- return;
13828
- }
13829
- } else {
13830
- stableFrames = 0;
13831
- }
13832
- if (elapsed >= maxWaitMs) {
13833
- const fabricImageDebug = [];
13834
- for (const obj of fabricObjects) {
13835
- getImageDebugInfo(obj, fabricImageDebug);
13209
+ const onReady = () => {
13210
+ this.waitForCanvasScene(container, config, pageIndex).then(async () => {
13211
+ try {
13212
+ const fabricInstance = this.getFabricCanvasFromContainer(container);
13213
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
13214
+ await this.waitForCanvasImages(container, expectedImageCount);
13215
+ await this.waitForStableTextMetrics(container, config);
13216
+ await this.waitForCanvasScene(container, config, pageIndex);
13217
+ firstMountSettled = true;
13218
+ if (hasAutoShrink && lateFontSettleDetected) {
13219
+ console.log("[canvas-renderer][parity] late font-settle detected remounting for auto-shrink reflow");
13220
+ await remountWithFreshKey();
13221
+ }
13222
+ const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
13223
+ const sourceCanvas = (fabricInstance == null ? void 0 : fabricInstance.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || fabricCanvas;
13224
+ const fabricInstanceAfter = this.getFabricCanvasFromContainer(container) || fabricInstance;
13225
+ const sourceCanvasAfter = (fabricInstanceAfter == null ? void 0 : fabricInstanceAfter.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || sourceCanvas;
13226
+ if (!sourceCanvas) {
13227
+ cleanup();
13228
+ reject(new Error("No canvas element found after render"));
13229
+ return;
13230
+ }
13231
+ const exportCanvas = document.createElement("canvas");
13232
+ exportCanvas.width = sourceCanvasAfter.width;
13233
+ exportCanvas.height = sourceCanvasAfter.height;
13234
+ const exportCtx = exportCanvas.getContext("2d");
13235
+ if (!exportCtx) {
13236
+ cleanup();
13237
+ reject(new Error("Failed to create export canvas"));
13238
+ return;
13239
+ }
13240
+ exportCtx.save();
13241
+ exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
13242
+ this.paintPageBackground(exportCtx, config.pages[pageIndex], canvasWidth, canvasHeight);
13243
+ exportCtx.restore();
13244
+ exportCtx.drawImage(sourceCanvasAfter, 0, 0);
13245
+ const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
13246
+ const dataUrl = exportCanvas.toDataURL(mimeType, quality);
13247
+ cleanup();
13248
+ resolve(dataUrl);
13249
+ } catch (err) {
13250
+ cleanup();
13251
+ reject(err);
13836
13252
  }
13837
- const domImageDebug = domImages.map((img, index) => ({
13838
- index,
13839
- src: (img.currentSrc || img.src || "").slice(0, 240),
13840
- complete: img.complete,
13841
- naturalWidth: img.naturalWidth,
13842
- naturalHeight: img.naturalHeight
13843
- }));
13844
- console.warn(`[canvas-renderer][asset-wait-timeout] elapsed=${elapsed}ms ${summary}`);
13845
- console.warn("[canvas-renderer][asset-wait-timeout][dom-images]", domImageDebug);
13846
- console.warn("[canvas-renderer][asset-wait-timeout][fabric-images]", fabricImageDebug);
13847
- settle();
13848
- return;
13849
- }
13850
- setTimeout(check, pollMs);
13253
+ });
13851
13254
  };
13852
- setTimeout(check, 0);
13255
+ root = client.createRoot(container);
13256
+ root.render(
13257
+ react.createElement(PreviewCanvas2, {
13258
+ config,
13259
+ pageIndex,
13260
+ zoom: pixelRatio,
13261
+ absoluteZoom: true,
13262
+ skipFontReadyWait: false,
13263
+ onReady
13264
+ })
13265
+ );
13853
13266
  });
13854
13267
  }
13855
- waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
13856
- return new Promise((resolve) => {
13857
- var _a, _b;
13858
- const start = Date.now();
13859
- const pageHasContent = (((_b = (_a = config.pages[pageIndex]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) ?? 0) > 0;
13860
- const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
13861
- const check = () => {
13862
- const fabricCanvas = this.getFabricCanvasFromContainer(container);
13863
- const lowerCanvas = (fabricCanvas == null ? void 0 : fabricCanvas.lowerCanvasEl) || container.querySelector("canvas.lower-canvas, canvas");
13864
- const objectCount = typeof (fabricCanvas == null ? void 0 : fabricCanvas.getObjects) === "function" ? fabricCanvas.getObjects().length : 0;
13865
- const ready = !!lowerCanvas && (!pageHasContent || objectCount > 0);
13866
- if (ready) {
13867
- console.log(`[canvas-renderer][scene-wait] ready after ${Date.now() - start}ms (objects=${objectCount})`);
13868
- settle();
13869
- return;
13870
- }
13871
- if (Date.now() - start >= maxWaitMs) {
13872
- console.warn(`[canvas-renderer][scene-wait-timeout] elapsed=${Date.now() - start}ms objects=${objectCount} pageHasContent=${pageHasContent}`);
13873
- settle();
13874
- return;
13268
+ // ─── Internal: capture SVG from a rendered Fabric canvas ───
13269
+ //
13270
+ // APPROACH: Use the SAME PreviewCanvas that renders perfect PNGs, then call
13271
+ // Fabric's toSVG() on that canvas. This guarantees 100% layout parity —
13272
+ // the SVG is a vector snapshot of exactly what's on screen.
13273
+ //
13274
+ // The trick: before calling toSVG(), we temporarily neutralize the viewport
13275
+ // transform and retina scaling so Fabric emits coordinates in logical
13276
+ // document space (e.g. 612x792) instead of inflated pixel space.
13277
+ captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
13278
+ return new Promise(async (resolve, reject) => {
13279
+ const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
13280
+ const container = document.createElement("div");
13281
+ container.style.cssText = `
13282
+ position: fixed; left: -99999px; top: -99999px;
13283
+ width: ${canvasWidth}px; height: ${canvasHeight}px;
13284
+ overflow: hidden; pointer-events: none; opacity: 0;
13285
+ `;
13286
+ document.body.appendChild(container);
13287
+ const timeout = setTimeout(() => {
13288
+ cleanup();
13289
+ reject(new Error("SVG render timeout (30s)"));
13290
+ }, 3e4);
13291
+ let root = null;
13292
+ const mountKey = 0;
13293
+ const cleanup = () => {
13294
+ clearTimeout(timeout);
13295
+ try {
13296
+ root == null ? void 0 : root.unmount();
13297
+ } catch {
13875
13298
  }
13876
- setTimeout(check, pollMs);
13299
+ container.remove();
13877
13300
  };
13878
- setTimeout(check, 0);
13301
+ const mountPreview = (readyHandler) => {
13302
+ root = client.createRoot(container);
13303
+ root.render(
13304
+ react.createElement(PreviewCanvas2, {
13305
+ key: `svg-capture-${mountKey}`,
13306
+ config,
13307
+ pageIndex,
13308
+ zoom: 1,
13309
+ absoluteZoom: true,
13310
+ skipFontReadyWait: false,
13311
+ onReady: readyHandler
13312
+ })
13313
+ );
13314
+ };
13315
+ const onReady = () => {
13316
+ this.waitForCanvasScene(container, config, pageIndex).then(async () => {
13317
+ var _a, _b;
13318
+ try {
13319
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
13320
+ await this.waitForCanvasImages(container, expectedImageCount);
13321
+ await this.waitForStableTextMetrics(container, config);
13322
+ await this.waitForCanvasScene(container, config, pageIndex);
13323
+ const fabricInstance = this.getFabricCanvasFromContainer(container);
13324
+ if (!fabricInstance) {
13325
+ cleanup();
13326
+ reject(new Error("No Fabric canvas instance found for SVG capture"));
13327
+ return;
13328
+ }
13329
+ const prevVPT = fabricInstance.viewportTransform ? [...fabricInstance.viewportTransform] : void 0;
13330
+ const prevSvgVPT = fabricInstance.svgViewportTransformation;
13331
+ const prevRetina = fabricInstance.enableRetinaScaling;
13332
+ const prevWidth = fabricInstance.width;
13333
+ const prevHeight = fabricInstance.height;
13334
+ fabricInstance.viewportTransform = [1, 0, 0, 1, 0, 0];
13335
+ fabricInstance.svgViewportTransformation = false;
13336
+ fabricInstance.enableRetinaScaling = false;
13337
+ fabricInstance.setDimensions(
13338
+ { width: canvasWidth, height: canvasHeight },
13339
+ { cssOnly: false, backstoreOnly: false }
13340
+ );
13341
+ const rawSvgString = fabricInstance.toSVG();
13342
+ const svgString = this.normalizeSvgDimensions(
13343
+ rawSvgString,
13344
+ canvasWidth,
13345
+ canvasHeight
13346
+ );
13347
+ fabricInstance.enableRetinaScaling = prevRetina;
13348
+ fabricInstance.setDimensions(
13349
+ { width: prevWidth, height: prevHeight },
13350
+ { cssOnly: false, backstoreOnly: false }
13351
+ );
13352
+ if (prevVPT) fabricInstance.viewportTransform = prevVPT;
13353
+ fabricInstance.svgViewportTransformation = prevSvgVPT;
13354
+ const page = config.pages[pageIndex];
13355
+ const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
13356
+ const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
13357
+ cleanup();
13358
+ resolve({
13359
+ svg: svgString,
13360
+ width: canvasWidth,
13361
+ height: canvasHeight,
13362
+ backgroundColor,
13363
+ backgroundGradient
13364
+ });
13365
+ } catch (err) {
13366
+ cleanup();
13367
+ reject(err);
13368
+ }
13369
+ });
13370
+ };
13371
+ mountPreview(onReady);
13879
13372
  });
13880
13373
  }
13881
- async waitForRelevantFonts(config, maxWaitMs = 1800) {
13882
- if (typeof document === "undefined" || !document.fonts) return;
13883
- const descriptors = collectFontDescriptorsFromConfig(config);
13884
- if (descriptors.length === 0) return;
13885
- const loads = Promise.all(
13886
- descriptors.map((descriptor) => {
13887
- const stylePrefix = descriptor.style === "italic" ? "italic " : "";
13888
- const spec = `${stylePrefix}${descriptor.weight} 16px "${descriptor.family}"`;
13889
- return document.fonts.load(spec).catch(() => []);
13890
- })
13891
- ).then(() => void 0);
13892
- await Promise.race([
13893
- loads,
13894
- new Promise((resolve) => setTimeout(resolve, maxWaitMs))
13895
- ]);
13896
- await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
13897
- }
13898
13374
  /**
13899
- * Block until the webfonts referenced by `config` have actually loaded
13900
- * (or `maxWaitMs` elapses). Used by the headless capture path BEFORE
13901
- * mounting `PreviewCanvas`, so the synchronous `createText` auto-shrink
13902
- * loop measures against final font metrics instead of fallback ones.
13903
- *
13904
- * Stronger than `ensureFontsForResolvedConfig` (which is fire-and-forget)
13905
- * — this awaits each `document.fonts.load(spec)` AND `document.fonts.ready`,
13906
- * racing the whole thing against `maxWaitMs` so a slow CDN can't hang the
13907
- * renderer.
13375
+ * Normalize the SVG's width/height/viewBox to match the logical page dimensions.
13376
+ * Fabric's toSVG() may output dimensions scaled by the canvas element's actual
13377
+ * pixel size (e.g., 2x due to devicePixelRatio), causing svg2pdf to render
13378
+ * content at the wrong scale. This rewrites the root <svg> attributes to ensure
13379
+ * the SVG coordinate system matches the intended page size exactly.
13908
13380
  */
13909
- async awaitFontsForConfig(config, maxWaitMs) {
13910
- if (typeof document === "undefined" || !document.fonts) return;
13911
- void ensureFontsForResolvedConfig(config);
13912
- await this.waitForRelevantFonts(config, maxWaitMs);
13913
- await Promise.race([
13914
- document.fonts.ready.catch(() => void 0).then(() => void 0),
13915
- new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
13916
- ]);
13381
+ normalizeSvgDimensions(svg, targetWidth, targetHeight) {
13382
+ const widthMatch = svg.match(/<svg[^>]*\bwidth="([^"]+)"/i);
13383
+ const heightMatch = svg.match(/<svg[^>]*\bheight="([^"]+)"/i);
13384
+ const svgWidth = widthMatch ? parseFloat(widthMatch[1]) : targetWidth;
13385
+ const svgHeight = heightMatch ? parseFloat(heightMatch[1]) : targetHeight;
13386
+ console.log(
13387
+ `[canvas-renderer][svg-normalize] root ${svgWidth}x${svgHeight} page ${targetWidth}x${targetHeight}`
13388
+ );
13389
+ let normalized = svg;
13390
+ if (/\bwidth="[^"]*"/i.test(normalized)) {
13391
+ normalized = normalized.replace(/(<svg[^>]*\b)width="[^"]*"/i, `$1width="${targetWidth}"`);
13392
+ } else {
13393
+ normalized = normalized.replace(/<svg\b/i, `<svg width="${targetWidth}"`);
13394
+ }
13395
+ if (/\bheight="[^"]*"/i.test(normalized)) {
13396
+ normalized = normalized.replace(/(<svg[^>]*\b)height="[^"]*"/i, `$1height="${targetHeight}"`);
13397
+ } else {
13398
+ normalized = normalized.replace(/<svg\b/i, `<svg height="${targetHeight}"`);
13399
+ }
13400
+ const viewBox = `0 0 ${targetWidth} ${targetHeight}`;
13401
+ if (/\bviewBox="[^"]*"/i.test(normalized)) {
13402
+ normalized = normalized.replace(/viewBox="[^"]*"/i, `viewBox="${viewBox}"`);
13403
+ } else {
13404
+ normalized = normalized.replace(/<svg\b/i, `<svg viewBox="${viewBox}"`);
13405
+ }
13406
+ normalized = normalized.replace(/="undefined"/g, '="0"');
13407
+ normalized = normalized.replace(/="NaN"/g, '="0"');
13408
+ if (/\bx="[^"]*"/i.test(normalized)) {
13409
+ normalized = normalized.replace(/(<svg[^>]*\b)x="[^"]*"/i, '$1x="0"');
13410
+ } else {
13411
+ normalized = normalized.replace(/<svg\b/i, '<svg x="0"');
13412
+ }
13413
+ if (/\by="[^"]*"/i.test(normalized)) {
13414
+ normalized = normalized.replace(/(<svg[^>]*\b)y="[^"]*"/i, '$1y="0"');
13415
+ } else {
13416
+ normalized = normalized.replace(/<svg\b/i, '<svg y="0"');
13417
+ }
13418
+ normalized = normalized.replace(/\bpreserveAspectRatio="[^"]*"/i, 'preserveAspectRatio="none"');
13419
+ if (!/\bpreserveAspectRatio="[^"]*"/i.test(normalized)) {
13420
+ normalized = normalized.replace(/<svg\b/i, '<svg preserveAspectRatio="none"');
13421
+ }
13422
+ return normalized;
13917
13423
  }
13918
- getNormalizedGradientStops(gradient) {
13919
- const stops = Array.isArray(gradient == null ? void 0 : gradient.stops) ? gradient.stops.map((stop) => ({
13920
- offset: Math.max(0, Math.min(1, Number((stop == null ? void 0 : stop.offset) ?? 0))),
13921
- color: String((stop == null ? void 0 : stop.color) ?? "#ffffff")
13922
- })).filter((stop) => Number.isFinite(stop.offset)).sort((a, b) => a.offset - b.offset) : [];
13923
- if (stops.length === 0) return [];
13924
- const normalized = [...stops];
13925
- if (normalized[0].offset > 0) {
13926
- normalized.unshift({ offset: 0, color: normalized[0].color });
13424
+ /**
13425
+ * Find the Fabric.Canvas instance that belongs to a given container element,
13426
+ * using the global __fabricCanvasRegistry (set by PageCanvas).
13427
+ */
13428
+ getFabricCanvasFromContainer(container) {
13429
+ const registry2 = window.__fabricCanvasRegistry;
13430
+ if (registry2 instanceof Map) {
13431
+ for (const entry of registry2.values()) {
13432
+ const canvas = (entry == null ? void 0 : entry.canvas) || entry;
13433
+ if (!canvas || typeof canvas.toSVG !== "function") continue;
13434
+ const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
13435
+ if (el && container.contains(el)) return canvas;
13436
+ }
13927
13437
  }
13928
- if (normalized[normalized.length - 1].offset < 1) {
13929
- normalized.push({ offset: 1, color: normalized[normalized.length - 1].color });
13438
+ return null;
13439
+ }
13440
+ async waitForStableTextMetrics(container, config) {
13441
+ var _a, _b, _c;
13442
+ if (typeof document !== "undefined") {
13443
+ void ensureFontsForResolvedConfig(config);
13444
+ await this.waitForRelevantFonts(config);
13930
13445
  }
13931
- return normalized;
13446
+ const fabricInstance = this.getFabricCanvasFromContainer(container);
13447
+ if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
13448
+ const waitForPaint = () => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r())));
13449
+ const primeCharBounds = (obj) => {
13450
+ if (obj instanceof fabric__namespace.Textbox) {
13451
+ const lines = obj._textLines;
13452
+ if (Array.isArray(lines)) {
13453
+ for (let i = 0; i < lines.length; i++) {
13454
+ try {
13455
+ obj.getLineWidth(i);
13456
+ } catch {
13457
+ }
13458
+ }
13459
+ }
13460
+ obj.dirty = true;
13461
+ return;
13462
+ }
13463
+ if (obj instanceof fabric__namespace.Group) {
13464
+ obj.getObjects().forEach(primeCharBounds);
13465
+ obj.dirty = true;
13466
+ }
13467
+ };
13468
+ fabricInstance.getObjects().forEach(primeCharBounds);
13469
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
13470
+ (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
13471
+ await waitForPaint();
13472
+ (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
13473
+ await waitForPaint();
13474
+ }
13475
+ }
13476
+ const FONT_WEIGHT_LABELS = {
13477
+ 300: "Light",
13478
+ 400: "Regular",
13479
+ 500: "Medium",
13480
+ 600: "SemiBold",
13481
+ 700: "Bold"
13482
+ };
13483
+ function resolveFontWeight(weight) {
13484
+ if (weight <= 350) return 300;
13485
+ if (weight <= 450) return 400;
13486
+ if (weight <= 550) return 500;
13487
+ if (weight <= 650) return 600;
13488
+ return 700;
13489
+ }
13490
+ const FONT_FALLBACK_SYMBOLS = "Noto Sans";
13491
+ const FONT_FALLBACK_DEVANAGARI = "Hind";
13492
+ const FONT_FILES = {
13493
+ "Playfair Display": {
13494
+ regular: "PlayfairDisplay-Regular.ttf",
13495
+ bold: "PlayfairDisplay-Bold.ttf",
13496
+ italic: "PlayfairDisplay-Italic.ttf",
13497
+ boldItalic: "PlayfairDisplay-BoldItalic.ttf"
13498
+ },
13499
+ "Merriweather": {
13500
+ regular: "Merriweather-Regular.ttf",
13501
+ bold: "Merriweather-Bold.ttf",
13502
+ light: "Merriweather-Light.ttf",
13503
+ italic: "Merriweather-Italic.ttf",
13504
+ boldItalic: "Merriweather-BoldItalic.ttf",
13505
+ lightItalic: "Merriweather-LightItalic.ttf"
13506
+ },
13507
+ "Lora": {
13508
+ regular: "Lora-Regular.ttf",
13509
+ bold: "Lora-Bold.ttf",
13510
+ italic: "Lora-Italic.ttf",
13511
+ boldItalic: "Lora-BoldItalic.ttf"
13512
+ },
13513
+ "Montserrat": {
13514
+ regular: "Montserrat-Regular.ttf",
13515
+ bold: "Montserrat-Bold.ttf",
13516
+ light: "Montserrat-Light.ttf",
13517
+ medium: "Montserrat-Medium.ttf",
13518
+ semibold: "Montserrat-SemiBold.ttf",
13519
+ italic: "Montserrat-Italic.ttf",
13520
+ boldItalic: "Montserrat-BoldItalic.ttf",
13521
+ lightItalic: "Montserrat-LightItalic.ttf",
13522
+ mediumItalic: "Montserrat-MediumItalic.ttf",
13523
+ semiboldItalic: "Montserrat-SemiBoldItalic.ttf"
13524
+ },
13525
+ "Open Sans": {
13526
+ regular: "OpenSans-Regular.ttf",
13527
+ bold: "OpenSans-Bold.ttf",
13528
+ light: "OpenSans-Light.ttf",
13529
+ semibold: "OpenSans-SemiBold.ttf",
13530
+ italic: "OpenSans-Italic.ttf",
13531
+ boldItalic: "OpenSans-BoldItalic.ttf",
13532
+ lightItalic: "OpenSans-LightItalic.ttf",
13533
+ semiboldItalic: "OpenSans-SemiBoldItalic.ttf"
13534
+ },
13535
+ "Roboto": {
13536
+ regular: "Roboto-Regular.ttf",
13537
+ bold: "Roboto-Bold.ttf",
13538
+ light: "Roboto-Light.ttf",
13539
+ medium: "Roboto-Medium.ttf",
13540
+ italic: "Roboto-Italic.ttf",
13541
+ boldItalic: "Roboto-BoldItalic.ttf",
13542
+ lightItalic: "Roboto-LightItalic.ttf",
13543
+ mediumItalic: "Roboto-MediumItalic.ttf"
13544
+ },
13545
+ "Lato": {
13546
+ regular: "Lato-Regular.ttf",
13547
+ bold: "Lato-Bold.ttf",
13548
+ light: "Lato-Light.ttf",
13549
+ italic: "Lato-Italic.ttf",
13550
+ boldItalic: "Lato-BoldItalic.ttf",
13551
+ lightItalic: "Lato-LightItalic.ttf"
13552
+ },
13553
+ "Raleway": {
13554
+ regular: "Raleway-Regular.ttf",
13555
+ bold: "Raleway-Bold.ttf",
13556
+ light: "Raleway-Light.ttf",
13557
+ medium: "Raleway-Medium.ttf",
13558
+ semibold: "Raleway-SemiBold.ttf",
13559
+ italic: "Raleway-Italic.ttf",
13560
+ boldItalic: "Raleway-BoldItalic.ttf",
13561
+ lightItalic: "Raleway-LightItalic.ttf"
13562
+ },
13563
+ "Poppins": {
13564
+ regular: "Poppins-Regular.ttf",
13565
+ bold: "Poppins-Bold.ttf",
13566
+ light: "Poppins-Light.ttf",
13567
+ medium: "Poppins-Medium.ttf",
13568
+ semibold: "Poppins-SemiBold.ttf",
13569
+ italic: "Poppins-Italic.ttf",
13570
+ boldItalic: "Poppins-BoldItalic.ttf",
13571
+ lightItalic: "Poppins-LightItalic.ttf",
13572
+ mediumItalic: "Poppins-MediumItalic.ttf",
13573
+ semiboldItalic: "Poppins-SemiBoldItalic.ttf"
13574
+ },
13575
+ "Inter": {
13576
+ regular: "Inter-Regular.ttf",
13577
+ bold: "Inter-Bold.ttf",
13578
+ italic: "Inter-Italic.ttf",
13579
+ boldItalic: "Inter-BoldItalic.ttf"
13580
+ },
13581
+ "Nunito": {
13582
+ regular: "Nunito-Regular.ttf",
13583
+ bold: "Nunito-Bold.ttf",
13584
+ light: "Nunito-Light.ttf",
13585
+ medium: "Nunito-Medium.ttf",
13586
+ semibold: "Nunito-SemiBold.ttf",
13587
+ italic: "Nunito-Italic.ttf",
13588
+ boldItalic: "Nunito-BoldItalic.ttf"
13589
+ },
13590
+ "Source Sans Pro": {
13591
+ regular: "SourceSansPro-Regular.ttf",
13592
+ bold: "SourceSansPro-Bold.ttf",
13593
+ light: "SourceSansPro-Light.ttf",
13594
+ italic: "SourceSansPro-Italic.ttf",
13595
+ boldItalic: "SourceSansPro-BoldItalic.ttf"
13596
+ },
13597
+ "Work Sans": {
13598
+ regular: "WorkSans-Regular.ttf",
13599
+ bold: "WorkSans-Bold.ttf",
13600
+ italic: "WorkSans-Italic.ttf",
13601
+ boldItalic: "WorkSans-BoldItalic.ttf"
13602
+ },
13603
+ "Oswald": { regular: "Oswald-Regular.ttf", bold: "Oswald-Bold.ttf" },
13604
+ "Bebas Neue": { regular: "BebasNeue-Regular.ttf" },
13605
+ "Abril Fatface": { regular: "AbrilFatface-Regular.ttf" },
13606
+ "Dancing Script": { regular: "DancingScript-Regular.ttf", bold: "DancingScript-Bold.ttf" },
13607
+ "Pacifico": { regular: "Pacifico-Regular.ttf" },
13608
+ "Great Vibes": { regular: "GreatVibes-Regular.ttf" },
13609
+ "DM Sans": {
13610
+ regular: "DMSans-Regular.ttf",
13611
+ bold: "DMSans-Bold.ttf",
13612
+ light: "DMSans-Light.ttf",
13613
+ medium: "DMSans-Medium.ttf",
13614
+ semibold: "DMSans-SemiBold.ttf",
13615
+ italic: "DMSans-RegularItalic.ttf",
13616
+ boldItalic: "DMSans-BoldItalic.ttf",
13617
+ lightItalic: "DMSans-LightItalic.ttf",
13618
+ mediumItalic: "DMSans-MediumItalic.ttf",
13619
+ semiboldItalic: "DMSans-SemiBoldItalic.ttf"
13620
+ },
13621
+ "Outfit": {
13622
+ regular: "Outfit-Regular.ttf",
13623
+ bold: "Outfit-Bold.ttf",
13624
+ light: "Outfit-Light.ttf",
13625
+ medium: "Outfit-Medium.ttf",
13626
+ semibold: "Outfit-SemiBold.ttf"
13627
+ },
13628
+ "Figtree": {
13629
+ regular: "Figtree-Regular.ttf",
13630
+ bold: "Figtree-Bold.ttf",
13631
+ light: "Figtree-Light.ttf",
13632
+ medium: "Figtree-Medium.ttf",
13633
+ semibold: "Figtree-SemiBold.ttf",
13634
+ italic: "Figtree-RegularItalic.ttf",
13635
+ boldItalic: "Figtree-BoldItalic.ttf",
13636
+ lightItalic: "Figtree-LightItalic.ttf",
13637
+ mediumItalic: "Figtree-MediumItalic.ttf",
13638
+ semiboldItalic: "Figtree-SemiBoldItalic.ttf"
13639
+ },
13640
+ "Manrope": {
13641
+ regular: "Manrope-Regular.ttf",
13642
+ bold: "Manrope-Bold.ttf",
13643
+ light: "Manrope-Light.ttf",
13644
+ medium: "Manrope-Medium.ttf",
13645
+ semibold: "Manrope-SemiBold.ttf"
13646
+ },
13647
+ "Space Grotesk": {
13648
+ regular: "SpaceGrotesk-Regular.ttf",
13649
+ bold: "SpaceGrotesk-Bold.ttf",
13650
+ light: "SpaceGrotesk-Light.ttf",
13651
+ medium: "SpaceGrotesk-Medium.ttf",
13652
+ semibold: "SpaceGrotesk-SemiBold.ttf"
13653
+ },
13654
+ "League Spartan": {
13655
+ regular: "LeagueSpartan-Regular.ttf",
13656
+ bold: "LeagueSpartan-Bold.ttf",
13657
+ light: "LeagueSpartan-Light.ttf",
13658
+ medium: "LeagueSpartan-Medium.ttf",
13659
+ semibold: "LeagueSpartan-SemiBold.ttf"
13660
+ },
13661
+ "EB Garamond": {
13662
+ regular: "EBGaramond-Regular.ttf",
13663
+ bold: "EBGaramond-Bold.ttf",
13664
+ medium: "EBGaramond-Medium.ttf",
13665
+ semibold: "EBGaramond-SemiBold.ttf",
13666
+ italic: "EBGaramond-RegularItalic.ttf",
13667
+ boldItalic: "EBGaramond-BoldItalic.ttf",
13668
+ mediumItalic: "EBGaramond-MediumItalic.ttf",
13669
+ semiboldItalic: "EBGaramond-SemiBoldItalic.ttf"
13670
+ },
13671
+ "Libre Baskerville": {
13672
+ regular: "LibreBaskerville-Regular.ttf",
13673
+ bold: "LibreBaskerville-Bold.ttf",
13674
+ italic: "LibreBaskerville-RegularItalic.ttf",
13675
+ boldItalic: "LibreBaskerville-BoldItalic.ttf"
13676
+ },
13677
+ "Crimson Text": {
13678
+ regular: "CrimsonText-Regular.ttf",
13679
+ bold: "CrimsonText-Bold.ttf",
13680
+ italic: "CrimsonText-Italic.ttf",
13681
+ boldItalic: "CrimsonText-BoldItalic.ttf"
13682
+ },
13683
+ "DM Serif Display": {
13684
+ regular: "DMSerifDisplay-Regular.ttf",
13685
+ italic: "DMSerifDisplay-Italic.ttf"
13686
+ },
13687
+ "Libre Franklin": {
13688
+ regular: "LibreFranklin-Regular.ttf",
13689
+ bold: "LibreFranklin-Bold.ttf",
13690
+ light: "LibreFranklin-Light.ttf",
13691
+ medium: "LibreFranklin-Medium.ttf",
13692
+ semibold: "LibreFranklin-SemiBold.ttf",
13693
+ italic: "LibreFranklin-RegularItalic.ttf",
13694
+ boldItalic: "LibreFranklin-BoldItalic.ttf",
13695
+ lightItalic: "LibreFranklin-LightItalic.ttf",
13696
+ mediumItalic: "LibreFranklin-MediumItalic.ttf",
13697
+ semiboldItalic: "LibreFranklin-SemiBoldItalic.ttf"
13698
+ },
13699
+ "Mulish": {
13700
+ regular: "Mulish-Regular.ttf",
13701
+ bold: "Mulish-Bold.ttf",
13702
+ light: "Mulish-Light.ttf",
13703
+ medium: "Mulish-Medium.ttf",
13704
+ semibold: "Mulish-SemiBold.ttf",
13705
+ italic: "Mulish-RegularItalic.ttf",
13706
+ boldItalic: "Mulish-BoldItalic.ttf",
13707
+ lightItalic: "Mulish-LightItalic.ttf",
13708
+ mediumItalic: "Mulish-MediumItalic.ttf",
13709
+ semiboldItalic: "Mulish-SemiBoldItalic.ttf"
13710
+ },
13711
+ "Quicksand": {
13712
+ regular: "Quicksand-Regular.ttf",
13713
+ bold: "Quicksand-Bold.ttf",
13714
+ light: "Quicksand-Light.ttf",
13715
+ medium: "Quicksand-Medium.ttf",
13716
+ semibold: "Quicksand-SemiBold.ttf"
13717
+ },
13718
+ "Rubik": {
13719
+ regular: "Rubik-Regular.ttf",
13720
+ bold: "Rubik-Bold.ttf",
13721
+ light: "Rubik-Light.ttf",
13722
+ medium: "Rubik-Medium.ttf",
13723
+ semibold: "Rubik-SemiBold.ttf",
13724
+ italic: "Rubik-RegularItalic.ttf",
13725
+ boldItalic: "Rubik-BoldItalic.ttf",
13726
+ lightItalic: "Rubik-LightItalic.ttf",
13727
+ mediumItalic: "Rubik-MediumItalic.ttf",
13728
+ semiboldItalic: "Rubik-SemiBoldItalic.ttf"
13729
+ },
13730
+ "Karla": {
13731
+ regular: "Karla-Regular.ttf",
13732
+ bold: "Karla-Bold.ttf",
13733
+ light: "Karla-Light.ttf",
13734
+ medium: "Karla-Medium.ttf",
13735
+ semibold: "Karla-SemiBold.ttf",
13736
+ italic: "Karla-RegularItalic.ttf",
13737
+ boldItalic: "Karla-BoldItalic.ttf",
13738
+ lightItalic: "Karla-LightItalic.ttf",
13739
+ mediumItalic: "Karla-MediumItalic.ttf",
13740
+ semiboldItalic: "Karla-SemiBoldItalic.ttf"
13741
+ },
13742
+ "Plus Jakarta Sans": {
13743
+ regular: "PlusJakartaSans-Regular.ttf",
13744
+ bold: "PlusJakartaSans-Bold.ttf",
13745
+ light: "PlusJakartaSans-Light.ttf",
13746
+ medium: "PlusJakartaSans-Medium.ttf",
13747
+ semibold: "PlusJakartaSans-SemiBold.ttf",
13748
+ italic: "PlusJakartaSans-RegularItalic.ttf",
13749
+ boldItalic: "PlusJakartaSans-BoldItalic.ttf",
13750
+ lightItalic: "PlusJakartaSans-LightItalic.ttf",
13751
+ mediumItalic: "PlusJakartaSans-MediumItalic.ttf",
13752
+ semiboldItalic: "PlusJakartaSans-SemiBoldItalic.ttf"
13753
+ },
13754
+ "Sora": {
13755
+ regular: "Sora-Regular.ttf",
13756
+ bold: "Sora-Bold.ttf",
13757
+ light: "Sora-Light.ttf",
13758
+ medium: "Sora-Medium.ttf",
13759
+ semibold: "Sora-SemiBold.ttf"
13760
+ },
13761
+ "Urbanist": {
13762
+ regular: "Urbanist-Regular.ttf",
13763
+ bold: "Urbanist-Bold.ttf",
13764
+ light: "Urbanist-Light.ttf",
13765
+ medium: "Urbanist-Medium.ttf",
13766
+ semibold: "Urbanist-SemiBold.ttf",
13767
+ italic: "Urbanist-RegularItalic.ttf",
13768
+ boldItalic: "Urbanist-BoldItalic.ttf",
13769
+ lightItalic: "Urbanist-LightItalic.ttf",
13770
+ mediumItalic: "Urbanist-MediumItalic.ttf",
13771
+ semiboldItalic: "Urbanist-SemiBoldItalic.ttf"
13772
+ },
13773
+ "Lexend": {
13774
+ regular: "Lexend-Regular.ttf",
13775
+ bold: "Lexend-Bold.ttf",
13776
+ light: "Lexend-Light.ttf",
13777
+ medium: "Lexend-Medium.ttf",
13778
+ semibold: "Lexend-SemiBold.ttf"
13779
+ },
13780
+ "Albert Sans": {
13781
+ regular: "AlbertSans-Regular.ttf",
13782
+ bold: "AlbertSans-Bold.ttf",
13783
+ light: "AlbertSans-Light.ttf",
13784
+ medium: "AlbertSans-Medium.ttf",
13785
+ semibold: "AlbertSans-SemiBold.ttf",
13786
+ italic: "AlbertSans-RegularItalic.ttf",
13787
+ boldItalic: "AlbertSans-BoldItalic.ttf",
13788
+ lightItalic: "AlbertSans-LightItalic.ttf",
13789
+ mediumItalic: "AlbertSans-MediumItalic.ttf",
13790
+ semiboldItalic: "AlbertSans-SemiBoldItalic.ttf"
13791
+ },
13792
+ "Noto Sans": {
13793
+ regular: "NotoSans-Regular.ttf",
13794
+ bold: "NotoSans-Bold.ttf",
13795
+ light: "NotoSans-Light.ttf",
13796
+ medium: "NotoSans-Medium.ttf",
13797
+ semibold: "NotoSans-SemiBold.ttf",
13798
+ italic: "NotoSans-RegularItalic.ttf",
13799
+ boldItalic: "NotoSans-BoldItalic.ttf",
13800
+ lightItalic: "NotoSans-LightItalic.ttf",
13801
+ mediumItalic: "NotoSans-MediumItalic.ttf",
13802
+ semiboldItalic: "NotoSans-SemiBoldItalic.ttf"
13803
+ },
13804
+ "Cabin": {
13805
+ regular: "Cabin-Regular.ttf",
13806
+ bold: "Cabin-Bold.ttf",
13807
+ medium: "Cabin-Medium.ttf",
13808
+ semibold: "Cabin-SemiBold.ttf",
13809
+ italic: "Cabin-RegularItalic.ttf",
13810
+ boldItalic: "Cabin-BoldItalic.ttf",
13811
+ mediumItalic: "Cabin-MediumItalic.ttf",
13812
+ semiboldItalic: "Cabin-SemiBoldItalic.ttf"
13813
+ },
13814
+ "Barlow": {
13815
+ regular: "Barlow-Regular.ttf",
13816
+ bold: "Barlow-Bold.ttf",
13817
+ light: "Barlow-Light.ttf",
13818
+ medium: "Barlow-Medium.ttf",
13819
+ semibold: "Barlow-SemiBold.ttf",
13820
+ italic: "Barlow-Italic.ttf",
13821
+ boldItalic: "Barlow-BoldItalic.ttf"
13822
+ },
13823
+ "Josefin Sans": {
13824
+ regular: "JosefinSans-Regular.ttf",
13825
+ bold: "JosefinSans-Bold.ttf",
13826
+ light: "JosefinSans-Light.ttf",
13827
+ medium: "JosefinSans-Medium.ttf",
13828
+ semibold: "JosefinSans-SemiBold.ttf",
13829
+ italic: "JosefinSans-RegularItalic.ttf",
13830
+ boldItalic: "JosefinSans-BoldItalic.ttf",
13831
+ lightItalic: "JosefinSans-LightItalic.ttf",
13832
+ mediumItalic: "JosefinSans-MediumItalic.ttf",
13833
+ semiboldItalic: "JosefinSans-SemiBoldItalic.ttf"
13834
+ },
13835
+ "Archivo": {
13836
+ regular: "Archivo-Regular.ttf",
13837
+ bold: "Archivo-Bold.ttf",
13838
+ light: "Archivo-Light.ttf",
13839
+ medium: "Archivo-Medium.ttf",
13840
+ semibold: "Archivo-SemiBold.ttf",
13841
+ italic: "Archivo-RegularItalic.ttf",
13842
+ boldItalic: "Archivo-BoldItalic.ttf",
13843
+ lightItalic: "Archivo-LightItalic.ttf",
13844
+ mediumItalic: "Archivo-MediumItalic.ttf",
13845
+ semiboldItalic: "Archivo-SemiBoldItalic.ttf"
13846
+ },
13847
+ "Overpass": {
13848
+ regular: "Overpass-Regular.ttf",
13849
+ bold: "Overpass-Bold.ttf",
13850
+ light: "Overpass-Light.ttf",
13851
+ medium: "Overpass-Medium.ttf",
13852
+ semibold: "Overpass-SemiBold.ttf",
13853
+ italic: "Overpass-RegularItalic.ttf",
13854
+ boldItalic: "Overpass-BoldItalic.ttf",
13855
+ lightItalic: "Overpass-LightItalic.ttf",
13856
+ mediumItalic: "Overpass-MediumItalic.ttf",
13857
+ semiboldItalic: "Overpass-SemiBoldItalic.ttf"
13858
+ },
13859
+ "Exo 2": {
13860
+ regular: "Exo2-Regular.ttf",
13861
+ bold: "Exo2-Bold.ttf",
13862
+ light: "Exo2-Light.ttf",
13863
+ medium: "Exo2-Medium.ttf",
13864
+ semibold: "Exo2-SemiBold.ttf",
13865
+ italic: "Exo2-RegularItalic.ttf",
13866
+ boldItalic: "Exo2-BoldItalic.ttf",
13867
+ lightItalic: "Exo2-LightItalic.ttf",
13868
+ mediumItalic: "Exo2-MediumItalic.ttf",
13869
+ semiboldItalic: "Exo2-SemiBoldItalic.ttf"
13870
+ },
13871
+ "Roboto Mono": {
13872
+ regular: "RobotoMono-Regular.ttf",
13873
+ bold: "RobotoMono-Bold.ttf",
13874
+ light: "RobotoMono-Light.ttf",
13875
+ medium: "RobotoMono-Medium.ttf",
13876
+ semibold: "RobotoMono-SemiBold.ttf",
13877
+ italic: "RobotoMono-RegularItalic.ttf",
13878
+ boldItalic: "RobotoMono-BoldItalic.ttf",
13879
+ lightItalic: "RobotoMono-LightItalic.ttf",
13880
+ mediumItalic: "RobotoMono-MediumItalic.ttf",
13881
+ semiboldItalic: "RobotoMono-SemiBoldItalic.ttf"
13882
+ },
13883
+ "Fira Code": {
13884
+ regular: "FiraCode-Regular.ttf",
13885
+ bold: "FiraCode-Bold.ttf",
13886
+ light: "FiraCode-Light.ttf",
13887
+ medium: "FiraCode-Medium.ttf",
13888
+ semibold: "FiraCode-SemiBold.ttf"
13889
+ },
13890
+ "JetBrains Mono": {
13891
+ regular: "JetBrainsMono-Regular.ttf",
13892
+ bold: "JetBrainsMono-Bold.ttf",
13893
+ light: "JetBrainsMono-Light.ttf",
13894
+ medium: "JetBrainsMono-Medium.ttf",
13895
+ semibold: "JetBrainsMono-SemiBold.ttf",
13896
+ italic: "JetBrainsMono-RegularItalic.ttf",
13897
+ boldItalic: "JetBrainsMono-BoldItalic.ttf",
13898
+ lightItalic: "JetBrainsMono-LightItalic.ttf",
13899
+ mediumItalic: "JetBrainsMono-MediumItalic.ttf",
13900
+ semiboldItalic: "JetBrainsMono-SemiBoldItalic.ttf"
13901
+ },
13902
+ "Source Code Pro": {
13903
+ regular: "SourceCodePro-Regular.ttf",
13904
+ bold: "SourceCodePro-Bold.ttf",
13905
+ light: "SourceCodePro-Light.ttf",
13906
+ medium: "SourceCodePro-Medium.ttf",
13907
+ semibold: "SourceCodePro-SemiBold.ttf",
13908
+ italic: "SourceCodePro-RegularItalic.ttf",
13909
+ boldItalic: "SourceCodePro-BoldItalic.ttf",
13910
+ lightItalic: "SourceCodePro-LightItalic.ttf",
13911
+ mediumItalic: "SourceCodePro-MediumItalic.ttf",
13912
+ semiboldItalic: "SourceCodePro-SemiBoldItalic.ttf"
13913
+ },
13914
+ "IBM Plex Mono": {
13915
+ regular: "IBMPlexMono-Regular.ttf",
13916
+ bold: "IBMPlexMono-Bold.ttf"
13917
+ },
13918
+ "Space Mono": {
13919
+ regular: "SpaceMono-Regular.ttf",
13920
+ bold: "SpaceMono-Bold.ttf",
13921
+ italic: "SpaceMono-Italic.ttf",
13922
+ boldItalic: "SpaceMono-BoldItalic.ttf"
13923
+ },
13924
+ "Sacramento": { regular: "Sacramento-Regular.ttf" },
13925
+ "Alex Brush": { regular: "AlexBrush-Regular.ttf" },
13926
+ "Allura": { regular: "Allura-Regular.ttf" },
13927
+ "Caveat": {
13928
+ regular: "Caveat-Regular.ttf",
13929
+ bold: "Caveat-Bold.ttf",
13930
+ medium: "Caveat-Medium.ttf",
13931
+ semibold: "Caveat-SemiBold.ttf"
13932
+ },
13933
+ "Lobster": { regular: "Lobster-Regular.ttf" },
13934
+ "Comfortaa": {
13935
+ regular: "Comfortaa-Regular.ttf",
13936
+ bold: "Comfortaa-Bold.ttf",
13937
+ light: "Comfortaa-Light.ttf",
13938
+ medium: "Comfortaa-Medium.ttf",
13939
+ semibold: "Comfortaa-SemiBold.ttf"
13940
+ },
13941
+ "Anton": { regular: "Anton-Regular.ttf" },
13942
+ "Teko": {
13943
+ regular: "Teko-Regular.ttf",
13944
+ bold: "Teko-Bold.ttf",
13945
+ light: "Teko-Light.ttf",
13946
+ medium: "Teko-Medium.ttf",
13947
+ semibold: "Teko-SemiBold.ttf"
13948
+ },
13949
+ "Noto Sans Devanagari": {
13950
+ regular: "NotoSansDevanagari-Regular.ttf",
13951
+ bold: "NotoSansDevanagari-Bold.ttf",
13952
+ light: "NotoSansDevanagari-Light.ttf",
13953
+ medium: "NotoSansDevanagari-Medium.ttf",
13954
+ semibold: "NotoSansDevanagari-SemiBold.ttf"
13955
+ },
13956
+ "Hind": {
13957
+ regular: "Hind-Regular.ttf",
13958
+ bold: "Hind-Bold.ttf",
13959
+ light: "Hind-Light.ttf",
13960
+ medium: "Hind-Medium.ttf",
13961
+ semibold: "Hind-SemiBold.ttf"
13962
+ },
13963
+ "Cinzel": { regular: "Cinzel-Regular.ttf", bold: "Cinzel-Bold.ttf" },
13964
+ "Cormorant Garamond": {
13965
+ regular: "CormorantGaramond-Regular.ttf",
13966
+ bold: "CormorantGaramond-Bold.ttf",
13967
+ light: "CormorantGaramond-Light.ttf",
13968
+ medium: "CormorantGaramond-Medium.ttf",
13969
+ semibold: "CormorantGaramond-SemiBold.ttf",
13970
+ italic: "CormorantGaramond-Italic.ttf",
13971
+ boldItalic: "CormorantGaramond-BoldItalic.ttf"
13932
13972
  }
13933
- paintPageBackground(ctx, page, width, height) {
13934
- var _a, _b;
13935
- const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
13936
- const gradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
13937
- ctx.clearRect(0, 0, width, height);
13938
- ctx.fillStyle = backgroundColor;
13939
- ctx.fillRect(0, 0, width, height);
13940
- const stops = this.getNormalizedGradientStops(gradient);
13941
- if (stops.length < 2) return;
13942
- try {
13943
- let canvasGradient = null;
13944
- if ((gradient == null ? void 0 : gradient.type) === "radial") {
13945
- const cx = Number.isFinite(gradient == null ? void 0 : gradient.cx) ? gradient.cx : 0.5;
13946
- const cy = Number.isFinite(gradient == null ? void 0 : gradient.cy) ? gradient.cy : 0.5;
13947
- const centerX = width * cx;
13948
- const centerY = height * cy;
13949
- const radius = Math.max(
13950
- Math.hypot(centerX, centerY),
13951
- Math.hypot(width - centerX, centerY),
13952
- Math.hypot(centerX, height - centerY),
13953
- Math.hypot(width - centerX, height - centerY)
13954
- );
13955
- canvasGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
13956
- } else if ((gradient == null ? void 0 : gradient.type) === "conic" && typeof ctx.createConicGradient === "function") {
13957
- const cx = Number.isFinite(gradient == null ? void 0 : gradient.cx) ? gradient.cx : 0.5;
13958
- const cy = Number.isFinite(gradient == null ? void 0 : gradient.cy) ? gradient.cy : 0.5;
13959
- const startAngle = (((gradient == null ? void 0 : gradient.angle) ?? 0) - 90) * Math.PI / 180;
13960
- canvasGradient = ctx.createConicGradient(startAngle, width * cx, height * cy);
13961
- } else {
13962
- const angleDeg = (gradient == null ? void 0 : gradient.angle) ?? 90;
13963
- const angleRad = angleDeg * Math.PI / 180;
13964
- const sinA = Math.sin(angleRad);
13965
- const cosA = Math.cos(angleRad);
13966
- const midX = width / 2;
13967
- const midY = height / 2;
13968
- const corners = [
13969
- [0, 0],
13970
- [width, 0],
13971
- [width, height],
13972
- [0, height]
13973
- ];
13974
- const projections = corners.map(([x, y]) => x * sinA - y * cosA);
13975
- const minProjection = Math.min(...projections);
13976
- const maxProjection = Math.max(...projections);
13977
- canvasGradient = ctx.createLinearGradient(
13978
- midX + minProjection * sinA,
13979
- midY - minProjection * cosA,
13980
- midX + maxProjection * sinA,
13981
- midY - maxProjection * cosA
13982
- );
13983
- }
13984
- if (!canvasGradient) return;
13985
- stops.forEach((stop) => canvasGradient.addColorStop(stop.offset, stop.color));
13986
- ctx.fillStyle = canvasGradient;
13987
- ctx.fillRect(0, 0, width, height);
13988
- } catch {
13989
- }
13973
+ };
13974
+ const WEIGHT_TO_KEYS = {
13975
+ 300: ["light", "regular"],
13976
+ 400: ["regular"],
13977
+ 500: ["medium", "regular"],
13978
+ 600: ["semibold", "bold", "regular"],
13979
+ 700: ["bold", "regular"]
13980
+ };
13981
+ const WEIGHT_TO_ITALIC_KEYS = {
13982
+ 300: ["lightItalic", "italic", "light", "regular"],
13983
+ 400: ["italic", "regular"],
13984
+ 500: ["mediumItalic", "italic", "medium", "regular"],
13985
+ 600: ["semiboldItalic", "boldItalic", "italic", "semibold", "bold", "regular"],
13986
+ 700: ["boldItalic", "italic", "bold", "regular"]
13987
+ };
13988
+ function getFontPathForWeight(files, weight, isItalic = false) {
13989
+ const keys = isItalic ? WEIGHT_TO_ITALIC_KEYS[weight] : WEIGHT_TO_KEYS[weight];
13990
+ if (!keys) return files.regular;
13991
+ for (const k of keys) {
13992
+ const path = files[k];
13993
+ if (path) return path;
13990
13994
  }
13991
- async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
13992
- const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
13993
- const canvasWidth = config.canvas.width;
13994
- const canvasHeight = config.canvas.height;
13995
- const hasAutoShrink = configHasAutoShrinkText(config);
13996
- let firstMountSettled = false;
13997
- let lateFontSettleDetected = false;
13998
- if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
13999
- document.fonts.ready.then(() => {
14000
- if (firstMountSettled) lateFontSettleDetected = true;
14001
- }).catch(() => {
14002
- });
14003
- }
14004
- return new Promise((resolve, reject) => {
14005
- const container = document.createElement("div");
14006
- container.style.cssText = `
14007
- position: fixed; left: -99999px; top: -99999px;
14008
- width: ${canvasWidth}px; height: ${canvasHeight}px;
14009
- overflow: hidden; pointer-events: none; opacity: 0;
14010
- `;
14011
- document.body.appendChild(container);
14012
- const timeout = setTimeout(() => {
14013
- cleanup();
14014
- reject(new Error("Render timeout (30s)"));
14015
- }, 3e4);
14016
- let root;
14017
- let mountKey = 0;
14018
- const cleanup = () => {
14019
- clearTimeout(timeout);
14020
- try {
14021
- root.unmount();
14022
- } catch {
14023
- }
14024
- container.remove();
14025
- };
14026
- const remountWithFreshKey = async () => {
14027
- mountKey += 1;
14028
- try {
14029
- clearMeasurementCache();
14030
- } catch {
14031
- }
14032
- try {
14033
- clearFabricCharCache();
14034
- } catch {
14035
- }
14036
- try {
14037
- root.unmount();
14038
- } catch {
14039
- }
14040
- root = client.createRoot(container);
14041
- await new Promise((settle) => {
14042
- const onReadyOnce = () => {
14043
- this.waitForCanvasScene(container, config, pageIndex).then(async () => {
14044
- const fabricInstance = this.getFabricCanvasFromContainer(container);
14045
- const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
14046
- await this.waitForCanvasImages(container, expectedImageCount);
14047
- await this.waitForStableTextMetrics(container, config);
14048
- await this.waitForCanvasScene(container, config, pageIndex);
14049
- if (!fabricInstance) return settle();
14050
- settle();
14051
- }).catch(() => settle());
14052
- };
14053
- root.render(
14054
- react.createElement(PreviewCanvas2, {
14055
- key: `remount-${mountKey}`,
14056
- config,
14057
- pageIndex,
14058
- zoom: pixelRatio,
14059
- absoluteZoom: true,
14060
- skipFontReadyWait: false,
14061
- onReady: onReadyOnce
14062
- })
14063
- );
14064
- });
14065
- };
14066
- const onReady = () => {
14067
- this.waitForCanvasScene(container, config, pageIndex).then(async () => {
14068
- try {
14069
- const fabricInstance = this.getFabricCanvasFromContainer(container);
14070
- const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
14071
- await this.waitForCanvasImages(container, expectedImageCount);
14072
- await this.waitForStableTextMetrics(container, config);
14073
- await this.waitForCanvasScene(container, config, pageIndex);
14074
- firstMountSettled = true;
14075
- if (hasAutoShrink && lateFontSettleDetected) {
14076
- console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
14077
- await remountWithFreshKey();
14078
- }
14079
- const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
14080
- const sourceCanvas = (fabricInstance == null ? void 0 : fabricInstance.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || fabricCanvas;
14081
- const fabricInstanceAfter = this.getFabricCanvasFromContainer(container) || fabricInstance;
14082
- const sourceCanvasAfter = (fabricInstanceAfter == null ? void 0 : fabricInstanceAfter.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || sourceCanvas;
14083
- if (!sourceCanvas) {
14084
- cleanup();
14085
- reject(new Error("No canvas element found after render"));
14086
- return;
14087
- }
14088
- const exportCanvas = document.createElement("canvas");
14089
- exportCanvas.width = sourceCanvasAfter.width;
14090
- exportCanvas.height = sourceCanvasAfter.height;
14091
- const exportCtx = exportCanvas.getContext("2d");
14092
- if (!exportCtx) {
14093
- cleanup();
14094
- reject(new Error("Failed to create export canvas"));
14095
- return;
14096
- }
14097
- exportCtx.save();
14098
- exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
14099
- this.paintPageBackground(exportCtx, config.pages[pageIndex], canvasWidth, canvasHeight);
14100
- exportCtx.restore();
14101
- exportCtx.drawImage(sourceCanvasAfter, 0, 0);
14102
- const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
14103
- const dataUrl = exportCanvas.toDataURL(mimeType, quality);
14104
- cleanup();
14105
- resolve(dataUrl);
14106
- } catch (err) {
14107
- cleanup();
14108
- reject(err);
14109
- }
14110
- });
14111
- };
14112
- root = client.createRoot(container);
14113
- root.render(
14114
- react.createElement(PreviewCanvas2, {
14115
- config,
14116
- pageIndex,
14117
- zoom: pixelRatio,
14118
- absoluteZoom: true,
14119
- skipFontReadyWait: false,
14120
- onReady
14121
- })
14122
- );
14123
- });
13995
+ return files.regular;
13996
+ }
13997
+ function isItalicPath(files, path) {
13998
+ return path === files.italic || path === files.boldItalic || path === files.lightItalic || path === files.mediumItalic || path === files.semiboldItalic;
13999
+ }
14000
+ function getJsPDFFontName(fontName) {
14001
+ return fontName.replace(/\s+/g, "");
14002
+ }
14003
+ function getEmbeddedJsPDFFontName(fontName, weight, isItalic = false) {
14004
+ const resolved = resolveFontWeight(weight);
14005
+ const label = FONT_WEIGHT_LABELS[resolved];
14006
+ const italicSuffix = isItalic ? "Italic" : "";
14007
+ return `${getJsPDFFontName(fontName)}-${label}${italicSuffix}`;
14008
+ }
14009
+ function isFontAvailable(fontName) {
14010
+ return fontName in FONT_FILES;
14011
+ }
14012
+ const ttfCache = /* @__PURE__ */ new Map();
14013
+ async function fetchTTFAsBase64(url) {
14014
+ const cached = ttfCache.get(url);
14015
+ if (cached) return cached;
14016
+ try {
14017
+ const res = await fetch(url);
14018
+ if (!res.ok) return null;
14019
+ const buf = await res.arrayBuffer();
14020
+ const bytes = new Uint8Array(buf);
14021
+ let binary = "";
14022
+ for (let i = 0; i < bytes.length; i++) {
14023
+ binary += String.fromCharCode(bytes[i]);
14024
+ }
14025
+ const b64 = btoa(binary);
14026
+ ttfCache.set(url, b64);
14027
+ return b64;
14028
+ } catch {
14029
+ return null;
14124
14030
  }
14125
- // ─── Internal: capture SVG from a rendered Fabric canvas ───
14126
- //
14127
- // APPROACH: Use the SAME PreviewCanvas that renders perfect PNGs, then call
14128
- // Fabric's toSVG() on that canvas. This guarantees 100% layout parity —
14129
- // the SVG is a vector snapshot of exactly what's on screen.
14130
- //
14131
- // The trick: before calling toSVG(), we temporarily neutralize the viewport
14132
- // transform and retina scaling so Fabric emits coordinates in logical
14133
- // document space (e.g. 612x792) instead of inflated pixel space.
14134
- captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
14135
- return new Promise(async (resolve, reject) => {
14136
- const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
14137
- const hasAutoShrink = configHasAutoShrinkText(config);
14138
- const container = document.createElement("div");
14139
- container.style.cssText = `
14140
- position: fixed; left: -99999px; top: -99999px;
14141
- width: ${canvasWidth}px; height: ${canvasHeight}px;
14142
- overflow: hidden; pointer-events: none; opacity: 0;
14143
- `;
14144
- document.body.appendChild(container);
14145
- const timeout = setTimeout(() => {
14146
- cleanup();
14147
- reject(new Error("SVG render timeout (30s)"));
14148
- }, 3e4);
14149
- let root = null;
14150
- let mountKey = 0;
14151
- let didPreviewParityRemount = false;
14152
- const cleanup = () => {
14153
- clearTimeout(timeout);
14154
- try {
14155
- root == null ? void 0 : root.unmount();
14156
- } catch {
14157
- }
14158
- container.remove();
14159
- };
14160
- const mountPreview = (readyHandler) => {
14161
- root = client.createRoot(container);
14162
- root.render(
14163
- react.createElement(PreviewCanvas2, {
14164
- key: `svg-capture-${mountKey}`,
14165
- config,
14166
- pageIndex,
14167
- zoom: 1,
14168
- absoluteZoom: true,
14169
- skipFontReadyWait: false,
14170
- onReady: readyHandler
14171
- })
14172
- );
14173
- };
14174
- const remountForPreviewParity = async () => {
14175
- if (didPreviewParityRemount) return;
14176
- didPreviewParityRemount = true;
14177
- mountKey += 1;
14178
- try {
14179
- clearMeasurementCache();
14180
- } catch {
14181
- }
14182
- try {
14183
- clearFabricCharCache();
14184
- } catch {
14185
- }
14186
- try {
14187
- root == null ? void 0 : root.unmount();
14188
- } catch {
14189
- }
14190
- await new Promise((settle) => {
14191
- mountPreview(() => {
14192
- this.waitForCanvasScene(container, config, pageIndex).then(async () => {
14193
- const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
14194
- await this.waitForCanvasImages(container, expectedImageCount);
14195
- await this.waitForStableTextMetrics(container, config);
14196
- await this.waitForCanvasScene(container, config, pageIndex);
14197
- settle();
14198
- }).catch(() => settle());
14199
- });
14200
- });
14201
- };
14202
- const onReady = () => {
14203
- this.waitForCanvasScene(container, config, pageIndex).then(async () => {
14204
- var _a, _b;
14205
- try {
14206
- const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
14207
- await this.waitForCanvasImages(container, expectedImageCount);
14208
- await this.waitForStableTextMetrics(container, config);
14209
- await this.waitForCanvasScene(container, config, pageIndex);
14210
- if (hasAutoShrink && !didPreviewParityRemount) {
14211
- console.log("[canvas-renderer][svg-parity] remounting once to match PixldocsPreview auto-shrink stabilization");
14212
- await remountForPreviewParity();
14213
- }
14214
- const fabricInstance = this.getFabricCanvasFromContainer(container);
14215
- if (!fabricInstance) {
14216
- cleanup();
14217
- reject(new Error("No Fabric canvas instance found for SVG capture"));
14218
- return;
14031
+ }
14032
+ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
14033
+ const fontFiles = FONT_FILES[fontName];
14034
+ if (!fontFiles) return false;
14035
+ const baseUrl = fontBaseUrl.endsWith("/") ? fontBaseUrl : fontBaseUrl + "/";
14036
+ const resolvedWeight = resolveFontWeight(weight);
14037
+ const fontPath = getFontPathForWeight(fontFiles, resolvedWeight, isItalic);
14038
+ if (!fontPath) return false;
14039
+ const hasItalicFile = isItalic && isItalicPath(fontFiles, fontPath);
14040
+ const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, hasItalicFile);
14041
+ const label = FONT_WEIGHT_LABELS[resolvedWeight];
14042
+ const italicSuffix = hasItalicFile ? "Italic" : "";
14043
+ const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
14044
+ const url = baseUrl + fontPath;
14045
+ try {
14046
+ const b64 = await fetchTTFAsBase64(url);
14047
+ if (!b64) return false;
14048
+ pdf.addFileToVFS(fileName, b64);
14049
+ pdf.addFont(fileName, jsPdfFontName, "normal");
14050
+ if (fontName !== jsPdfFontName) {
14051
+ try {
14052
+ pdf.addFont(fileName, fontName, "normal");
14053
+ } catch {
14054
+ }
14055
+ }
14056
+ return true;
14057
+ } catch (e) {
14058
+ console.warn(`[pdf-fonts] Failed to embed ${fontName} w${weight}:`, e);
14059
+ return false;
14060
+ }
14061
+ }
14062
+ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
14063
+ const fontKeys = /* @__PURE__ */ new Set();
14064
+ const SEP = "";
14065
+ const walkElements = (elements) => {
14066
+ for (const el of elements) {
14067
+ if (el.fontFamily) {
14068
+ const w = resolveFontWeight(el.fontWeight ?? 400);
14069
+ fontKeys.add(`${el.fontFamily}${SEP}${w}`);
14070
+ }
14071
+ if (el.styles && typeof el.styles === "object") {
14072
+ for (const lineKey of Object.keys(el.styles)) {
14073
+ const lineStyles = el.styles[lineKey];
14074
+ if (lineStyles && typeof lineStyles === "object") {
14075
+ for (const charKey of Object.keys(lineStyles)) {
14076
+ const s = lineStyles[charKey];
14077
+ if (s == null ? void 0 : s.fontFamily) {
14078
+ const w = resolveFontWeight(s.fontWeight ?? 400);
14079
+ fontKeys.add(`${s.fontFamily}${SEP}${w}`);
14080
+ }
14219
14081
  }
14220
- const prevVPT = fabricInstance.viewportTransform ? [...fabricInstance.viewportTransform] : void 0;
14221
- const prevSvgVPT = fabricInstance.svgViewportTransformation;
14222
- const prevRetina = fabricInstance.enableRetinaScaling;
14223
- const prevWidth = fabricInstance.width;
14224
- const prevHeight = fabricInstance.height;
14225
- fabricInstance.viewportTransform = [1, 0, 0, 1, 0, 0];
14226
- fabricInstance.svgViewportTransformation = false;
14227
- fabricInstance.enableRetinaScaling = false;
14228
- fabricInstance.setDimensions(
14229
- { width: canvasWidth, height: canvasHeight },
14230
- { cssOnly: false, backstoreOnly: false }
14231
- );
14232
- const rawSvgString = fabricInstance.toSVG();
14233
- const svgString = this.normalizeSvgDimensions(
14234
- rawSvgString,
14235
- canvasWidth,
14236
- canvasHeight
14237
- );
14238
- fabricInstance.enableRetinaScaling = prevRetina;
14239
- fabricInstance.setDimensions(
14240
- { width: prevWidth, height: prevHeight },
14241
- { cssOnly: false, backstoreOnly: false }
14242
- );
14243
- if (prevVPT) fabricInstance.viewportTransform = prevVPT;
14244
- fabricInstance.svgViewportTransformation = prevSvgVPT;
14245
- const page = config.pages[pageIndex];
14246
- const backgroundColor = ((_a = page == null ? void 0 : page.settings) == null ? void 0 : _a.backgroundColor) || "#ffffff";
14247
- const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
14248
- cleanup();
14249
- resolve({
14250
- svg: svgString,
14251
- width: canvasWidth,
14252
- height: canvasHeight,
14253
- backgroundColor,
14254
- backgroundGradient
14255
- });
14256
- } catch (err) {
14257
- cleanup();
14258
- reject(err);
14259
14082
  }
14260
- });
14261
- };
14262
- mountPreview(onReady);
14263
- });
14083
+ }
14084
+ }
14085
+ if (el.children) walkElements(el.children);
14086
+ if (el.objects) walkElements(el.objects);
14087
+ }
14088
+ };
14089
+ for (const page of (config == null ? void 0 : config.pages) || []) {
14090
+ if (page.children) walkElements(page.children);
14091
+ if (page.elements) walkElements(page.elements);
14264
14092
  }
14265
- /**
14266
- * Normalize the SVG's width/height/viewBox to match the logical page dimensions.
14267
- * Fabric's toSVG() may output dimensions scaled by the canvas element's actual
14268
- * pixel size (e.g., 2x due to devicePixelRatio), causing svg2pdf to render
14269
- * content at the wrong scale. This rewrites the root <svg> attributes to ensure
14270
- * the SVG coordinate system matches the intended page size exactly.
14271
- */
14272
- normalizeSvgDimensions(svg, targetWidth, targetHeight) {
14273
- const widthMatch = svg.match(/<svg[^>]*\bwidth="([^"]+)"/i);
14274
- const heightMatch = svg.match(/<svg[^>]*\bheight="([^"]+)"/i);
14275
- const svgWidth = widthMatch ? parseFloat(widthMatch[1]) : targetWidth;
14276
- const svgHeight = heightMatch ? parseFloat(heightMatch[1]) : targetHeight;
14277
- console.log(
14278
- `[canvas-renderer][svg-normalize] root ${svgWidth}x${svgHeight} → page ${targetWidth}x${targetHeight}`
14093
+ fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400`);
14094
+ for (const w of [300, 400, 500, 600, 700]) {
14095
+ fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}`);
14096
+ }
14097
+ const embedded = /* @__PURE__ */ new Set();
14098
+ const tasks = [];
14099
+ for (const key of fontKeys) {
14100
+ const sep = key.indexOf(SEP);
14101
+ const fontName = key.slice(0, sep);
14102
+ const weight = parseInt(key.slice(sep + 1), 10);
14103
+ if (!isFontAvailable(fontName)) continue;
14104
+ tasks.push(
14105
+ embedFont(pdf, fontName, weight, fontBaseUrl).then((ok) => {
14106
+ if (ok) embedded.add(key);
14107
+ })
14279
14108
  );
14280
- let normalized = svg;
14281
- if (/\bwidth="[^"]*"/i.test(normalized)) {
14282
- normalized = normalized.replace(/(<svg[^>]*\b)width="[^"]*"/i, `$1width="${targetWidth}"`);
14283
- } else {
14284
- normalized = normalized.replace(/<svg\b/i, `<svg width="${targetWidth}"`);
14285
- }
14286
- if (/\bheight="[^"]*"/i.test(normalized)) {
14287
- normalized = normalized.replace(/(<svg[^>]*\b)height="[^"]*"/i, `$1height="${targetHeight}"`);
14288
- } else {
14289
- normalized = normalized.replace(/<svg\b/i, `<svg height="${targetHeight}"`);
14290
- }
14291
- const viewBox = `0 0 ${targetWidth} ${targetHeight}`;
14292
- if (/\bviewBox="[^"]*"/i.test(normalized)) {
14293
- normalized = normalized.replace(/viewBox="[^"]*"/i, `viewBox="${viewBox}"`);
14294
- } else {
14295
- normalized = normalized.replace(/<svg\b/i, `<svg viewBox="${viewBox}"`);
14109
+ }
14110
+ await Promise.all(tasks);
14111
+ console.log(`[pdf-fonts] Embedded ${embedded.size} font variants from config`);
14112
+ return embedded;
14113
+ }
14114
+ function isDevanagari(char) {
14115
+ const c = char.codePointAt(0) ?? 0;
14116
+ return c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423;
14117
+ }
14118
+ function containsDevanagari(text) {
14119
+ if (!text) return false;
14120
+ for (const char of text) {
14121
+ if (isDevanagari(char)) return true;
14122
+ }
14123
+ return false;
14124
+ }
14125
+ function isBasicLatinOrLatin1(char) {
14126
+ const c = char.codePointAt(0) ?? 0;
14127
+ return c <= 591;
14128
+ }
14129
+ function classifyChar(char) {
14130
+ if (isBasicLatinOrLatin1(char)) return "main";
14131
+ if (isDevanagari(char)) return "devanagari";
14132
+ return "symbol";
14133
+ }
14134
+ function splitIntoRuns(text) {
14135
+ if (!text) return [];
14136
+ const runs = [];
14137
+ let currentType = null;
14138
+ let currentText = "";
14139
+ for (const char of text) {
14140
+ const type = classifyChar(char);
14141
+ if (type !== currentType && currentText) {
14142
+ runs.push({ text: currentText, runType: currentType });
14143
+ currentText = "";
14296
14144
  }
14297
- normalized = normalized.replace(/="undefined"/g, '="0"');
14298
- normalized = normalized.replace(/="NaN"/g, '="0"');
14299
- if (/\bx="[^"]*"/i.test(normalized)) {
14300
- normalized = normalized.replace(/(<svg[^>]*\b)x="[^"]*"/i, '$1x="0"');
14301
- } else {
14302
- normalized = normalized.replace(/<svg\b/i, '<svg x="0"');
14145
+ currentType = type;
14146
+ currentText += char;
14147
+ }
14148
+ if (currentText && currentType) {
14149
+ runs.push({ text: currentText, runType: currentType });
14150
+ }
14151
+ return runs;
14152
+ }
14153
+ function rewriteSvgFontsForJsPDF(svgStr) {
14154
+ var _a, _b;
14155
+ const parser = new DOMParser();
14156
+ const doc = parser.parseFromString(svgStr, "image/svg+xml");
14157
+ const textEls = doc.querySelectorAll("text, tspan, textPath");
14158
+ const readStyleToken = (style, prop) => {
14159
+ var _a2;
14160
+ const match = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
14161
+ return ((_a2 = match == null ? void 0 : match[1]) == null ? void 0 : _a2.trim()) || null;
14162
+ };
14163
+ const resolveInheritedValue = (el, attr, styleProp = attr) => {
14164
+ var _a2;
14165
+ let current = el;
14166
+ while (current) {
14167
+ const attrVal = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
14168
+ if (attrVal) return attrVal;
14169
+ const styleVal = readStyleToken(current.getAttribute("style") || "", styleProp);
14170
+ if (styleVal) return styleVal;
14171
+ current = current.parentElement;
14303
14172
  }
14304
- if (/\by="[^"]*"/i.test(normalized)) {
14305
- normalized = normalized.replace(/(<svg[^>]*\b)y="[^"]*"/i, '$1y="0"');
14173
+ return null;
14174
+ };
14175
+ const resolveWeightNum = (weightRaw) => {
14176
+ const parsedWeight = Number.parseInt(weightRaw, 10);
14177
+ return Number.isFinite(parsedWeight) ? parsedWeight : /bold/i.test(weightRaw) ? 700 : /medium/i.test(weightRaw) ? 500 : /semi/i.test(weightRaw) ? 600 : /light/i.test(weightRaw) ? 300 : 400;
14178
+ };
14179
+ const buildStyleString = (existingStyle, fontName) => {
14180
+ const stylePairs = existingStyle.split(";").map((part) => part.trim()).filter(Boolean).filter((part) => !/^font-family\s*:/i.test(part) && !/^font-weight\s*:/i.test(part) && !/^font-style\s*:/i.test(part));
14181
+ stylePairs.push(`font-family: ${fontName}`);
14182
+ stylePairs.push(`font-weight: normal`);
14183
+ stylePairs.push(`font-style: normal`);
14184
+ return stylePairs.join("; ");
14185
+ };
14186
+ for (const el of textEls) {
14187
+ const inlineStyle = el.getAttribute("style") || "";
14188
+ const rawFf = resolveInheritedValue(el, "font-family");
14189
+ if (!rawFf) continue;
14190
+ const clean = (_a = rawFf.split(",")[0]) == null ? void 0 : _a.replace(/['"]/g, "").trim();
14191
+ if (!isFontAvailable(clean)) continue;
14192
+ const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
14193
+ const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
14194
+ const weight = resolveWeightNum(weightRaw);
14195
+ const resolved = resolveFontWeight(weight);
14196
+ const isItalic = /italic|oblique/i.test(styleRaw);
14197
+ const jsPdfName = getEmbeddedJsPDFFontName(clean, resolved, isItalic);
14198
+ el.setAttribute("data-source-font-family", clean);
14199
+ el.setAttribute("data-source-font-weight", String(resolved));
14200
+ el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
14201
+ const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
14202
+ const hasDevanagari = containsDevanagari(directText);
14203
+ if (hasDevanagari && directText.length > 0) {
14204
+ const devanagariWeight = resolveFontWeight(weight);
14205
+ const devanagariJsPdfName = getEmbeddedJsPDFFontName(FONT_FALLBACK_DEVANAGARI, devanagariWeight);
14206
+ const symbolJsPdfName = isFontAvailable(FONT_FALLBACK_SYMBOLS) ? getEmbeddedJsPDFFontName(FONT_FALLBACK_SYMBOLS, 400) : jsPdfName;
14207
+ const childNodes = Array.from(el.childNodes);
14208
+ for (const node of childNodes) {
14209
+ if (node.nodeType !== 3 || !node.textContent) continue;
14210
+ const runs = splitIntoRuns(node.textContent);
14211
+ if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) !== "devanagari") continue;
14212
+ const fragment = doc.createDocumentFragment();
14213
+ for (const run of runs) {
14214
+ const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
14215
+ let runFont;
14216
+ if (run.runType === "devanagari") {
14217
+ runFont = devanagariJsPdfName;
14218
+ } else if (run.runType === "symbol") {
14219
+ runFont = symbolJsPdfName;
14220
+ } else {
14221
+ runFont = jsPdfName;
14222
+ }
14223
+ tspan.setAttribute("font-family", runFont);
14224
+ tspan.setAttribute("font-weight", "normal");
14225
+ tspan.setAttribute("font-style", "normal");
14226
+ tspan.textContent = run.text;
14227
+ fragment.appendChild(tspan);
14228
+ }
14229
+ el.replaceChild(fragment, node);
14230
+ }
14231
+ el.setAttribute("font-family", jsPdfName);
14232
+ el.setAttribute("font-weight", "normal");
14233
+ el.setAttribute("font-style", "normal");
14234
+ el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
14306
14235
  } else {
14307
- normalized = normalized.replace(/<svg\b/i, '<svg y="0"');
14308
- }
14309
- normalized = normalized.replace(/\bpreserveAspectRatio="[^"]*"/i, 'preserveAspectRatio="none"');
14310
- if (!/\bpreserveAspectRatio="[^"]*"/i.test(normalized)) {
14311
- normalized = normalized.replace(/<svg\b/i, '<svg preserveAspectRatio="none"');
14236
+ el.setAttribute("font-family", jsPdfName);
14237
+ el.setAttribute("font-weight", "normal");
14238
+ el.setAttribute("font-style", "normal");
14239
+ el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
14312
14240
  }
14313
- return normalized;
14314
14241
  }
14315
- /**
14316
- * Find the Fabric.Canvas instance that belongs to a given container element,
14317
- * using the global __fabricCanvasRegistry (set by PageCanvas).
14318
- */
14319
- getFabricCanvasFromContainer(container) {
14320
- const registry2 = window.__fabricCanvasRegistry;
14321
- if (registry2 instanceof Map) {
14322
- for (const entry of registry2.values()) {
14323
- const canvas = (entry == null ? void 0 : entry.canvas) || entry;
14324
- if (!canvas || typeof canvas.toSVG !== "function") continue;
14325
- const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
14326
- if (el && container.contains(el)) return canvas;
14242
+ return new XMLSerializer().serializeToString(doc.documentElement);
14243
+ }
14244
+ function extractFontFamiliesFromSvgs(svgs) {
14245
+ const families = /* @__PURE__ */ new Set();
14246
+ const regex = /font-family[=:]\s*["']?([^"';},]+)/gi;
14247
+ for (const svg of svgs) {
14248
+ let m;
14249
+ while ((m = regex.exec(svg)) !== null) {
14250
+ const raw = m[1].trim().split(",")[0].trim().replace(/^['"]|['"]$/g, "");
14251
+ if (raw && raw !== "serif" && raw !== "sans-serif" && raw !== "monospace") {
14252
+ families.add(raw);
14327
14253
  }
14328
14254
  }
14329
- return null;
14330
14255
  }
14331
- async waitForStableTextMetrics(container, config) {
14332
- var _a, _b, _c;
14333
- if (typeof document !== "undefined") {
14334
- void ensureFontsForResolvedConfig(config);
14335
- await this.waitForRelevantFonts(config);
14256
+ return families;
14257
+ }
14258
+ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
14259
+ const embedded = /* @__PURE__ */ new Set();
14260
+ const weights = [300, 400, 500, 600, 700];
14261
+ const tasks = [];
14262
+ for (const family of fontFamilies) {
14263
+ if (!isFontAvailable(family)) {
14264
+ console.warn(`[pdf-fonts] No TTF mapping for "${family}" — will use Helvetica fallback`);
14265
+ continue;
14336
14266
  }
14337
- const fabricInstance = this.getFabricCanvasFromContainer(container);
14338
- if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
14339
- const waitForPaint = () => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r())));
14340
- const primeCharBounds = (obj) => {
14341
- if (obj instanceof fabric__namespace.Textbox) {
14342
- const lines = obj._textLines;
14343
- if (Array.isArray(lines)) {
14344
- for (let i = 0; i < lines.length; i++) {
14345
- try {
14346
- obj.getLineWidth(i);
14347
- } catch {
14348
- }
14349
- }
14350
- }
14351
- obj.dirty = true;
14352
- return;
14353
- }
14354
- if (obj instanceof fabric__namespace.Group) {
14355
- obj.getObjects().forEach(primeCharBounds);
14356
- obj.dirty = true;
14357
- }
14267
+ for (const w of weights) {
14268
+ tasks.push(
14269
+ embedFont(pdf, family, w, fontBaseUrl).then((ok) => {
14270
+ if (ok) embedded.add(`${family}${w}`);
14271
+ })
14272
+ );
14273
+ }
14274
+ }
14275
+ await Promise.all(tasks);
14276
+ console.log(`[pdf-fonts] Embedded ${embedded.size} font variants for ${fontFamilies.size} families`);
14277
+ return embedded;
14278
+ }
14279
+ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
14280
+ __proto__: null,
14281
+ FONT_FALLBACK_DEVANAGARI,
14282
+ FONT_FALLBACK_SYMBOLS,
14283
+ FONT_FILES,
14284
+ FONT_WEIGHT_LABELS,
14285
+ embedFont,
14286
+ embedFontsForConfig,
14287
+ embedFontsInPdf,
14288
+ extractFontFamiliesFromSvgs,
14289
+ getEmbeddedJsPDFFontName,
14290
+ getFontPathForWeight,
14291
+ isFontAvailable,
14292
+ resolveFontWeight,
14293
+ rewriteSvgFontsForJsPDF
14294
+ }, Symbol.toStringTag, { value: "Module" }));
14295
+ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
14296
+ try {
14297
+ if (typeof DOMParser === "undefined") return;
14298
+ const doc = new DOMParser().parseFromString(svgStr, "image/svg+xml");
14299
+ if (doc.querySelector("parsererror")) {
14300
+ console.warn(`${tag} page=${pageIndex} stage=${stage} parse-error`);
14301
+ return;
14302
+ }
14303
+ const root = doc.documentElement;
14304
+ const svgWidth = root == null ? void 0 : root.getAttribute("width");
14305
+ const svgHeight = root == null ? void 0 : root.getAttribute("height");
14306
+ const svgViewBox = root == null ? void 0 : root.getAttribute("viewBox");
14307
+ const texts = Array.from(doc.querySelectorAll("text"));
14308
+ const summary = {
14309
+ page: pageIndex,
14310
+ stage,
14311
+ svgLen: svgStr.length,
14312
+ svgWidth,
14313
+ svgHeight,
14314
+ svgViewBox,
14315
+ textCount: texts.length
14358
14316
  };
14359
- fabricInstance.getObjects().forEach(primeCharBounds);
14360
- (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
14361
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
14362
- await waitForPaint();
14363
- (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
14364
- await waitForPaint();
14317
+ console.log(`${tag} ${stage} page=${pageIndex} summary`, summary);
14318
+ const sample = texts.slice(0, maxItems).map((t, idx) => {
14319
+ var _a, _b;
14320
+ const tspans = Array.from(t.querySelectorAll("tspan"));
14321
+ const tspanInfo = tspans.slice(0, 4).map((s) => ({
14322
+ x: s.getAttribute("x"),
14323
+ y: s.getAttribute("y"),
14324
+ text: (s.textContent || "").slice(0, 40)
14325
+ }));
14326
+ let containerWidth = null;
14327
+ let cursor = t.parentElement;
14328
+ while (cursor && !containerWidth) {
14329
+ containerWidth = (_a = cursor.getAttribute) == null ? void 0 : _a.call(cursor, "width");
14330
+ cursor = cursor.parentElement;
14331
+ if (cursor && ((_b = cursor.tagName) == null ? void 0 : _b.toLowerCase()) === "svg") break;
14332
+ }
14333
+ return {
14334
+ idx,
14335
+ x: t.getAttribute("x"),
14336
+ y: t.getAttribute("y"),
14337
+ fontSize: t.getAttribute("font-size"),
14338
+ fontFamily: t.getAttribute("font-family"),
14339
+ fontWeight: t.getAttribute("font-weight"),
14340
+ textAnchor: t.getAttribute("text-anchor"),
14341
+ transform: t.getAttribute("transform"),
14342
+ style: (t.getAttribute("style") || "").slice(0, 120),
14343
+ ancestorWidth: containerWidth,
14344
+ textContent: (t.textContent || "").slice(0, 60),
14345
+ tspanCount: tspans.length,
14346
+ tspanSample: tspanInfo
14347
+ };
14348
+ });
14349
+ console.log(`${tag} ${stage} page=${pageIndex} text-sample (first ${sample.length}/${texts.length})`, sample);
14350
+ } catch (err) {
14351
+ console.warn(`${tag} ${stage} page=${pageIndex} dump threw`, err);
14365
14352
  }
14366
14353
  }
14367
14354
  const SVG_DRAWABLE_TAGS = /* @__PURE__ */ new Set([
@@ -15659,6 +15646,15 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
15659
15646
  const { title, stripPageBackground } = options;
15660
15647
  const firstPage = svgResults[0];
15661
15648
  const orientation = firstPage.width > firstPage.height ? "landscape" : "portrait";
15649
+ const PARITY_TAG = "[canvas-renderer][parity-diag][pkg-pdf]";
15650
+ console.log(`${PARITY_TAG} pkg-version=0.5.71 pages=${svgResults.length}`);
15651
+ try {
15652
+ for (let pi = 0; pi < svgResults.length; pi++) {
15653
+ dumpSvgTextDiagnostics(svgResults[pi].svg, pi, PARITY_TAG, "STAGE-1-raw-toSVG");
15654
+ }
15655
+ } catch (e) {
15656
+ console.warn(`${PARITY_TAG} dump failed`, e);
15657
+ }
15662
15658
  const pdf = new jspdf.jsPDF({
15663
15659
  orientation,
15664
15660
  unit: "px",
@@ -15688,6 +15684,15 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
15688
15684
  stripPageBackground: shouldStripBg
15689
15685
  });
15690
15686
  if (processedSvg) {
15687
+ try {
15688
+ dumpSvgTextDiagnostics(
15689
+ new XMLSerializer().serializeToString(processedSvg),
15690
+ i,
15691
+ PARITY_TAG,
15692
+ "STAGE-2-after-prepareLiveCanvasSvgForPdf"
15693
+ );
15694
+ } catch {
15695
+ }
15691
15696
  await convertTextDecorationsToLines(processedSvg);
15692
15697
  if (shouldOutlineText) {
15693
15698
  try {
@@ -15709,6 +15714,15 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
15709
15714
  const reParser = new DOMParser();
15710
15715
  const reDoc = reParser.parseFromString(rewrittenSvg, "image/svg+xml");
15711
15716
  processedSvg = reDoc.documentElement;
15717
+ try {
15718
+ dumpSvgTextDiagnostics(
15719
+ rewrittenSvg,
15720
+ i,
15721
+ PARITY_TAG,
15722
+ "STAGE-3-after-rewriteSvgFontsForJsPDF"
15723
+ );
15724
+ } catch {
15725
+ }
15712
15726
  }
15713
15727
  if (processedSvg) {
15714
15728
  pdf.setFillColor(0, 0, 0);
@@ -15732,7 +15746,8 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
15732
15746
  }
15733
15747
  const pdfExport = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
15734
15748
  __proto__: null,
15735
- assemblePdfFromSvgs
15749
+ assemblePdfFromSvgs,
15750
+ dumpSvgTextDiagnostics
15736
15751
  }, Symbol.toStringTag, { value: "Module" }));
15737
15752
  function collectImageUrls(config) {
15738
15753
  const urls = [];
@@ -15835,6 +15850,7 @@ exports.collectFontDescriptorsFromConfig = collectFontDescriptorsFromConfig;
15835
15850
  exports.collectFontsFromConfig = collectFontsFromConfig;
15836
15851
  exports.collectImageUrls = collectImageUrls;
15837
15852
  exports.configHasAutoShrinkText = configHasAutoShrinkText$1;
15853
+ exports.dumpSvgTextDiagnostics = dumpSvgTextDiagnostics;
15838
15854
  exports.embedFont = embedFont;
15839
15855
  exports.embedFontsForConfig = embedFontsForConfig;
15840
15856
  exports.embedFontsInPdf = embedFontsInPdf;