@embedpdf/engines 2.0.2 → 2.1.0
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/direct-engine-BeZ18SKz.cjs +2 -0
- package/dist/direct-engine-BeZ18SKz.cjs.map +1 -0
- package/dist/{direct-engine-CHrj3o_a.js → direct-engine-CB3k-o0I.js} +509 -9
- package/dist/direct-engine-CB3k-o0I.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/orchestrator/remote-executor.d.ts +5 -0
- package/dist/lib/pdfium/cdn-fonts.d.ts +50 -0
- package/dist/lib/pdfium/engine.d.ts +17 -2
- package/dist/lib/pdfium/font-fallback.d.ts +207 -0
- package/dist/lib/pdfium/index.cjs +1 -1
- package/dist/lib/pdfium/index.cjs.map +1 -1
- package/dist/lib/pdfium/index.d.ts +2 -0
- package/dist/lib/pdfium/index.js +55 -2
- package/dist/lib/pdfium/index.js.map +1 -1
- package/dist/lib/pdfium/runner.d.ts +5 -1
- package/dist/lib/pdfium/web/direct-engine.cjs +1 -1
- package/dist/lib/pdfium/web/direct-engine.d.ts +8 -0
- package/dist/lib/pdfium/web/direct-engine.js +1 -1
- package/dist/lib/pdfium/web/worker-engine.cjs +1 -1
- package/dist/lib/pdfium/web/worker-engine.cjs.map +1 -1
- package/dist/lib/pdfium/web/worker-engine.d.ts +8 -0
- package/dist/lib/pdfium/web/worker-engine.js +5 -4
- package/dist/lib/pdfium/web/worker-engine.js.map +1 -1
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +14 -4
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +14 -4
- package/dist/react/index.js.map +1 -1
- package/dist/shared-preact/hooks/use-pdfium-engine.d.ts +5 -0
- package/dist/shared-react/hooks/use-pdfium-engine.d.ts +5 -0
- package/dist/svelte/hooks/use-pdfium-engine.svelte.d.ts +5 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +8 -3
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/composables/use-pdfium-engine.d.ts +5 -0
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +4 -4
- package/dist/vue/index.js.map +1 -1
- package/package.json +10 -3
- package/dist/direct-engine-B7b7cTsH.cjs +0 -2
- package/dist/direct-engine-B7b7cTsH.cjs.map +0 -1
- package/dist/direct-engine-CHrj3o_a.js.map +0 -1
|
@@ -380,8 +380,8 @@ const MEMORY_LIMITS = {
|
|
|
380
380
|
const LIMITS = {
|
|
381
381
|
MEMORY: MEMORY_LIMITS
|
|
382
382
|
};
|
|
383
|
-
const LOG_SOURCE$
|
|
384
|
-
const LOG_CATEGORY$
|
|
383
|
+
const LOG_SOURCE$2 = "PDFiumEngine";
|
|
384
|
+
const LOG_CATEGORY$2 = "MemoryManager";
|
|
385
385
|
class MemoryManager {
|
|
386
386
|
constructor(pdfiumModule, logger) {
|
|
387
387
|
this.pdfiumModule = pdfiumModule;
|
|
@@ -418,7 +418,7 @@ class MemoryManager {
|
|
|
418
418
|
free(ptr) {
|
|
419
419
|
const allocation = this.allocations.get(ptr);
|
|
420
420
|
if (!allocation) {
|
|
421
|
-
this.logger.warn(LOG_SOURCE$
|
|
421
|
+
this.logger.warn(LOG_SOURCE$2, LOG_CATEGORY$2, `Freeing untracked pointer: ${ptr}`);
|
|
422
422
|
} else {
|
|
423
423
|
this.totalAllocated -= allocation.size;
|
|
424
424
|
this.allocations.delete(ptr);
|
|
@@ -441,16 +441,494 @@ class MemoryManager {
|
|
|
441
441
|
checkLeaks() {
|
|
442
442
|
if (this.allocations.size > 0) {
|
|
443
443
|
this.logger.warn(
|
|
444
|
-
LOG_SOURCE$
|
|
445
|
-
LOG_CATEGORY$
|
|
444
|
+
LOG_SOURCE$2,
|
|
445
|
+
LOG_CATEGORY$2,
|
|
446
446
|
`Potential memory leak: ${this.allocations.size} unfreed allocations`
|
|
447
447
|
);
|
|
448
448
|
for (const [ptr, alloc] of this.allocations) {
|
|
449
|
-
this.logger.warn(LOG_SOURCE$
|
|
449
|
+
this.logger.warn(LOG_SOURCE$2, LOG_CATEGORY$2, ` - ${ptr}: ${alloc.size} bytes`, alloc.stack);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const SYSFONTINFO_SIZE = 36;
|
|
455
|
+
const OFFSET_VERSION = 0;
|
|
456
|
+
const OFFSET_RELEASE = 4;
|
|
457
|
+
const OFFSET_ENUMFONTS = 8;
|
|
458
|
+
const OFFSET_MAPFONT = 12;
|
|
459
|
+
const OFFSET_GETFONT = 16;
|
|
460
|
+
const OFFSET_GETFONTDATA = 20;
|
|
461
|
+
const OFFSET_GETFACENAME = 24;
|
|
462
|
+
const OFFSET_GETFONTCHARSET = 28;
|
|
463
|
+
const OFFSET_DELETEFONT = 32;
|
|
464
|
+
const LOG_SOURCE$1 = "pdfium";
|
|
465
|
+
const LOG_CATEGORY$1 = "font-fallback";
|
|
466
|
+
class FontFallbackManager {
|
|
467
|
+
constructor(config, logger = new NoopLogger()) {
|
|
468
|
+
this.fontHandles = /* @__PURE__ */ new Map();
|
|
469
|
+
this.fontCache = /* @__PURE__ */ new Map();
|
|
470
|
+
this.nextHandleId = 1;
|
|
471
|
+
this.module = null;
|
|
472
|
+
this.enabled = false;
|
|
473
|
+
this.structPtr = 0;
|
|
474
|
+
this.releaseFnPtr = 0;
|
|
475
|
+
this.enumFontsFnPtr = 0;
|
|
476
|
+
this.mapFontFnPtr = 0;
|
|
477
|
+
this.getFontFnPtr = 0;
|
|
478
|
+
this.getFontDataFnPtr = 0;
|
|
479
|
+
this.getFaceNameFnPtr = 0;
|
|
480
|
+
this.getFontCharsetFnPtr = 0;
|
|
481
|
+
this.deleteFontFnPtr = 0;
|
|
482
|
+
this.fontConfig = config;
|
|
483
|
+
this.logger = logger;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Initialize the font fallback system and attach to PDFium module
|
|
487
|
+
*/
|
|
488
|
+
initialize(module) {
|
|
489
|
+
if (this.enabled) {
|
|
490
|
+
this.logger.warn(LOG_SOURCE$1, LOG_CATEGORY$1, "Font fallback already initialized");
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
this.module = module;
|
|
494
|
+
const pdfium = module.pdfium;
|
|
495
|
+
if (typeof pdfium.addFunction !== "function") {
|
|
496
|
+
this.logger.error(
|
|
497
|
+
LOG_SOURCE$1,
|
|
498
|
+
LOG_CATEGORY$1,
|
|
499
|
+
"addFunction not available. Make sure WASM is compiled with -sALLOW_TABLE_GROWTH"
|
|
500
|
+
);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
try {
|
|
504
|
+
this.structPtr = pdfium.wasmExports.malloc(SYSFONTINFO_SIZE);
|
|
505
|
+
if (!this.structPtr) {
|
|
506
|
+
throw new Error("Failed to allocate FPDF_SYSFONTINFO struct");
|
|
507
|
+
}
|
|
508
|
+
for (let i = 0; i < SYSFONTINFO_SIZE; i++) {
|
|
509
|
+
pdfium.setValue(this.structPtr + i, 0, "i8");
|
|
510
|
+
}
|
|
511
|
+
this.releaseFnPtr = pdfium.addFunction((_pThis) => {
|
|
512
|
+
}, "vi");
|
|
513
|
+
this.enumFontsFnPtr = pdfium.addFunction((_pThis, _pMapper) => {
|
|
514
|
+
}, "vii");
|
|
515
|
+
this.mapFontFnPtr = pdfium.addFunction(
|
|
516
|
+
(_pThis, weight, bItalic, charset, pitchFamily, facePtr, bExactPtr) => {
|
|
517
|
+
const face = facePtr ? pdfium.UTF8ToString(facePtr) : "";
|
|
518
|
+
const handle = this.mapFont(weight, bItalic, charset, pitchFamily, face);
|
|
519
|
+
if (bExactPtr) {
|
|
520
|
+
pdfium.setValue(bExactPtr, 0, "i32");
|
|
521
|
+
}
|
|
522
|
+
return handle;
|
|
523
|
+
},
|
|
524
|
+
"iiiiiiii"
|
|
525
|
+
);
|
|
526
|
+
this.getFontFnPtr = pdfium.addFunction((_pThis, facePtr) => {
|
|
527
|
+
const face = facePtr ? pdfium.UTF8ToString(facePtr) : "";
|
|
528
|
+
return this.mapFont(400, 0, 0, 0, face);
|
|
529
|
+
}, "iii");
|
|
530
|
+
this.getFontDataFnPtr = pdfium.addFunction(
|
|
531
|
+
(_pThis, hFont, table, buffer, bufSize) => {
|
|
532
|
+
return this.getFontData(hFont, table, buffer, bufSize);
|
|
533
|
+
},
|
|
534
|
+
"iiiiii"
|
|
535
|
+
);
|
|
536
|
+
this.getFaceNameFnPtr = pdfium.addFunction(
|
|
537
|
+
(_pThis, _hFont, _buffer, _bufSize) => {
|
|
538
|
+
return 0;
|
|
539
|
+
},
|
|
540
|
+
"iiiii"
|
|
541
|
+
);
|
|
542
|
+
this.getFontCharsetFnPtr = pdfium.addFunction((_pThis, hFont) => {
|
|
543
|
+
const handle = this.fontHandles.get(hFont);
|
|
544
|
+
return (handle == null ? void 0 : handle.charset) ?? 0;
|
|
545
|
+
}, "iii");
|
|
546
|
+
this.deleteFontFnPtr = pdfium.addFunction((_pThis, hFont) => {
|
|
547
|
+
this.deleteFont(hFont);
|
|
548
|
+
}, "vii");
|
|
549
|
+
pdfium.setValue(this.structPtr + OFFSET_VERSION, 1, "i32");
|
|
550
|
+
pdfium.setValue(this.structPtr + OFFSET_RELEASE, this.releaseFnPtr, "i32");
|
|
551
|
+
pdfium.setValue(this.structPtr + OFFSET_ENUMFONTS, this.enumFontsFnPtr, "i32");
|
|
552
|
+
pdfium.setValue(this.structPtr + OFFSET_MAPFONT, this.mapFontFnPtr, "i32");
|
|
553
|
+
pdfium.setValue(this.structPtr + OFFSET_GETFONT, this.getFontFnPtr, "i32");
|
|
554
|
+
pdfium.setValue(this.structPtr + OFFSET_GETFONTDATA, this.getFontDataFnPtr, "i32");
|
|
555
|
+
pdfium.setValue(this.structPtr + OFFSET_GETFACENAME, this.getFaceNameFnPtr, "i32");
|
|
556
|
+
pdfium.setValue(this.structPtr + OFFSET_GETFONTCHARSET, this.getFontCharsetFnPtr, "i32");
|
|
557
|
+
pdfium.setValue(this.structPtr + OFFSET_DELETEFONT, this.deleteFontFnPtr, "i32");
|
|
558
|
+
module.FPDF_SetSystemFontInfo(this.structPtr);
|
|
559
|
+
this.enabled = true;
|
|
560
|
+
this.logger.info(
|
|
561
|
+
LOG_SOURCE$1,
|
|
562
|
+
LOG_CATEGORY$1,
|
|
563
|
+
"Font fallback system initialized (pure TypeScript)",
|
|
564
|
+
Object.keys(this.fontConfig.fonts)
|
|
565
|
+
);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
this.logger.error(LOG_SOURCE$1, LOG_CATEGORY$1, "Failed to initialize font fallback", error);
|
|
568
|
+
this.cleanup();
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Disable the font fallback system and clean up resources
|
|
574
|
+
*/
|
|
575
|
+
disable() {
|
|
576
|
+
if (!this.enabled || !this.module) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
this.module.FPDF_SetSystemFontInfo(0);
|
|
580
|
+
this.cleanup();
|
|
581
|
+
this.enabled = false;
|
|
582
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, "Font fallback system disabled");
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Clean up allocated resources
|
|
586
|
+
*/
|
|
587
|
+
cleanup() {
|
|
588
|
+
if (!this.module) return;
|
|
589
|
+
const pdfium = this.module.pdfium;
|
|
590
|
+
if (this.structPtr) {
|
|
591
|
+
pdfium.wasmExports.free(this.structPtr);
|
|
592
|
+
this.structPtr = 0;
|
|
593
|
+
}
|
|
594
|
+
const removeIfExists = (ptr) => {
|
|
595
|
+
if (ptr && typeof pdfium.removeFunction === "function") {
|
|
596
|
+
try {
|
|
597
|
+
pdfium.removeFunction(ptr);
|
|
598
|
+
} catch {
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
removeIfExists(this.releaseFnPtr);
|
|
603
|
+
removeIfExists(this.enumFontsFnPtr);
|
|
604
|
+
removeIfExists(this.mapFontFnPtr);
|
|
605
|
+
removeIfExists(this.getFontFnPtr);
|
|
606
|
+
removeIfExists(this.getFontDataFnPtr);
|
|
607
|
+
removeIfExists(this.getFaceNameFnPtr);
|
|
608
|
+
removeIfExists(this.getFontCharsetFnPtr);
|
|
609
|
+
removeIfExists(this.deleteFontFnPtr);
|
|
610
|
+
this.releaseFnPtr = 0;
|
|
611
|
+
this.enumFontsFnPtr = 0;
|
|
612
|
+
this.mapFontFnPtr = 0;
|
|
613
|
+
this.getFontFnPtr = 0;
|
|
614
|
+
this.getFontDataFnPtr = 0;
|
|
615
|
+
this.getFaceNameFnPtr = 0;
|
|
616
|
+
this.getFontCharsetFnPtr = 0;
|
|
617
|
+
this.deleteFontFnPtr = 0;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Check if font fallback is enabled
|
|
621
|
+
*/
|
|
622
|
+
isEnabled() {
|
|
623
|
+
return this.enabled;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Get statistics about font loading
|
|
627
|
+
*/
|
|
628
|
+
getStats() {
|
|
629
|
+
return {
|
|
630
|
+
handleCount: this.fontHandles.size,
|
|
631
|
+
cacheSize: this.fontCache.size,
|
|
632
|
+
cachedUrls: Array.from(this.fontCache.keys())
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Pre-load fonts for specific charsets (optional optimization)
|
|
637
|
+
* This can be called to warm the cache before rendering
|
|
638
|
+
*/
|
|
639
|
+
async preloadFonts(charsets) {
|
|
640
|
+
const urls = charsets.map((charset) => this.getFontUrlForCharset(charset)).filter((url) => url !== null);
|
|
641
|
+
const uniqueUrls = [...new Set(urls)];
|
|
642
|
+
await Promise.all(
|
|
643
|
+
uniqueUrls.map(async (url) => {
|
|
644
|
+
if (!this.fontCache.has(url)) {
|
|
645
|
+
try {
|
|
646
|
+
const data = await this.fetchFontAsync(url);
|
|
647
|
+
if (data) {
|
|
648
|
+
this.fontCache.set(url, data);
|
|
649
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, `Pre-loaded font: ${url}`);
|
|
650
|
+
}
|
|
651
|
+
} catch (error) {
|
|
652
|
+
this.logger.warn(LOG_SOURCE$1, LOG_CATEGORY$1, `Failed to pre-load font: ${url}`, error);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
// ============================================================================
|
|
659
|
+
// PDFium Callback Implementations
|
|
660
|
+
// ============================================================================
|
|
661
|
+
/**
|
|
662
|
+
* MapFont - called by PDFium when it needs a font
|
|
663
|
+
*/
|
|
664
|
+
mapFont(weight, bItalic, charset, pitchFamily, face) {
|
|
665
|
+
const italic = bItalic !== 0;
|
|
666
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, "MapFont called", {
|
|
667
|
+
weight,
|
|
668
|
+
italic,
|
|
669
|
+
charset,
|
|
670
|
+
pitchFamily,
|
|
671
|
+
face
|
|
672
|
+
});
|
|
673
|
+
const result = this.findBestFontMatch(charset, weight, italic);
|
|
674
|
+
if (!result) {
|
|
675
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, `No font configured for charset ${charset}`);
|
|
676
|
+
return 0;
|
|
677
|
+
}
|
|
678
|
+
const handle = {
|
|
679
|
+
id: this.nextHandleId++,
|
|
680
|
+
charset,
|
|
681
|
+
weight,
|
|
682
|
+
italic,
|
|
683
|
+
url: result.url,
|
|
684
|
+
data: null
|
|
685
|
+
};
|
|
686
|
+
this.fontHandles.set(handle.id, handle);
|
|
687
|
+
this.logger.debug(
|
|
688
|
+
LOG_SOURCE$1,
|
|
689
|
+
LOG_CATEGORY$1,
|
|
690
|
+
`Created font handle ${handle.id} for ${result.url} (requested: weight=${weight}, italic=${italic}, matched: weight=${result.matchedWeight}, italic=${result.matchedItalic})`
|
|
691
|
+
);
|
|
692
|
+
return handle.id;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* GetFontData - called by PDFium to get font bytes
|
|
696
|
+
*/
|
|
697
|
+
getFontData(fontHandle, table, bufferPtr, bufSize) {
|
|
698
|
+
const handle = this.fontHandles.get(fontHandle);
|
|
699
|
+
if (!handle) {
|
|
700
|
+
this.logger.warn(LOG_SOURCE$1, LOG_CATEGORY$1, `Unknown font handle: ${fontHandle}`);
|
|
701
|
+
return 0;
|
|
702
|
+
}
|
|
703
|
+
if (!handle.data) {
|
|
704
|
+
if (this.fontCache.has(handle.url)) {
|
|
705
|
+
handle.data = this.fontCache.get(handle.url);
|
|
706
|
+
} else {
|
|
707
|
+
handle.data = this.fetchFontSync(handle.url);
|
|
708
|
+
if (handle.data) {
|
|
709
|
+
this.fontCache.set(handle.url, handle.data);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (!handle.data) {
|
|
714
|
+
this.logger.warn(LOG_SOURCE$1, LOG_CATEGORY$1, `Failed to load font: ${handle.url}`);
|
|
715
|
+
return 0;
|
|
716
|
+
}
|
|
717
|
+
const fontData = handle.data;
|
|
718
|
+
if (table !== 0) {
|
|
719
|
+
this.logger.debug(
|
|
720
|
+
LOG_SOURCE$1,
|
|
721
|
+
LOG_CATEGORY$1,
|
|
722
|
+
`Table ${table} requested - returning 0 to request whole file`
|
|
723
|
+
);
|
|
724
|
+
return 0;
|
|
725
|
+
}
|
|
726
|
+
if (bufferPtr === 0 || bufSize < fontData.length) {
|
|
727
|
+
return fontData.length;
|
|
728
|
+
}
|
|
729
|
+
if (this.module) {
|
|
730
|
+
const heap = this.module.pdfium.HEAPU8;
|
|
731
|
+
heap.set(fontData, bufferPtr);
|
|
732
|
+
this.logger.debug(
|
|
733
|
+
LOG_SOURCE$1,
|
|
734
|
+
LOG_CATEGORY$1,
|
|
735
|
+
`Copied ${fontData.length} bytes to buffer for handle ${fontHandle}`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
return fontData.length;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* DeleteFont - called by PDFium when done with a font
|
|
742
|
+
*/
|
|
743
|
+
deleteFont(fontHandle) {
|
|
744
|
+
const handle = this.fontHandles.get(fontHandle);
|
|
745
|
+
if (handle) {
|
|
746
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, `Deleting font handle ${fontHandle}`);
|
|
747
|
+
this.fontHandles.delete(fontHandle);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
// ============================================================================
|
|
751
|
+
// Helper Methods
|
|
752
|
+
// ============================================================================
|
|
753
|
+
/**
|
|
754
|
+
* Find the best matching font variant for the given parameters
|
|
755
|
+
*/
|
|
756
|
+
findBestFontMatch(charset, requestedWeight, requestedItalic) {
|
|
757
|
+
const { fonts, defaultFont, baseUrl } = this.fontConfig;
|
|
758
|
+
const entry = fonts[charset] ?? defaultFont;
|
|
759
|
+
if (!entry) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
const variants = this.normalizeToVariants(entry);
|
|
763
|
+
if (variants.length === 0) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
const best = this.selectBestVariant(variants, requestedWeight, requestedItalic);
|
|
767
|
+
let url = best.url;
|
|
768
|
+
if (baseUrl && !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("/")) {
|
|
769
|
+
url = `${baseUrl}/${url}`;
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
url,
|
|
773
|
+
matchedWeight: best.weight ?? 400,
|
|
774
|
+
matchedItalic: best.italic ?? false
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Normalize a FontEntry to an array of FontVariants
|
|
779
|
+
*/
|
|
780
|
+
normalizeToVariants(entry) {
|
|
781
|
+
if (typeof entry === "string") {
|
|
782
|
+
return [{ url: entry, weight: 400, italic: false }];
|
|
783
|
+
}
|
|
784
|
+
if (Array.isArray(entry)) {
|
|
785
|
+
return entry.map((v) => ({
|
|
786
|
+
url: v.url,
|
|
787
|
+
weight: v.weight ?? 400,
|
|
788
|
+
italic: v.italic ?? false
|
|
789
|
+
}));
|
|
790
|
+
}
|
|
791
|
+
return [{ url: entry.url, weight: entry.weight ?? 400, italic: entry.italic ?? false }];
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Select the best matching variant based on weight and italic
|
|
795
|
+
* Uses CSS font matching algorithm principles:
|
|
796
|
+
* 1. Exact italic match preferred
|
|
797
|
+
* 2. Closest weight (with bias toward bolder for weights >= 400)
|
|
798
|
+
*/
|
|
799
|
+
selectBestVariant(variants, requestedWeight, requestedItalic) {
|
|
800
|
+
if (variants.length === 1) {
|
|
801
|
+
return variants[0];
|
|
802
|
+
}
|
|
803
|
+
const italicMatches = variants.filter((v) => (v.italic ?? false) === requestedItalic);
|
|
804
|
+
const candidates = italicMatches.length > 0 ? italicMatches : variants;
|
|
805
|
+
let bestMatch = candidates[0];
|
|
806
|
+
let bestDistance = Math.abs((bestMatch.weight ?? 400) - requestedWeight);
|
|
807
|
+
for (const variant of candidates) {
|
|
808
|
+
const variantWeight = variant.weight ?? 400;
|
|
809
|
+
const distance = Math.abs(variantWeight - requestedWeight);
|
|
810
|
+
if (distance < bestDistance) {
|
|
811
|
+
bestMatch = variant;
|
|
812
|
+
bestDistance = distance;
|
|
813
|
+
} else if (distance === bestDistance) {
|
|
814
|
+
const currentWeight = bestMatch.weight ?? 400;
|
|
815
|
+
if (requestedWeight >= 500) {
|
|
816
|
+
if (variantWeight > currentWeight) {
|
|
817
|
+
bestMatch = variant;
|
|
818
|
+
}
|
|
819
|
+
} else {
|
|
820
|
+
if (variantWeight < currentWeight) {
|
|
821
|
+
bestMatch = variant;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return bestMatch;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Get font URL for a charset (backward compatible helper)
|
|
830
|
+
*/
|
|
831
|
+
getFontUrlForCharset(charset) {
|
|
832
|
+
const result = this.findBestFontMatch(charset, 400, false);
|
|
833
|
+
return (result == null ? void 0 : result.url) ?? null;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Fetch font data synchronously
|
|
837
|
+
* Uses custom fontLoader if provided, otherwise falls back to XMLHttpRequest (browser)
|
|
838
|
+
*/
|
|
839
|
+
fetchFontSync(pathOrUrl) {
|
|
840
|
+
this.logger.debug(LOG_SOURCE$1, LOG_CATEGORY$1, `Fetching font synchronously: ${pathOrUrl}`);
|
|
841
|
+
if (this.fontConfig.fontLoader) {
|
|
842
|
+
try {
|
|
843
|
+
const data = this.fontConfig.fontLoader(pathOrUrl);
|
|
844
|
+
if (data) {
|
|
845
|
+
this.logger.info(
|
|
846
|
+
LOG_SOURCE$1,
|
|
847
|
+
LOG_CATEGORY$1,
|
|
848
|
+
`Loaded font via custom loader: ${pathOrUrl} (${data.length} bytes)`
|
|
849
|
+
);
|
|
850
|
+
} else {
|
|
851
|
+
this.logger.warn(
|
|
852
|
+
LOG_SOURCE$1,
|
|
853
|
+
LOG_CATEGORY$1,
|
|
854
|
+
`Custom font loader returned null for: ${pathOrUrl}`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
return data;
|
|
858
|
+
} catch (error) {
|
|
859
|
+
this.logger.error(
|
|
860
|
+
LOG_SOURCE$1,
|
|
861
|
+
LOG_CATEGORY$1,
|
|
862
|
+
`Error in custom font loader: ${pathOrUrl}`,
|
|
863
|
+
error
|
|
864
|
+
);
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
try {
|
|
869
|
+
const xhr = new XMLHttpRequest();
|
|
870
|
+
xhr.open("GET", pathOrUrl, false);
|
|
871
|
+
xhr.responseType = "arraybuffer";
|
|
872
|
+
xhr.send();
|
|
873
|
+
if (xhr.status === 200) {
|
|
874
|
+
const data = new Uint8Array(xhr.response);
|
|
875
|
+
this.logger.info(
|
|
876
|
+
LOG_SOURCE$1,
|
|
877
|
+
LOG_CATEGORY$1,
|
|
878
|
+
`Loaded font: ${pathOrUrl} (${data.length} bytes)`
|
|
879
|
+
);
|
|
880
|
+
return data;
|
|
881
|
+
} else {
|
|
882
|
+
this.logger.error(
|
|
883
|
+
LOG_SOURCE$1,
|
|
884
|
+
LOG_CATEGORY$1,
|
|
885
|
+
`Failed to load font: ${pathOrUrl} (HTTP ${xhr.status})`
|
|
886
|
+
);
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
} catch (error) {
|
|
890
|
+
this.logger.error(LOG_SOURCE$1, LOG_CATEGORY$1, `Error fetching font: ${pathOrUrl}`, error);
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Fetch font data asynchronously (for preloading)
|
|
896
|
+
* Uses custom fontLoader if provided, otherwise falls back to fetch API
|
|
897
|
+
*/
|
|
898
|
+
async fetchFontAsync(pathOrUrl) {
|
|
899
|
+
if (this.fontConfig.fontLoader) {
|
|
900
|
+
try {
|
|
901
|
+
return this.fontConfig.fontLoader(pathOrUrl);
|
|
902
|
+
} catch {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
const response = await fetch(pathOrUrl);
|
|
908
|
+
if (response.ok) {
|
|
909
|
+
const buffer = await response.arrayBuffer();
|
|
910
|
+
return new Uint8Array(buffer);
|
|
450
911
|
}
|
|
912
|
+
return null;
|
|
913
|
+
} catch {
|
|
914
|
+
return null;
|
|
451
915
|
}
|
|
452
916
|
}
|
|
453
917
|
}
|
|
918
|
+
function createNodeFontLoader(fs, path, basePath) {
|
|
919
|
+
return (fontPath) => {
|
|
920
|
+
try {
|
|
921
|
+
const fullPath = path.join(basePath, fontPath);
|
|
922
|
+
const data = fs.readFileSync(fullPath);
|
|
923
|
+
if (data instanceof Uint8Array) {
|
|
924
|
+
return data;
|
|
925
|
+
}
|
|
926
|
+
return new Uint8Array(data);
|
|
927
|
+
} catch {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
}
|
|
454
932
|
var BitmapFormat = /* @__PURE__ */ ((BitmapFormat2) => {
|
|
455
933
|
BitmapFormat2[BitmapFormat2["Bitmap_Gray"] = 1] = "Bitmap_Gray";
|
|
456
934
|
BitmapFormat2[BitmapFormat2["Bitmap_BGR"] = 2] = "Bitmap_BGR";
|
|
@@ -494,7 +972,8 @@ class PdfiumNative {
|
|
|
494
972
|
constructor(pdfiumModule, options = {}) {
|
|
495
973
|
this.pdfiumModule = pdfiumModule;
|
|
496
974
|
this.memoryLeakCheckInterval = null;
|
|
497
|
-
|
|
975
|
+
this.fontFallbackManager = null;
|
|
976
|
+
const { logger = new NoopLogger(), fontFallback } = options;
|
|
498
977
|
this.logger = logger;
|
|
499
978
|
this.memoryManager = new MemoryManager(this.pdfiumModule, this.logger);
|
|
500
979
|
this.cache = new PdfCache(this.pdfiumModule, this.memoryManager);
|
|
@@ -507,6 +986,11 @@ class PdfiumNative {
|
|
|
507
986
|
this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Initialize`, "Begin", "General");
|
|
508
987
|
this.pdfiumModule.PDFiumExt_Init();
|
|
509
988
|
this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Initialize`, "End", "General");
|
|
989
|
+
if (fontFallback) {
|
|
990
|
+
this.fontFallbackManager = new FontFallbackManager(fontFallback, this.logger);
|
|
991
|
+
this.fontFallbackManager.initialize(this.pdfiumModule);
|
|
992
|
+
this.logger.info(LOG_SOURCE, LOG_CATEGORY, "Font fallback system enabled");
|
|
993
|
+
}
|
|
510
994
|
}
|
|
511
995
|
/**
|
|
512
996
|
* {@inheritDoc @embedpdf/models!PdfEngine.destroy}
|
|
@@ -516,6 +1000,10 @@ class PdfiumNative {
|
|
|
516
1000
|
destroy() {
|
|
517
1001
|
this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "destroy");
|
|
518
1002
|
this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Destroy`, "Begin", "General");
|
|
1003
|
+
if (this.fontFallbackManager) {
|
|
1004
|
+
this.fontFallbackManager.disable();
|
|
1005
|
+
this.fontFallbackManager = null;
|
|
1006
|
+
}
|
|
519
1007
|
this.pdfiumModule.FPDF_DestroyLibrary();
|
|
520
1008
|
if (this.memoryLeakCheckInterval) {
|
|
521
1009
|
clearInterval(this.memoryLeakCheckInterval);
|
|
@@ -524,6 +1012,13 @@ class PdfiumNative {
|
|
|
524
1012
|
this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Destroy`, "End", "General");
|
|
525
1013
|
return PdfTaskHelper.resolve(true);
|
|
526
1014
|
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Get the font fallback manager instance
|
|
1017
|
+
* Useful for pre-loading fonts or checking stats
|
|
1018
|
+
*/
|
|
1019
|
+
getFontFallbackManager() {
|
|
1020
|
+
return this.fontFallbackManager;
|
|
1021
|
+
}
|
|
527
1022
|
/** Write a UTF-16LE (WIDESTRING) to wasm, call `fn(ptr)`, then free. */
|
|
528
1023
|
withWString(value, fn) {
|
|
529
1024
|
const length = (value.length + 1) * 2;
|
|
@@ -6788,7 +7283,10 @@ async function createPdfiumEngine(wasmUrl, options) {
|
|
|
6788
7283
|
const response = await fetch(wasmUrl);
|
|
6789
7284
|
const wasmBinary = await response.arrayBuffer();
|
|
6790
7285
|
const wasmModule = await init({ wasmBinary });
|
|
6791
|
-
const native = new PdfiumNative(wasmModule, {
|
|
7286
|
+
const native = new PdfiumNative(wasmModule, {
|
|
7287
|
+
logger: options == null ? void 0 : options.logger,
|
|
7288
|
+
fontFallback: options == null ? void 0 : options.fontFallback
|
|
7289
|
+
});
|
|
6792
7290
|
return new PdfEngine(native, {
|
|
6793
7291
|
imageConverter: browserImageDataToBlobConverter,
|
|
6794
7292
|
logger: options == null ? void 0 : options.logger
|
|
@@ -6796,13 +7294,15 @@ async function createPdfiumEngine(wasmUrl, options) {
|
|
|
6796
7294
|
}
|
|
6797
7295
|
export {
|
|
6798
7296
|
BitmapFormat as B,
|
|
7297
|
+
FontFallbackManager as F,
|
|
6799
7298
|
PdfiumErrorCode as P,
|
|
6800
7299
|
RenderFlag as R,
|
|
6801
7300
|
PdfiumNative as a,
|
|
6802
7301
|
readArrayBuffer as b,
|
|
6803
7302
|
createPdfiumEngine as c,
|
|
6804
7303
|
computeFormDrawParams as d,
|
|
7304
|
+
createNodeFontLoader as e,
|
|
6805
7305
|
isValidCustomKey as i,
|
|
6806
7306
|
readString as r
|
|
6807
7307
|
};
|
|
6808
|
-
//# sourceMappingURL=direct-engine-
|
|
7308
|
+
//# sourceMappingURL=direct-engine-CB3k-o0I.js.map
|