@@ -481,6 +481,164 @@ def run_task_test():
481481 l .ok ()
482482
483483
484+ # -----------------------------------------------------------------------------
485+ def run_task_test_wasmtime ():
486+ from pathlib import Path
487+
488+ from wasmtime import Engine , FuncType , Linker , Module , Store , WasiConfig
489+
490+ l .colored ("Testing with wasmtime..." , l .YELLOW )
491+
492+ current_dir = f .current_dir ()
493+
494+ for config in c .configurations_wasm :
495+ for target in c .targets_wasm :
496+ l .colored (
497+ 'Testing arch "{0}" and configuration "{1}"...' .format (
498+ target ["target_cpu" ], config
499+ ),
500+ l .YELLOW ,
501+ )
502+
503+ # paths
504+ relative_dir = os .path .join (
505+ "build" ,
506+ target ["target_os" ],
507+ target ["target_cpu" ],
508+ config ,
509+ )
510+
511+ root_dir = os .path .join (current_dir , relative_dir )
512+ node_dir = os .path .join (root_dir , "node" )
513+ wasm_file = os .path .join (node_dir , "pdfium.std.wasm" )
514+
515+ # check if wasm file exists
516+ if not f .file_exists (wasm_file ):
517+ l .e (f"WASM file not found: { wasm_file } " )
518+ continue
519+
520+ l .bullet (f"WASM file: { wasm_file } " , l .YELLOW )
521+
522+ # create engine and load the pdfium.wasm module
523+ engine = Engine ()
524+ module = Module .from_file (engine , wasm_file )
525+
526+ # create WASI context and store
527+ wasi_config = WasiConfig ()
528+ wasi_config .inherit_stdin ()
529+ wasi_config .inherit_stdout ()
530+ wasi_config .inherit_stderr ()
531+
532+ store = Store (engine )
533+ store .set_wasi (wasi_config )
534+
535+ # create a linker and add WASI support
536+ linker = Linker (engine )
537+ linker .define_wasi ()
538+
539+ # define stub functions for unknown imports
540+ for imp in module .imports :
541+ module_name = imp .module
542+ field_name = imp .name
543+
544+ # check if this is a function import
545+ if isinstance (imp .type , FuncType ):
546+ func_type = imp .type
547+
548+ # create a stub function that returns default values
549+ def make_stub (ft ):
550+ def stub_func (* _args ):
551+ # return default values (0 or None) based on results
552+ if ft .results :
553+ if len (ft .results ) == 1 :
554+ return 0
555+ return tuple (0 for _ in ft .results )
556+ return None
557+
558+ return stub_func
559+
560+ try :
561+ linker .define_func (
562+ module_name , field_name , func_type , make_stub (func_type )
563+ )
564+ except Exception :
565+ # already defined (e.g., by WASI)
566+ pass
567+
568+ # instantiate the module
569+ instance = linker .instantiate (store , module )
570+ exports = instance .exports (store )
571+
572+ # get and call FPDF_InitLibrary function
573+ try :
574+ init_library = exports ["FPDF_InitLibrary" ]
575+ init_library (store )
576+ l .bullet ("FPDF_InitLibrary successfully called" , l .GREEN )
577+ except KeyError :
578+ l .e ("Function 'FPDF_InitLibrary' not found" )
579+ continue
580+
581+ # test with a sample PDF file
582+ sample_pdf = os .path .join (
583+ current_dir , "sample-wasm" , "assets" , "web-assembly.pdf"
584+ )
585+
586+ if not f .file_exists (sample_pdf ):
587+ l .bullet ("Sample PDF not found, skipping document test" , l .PURPLE )
588+ continue
589+
590+ l .bullet (f"Testing with PDF: { sample_pdf } " , l .YELLOW )
591+
592+ # read PDF data
593+ pdf_path = Path (sample_pdf )
594+ pdf_data = pdf_path .read_bytes ()
595+
596+ # allocate memory for the PDF data
597+ malloc = exports ["malloc" ]
598+ memory = exports ["memory" ]
599+
600+ # allocate buffer in WASM memory
601+ buf_ptr = malloc (store , len (pdf_data ))
602+ mem_data = memory .data_ptr (store )
603+
604+ # copy PDF data to WASM memory
605+ for i , byte in enumerate (pdf_data ):
606+ mem_data [buf_ptr + i ] = byte
607+
608+ # load the PDF document from memory
609+ fpdf_load_mem_document = exports ["FPDF_LoadMemDocument" ]
610+ doc = fpdf_load_mem_document (store , buf_ptr , len (pdf_data ), 0 )
611+
612+ if doc == 0 :
613+ get_last_error = exports ["FPDF_GetLastError" ]
614+ error = get_last_error (store )
615+ l .e (f"Failed to load PDF. Error code: { error } " )
616+
617+ # free the allocated memory
618+ free = exports ["free" ]
619+ free (store , buf_ptr )
620+ continue
621+
622+ # get page count
623+ fpdf_get_page_count = exports ["FPDF_GetPageCount" ]
624+ page_count = fpdf_get_page_count (store , doc )
625+
626+ l .bullet (f"PDF: { pdf_path .name } " , l .GREEN )
627+ l .bullet (f"Number of pages: { page_count } " , l .GREEN )
628+
629+ # close the document
630+ fpdf_close_document = exports ["FPDF_CloseDocument" ]
631+ fpdf_close_document (store , doc )
632+
633+ # free the allocated memory
634+ free = exports ["free" ]
635+ free (store , buf_ptr )
636+
637+ l .bullet ("Test completed successfully" , l .GREEN )
638+
639+ l .ok ()
640+
641+
484642# -----------------------------------------------------------------------------
485643def run_task_generate ():
486644 l .colored ("Generating..." , l .YELLOW )
@@ -591,7 +749,8 @@ def run_task_generate():
591749 "{0}" .format ("-g" if config == "debug" else "-O2" ),
592750 "-s" ,
593751 f"EXPORTED_FUNCTIONS={ complete_functions_list } " ,
594- "-s" , "ALLOW_TABLE_GROWTH" ,
752+ "-s" ,
753+ "ALLOW_TABLE_GROWTH" ,
595754 "-s" ,
596755 'EXPORTED_RUNTIME_METHODS=\' ["ccall", "cwrap", "wasmExports", "HEAP8", "HEAP16", "HEAP32", "HEAPU8", "HEAPU16", "HEAPU32", "HEAPF32", "HEAPF64", "addFunction", "removeFunction", "setValue"]\' ' ,
597756 "custom.cpp" ,
@@ -624,12 +783,27 @@ def run_task_generate():
624783 l .colored ("Compiling ES6 module with emscripten..." , l .YELLOW )
625784 es6_command = [
626785 * base_command ,
627- "-s" "EXPORT_ES6=1" ,
786+ "-s" ,
787+ "EXPORT_ES6=1" ,
628788 "-o" ,
629789 os .path .join (gen_out_dir , "pdfium.esm.js" ),
630790 ]
631791 r .run (" " .join (es6_command ), cwd = gen_utils_dir , shell = True )
632792
793+ # Generate STANDALONE module, only .js will be generated (no .wasm)
794+ l .colored ("Compiling STANDALONE module with emscripten..." , l .YELLOW )
795+ std_command = [
796+ * base_command ,
797+ "-s" "STANDALONE_WASM=1" ,
798+ "-sSTANDALONE_WASM=1" ,
799+ "-sWASM_ASYNC_COMPILATION=0" ,
800+ "-sWARN_ON_UNDEFINED_SYMBOLS=1" ,
801+ "-sERROR_ON_UNDEFINED_SYMBOLS=0" ,
802+ "-o" ,
803+ os .path .join (gen_out_dir , "pdfium.std.js" ),
804+ ]
805+ r .run (" " .join (std_command ), cwd = gen_utils_dir , shell = True )
806+
633807 # copy files
634808 l .colored ("Copying compiled files..." , l .YELLOW )
635809
@@ -769,7 +943,9 @@ def run_task_archive():
769943 )
770944
771945 # Create per config "npm install"-compatible tarball
772- per_config_tar = tarfile .open (os .path .join (current_dir , f"wasm-{ config } .tgz" ), "w:gz" )
946+ per_config_tar = tarfile .open (
947+ os .path .join (current_dir , f"wasm-{ config } .tgz" ), "w:gz"
948+ )
773949 per_config_tar .add (
774950 name = lib_dir ,
775951 # Use "package" as the root directory to be compatible with "npm install"
0 commit comments