@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.
Files changed (50) hide show
  1. package/dist/direct-engine-BeZ18SKz.cjs +2 -0
  2. package/dist/direct-engine-BeZ18SKz.cjs.map +1 -0
  3. package/dist/{direct-engine-CHrj3o_a.js → direct-engine-CB3k-o0I.js} +509 -9
  4. package/dist/direct-engine-CB3k-o0I.js.map +1 -0
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.js +11 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/orchestrator/remote-executor.d.ts +5 -0
  10. package/dist/lib/pdfium/cdn-fonts.d.ts +50 -0
  11. package/dist/lib/pdfium/engine.d.ts +17 -2
  12. package/dist/lib/pdfium/font-fallback.d.ts +207 -0
  13. package/dist/lib/pdfium/index.cjs +1 -1
  14. package/dist/lib/pdfium/index.cjs.map +1 -1
  15. package/dist/lib/pdfium/index.d.ts +2 -0
  16. package/dist/lib/pdfium/index.js +55 -2
  17. package/dist/lib/pdfium/index.js.map +1 -1
  18. package/dist/lib/pdfium/runner.d.ts +5 -1
  19. package/dist/lib/pdfium/web/direct-engine.cjs +1 -1
  20. package/dist/lib/pdfium/web/direct-engine.d.ts +8 -0
  21. package/dist/lib/pdfium/web/direct-engine.js +1 -1
  22. package/dist/lib/pdfium/web/worker-engine.cjs +1 -1
  23. package/dist/lib/pdfium/web/worker-engine.cjs.map +1 -1
  24. package/dist/lib/pdfium/web/worker-engine.d.ts +8 -0
  25. package/dist/lib/pdfium/web/worker-engine.js +5 -4
  26. package/dist/lib/pdfium/web/worker-engine.js.map +1 -1
  27. package/dist/preact/index.cjs +1 -1
  28. package/dist/preact/index.cjs.map +1 -1
  29. package/dist/preact/index.js +14 -4
  30. package/dist/preact/index.js.map +1 -1
  31. package/dist/react/index.cjs +1 -1
  32. package/dist/react/index.cjs.map +1 -1
  33. package/dist/react/index.js +14 -4
  34. package/dist/react/index.js.map +1 -1
  35. package/dist/shared-preact/hooks/use-pdfium-engine.d.ts +5 -0
  36. package/dist/shared-react/hooks/use-pdfium-engine.d.ts +5 -0
  37. package/dist/svelte/hooks/use-pdfium-engine.svelte.d.ts +5 -0
  38. package/dist/svelte/index.cjs +1 -1
  39. package/dist/svelte/index.cjs.map +1 -1
  40. package/dist/svelte/index.js +8 -3
  41. package/dist/svelte/index.js.map +1 -1
  42. package/dist/vue/composables/use-pdfium-engine.d.ts +5 -0
  43. package/dist/vue/index.cjs +1 -1
  44. package/dist/vue/index.cjs.map +1 -1
  45. package/dist/vue/index.js +4 -4
  46. package/dist/vue/index.js.map +1 -1
  47. package/package.json +10 -3
  48. package/dist/direct-engine-B7b7cTsH.cjs +0 -2
  49. package/dist/direct-engine-B7b7cTsH.cjs.map +0 -1
  50. 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$1 = "PDFiumEngine";
384
- const LOG_CATEGORY$1 = "MemoryManager";
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$1, LOG_CATEGORY$1, `Freeing untracked pointer: ${ptr}`);
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$1,
445
- LOG_CATEGORY$1,
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$1, LOG_CATEGORY$1, ` - ${ptr}: ${alloc.size} bytes`, alloc.stack);
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
- const { logger = new NoopLogger() } = options;
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, { logger: options == null ? void 0 : options.logger });
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-CHrj3o_a.js.map
7308
+ //# sourceMappingURL=direct-engine-CB3k-o0I.js.map