@pixldocs/canvas-renderer 0.5.70 → 0.5.72

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