diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b7ac1c3..a7242df 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -63,6 +63,15 @@ if(PDFENGINE_WITH_PDFIUM) target_compile_definitions(pdfengine PRIVATE PDFENGINE_WITH_PDFIUM) endif() +# Directory of the bundled fallback fonts (Carlito/Tinos). Under WASM they're embedded into the +# module's in-memory FS at /fonts (see wasm/CMakeLists.txt --embed-file); natively the engine reads +# them straight from the source assets dir. font_fallback.cpp prefers these over OS fonts. +if(EMSCRIPTEN) + target_compile_definitions(pdfengine PRIVATE PDFENGINE_FONT_DIR="/fonts") +else() + target_compile_definitions(pdfengine PRIVATE PDFENGINE_FONT_DIR="${CMAKE_SOURCE_DIR}/engine/assets/fonts") +endif() + if(PDFENGINE_WITH_SKIA) target_link_libraries(pdfengine PRIVATE skia::skia) target_compile_definitions(pdfengine PUBLIC PDFENGINE_WITH_SKIA) diff --git a/engine/assets/fonts/Carlito-Bold.ttf b/engine/assets/fonts/Carlito-Bold.ttf new file mode 100644 index 0000000..67543b5 Binary files /dev/null and b/engine/assets/fonts/Carlito-Bold.ttf differ diff --git a/engine/assets/fonts/Carlito-BoldItalic.ttf b/engine/assets/fonts/Carlito-BoldItalic.ttf new file mode 100644 index 0000000..e2c5d88 Binary files /dev/null and b/engine/assets/fonts/Carlito-BoldItalic.ttf differ diff --git a/engine/assets/fonts/Carlito-Italic.ttf b/engine/assets/fonts/Carlito-Italic.ttf new file mode 100644 index 0000000..8dc3c84 Binary files /dev/null and b/engine/assets/fonts/Carlito-Italic.ttf differ diff --git a/engine/assets/fonts/Carlito-Regular.ttf b/engine/assets/fonts/Carlito-Regular.ttf new file mode 100644 index 0000000..427b959 Binary files /dev/null and b/engine/assets/fonts/Carlito-Regular.ttf differ diff --git a/engine/assets/fonts/Tinos-Bold.ttf b/engine/assets/fonts/Tinos-Bold.ttf new file mode 100644 index 0000000..887fc97 Binary files /dev/null and b/engine/assets/fonts/Tinos-Bold.ttf differ diff --git a/engine/assets/fonts/Tinos-BoldItalic.ttf b/engine/assets/fonts/Tinos-BoldItalic.ttf new file mode 100644 index 0000000..a9abd08 Binary files /dev/null and b/engine/assets/fonts/Tinos-BoldItalic.ttf differ diff --git a/engine/assets/fonts/Tinos-Italic.ttf b/engine/assets/fonts/Tinos-Italic.ttf new file mode 100644 index 0000000..5aed4f3 Binary files /dev/null and b/engine/assets/fonts/Tinos-Italic.ttf differ diff --git a/engine/assets/fonts/Tinos-Regular.ttf b/engine/assets/fonts/Tinos-Regular.ttf new file mode 100644 index 0000000..893a1b5 Binary files /dev/null and b/engine/assets/fonts/Tinos-Regular.ttf differ diff --git a/engine/src/fonts/pdf_fonts/font_fallback.cpp b/engine/src/fonts/pdf_fonts/font_fallback.cpp index 68e9cd9..d7e0ef2 100644 --- a/engine/src/fonts/pdf_fonts/font_fallback.cpp +++ b/engine/src/fonts/pdf_fonts/font_fallback.cpp @@ -27,8 +27,30 @@ FontFallback::FontFallback() { void FontFallback::initializeDefaults() { default_rules_.clear(); + #ifdef PDFENGINE_FONT_DIR + { + const std::string fd = PDFENGINE_FONT_DIR; + auto sans = [&](const char* s) { return std::vector{ fd + "/Carlito-" + s + ".ttf" }; }; + auto serif = [&](const char* s) { return std::vector{ fd + "/Tinos-" + s + ".ttf" }; }; + for (const char* fam : {"calibri", "carlito"}) { + const std::string f = fam; + default_rules_.push_back({f + "-bolditalic", sans("BoldItalic")}); + default_rules_.push_back({f + "-bold", sans("Bold")}); + default_rules_.push_back({f + "-italic", sans("Italic")}); + default_rules_.push_back({f + "-oblique", sans("Italic")}); + default_rules_.push_back({f, sans("Regular")}); + } + for (const char* fam : {"times", "tinos", "serif"}) { + const std::string f = fam; + default_rules_.push_back({f + "-bolditalic", serif("BoldItalic")}); + default_rules_.push_back({f + "-bold", serif("Bold")}); + default_rules_.push_back({f + "-italic", serif("Italic")}); + default_rules_.push_back({f, serif("Regular")}); + } + } + #endif + #if defined(_WIN32) - // Helvetica / Arial fallback rules default_rules_.push_back({"helvetica-bolditalic", {"C:\\Windows\\Fonts\\LiberationSans-BoldItalic.ttf", "C:\\Windows\\Fonts\\arialbi.ttf"}}); default_rules_.push_back({"helvetica-bold", {"C:\\Windows\\Fonts\\LiberationSans-Bold.ttf", "C:\\Windows\\Fonts\\arialbd.ttf"}}); default_rules_.push_back({"helvetica-oblique", {"C:\\Windows\\Fonts\\LiberationSans-Italic.ttf", "C:\\Windows\\Fonts\\ariali.ttf"}}); @@ -38,13 +60,11 @@ void FontFallback::initializeDefaults() { default_rules_.push_back({"arial-italic", {"C:\\Windows\\Fonts\\LiberationSans-Italic.ttf", "C:\\Windows\\Fonts\\ariali.ttf"}}); default_rules_.push_back({"arial", {"C:\\Windows\\Fonts\\LiberationSans-Regular.ttf", "C:\\Windows\\Fonts\\arial.ttf"}}); - // Times fallback rules default_rules_.push_back({"times-bolditalic", {"C:\\Windows\\Fonts\\LiberationSerif-BoldItalic.ttf", "C:\\Windows\\Fonts\\timesbi.ttf"}}); default_rules_.push_back({"times-bold", {"C:\\Windows\\Fonts\\LiberationSerif-Bold.ttf", "C:\\Windows\\Fonts\\timesbd.ttf"}}); default_rules_.push_back({"times-italic", {"C:\\Windows\\Fonts\\LiberationSerif-Italic.ttf", "C:\\Windows\\Fonts\\timesi.ttf"}}); default_rules_.push_back({"times", {"C:\\Windows\\Fonts\\LiberationSerif-Regular.ttf", "C:\\Windows\\Fonts\\times.ttf"}}); - // Courier fallback rules default_rules_.push_back({"courier-bolditalic", {"C:\\Windows\\Fonts\\LiberationMono-BoldItalic.ttf", "C:\\Windows\\Fonts\\courbi.ttf"}}); default_rules_.push_back({"courier-bold", {"C:\\Windows\\Fonts\\LiberationMono-Bold.ttf", "C:\\Windows\\Fonts\\courbd.ttf"}}); default_rules_.push_back({"courier-oblique", {"C:\\Windows\\Fonts\\LiberationMono-Italic.ttf", "C:\\Windows\\Fonts\\couri.ttf"}}); @@ -63,7 +83,6 @@ void FontFallback::initializeDefaults() { default_rules_.push_back({"japanese", {"C:\\Windows\\Fonts\\msgothic.ttc"}}); default_rules_.push_back({"korean", {"C:\\Windows\\Fonts\\malgun.ttf"}}); #elif defined(__APPLE__) - // Helvetica / Arial fallback rules default_rules_.push_back({"helvetica-bolditalic", {"/Library/Fonts/LiberationSans-BoldItalic.ttf", "/System/Library/Fonts/Supplemental/Arial Bold Italic.ttf", "/Library/Fonts/Arial Bold Italic.ttf"}}); default_rules_.push_back({"helvetica-bold", {"/Library/Fonts/LiberationSans-Bold.ttf", "/System/Library/Fonts/Supplemental/Arial Bold.ttf", "/Library/Fonts/Arial Bold.ttf"}}); default_rules_.push_back({"helvetica-oblique", {"/Library/Fonts/LiberationSans-Italic.ttf", "/System/Library/Fonts/Supplemental/Arial Italic.ttf", "/Library/Fonts/Arial Italic.ttf"}}); @@ -73,19 +92,16 @@ void FontFallback::initializeDefaults() { default_rules_.push_back({"arial-italic", {"/Library/Fonts/LiberationSans-Italic.ttf", "/System/Library/Fonts/Supplemental/Arial Italic.ttf", "/Library/Fonts/Arial Italic.ttf"}}); default_rules_.push_back({"arial", {"/Library/Fonts/LiberationSans-Regular.ttf", "/Library/Fonts/Arial.ttf"}}); - // Times fallback rules default_rules_.push_back({"times-bolditalic", {"/Library/Fonts/LiberationSerif-BoldItalic.ttf", "/System/Library/Fonts/Supplemental/Times New Roman Bold Italic.ttf", "/Library/Fonts/Times New Roman Bold Italic.ttf"}}); default_rules_.push_back({"times-bold", {"/Library/Fonts/LiberationSerif-Bold.ttf", "/System/Library/Fonts/Supplemental/Times New Roman Bold.ttf", "/Library/Fonts/Times New Roman Bold.ttf"}}); default_rules_.push_back({"times-italic", {"/Library/Fonts/LiberationSerif-Italic.ttf", "/System/Library/Fonts/Supplemental/Times New Roman Italic.ttf", "/Library/Fonts/Times New Roman Italic.ttf"}}); default_rules_.push_back({"times", {"/Library/Fonts/LiberationSerif-Regular.ttf", "/Library/Fonts/Times New Roman.ttf", "/System/Library/Fonts/Times.ttc"}}); - // Courier fallback rules default_rules_.push_back({"courier-bolditalic", {"/Library/Fonts/LiberationMono-BoldItalic.ttf", "/System/Library/Fonts/Supplemental/Courier New Bold Italic.ttf", "/Library/Fonts/Courier New Bold Italic.ttf"}}); default_rules_.push_back({"courier-bold", {"/Library/Fonts/LiberationMono-Bold.ttf", "/System/Library/Fonts/Supplemental/Courier New Bold.ttf", "/Library/Fonts/Courier New Bold.ttf"}}); default_rules_.push_back({"courier-oblique", {"/Library/Fonts/LiberationMono-Italic.ttf", "/System/Library/Fonts/Supplemental/Courier New Italic.ttf", "/Library/Fonts/Courier New Italic.ttf"}}); default_rules_.push_back({"courier", {"/Library/Fonts/LiberationMono-Regular.ttf", "/Library/Fonts/Courier New.ttf", "/System/Library/Fonts/Courier.dfont"}}); #else - // Helvetica / Arial fallback rules default_rules_.push_back({"helvetica-bolditalic", {"/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf"}}); default_rules_.push_back({"helvetica-bold", {"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"}}); default_rules_.push_back({"helvetica-oblique", {"/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf"}}); @@ -95,13 +111,11 @@ void FontFallback::initializeDefaults() { default_rules_.push_back({"arial-italic", {"/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf"}}); default_rules_.push_back({"arial", {"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"}}); - // Times fallback rules default_rules_.push_back({"times-bolditalic", {"/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSerif-BoldItalic.ttf"}}); default_rules_.push_back({"times-bold", {"/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf"}}); default_rules_.push_back({"times-italic", {"/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Italic.ttf"}}); default_rules_.push_back({"times", {"/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf"}}); - // Courier fallback rules default_rules_.push_back({"courier-bolditalic", {"/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-BoldOblique.ttf"}}); default_rules_.push_back({"courier-bold", {"/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf"}}); default_rules_.push_back({"courier-oblique", {"/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Oblique.ttf"}}); @@ -143,6 +157,26 @@ std::string FontFallback::getFallbackFontPath(const std::string& fontName, bool } } + // Generic bundled catch-all: any font that matched NO specific rule above (an exotic/embedded + // family we don't recognise) still gets a real, present-on-both-engines fallback instead of OS + // Arial (native only) or nothing (WASM -> garble). Pick serif (Tinos) vs sans (Carlito) from the + // name; default sans. Recognised families (helvetica/arial/times/calibri/courier) already + // returned above, so this never overrides them. Keeps WASM == native for every font. + #ifdef PDFENGINE_FONT_DIR + { + auto has = [&](const char* s) { return lowerName.find(s) != std::string::npos; }; + const bool serif = has("times") || has("serif") || has("roman") || has("georgia") || + has("garamond") || has("minion") || has("cambria") || has("tinos") || + has("book antiqua") || has("palatino"); + const std::string base = serif ? "Tinos" : "Carlito"; + const std::string style = (bold && italic) ? "BoldItalic" : bold ? "Bold" : italic ? "Italic" : "Regular"; + const std::string p = std::string(PDFENGINE_FONT_DIR) + "/" + base + "-" + style + ".ttf"; + if (std::filesystem::exists(p)) { + return p; + } + } + #endif + #if defined(_WIN32) std::vector lastResort; if (bold && italic) lastResort = {"C:\\Windows\\Fonts\\arialbi.ttf", "C:\\Windows\\Fonts\\timesbi.ttf"}; @@ -190,4 +224,4 @@ void FontFallback::resetToDefaults() { custom_rules_.clear(); } -} // namespace pdfengine::fonts::pdf_fonts +} \ No newline at end of file diff --git a/frontend/public/pdfium-engine.mjs b/frontend/public/pdfium-engine.mjs index 7597547..c91e7c3 100644 --- a/frontend/public/pdfium-engine.mjs +++ b/frontend/public/pdfium-engine.mjs @@ -1,2 +1,2 @@ -async function Module(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var readyPromiseResolve,readyPromiseReject;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["R"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("pdfengine.wasm")}return new URL("pdfengine.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;abort()};var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var zeroMemory=(ptr,size)=>HEAPU8.fill(0,ptr,ptr+size);var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(ptr)zeroMemory(ptr,size);return ptr};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)abort("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{return SYSCALLS.writeStat(buf,FS.fstat(fd))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getdents64(fd,dirp,count){try{var stream=SYSCALLS.getStreamFromFD(fd);stream.getdents||=FS.readdir(stream.path);var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var startIdx=Math.floor(off/struct_size);var endIdx=Math.min(stream.getdents.length,startIdx+Math.floor(count/struct_size));for(var idx=startIdx;idx>3]=BigInt(id);HEAP64[dirp+pos+8>>3]=BigInt((idx+1)*struct_size);HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.lstat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.writeStat(buf,nofollow?FS.lstat(path):FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(!flags){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{return-28}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var __emscripten_throw_longjmp=()=>{throw new EmscriptenSjLj};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function __mmap_js(len,prot,flags,fd,offset,allocated,addr){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetperformance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e3*1e3);HEAP64[ptime>>3]=BigInt(nsec);return 0}var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4}HEAP8[pbuf]=type;HEAP16[pbuf+2>>1]=flags;HEAP64[pbuf+8>>3]=BigInt(rightsBase);HEAP64[pbuf+16>>3]=BigInt(rightsInheriting);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;var _loadDocument,_pageCount,_renderPagePng,_previewRender,_previewRenderRegion,_previewRenderPaginated,_lastRegionCount,_lastRegionPtr,_lastRegionW,_lastRegionH,_lastRegionPage,_lastRegionYTop,_lastRenderPtr,_lastRenderW,_lastRenderH,_lastLayoutJson,_freeDocument,_engineBuildInfo,_free,_malloc,_emscripten_builtin_memalign,_setThrew,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_loadDocument=Module["_loadDocument"]=wasmExports["T"];_pageCount=Module["_pageCount"]=wasmExports["U"];_renderPagePng=Module["_renderPagePng"]=wasmExports["V"];_previewRender=Module["_previewRender"]=wasmExports["W"];_previewRenderRegion=Module["_previewRenderRegion"]=wasmExports["X"];_previewRenderPaginated=Module["_previewRenderPaginated"]=wasmExports["Y"];_lastRegionCount=Module["_lastRegionCount"]=wasmExports["Z"];_lastRegionPtr=Module["_lastRegionPtr"]=wasmExports["_"];_lastRegionW=Module["_lastRegionW"]=wasmExports["$"];_lastRegionH=Module["_lastRegionH"]=wasmExports["aa"];_lastRegionPage=Module["_lastRegionPage"]=wasmExports["ba"];_lastRegionYTop=Module["_lastRegionYTop"]=wasmExports["ca"];_lastRenderPtr=Module["_lastRenderPtr"]=wasmExports["da"];_lastRenderW=Module["_lastRenderW"]=wasmExports["ea"];_lastRenderH=Module["_lastRenderH"]=wasmExports["fa"];_lastLayoutJson=Module["_lastLayoutJson"]=wasmExports["ga"];_freeDocument=Module["_freeDocument"]=wasmExports["ha"];_engineBuildInfo=Module["_engineBuildInfo"]=wasmExports["ia"];_free=Module["_free"]=wasmExports["ja"];_malloc=Module["_malloc"]=wasmExports["ka"];_emscripten_builtin_memalign=wasmExports["la"];_setThrew=wasmExports["ma"];__emscripten_stack_restore=wasmExports["na"];__emscripten_stack_alloc=wasmExports["oa"];_emscripten_stack_get_current=wasmExports["pa"];memory=wasmMemory=wasmExports["Q"];__indirect_function_table=wasmTable=wasmExports["S"]}var wasmImports={a:___cxa_throw,i:___syscall_fcntl64,J:___syscall_fstat64,x:___syscall_getdents64,N:___syscall_ioctl,G:___syscall_lstat64,H:___syscall_newfstatat,k:___syscall_openat,v:___syscall_rmdir,I:___syscall_stat64,w:___syscall_unlinkat,L:__abort_js,s:__emscripten_throw_longjmp,A:__gmtime_js,B:__localtime_js,y:__mmap_js,z:__munmap_js,M:__tzset_js,K:_clock_time_get,l:_emscripten_date_now,u:_emscripten_get_heap_max,t:_emscripten_resize_heap,E:_environ_get,F:_environ_sizes_get,O:_exit,g:_fd_close,D:_fd_fdstat_get,n:_fd_read,C:_fd_seek,m:_fd_write,d:invoke_ii,f:invoke_iii,h:invoke_iiii,j:invoke_iiiii,P:invoke_iiiiiii,o:invoke_iiiiiiiiii,p:invoke_v,c:invoke_vi,b:invoke_vii,e:invoke_viii,r:invoke_viiii,q:invoke_viiiiiiiii};function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +async function Module(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var readyPromiseResolve,readyPromiseReject;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["S"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("pdfengine.wasm")}return new URL("pdfengine.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;abort()};var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var zeroMemory=(ptr,size)=>HEAPU8.fill(0,ptr,ptr+size);var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(ptr)zeroMemory(ptr,size);return ptr};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)abort("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{return SYSCALLS.writeStat(buf,FS.fstat(fd))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getdents64(fd,dirp,count){try{var stream=SYSCALLS.getStreamFromFD(fd);stream.getdents||=FS.readdir(stream.path);var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var startIdx=Math.floor(off/struct_size);var endIdx=Math.min(stream.getdents.length,startIdx+Math.floor(count/struct_size));for(var idx=startIdx;idx>3]=BigInt(id);HEAP64[dirp+pos+8>>3]=BigInt((idx+1)*struct_size);HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.lstat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.writeStat(buf,nofollow?FS.lstat(path):FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(!flags){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{return-28}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var __emscripten_fs_load_embedded_files=ptr=>{do{var name_addr=HEAPU32[ptr>>2];ptr+=4;var len=HEAPU32[ptr>>2];ptr+=4;var content=HEAPU32[ptr>>2];ptr+=4;var name=UTF8ToString(name_addr);FS.createPath("/",PATH.dirname(name),true,true);FS.createDataFile(name,null,HEAP8.subarray(content,content+len),true,true,true)}while(HEAPU32[ptr>>2])};var __emscripten_throw_longjmp=()=>{throw new EmscriptenSjLj};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function __mmap_js(len,prot,flags,fd,offset,allocated,addr){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetperformance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e3*1e3);HEAP64[ptime>>3]=BigInt(nsec);return 0}var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4}HEAP8[pbuf]=type;HEAP16[pbuf+2>>1]=flags;HEAP64[pbuf+8>>3]=BigInt(rightsBase);HEAP64[pbuf+16>>3]=BigInt(rightsInheriting);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var FS_createPath=(...args)=>FS.createPath(...args);var FS_unlink=(...args)=>FS.unlink(...args);var FS_createLazyFile=(...args)=>FS.createLazyFile(...args);var FS_createDevice=(...args)=>FS.createDevice(...args);FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["FS_preloadFile"]=FS_preloadFile;Module["FS_unlink"]=FS_unlink;Module["FS_createPath"]=FS_createPath;Module["FS_createDevice"]=FS_createDevice;Module["FS_createDataFile"]=FS_createDataFile;Module["FS_createLazyFile"]=FS_createLazyFile;var _loadDocument,_pageCount,_renderPagePng,_previewRender,_previewRenderRegion,_previewRenderPaginated,_lastRegionCount,_lastRegionPtr,_lastRegionW,_lastRegionH,_lastRegionPage,_lastRegionYTop,_lastRenderPtr,_lastRenderW,_lastRenderH,_lastLayoutJson,_freeDocument,_engineBuildInfo,_free,_malloc,_emscripten_builtin_memalign,_setThrew,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_loadDocument=Module["_loadDocument"]=wasmExports["U"];_pageCount=Module["_pageCount"]=wasmExports["V"];_renderPagePng=Module["_renderPagePng"]=wasmExports["W"];_previewRender=Module["_previewRender"]=wasmExports["X"];_previewRenderRegion=Module["_previewRenderRegion"]=wasmExports["Y"];_previewRenderPaginated=Module["_previewRenderPaginated"]=wasmExports["Z"];_lastRegionCount=Module["_lastRegionCount"]=wasmExports["_"];_lastRegionPtr=Module["_lastRegionPtr"]=wasmExports["$"];_lastRegionW=Module["_lastRegionW"]=wasmExports["aa"];_lastRegionH=Module["_lastRegionH"]=wasmExports["ba"];_lastRegionPage=Module["_lastRegionPage"]=wasmExports["ca"];_lastRegionYTop=Module["_lastRegionYTop"]=wasmExports["da"];_lastRenderPtr=Module["_lastRenderPtr"]=wasmExports["ea"];_lastRenderW=Module["_lastRenderW"]=wasmExports["fa"];_lastRenderH=Module["_lastRenderH"]=wasmExports["ga"];_lastLayoutJson=Module["_lastLayoutJson"]=wasmExports["ha"];_freeDocument=Module["_freeDocument"]=wasmExports["ia"];_engineBuildInfo=Module["_engineBuildInfo"]=wasmExports["ja"];_free=Module["_free"]=wasmExports["ka"];_malloc=Module["_malloc"]=wasmExports["la"];_emscripten_builtin_memalign=wasmExports["ma"];_setThrew=wasmExports["na"];__emscripten_stack_restore=wasmExports["oa"];__emscripten_stack_alloc=wasmExports["pa"];_emscripten_stack_get_current=wasmExports["qa"];memory=wasmMemory=wasmExports["R"];__indirect_function_table=wasmTable=wasmExports["T"]}var wasmImports={a:___cxa_throw,i:___syscall_fcntl64,J:___syscall_fstat64,x:___syscall_getdents64,N:___syscall_ioctl,G:___syscall_lstat64,H:___syscall_newfstatat,k:___syscall_openat,v:___syscall_rmdir,I:___syscall_stat64,w:___syscall_unlinkat,L:__abort_js,O:__emscripten_fs_load_embedded_files,s:__emscripten_throw_longjmp,A:__gmtime_js,B:__localtime_js,y:__mmap_js,z:__munmap_js,M:__tzset_js,K:_clock_time_get,l:_emscripten_date_now,u:_emscripten_get_heap_max,t:_emscripten_resize_heap,E:_environ_get,F:_environ_sizes_get,P:_exit,g:_fd_close,D:_fd_fdstat_get,n:_fd_read,C:_fd_seek,m:_fd_write,d:invoke_ii,f:invoke_iii,h:invoke_iiii,j:invoke_iiiii,Q:invoke_iiiiiii,o:invoke_iiiiiiiiii,p:invoke_v,c:invoke_vi,b:invoke_vii,e:invoke_viii,r:invoke_viiii,q:invoke_viiiiiiiii};function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} ;return moduleRtn}export default Module; diff --git a/frontend/public/pdfium-engine.wasm b/frontend/public/pdfium-engine.wasm index 2784a3b..86bee67 100644 Binary files a/frontend/public/pdfium-engine.wasm and b/frontend/public/pdfium-engine.wasm differ diff --git a/frontend/src/lib/pdfiumEngine.ts b/frontend/src/lib/pdfiumEngine.ts index 94f7762..68d7ea7 100644 --- a/frontend/src/lib/pdfiumEngine.ts +++ b/frontend/src/lib/pdfiumEngine.ts @@ -16,7 +16,7 @@ function getModule(): Promise { if (!modulePromise) { modulePromise = (async () => { try { - const V = '20260618c'; + const V = '20260618f-allfonts'; const resp = await fetch(`/pdfium-engine.mjs?v=${V}`, { cache: 'no-store' }); if (!resp.ok) throw new Error(`pdfium-engine.mjs ${resp.status}`); const blobUrl = URL.createObjectURL(new Blob([await resp.text()], { type: 'text/javascript' })); diff --git a/tests/edits/_bullet_repro.out.txt b/tests/edits/_bullet_repro.out.txt deleted file mode 100644 index dbfd81f..0000000 --- a/tests/edits/_bullet_repro.out.txt +++ /dev/null @@ -1,12 +0,0 @@ -editing bullet: paragraph 5, line 2, x=66.6 - scoped item lines [2..4): • Architecture & APIs: Microservices, | Design Patterns - op columnLeft(text indent)=78.1 pushColumnLeft(marker)=66.6 objs=16 - ---- AFTER (edited bullet lines) --- - ---- ADJACENT bullets (must be unchanged) --- - [OK ] 'Languages:': '• Languages: Core Java, Java 8+, OOPs, Collections, Streams, L' - [OK ] 'Frameworks': '• Frameworks & Libraries: Spring Boot, Spring MVC, Spring Data' - [OK ] 'Databases': '• Databases & Caching: MySQL (Indexing, Query Tuning, Transact' - [OK ] 'Messaging': '• Messaging & Streaming: Apache Kafka (Producer/Consumer, Part' - [OK ] 'Cloud': '• Cloud & DevOps: AWS (EC2, S3, Lambda, RDS, CloudWatch), Dock' diff --git a/tests/edits/_bullet_repro.png b/tests/edits/_bullet_repro.png deleted file mode 100644 index 4962fda..0000000 Binary files a/tests/edits/_bullet_repro.png and /dev/null differ diff --git a/tests/edits/_bullet_repro.py b/tests/edits/_bullet_repro.py deleted file mode 100644 index ddb9110..0000000 --- a/tests/edits/_bullet_repro.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 -"""Reproduce the bullet-item reflow corruption against the CURRENT build .pyd: edit one bullet -("Architecture & APIs") and check whether (a) the edited item keeps its hanging indent and (b) -ADJACENT bullets are left intact. Mimics the frontend buildBulletItem scoping. -Run: gateway/.venv/Scripts/python.exe tests/edits/_bullet_repro.py -""" -from __future__ import annotations -import io, json, sys -from pathlib import Path - -sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") # fresh build -import pdfengine # noqa: E402 - -ROOT = Path(__file__).resolve().parents[2] -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" -OUT = Path(__file__).resolve().parent / "_bullet_repro.out.txt" -rep = io.StringIO() -def log(*a): rep.write(" ".join(str(x) for x in a) + "\n") - - -def is_marker(t): - t = (t or "").strip() - return t == "" or (len(t) <= 2 and not t.isalnum()) # bullet glyph / symbol, not a word - - -def line_text(l): - return "".join(r.text for r in l.runs) - - -def para_text(p): - return "".join(line_text(l) for l in p.lines) - - -def find_line(model, needle): - for pi, p in enumerate(model.paragraphs): - for li, l in enumerate(p.lines): - if needle in line_text(l): - return pi, p, li - return -1, None, -1 - - -def build_bullet_op(p, click_line, page_index, edit_fn): - lines = p.lines - col_left = min(l.x for l in lines) - col_right = max(l.x + l.w for l in lines) - - def is_start(idx): - rs = list(lines[idx].runs) - return idx == 0 or (bool(rs) and is_marker(rs[0].text)) - - start = click_line - while start > 0 and not is_start(start): - start -= 1 - end = click_line + 1 - while end < len(lines) and not is_start(end): - end += 1 - item_lines = lines[start:end] - log(f" scoped item lines [{start}..{end}): " + " | ".join(line_text(l)[:38] for l in item_lines)) - - text_indent = item_lines[0].x - obj_idx, runs = [], [] - for idx, l in enumerate(item_lines): - rs = list(l.runs) - if idx == 0 and rs and is_marker(rs[0].text): - ti = 1 - while ti < len(rs) and not (rs[ti].text or "").strip(): - ti += 1 - if ti < len(rs): - text_indent = rs[ti].x - rs = rs[ti:] - for r in rs: - obj_idx.extend(list(r.object_indices)) - if r.text: - runs.append({"text": r.text, "internalFontId": r.internal_font_id, - "fontSize": r.font_size, "color": "#000000"}) - runs = edit_fn(runs) - baselines = [l.baseline_y for l in lines] - leading = abs(baselines[start] - baselines[start + 1]) if start + 1 < len(baselines) else 14.0 - return { - "id": "rf", "type": "reflow_paragraph", "pageIndex": page_index, - "data": { - "objectIndices": sorted(set(obj_idx)), - "runs": runs, - "columnLeft": text_indent, "columnRight": col_right, - "pushColumnLeft": col_left, - "firstBaselineY": baselines[start], "leading": leading, - "oldLineCount": len(item_lines), "align": "left", - }, - } - - -WATCH = ("Languages:", "Frameworks", "Databases", "Messaging", "Cloud") - - -def run(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m = doc.get_page(0).extract_document_model() - pi, p, li = find_line(m, "Architecture & APIs") - if p is None: - log("Architecture bullet not found"); return - log(f"editing bullet: paragraph {pi}, line {li}, x={p.lines[li].x:.1f}") - before = {} - for n in WATCH: - _, pp, ll = find_line(m, n) - before[n] = line_text(pp.lines[ll]) if pp else "" - - def edit(runs): - out = [] - for r in runs: - out.append(r) - if "Architecture," in r["text"]: - out.append({"text": " in the main app", "internalFontId": r["internalFontId"], - "fontSize": r["fontSize"], "color": "#000000"}) - return out - - op = build_bullet_op(p, li, 0, edit) - log(f" op columnLeft(text indent)={op['data']['columnLeft']:.1f} pushColumnLeft(marker)={op['data']['pushColumnLeft']:.1f} objs={len(op['data']['objectIndices'])}") - doc.apply_edits(json.dumps({"version": "1.0", "operations": [op]})) - m2 = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "").get_page(0).extract_document_model() - - log("\n--- AFTER (edited bullet lines) ---") - _, pa, la = find_line(m2, "in the main app") - if pa is None: - _, pa, la = find_line(m2, "API Gateway") - if pa: - for l in pa.lines: - t = line_text(l) - if any(k in t for k in ("Architecture", "Design", "app", "SOLID")): - fonts = sorted({r.internal_font_id for r in l.runs if r.text.strip()}) - log(f" x={l.x:.1f} fonts={fonts} | {t[:64]!r}") - - log("\n--- ADJACENT bullets (must be unchanged) ---") - for n in WATCH: - _, pp, ll = find_line(m2, n) - after = line_text(pp.lines[ll]) if pp else "" - same = "OK " if after == before[n] else "CHANGED" - log(f" [{same}] {n!r}: {after[:62]!r}") - - -if __name__ == "__main__": - run() - OUT.write_text(rep.getvalue(), encoding="utf-8") - print(rep.getvalue()) diff --git a/tests/edits/_font_fallback_check.py b/tests/edits/_font_fallback_check.py new file mode 100644 index 0000000..b1832a8 --- /dev/null +++ b/tests/edits/_font_fallback_check.py @@ -0,0 +1,72 @@ +"""Phase 1 check: editing a styled run with characters NOT in its embedded subset now falls back to +the bundled Carlito (metric-compatible with Calibri) instead of OS Arial/Helvetica, and the new +glyphs survive. + +Run: gateway/.venv/Scripts/python.exe tests/edits/_font_fallback_check.py +""" +from __future__ import annotations +import json, sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") +sys.path.insert(1, str(ROOT / "gateway")) +import pdfengine +PDF = ROOT / "Mo-Faishal-Qureshi.pdf" + + +def ptext(p): + return "".join(r.text for ln in p.lines for r in ln.runs) + + +def main(): + doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") + for pageno in range(doc.page_count): + m = doc.get_page(pageno).extract_document_model() + para = next((p for p in m.paragraphs if "Present" in ptext(p)), None) + if para: + break + if not para: + print("FAIL: couldn't find the 'Present' italic paragraph"); return + fonts_before = sorted({r.internal_font_id for ln in para.lines for r in ln.runs if r.text.strip()}) + print("para text:", ptext(para)[:70]) + print("fonts before:", fonts_before) + + oi = [i for ln in para.lines for r in ln.runs for i in r.object_indices] + cl = min(ln.x for ln in para.lines); cr = max(ln.x + ln.w for ln in para.lines) + fb = max(ln.baseline_y for ln in para.lines) + bls = sorted({round(ln.baseline_y, 1) for ln in para.lines}, reverse=True) + lead = abs(bls[0]-bls[1]) if len(bls) > 1 else 13.0 + fid = para.lines[0].runs[0].internal_font_id + new_text = ptext(para).strip() + " Zephyr xkcd | the quux" + + op = {"version": "1.0", "operations": [{"id": "f", "type": "reflow_paragraph", "pageIndex": pageno, "data": { + "objectIndices": oi, "runs": [{"text": new_text, "internalFontId": fid, "fontSize": 11.0, "color": "#1a5276"}], + "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, "leading": lead, + "oldLineCount": len(para.lines), "align": "left", "paraId": "FONTCHK"}}]} + doc.apply_edits(json.dumps(op)) + r = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") + + edited = None + for pageno2 in range(r.page_count): + mm = r.get_page(pageno2).extract_document_model() + for p in mm.paragraphs: + if "Zephyr" in ptext(p): + edited = p; break + if edited: + break + if not edited: + print("FAIL: edited paragraph not found after save"); return + names = sorted({(r.font_name or "") for ln in edited.lines for r in ln.runs if r.text.strip()}) + txt = ptext(edited) + print("\nedited fonts:", names) + print("glyphs survived: Zephyr=%s '|'=%s the=%s xkcd=%s" % + ("Zephyr" in txt, "|" in txt, "the" in txt.replace(" ", ""), "xkcd" in txt.replace(" ", ""))) + carlito = any("carlito" in n.lower() for n in names) + arial = any(("arial" in n.lower() or "helvetica" in n.lower() or "liberation" in n.lower()) for n in names) + print(f"\n=> fallback uses Carlito: {carlito} (uses Arial/Helvetica: {arial})") + print("PHASE1 OK" if carlito and not arial else "PHASE1: check fonts above") + + +if __name__ == "__main__": + main() diff --git a/tests/edits/_hyphen_check.py b/tests/edits/_hyphen_check.py deleted file mode 100644 index 8edb91f..0000000 --- a/tests/edits/_hyphen_check.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Does cross-page MIGRATION preserve hyphens/dashes? Grow the page-0 Technologies paragraph so it -pushes the 'Architected ... high-scale e-commerce' paragraph onto page 2 (migration), then check the -migrated text still has its hyphens. - -Run: gateway/.venv/Scripts/python.exe tests/edits/_hyphen_check.py -""" -from __future__ import annotations -import json, sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") -sys.path.insert(1, str(ROOT / "gateway")) -import pdfengine -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" - - -def ptext(p): - return "".join(r.text for ln in p.lines for r in ln.runs) - - -def find(doc, page, needle): - for p in doc.get_page(page).extract_document_model().paragraphs: - if needle in ptext(p): - return p - return None - - -def alltext(doc): - out = [] - for i in range(doc.page_count): - for p in doc.get_page(i).extract_document_model().paragraphs: - out.append(ptext(p)) - return " ".join(out) - - -def main(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - before = alltext(doc) - print("BEFORE migration:") - print(" 'high' + U+002D + 'scale' :", ("high-scale" in before)) - print(" 'high' + U+2011 + 'scale' :", ("high‑scale" in before)) - print(" 'e' + U+002D + 'commerce' :", ("e-commerce" in before)) - print(" 'e' + U+2011 + 'commerce' :", ("e‑commerce" in before)) - - tech = find(doc, 0, "Technologies") - oi = [i for ln in tech.lines for r in ln.runs for i in r.object_indices] - cl = min(ln.x for ln in tech.lines); cr = max(ln.x + ln.w for ln in tech.lines) - fb = max(ln.baseline_y for ln in tech.lines) - bls = sorted({round(ln.baseline_y, 1) for ln in tech.lines}, reverse=True) - lead = abs(bls[0]-bls[1]) if len(bls) > 1 else 14.0 - big = ptext(tech).strip() + " " + ("filler " * 60) - op = {"version": "1.0", "operations": [{"id": "g", "type": "reflow_paragraph", "pageIndex": 0, "data": { - "objectIndices": oi, "runs": [{"text": big, "internalFontId": tech.lines[0].runs[0].internal_font_id, - "fontSize": 11.0, "color": "#000000"}], "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, - "leading": lead, "oldLineCount": len(tech.lines), "align": "left", "paraId": "TECHPARA"}}]} - doc.apply_edits(json.dumps(op)) - r = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") - after = alltext(r) - print(f"\nAFTER migration (pages={r.page_count}):") - print(" 'high' + U+002D + 'scale' :", ("high-scale" in after)) - print(" 'high' + U+2011 + 'scale' :", ("high‑scale" in after)) - print(" 'e' + U+002D + 'commerce' :", ("e-commerce" in after)) - print(" 'e' + U+2011 + 'commerce' :", ("e‑commerce" in after)) - for i in range(r.page_count): - a = find(r, i, "Architected") - if a: - safe = ptext(a)[:120].encode("ascii", "replace").decode("ascii") - print(f"\n Architected (page {i}): {safe!r}") - break - - -if __name__ == "__main__": - main() diff --git a/tests/edits/_italic_diag.py b/tests/edits/_italic_diag.py new file mode 100644 index 0000000..9c41db3 --- /dev/null +++ b/tests/edits/_italic_diag.py @@ -0,0 +1,61 @@ +"""Diagnose: when the ITALIC run's fid is used, does the engine emit an ITALIC fallback (Carlito- +Italic)? Rules out an engine style-derivation bug vs. a frontend mis-tagging issue. + +Run: gateway/.venv/Scripts/python.exe tests/edits/_italic_diag.py +""" +from __future__ import annotations +import json, sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") +sys.path.insert(1, str(ROOT / "gateway")) +import pdfengine +PDF = ROOT / "Mo-Faishal-Qureshi.pdf" + + +def ptext(p): + return "".join(r.text for ln in p.lines for r in ln.runs) + + +def main(): + doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") + italic_fid = None; para = None; pageno = 0 + for pg in range(doc.page_count): + m = doc.get_page(pg).extract_document_model() + for p in m.paragraphs: + for ln in p.lines: + for r in ln.runs: + if "italic" in (r.internal_font_id or "").lower() and r.text.strip(): + italic_fid = r.internal_font_id; para = p; pageno = pg; break + if italic_fid: break + if italic_fid: break + if italic_fid: break + if not italic_fid: + print("no italic run found"); return + print("italic fid:", italic_fid) + print("para:", ptext(para)[:80]) + + oi = [i for ln in para.lines for r in ln.runs for i in r.object_indices] + cl = min(ln.x for ln in para.lines); cr = max(ln.x + ln.w for ln in para.lines) + fb = max(ln.baseline_y for ln in para.lines) + op = {"version": "1.0", "operations": [{"id": "i", "type": "reflow_paragraph", "pageIndex": pageno, "data": { + "objectIndices": oi, "runs": [{"text": "Mar 2023 Present Zephyr xyzk | the quux", + "internalFontId": italic_fid, "fontSize": 11.0, "color": "#1a5276"}], + "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, "leading": 13.0, + "oldLineCount": len(para.lines), "align": "left", "paraId": "ITALCHK"}}]} + doc.apply_edits(json.dumps(op)) + r = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") + names = set() + for pg in range(r.page_count): + for p in r.get_page(pg).extract_document_model().paragraphs: + if "Zephyr" in ptext(p): + for ln in p.lines: + for rr in ln.runs: + if rr.text.strip(): names.add(rr.font_name or "") + print("emitted fonts:", sorted(names)) + print("=> italic fallback used:", any("italic" in n.lower() for n in names)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/edits/_overflow_diag.py b/tests/edits/_overflow_diag.py deleted file mode 100644 index b87e7a4..0000000 --- a/tests/edits/_overflow_diag.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Diagnose early-overflow: edit the real Technologies paragraph with a long single-run text and -report where each resulting line lands (page + baseline) vs the page-bottom limit. - -Run: gateway/.venv/Scripts/python.exe tests/edits/_overflow_diag.py -""" -from __future__ import annotations -import json, sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") -sys.path.insert(1, str(ROOT / "gateway")) -import pdfengine -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" - - -def ptext(p): - return "".join(r.text for ln in p.lines for r in ln.runs) - - -def main(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m = doc.get_page(0).extract_document_model() - tech = next(p for p in m.paragraphs if "Technologies" in ptext(p)) - oi = [i for ln in tech.lines for r in ln.runs for i in r.object_indices] - cl = min(ln.x for ln in tech.lines); cr = max(ln.x + ln.w for ln in tech.lines) - fb = max(ln.baseline_y for ln in tech.lines) - bls = sorted({round(ln.baseline_y, 1) for ln in tech.lines}, reverse=True) - lead = abs(bls[0]-bls[1]) if len(bls) > 1 else 14.0 - page_h = doc.get_page(0).height - tops = [] - for p in m.paragraphs: - for ln in p.lines: - if (ln.x + ln.w) > cl and ln.x < cr: - tops.append(ln.y + ln.h) - max_top = max(tops) if tops else 0 - mirror = page_h - max_top - bottom_limit = max(18.0, min(mirror, page_h * 0.25)) - print(f"page_h={page_h:.0f} tech firstBaselineY={fb:.1f} leading={lead:.1f} cols=[{cl:.0f},{cr:.0f}]") - print(f"max_top(in-col)={max_top:.1f} mirror={mirror:.1f} bottomLimitY(approx)={bottom_limit:.1f}") - room_lines = int((fb - bottom_limit) / lead) - print(f"=> room for ~{room_lines} lines on page 0 before hitting the bottom limit") - - new_text = ptext(tech).strip() + " " + " ".join(f"w{n}" for n in range(40)) - op = {"version": "1.0", "operations": [{"id": "g", "type": "reflow_paragraph", "pageIndex": 0, "data": { - "objectIndices": oi, "runs": [{"text": new_text, "internalFontId": tech.lines[0].runs[0].internal_font_id, - "fontSize": 11.0, "color": "#000000"}], "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, - "leading": lead, "oldLineCount": len(tech.lines), "align": "left", "paraId": "DIAG"}}]} - doc.apply_edits(json.dumps(op)) - r = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") - print(f"\nAFTER edit: pages={r.page_count}") - for i in range(r.page_count): - mm = r.get_page(i).extract_document_model() - diag_lines = [ln for p in mm.paragraphs for ln in p.lines - if any(getattr(rr, "para_id", "") == "DIAG" for rr in ln.runs)] - ys = sorted((ln.baseline_y for ln in diag_lines), reverse=True) - print(f" page {i}: {len(diag_lines)} DIAG line(s) baselineY range " - f"{ys[0]:.0f}..{ys[-1]:.0f}" if ys else f" page {i}: 0 DIAG lines") - - -if __name__ == "__main__": - main() diff --git a/tests/edits/_paraid_probe.py b/tests/edits/_paraid_probe.py deleted file mode 100644 index 9aead0f..0000000 --- a/tests/edits/_paraid_probe.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Stage 1 verification: PDFPARA object marks survive (a) save/reload and (b) cross-page migration. - -Run: gateway/.venv/Scripts/python.exe tests/edits/_paraid_probe.py -""" -from __future__ import annotations -import json, sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") -sys.path.insert(1, str(ROOT / "gateway")) -import pdfengine - - -def build_pdf() -> bytes: - content = ( - b"BT /F1 14 Tf 72 720 Td (HEADING) Tj ET\n" - b"BT /F1 11 Tf 72 150 Td (The quick brown fox jumps over the lazy) Tj ET\n" - b"BT /F1 11 Tf 72 136 Td (dog near the river bank on a sunny) Tj ET\n" - b"BT /F1 11 Tf 72 122 Td (afternoon in early spring.) Tj ET\n" - ) - objs = [ - b"<< /Type /Catalog /Pages 2 0 R >>", - b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>", - b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>", - b"<< /Length %d >>\nstream\n" % len(content) + content + b"endstream", - b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", - ] - pdf = b"%PDF-1.7\n"; offs = [] - for i, o in enumerate(objs, 1): - offs.append(len(pdf)); pdf += b"%d 0 obj\n" % i + o + b"\nendobj\n" - xref = len(pdf); pdf += b"xref\n0 %d\n0000000000 65535 f \n" % (len(objs) + 1) - for o in offs: pdf += b"%010d 00000 n \n" % o - pdf += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (len(objs) + 1, xref) - return pdf - - -def body_para(doc, page=0): - m = doc.get_page(page).extract_document_model() - return next(p for p in m.paragraphs - if "quick brown fox" in " ".join("".join(r.text for r in ln.runs) for ln in p.lines)) - - -def reflow(doc, page, para, text, para_id): - oi = [i for ln in para.lines for r in ln.runs for i in r.object_indices] - cl = min(ln.x for ln in para.lines); cr = max(ln.x + ln.w for ln in para.lines) - fb = max(ln.baseline_y for ln in para.lines) - bls = sorted({round(ln.baseline_y, 1) for ln in para.lines}, reverse=True) - lead = abs(bls[0]-bls[1]) if len(bls) > 1 else 14.0 - op = {"version": "1.0", "operations": [{"id": "p", "type": "reflow_paragraph", "pageIndex": page, "data": { - "objectIndices": oi, "runs": [{"text": text, "internalFontId": para.lines[0].runs[0].internal_font_id, - "fontSize": 11.0, "color": "#000000"}], "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, - "leading": lead, "oldLineCount": len(para.lines), "align": "left", "paraId": para_id}}]} - doc.apply_edits(json.dumps(op)) - return pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") - - -def all_para_ids(doc): - ids = {} - for i in range(doc.page_count): - m = doc.get_page(i).extract_document_model() - for p in m.paragraphs: - for ln in p.lines: - for r in ln.runs: - if getattr(r, "para_id", ""): - ids.setdefault(r.para_id, []).append(i) - return ids - - -def main() -> int: - ok = True - doc = pdfengine.PdfDocument.load_from_memory(build_pdf(), "") - doc = reflow(doc, 0, body_para(doc), "Short edited text here.", "PARA_TEST_42") - ids = all_para_ids(doc) - if "PARA_TEST_42" in ids: - print(f" ok mark survived save/reload: PARA_TEST_42 on pages {sorted(set(ids['PARA_TEST_42']))}") - else: - print(f" FAIL no paraId after save/reload. ids={ids}"); ok = False - - doc2 = pdfengine.PdfDocument.load_from_memory(build_pdf(), "") - big = "Edited overflow text " + " ".join(f"m{n:03d}" for n in range(80)) - doc2 = reflow(doc2, 0, body_para(doc2), big, "SPAN_ID_7") - ids2 = all_para_ids(doc2) - pages = sorted(set(ids2.get("SPAN_ID_7", []))) - if len(pages) >= 2: - print(f" ok mark survived cross-page migration: SPAN_ID_7 on pages {pages}") - else: - print(f" FAIL paraId not on both pages after overflow. pages={pages} all={ids2}"); ok = False - - print("\nPASS" if ok else "\nFAIL") - return 0 if ok else 1 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/tests/edits/_reflow_repro.out.txt b/tests/edits/_reflow_repro.out.txt deleted file mode 100644 index 35b5189..0000000 --- a/tests/edits/_reflow_repro.out.txt +++ /dev/null @@ -1,41 +0,0 @@ -PDF: Mo-Faishal-Qureshi.pdf pages=2 - -=== PAGE 0 (612x792) paragraphs=10 === - [0.0] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='MO FAISHAL QURESHI' - [0.1] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Software Engineer | 3.2 Years Experience' - [0.2] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Professional Summary' - [0.3] lines=5 fonts=['Calibri_TrueType_32'] - text='Java Backend Developer with 3.2 years of experience in designing, developing, and deployin' - [0.4] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Technical Skills' - [0.5] lines=11 fonts=['BCDGEE+TimesNewRomanPSMT', 'Calibri-Bold_TrueType_32', 'Calibri_TrueType_32'] - text='• Languages: Core Java, Java 8+, OOPs, Collections, Streams, Lambda • ' - [0.6] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Professional Experience' - [0.7] lines=19 fonts=['BCDGEE+TimesNewRomanPSMT', 'BCDJEE+Calibri-Italic', 'BCDKEE+Calibri', 'Calibri-Bold_TrueType_32', 'Calibri-Italic_TrueType_96', 'Calibri_TrueType_32'] - text='Software Engineer | TapQwik Software Pvt. Ltd. Mar 2023 – Present | ' - [0.8] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Projects' - [0.9] lines=3 fonts=['BCDLEE+Calibri-Bold', 'Calibri-Bold_TrueType_32', 'Calibri_TrueType_32'] - text='Project: LG – E-Commerce Platform Technologies: Java 8/17, Spring Boot, ' - -=== PAGE 1 (612x792) paragraphs=8 === - [1.0] lines=13 fonts=['BCDGEE+TimesNewRomanPSMT', 'BCDKEE+Calibri', 'Calibri-Bold_TrueType_32', 'Calibri_TrueType_32'] - text='Architected and developed a high-scale e-commerce backend for LG Electro' - [1.1] lines=16 fonts=['BCDGEE+TimesNewRomanPSMT', 'BCDKEE+Calibri', 'BCDLEE+Calibri-Bold', 'Calibri-Bold_TrueType_32', 'Calibri_TrueType_32'] - text='Project: Wego – Hotel Booking Platform Technologies: Java 8/17, Spring B' - [1.2] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Education' - [1.3] lines=2 fonts=['Calibri-Bold_TrueType_32', 'Calibri-Italic_TrueType_96', 'Calibri_TrueType_32'] - text='Bachelor of Computer Applications 2023 HKBK Degree College | Bangalore, ' - [1.4] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Courses & Certifications' - [1.5] lines=3 fonts=['BCDGEE+TimesNewRomanPSMT', 'BCDKEE+Calibri', 'Calibri_TrueType_32'] - text='• Java Development Certification Course • Apache Kafka for Java Develope' - [1.6] lines=1 fonts=['Calibri-Bold_TrueType_32'] - text='Soft Skills' - [1.7] lines=2 fonts=['BCDKEE+Calibri', 'Calibri_TrueType_32'] - text='Problem-Solving • Team Collaboration • Adaptability • Ownership & Accoun' diff --git a/tests/edits/_reflow_repro.py b/tests/edits/_reflow_repro.py deleted file mode 100644 index d3d1d7a..0000000 --- a/tests/edits/_reflow_repro.py +++ /dev/null @@ -1,380 +0,0 @@ -#!/usr/bin/env python3 -"""Scratch harness: reproduce the multi-edit font-collision scramble, and (after the -fix) verify it's gone. Reads engine output through pybind (NOT HTTP/stdin) to avoid the -Windows cp1252 mojibake trap. Writes a UTF-8 report to _reflow_repro.out.txt. - -Run: gateway/.venv/Scripts/python.exe tests/edits/_reflow_repro.py -""" -from __future__ import annotations - -import io -import json -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, str(ROOT / "gateway")) -import pdfengine # noqa: E402 - -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" -OUT = Path(__file__).resolve().parent / "_reflow_repro.out.txt" -report = io.StringIO() - - -def log(*a): - report.write(" ".join(str(x) for x in a) + "\n") - - -def para_text(p): - return " ".join(r.text for l in p.lines for r in l.runs) - - -def para_fonts(p): - return sorted({r.internal_font_id for l in p.lines for r in l.runs if r.text.strip()}) - - -def line_advances(line): - """Per-character ORIGINAL advances for every run in a line, computed ACROSS the line so each - glyph's advance = the next glyph's origin minus this one (captures inter-run gaps exactly; a - per-run computation would overshoot by each run's left-side bearing and smear cumulatively). - Returns {run_index_in_line: [adv,...]} only for runs whose glyphs align 1:1 with their chars.""" - seq = [] # (run_idx_in_line, char_idx, origin_x) in reading order - aligned = {} - for ri, r in enumerate(line.runs): - gs = list(r.glyphs) - ok = bool(r.text) and len(gs) == len(r.text) - aligned[ri] = ok - if ok: - for ci in range(len(r.text)): - seq.append((ri, ci, gs[ci].origin_x)) - out = {ri: [0.0] * len(line.runs[ri].text) for ri in aligned if aligned[ri]} - anchor_x = seq[0][2] if seq else line.x # the line's FIRST glyph origin (true left anchor) - for k, (ri, ci, ox) in enumerate(seq): - if k + 1 < len(seq): - out[ri][ci] = seq[k + 1][2] - ox # delta to next glyph (incl. inter-run gap) - else: - out[ri][ci] = (line.x + line.w) - ox # last glyph of line: to the line's right edge - # Guard: drop any run that produced a non-positive advance (out-of-order glyphs) -> measure it. - for ri in list(out.keys()): - if any(a <= 0 for a in out[ri]): - del out[ri] - return out, anchor_x - - -def build_reflow_op(p, page_index, new_runs=None, use_lines=False): - """Build a reflow_paragraph op for paragraph p. If new_runs is None, re-emit the - paragraph's own runs unchanged (so a correct engine is a no-op on text). - use_lines=True sends the paragraph's ORIGINAL visual line breaks (the no-change-on-click - path) so the engine emits them verbatim instead of greedy-wrapping.""" - runs = [] - obj_idx = [] - op_lines = [] - op_line_x = [] - op_line_base = [] - for l in p.lines: - line_frags = [] - advs, anchor_x = line_advances(l) - for ri, r in enumerate(l.runs): - obj_idx.extend(list(r.object_indices)) - if r.text == "": - continue - frag = {"text": r.text, "internalFontId": r.internal_font_id, - "fontSize": r.font_size, "color": "#000000"} - if ri in advs: - frag["advances"] = advs[ri] - runs.append(frag) - line_frags.append(frag) - if line_frags: - op_lines.append(line_frags) - op_line_x.append(anchor_x) - op_line_base.append(l.baseline_y) - if new_runs is not None: - runs = new_runs - baselines = [l.baseline_y for l in p.lines] - first_baseline = baselines[0] if baselines else (p.y + p.h) - if len(baselines) >= 2: - leading = abs(baselines[0] - baselines[1]) - else: - leading = p.lines[0].h if p.lines else 14.0 - if leading <= 0: - leading = 14.0 - return { - "id": "rf", "type": "reflow_paragraph", "pageIndex": page_index, - "data": { - "objectIndices": sorted(set(obj_idx)), - "runs": runs, - "columnLeft": p.x, - "columnRight": p.x + p.w, - "firstBaselineY": first_baseline, - "leading": leading, - "oldLineCount": len(p.lines), - "align": "left", - **({"lines": op_lines, "lineBaselineY": op_line_base, "lineX": op_line_x} - if use_lines and new_runs is None else {}), - }, - } - - -def dump_structure(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - npages = 0 - while True: - try: - doc.get_page(npages) - npages += 1 - except Exception: - break - log(f"PDF: {PDF.name} pages={npages}") - for pi in range(npages): - m = doc.get_page(pi).extract_document_model() - log(f"\n=== PAGE {pi} ({m.width:.0f}x{m.height:.0f}) paragraphs={len(m.paragraphs)} ===") - for i, p in enumerate(m.paragraphs): - t = para_text(p) - log(f" [{pi}.{i}] lines={len(p.lines)} fonts={para_fonts(p)}") - log(f" text={t[:90]!r}") - - -def find_para(model, needle): - for i, p in enumerate(model.paragraphs): - if needle in para_text(p): - return i, p - return -1, None - - -def repro(page_index=0, a_needle="Java Backend Developer", b_needle="Core Java", - survive=("Core", "Java")): - """Faithful app flow: edit paragraph A (modified), re-extract, then re-emit - paragraph B UNCHANGED. B's text must survive intact in the committed PDF.""" - # ---- CONTROL: B-only on a fresh doc (proves B alone is fine) ---- - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m0 = doc.get_page(page_index).extract_document_model() - bi, pb = find_para(m0, b_needle) - log(f"\n--- CONTROL: re-emit B [{page_index}.{bi}] unchanged (no prior edit) ---") - log(f" B text(before) = {para_text(pb)[:80]!r}") - doc.apply_edits(json.dumps({"version": "1.0", "operations": [build_reflow_op(pb, page_index)]})) - m_ctrl = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "").get_page(page_index).extract_document_model() - _, pb_ctrl = find_para(m_ctrl, survive[0]) - ctrl_text = para_text(pb_ctrl) if pb_ctrl else "" - log(f" B text(after) = {ctrl_text[:80]!r}") - ctrl_ok = all(s in ctrl_text for s in survive) - log(f" CONTROL survive={survive} -> {'OK' if ctrl_ok else 'LOST'}") - - # ---- TEST: edit A first, re-extract, then re-emit B unchanged ---- - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m0 = doc.get_page(page_index).extract_document_model() - ai, pa = find_para(m0, a_needle) - log(f"\n--- TEST: edit A [{page_index}.{ai}] THEN re-emit B unchanged (same doc) ---") - # Modify A: prepend a word to its first run. - a_runs = [] - first = True - for l in pa.lines: - for r in l.runs: - if r.text == "": - continue - txt = ("EDITED " + r.text) if first else r.text - first = False - a_runs.append({"text": txt, "internalFontId": r.internal_font_id, - "fontSize": r.font_size, "color": "#000000"}) - doc.apply_edits(json.dumps({"version": "1.0", - "operations": [build_reflow_op(pa, page_index, new_runs=a_runs)]})) - # Re-extract (frontend re-fetches the model after each edit) and re-locate B. - m1 = doc.get_page(page_index).extract_document_model() - bi2, pb2 = find_para(m1, b_needle) - if pb2 is None: - log(f" !! B not found after edit A (searched {b_needle!r})") - else: - log(f" B text(before 2nd edit) = {para_text(pb2)[:80]!r}") - doc.apply_edits(json.dumps({"version": "1.0", "operations": [build_reflow_op(pb2, page_index)]})) - # In-memory (pre-save) B text: localizes corruption to emission vs save-merge. - m_inmem = doc.get_page(page_index).extract_document_model() - _, pb_inmem = find_para(m_inmem, survive[0]) - log(f" B text(in-memory, pre-save) = {(para_text(pb_inmem) if pb_inmem else '')[:80]!r}") - m2 = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "").get_page(page_index).extract_document_model() - _, pb_test = find_para(m2, survive[0]) - test_text = para_text(pb_test) if pb_test else "" - log(f" B text(after) = {test_text[:80]!r}") - test_ok = all(s in test_text for s in survive) - log(f" TEST survive={survive} -> {'OK' if test_ok else 'SCRAMBLED/LOST'}") - if not test_ok: - # Show the corrupted bullets paragraph (locate by leftover 'Languages'/'ang' or dump near B). - for i, p in enumerate(m2.paragraphs): - t = para_text(p) - if "ang" in t or "OOP" in t or "Collec" in t or "Stream" in t or (b_needle[:3] in t): - log(f" >> scrambled B candidate [{page_index}.{i}] = {t[:90]!r}") - log(f"\n==> CONTROL={'OK' if ctrl_ok else 'FAIL'} TEST={'OK' if test_ok else 'FAIL'} " - f"(bug reproduced if CONTROL=OK and TEST=FAIL)") - - -def stress(): - """Sequentially reflow EVERY paragraph on both pages (re-extracting between each, like the - real app), then assert distinctive substrings from each survive — catches any font scramble - across non-subset AND subset (BCDKEE+Calibri...) fonts under heavy multi-edit.""" - # Distinctive substrings to verify survive on each page after all edits. - checks = { - 0: ["Professional", "Backend", "Core", "Java", "Frameworks", "Microservices", - "TapQwik", "Present", "LG", "Commerce"], - 1: ["Architected", "Wego", "Booking", "Education", "Bachelor", "Certification", - "Kafka", "Problem", "Ownership"], - } - # Edit (reflow-in-place) the paragraph CONTAINING each anchor, sequentially, re-extracting - # between edits — exactly how a user edits N paragraphs in one session. - anchors = { - 0: ["Backend", "Core Java", "Mar 2023", "LG"], - 1: ["Architected", "Wego", "Java Development", "Problem"], - } - failures = [] - for pi in (0, 1): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - log(f"\n=== STRESS page {pi}: sequentially reflowing {len(anchors[pi])} targeted paragraphs ===") - for anchor in anchors[pi]: - m = doc.get_page(pi).extract_document_model() - _, p = find_para(m, anchor) - if p is None: - log(f" anchor {anchor!r}: paragraph not found (possibly merged by a prior edit)") - continue - try: - doc.apply_edits(json.dumps({"version": "1.0", "operations": [build_reflow_op(p, pi)]})) - except Exception as exc: - log(f" anchor {anchor!r}: apply failed: {exc}") - final = para_textall(doc.get_page(pi).extract_document_model()) - for needle in checks[pi]: - if needle not in final: - failures.append((pi, needle)) - present = [n for n in checks[pi] if n in final] - log(f" page {pi}: {len(present)}/{len(checks[pi])} substrings survived: " - f"missing={[n for n in checks[pi] if n not in final]}") - log(f"\n==> STRESS {'PASS' if not failures else 'FAIL ' + str(failures)}") - - -def para_textall(model): - return " ".join(r.text for p in model.paragraphs for l in p.lines for r in l.runs) - - -def _png_to_img(pageimg): - from PIL import Image # noqa: PLC0415 - data = pageimg.data - if len(data) >= 8 and data[:8] == b"\x89PNG\r\n\x1a\n": - return Image.open(io.BytesIO(data)).convert("RGB") - n = pageimg.width * pageimg.height - mode = "RGBA" if len(data) == n * 4 else "RGB" - return Image.frombytes(mode, (pageimg.width, pageimg.height), data).convert("RGB") - - -def _diff_stats(base_img, edited_img): - """Return (total_nonzero_px, bbox_of_diff) between two RGB images.""" - from PIL import ImageChops # noqa: PLC0415 - diff = ImageChops.difference(base_img, edited_img) - bbox = diff.getbbox() # None if identical - nz = 0 - for px in diff.getdata(): - if px != (0, 0, 0): - nz += 1 - return nz, bbox - - -def overlay(page_index=0, anchor="Java Backend Developer", edit=None, dpi=150, use_lines=False): - """Objective pixel gate. Render the page, then reflow the paragraph containing `anchor` - (UNCHANGED if edit is None, else `edit(runs)->runs`), render again, and report the pixel - diff. A no-op reflow should ideally diff ~0 everywhere (the drift baseline); a real edit - should diff ONLY inside the edited region.""" - base = _png_to_img(pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "").get_page(page_index).render(dpi)) - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m = doc.get_page(page_index).extract_document_model() - i, p = find_para(m, anchor) - if p is None: - log(f"overlay: anchor {anchor!r} not found"); return - new_runs = None - if edit is not None: - base_runs = [{"text": r.text, "internalFontId": r.internal_font_id, "fontSize": r.font_size, "color": "#000000"} - for l in p.lines for r in l.runs if r.text] - new_runs = edit(base_runs) - op = build_reflow_op(p, page_index, new_runs=new_runs, use_lines=use_lines) - doc.apply_edits(json.dumps({"version": "1.0", "operations": [op]})) - edited = _png_to_img(doc.get_page(page_index).render(dpi)) - nz, bbox = _diff_stats(base, edited) - total = base.width * base.height - log(f"overlay [{page_index}] anchor={anchor!r} edit={'no-op' if edit is None else 'changed'} " - f"path={'lines' if use_lines else 'greedy'} dpi={dpi}") - log(f" diff pixels = {nz} / {total} ({100.0*nz/total:.3f}%) bbox={bbox}") - return nz, bbox - - -def render_after_edits(): - """Apply A (Summary) then B (bullets) on one doc, save, reload, render page 0 to PNG — - GROUND TRUTH: is the committed PDF visually corrupt, or only text extraction?""" - from PIL import Image # noqa: PLC0415 - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m0 = doc.get_page(0).extract_document_model() - _, pa = find_para(m0, "Java Backend Developer") - a_runs = [] - first = True - for l in pa.lines: - for r in l.runs: - if r.text == "": - continue - a_runs.append({"text": ("EDITED " + r.text) if first else r.text, - "internalFontId": r.internal_font_id, "fontSize": r.font_size, "color": "#000000"}) - first = False - doc.apply_edits(json.dumps({"version": "1.0", "operations": [build_reflow_op(pa, 0, new_runs=a_runs)]})) - m1 = doc.get_page(0).extract_document_model() - _, pb = find_para(m1, "Core Java") - doc.apply_edits(json.dumps({"version": "1.0", "operations": [build_reflow_op(pb, 0)]})) - out = doc.save_full() - img = pdfengine.PdfDocument.load_from_memory(out, "").get_page(0).render(150) - data = img.data - png = Path(__file__).resolve().parent / "_reflow_render.png" - n = img.width * img.height - if len(data) >= 8 and data[:8] == b"\x89PNG\r\n\x1a\n": - png.write_bytes(data) # already PNG-encoded - else: - mode = "RGBA" if len(data) == n * 4 else ("RGB" if len(data) == n * 3 else None) - from PIL import Image # noqa: PLC0415 - Image.frombytes(mode, (img.width, img.height), data).convert("RGB").save(png) - log(f"rendered committed page0 -> {png} ({img.width}x{img.height}, {len(data)} bytes)") - - -def probe_fonts(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - for pi in (0, 1): - fonts = doc.get_page(pi).get_fonts() - log(f"\n=== PAGE {pi} fonts ({len(fonts)}) ===") - for f in fonts: - log(f" font_name={f.font_name!r:34} internal_id={f.internal_font_id!r:28} " - f"type={f.type!r:12} flags={f.flags:<4} subset={f.is_subset} tag={f.subset_tag!r} " - f"embedded={f.is_embedded}") - - -if __name__ == "__main__": - mode = sys.argv[1] if len(sys.argv) > 1 else "dump" - if mode == "dump": - dump_structure() - elif mode == "repro": - repro() - elif mode == "repro_diff": - # A = Calibri body (Summary); B = a Calibri-Bold-only heading ("Projects"). - # Different base fonts -> if B survives, the scramble is same-/BaseFont aliasing. - repro(a_needle="Java Backend Developer", b_needle="Projects", survive=("Projects",)) - elif mode == "probe": - probe_fonts() - elif mode == "render": - render_after_edits() - elif mode == "stress": - stress() - elif mode == "overlay": - # Baseline drift: no-op reflow, GREEDY path (post-edit behavior). - log("== GREEDY path (re-wrap from scratch; the 'while typing' path) ==") - overlay(0, "Java Backend Developer") - overlay(0, "Core Java") - overlay(1, "Architected and") - # no-op reflow, LINES path (the no-change-on-click path). - log("\n== LINES path (original breaks emitted verbatim; the 'on open' path) ==") - overlay(0, "Java Backend Developer", use_lines=True) - overlay(0, "Core Java", use_lines=True) - overlay(1, "Architected and", use_lines=True) - # noise floor for reference - base = _png_to_img(pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "").get_page(0).render(150)) - b2 = _png_to_img(pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "").get_page(0).render(150)) - nz, _ = _diff_stats(base, b2) - log(f"\n(noise floor, same pdf twice = {nz} px)") - OUT.write_text(report.getvalue(), encoding="utf-8") - print(f"wrote {OUT}") diff --git a/tests/edits/_symbol_diag.py b/tests/edits/_symbol_diag.py new file mode 100644 index 0000000..81ba9d7 --- /dev/null +++ b/tests/edits/_symbol_diag.py @@ -0,0 +1,64 @@ +"""Diagnose the 'star broke the font + overlap' report. Edit a Calibri run adding * and the unicode +star, then check: which font emitted, whether the glyphs are present, and whether consecutive glyphs +OVERLAP (x positions go backwards / a glyph has ~0 advance). + +Run: gateway/.venv/Scripts/python.exe tests/edits/_symbol_diag.py +""" +from __future__ import annotations +import json, sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib") +sys.path.insert(1, str(ROOT / "gateway")) +import pdfengine +PDF = ROOT / "Mo-Faishal-Qureshi.pdf" + + +def ptext(p): + return "".join(r.text for ln in p.lines for r in ln.runs) + + +def main(): + doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") + m = doc.get_page(1).extract_document_model() + para = next(p for p in m.paragraphs if "Designed high-performance" in ptext(p)) + oi = [i for ln in para.lines for r in ln.runs for i in r.object_indices] + cl = min(ln.x for ln in para.lines); cr = max(ln.x + ln.w for ln in para.lines) + fb = max(ln.baseline_y for ln in para.lines) + bls = sorted({round(ln.baseline_y, 1) for ln in para.lines}, reverse=True) + lead = abs(bls[0]-bls[1]) if len(bls) > 1 else 14.0 + fid = para.lines[0].runs[0].internal_font_id + print("fid:", fid) + new_text = "Star test asterisk * and blackstar ★ end" + + op = {"version": "1.0", "operations": [{"id": "s", "type": "reflow_paragraph", "pageIndex": 1, "data": { + "objectIndices": oi, "runs": [{"text": new_text, "internalFontId": fid, "fontSize": 11.0, "color": "#000000"}], + "columnLeft": cl, "columnRight": cr, "firstBaselineY": fb, "leading": lead, + "oldLineCount": len(para.lines), "align": "left", "paraId": "SYMCHK"}}]} + doc.apply_edits(json.dumps(op)) + r = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") + + for pg in range(r.page_count): + for p in r.get_page(pg).extract_document_model().paragraphs: + if "asterisk" in ptext(p): + fonts = sorted({rr.font_name for ln in p.lines for rr in ln.runs if rr.text.strip()}) + txt = ptext(p) + print("emitted fonts:", fonts) + print("has '*':", "*" in txt, " has U+2605:", "★" in txt) + gl = [(g.text, g.origin_x) for ln in p.lines for rr in ln.runs for g in rr.glyphs] + print("All glyphs:") + for text, x in gl: + print(f" {repr(text)}: x={x:.2f}") + overlaps = 0 + for a, b in zip(gl, gl[1:]): + if b[1] <= a[1] - 0.5 and b[1] > 0: + overlaps += 1 + print(f"glyphs={len(gl)} backward/overlap steps={overlaps}") + s = txt.find("blackstar") + print("around star:", repr(txt[max(0,s-6):s+20])) + return + + +if __name__ == "__main__": + main() diff --git a/tests/edits/_verify_build.out.txt b/tests/edits/_verify_build.out.txt deleted file mode 100644 index f274528..0000000 --- a/tests/edits/_verify_build.out.txt +++ /dev/null @@ -1,24 +0,0 @@ - -########## REFLOW stress (multi-edit survival) ########## - -=== STRESS page 0: sequentially reflowing 4 targeted paragraphs === - page 0: 10/10 substrings survived: missing=[] - -=== STRESS page 1: sequentially reflowing 4 targeted paragraphs === - page 1: 9/9 substrings survived: missing=[] - -==> STRESS PASS - -########## REFLOW no-op overlay (regression: left + justify paragraphs must stay ~baseline) ########## -overlay [0] anchor='Java Backend Developer' edit=no-op path=greedy dpi=150 - diff pixels = 35821 / 2103750 (1.703%) bbox=(112, 263, 1150, 1435) -overlay [0] anchor='Java Backend Developer' edit=no-op path=lines dpi=150 - diff pixels = 35441 / 2103750 (1.685%) bbox=(112, 263, 1150, 1435) - -########## CENTER align smoke ########## - 'Software Engineer' heading not found (skip center smoke) - -########## RAW TEXT StreamEditor smoke (P1a) ########## - extract_text_objects(0) -> 727 objects - replace obj#2 'FAISHAL' -> 'FAISHAX' : success=True - re-extract obj#2 = 'FAISHAX' (expected 'FAISHAX', match=True) diff --git a/tests/edits/_verify_build.py b/tests/edits/_verify_build.py deleted file mode 100644 index ec6514b..0000000 --- a/tests/edits/_verify_build.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 -"""Verify the FRESH build-dir .pyd (the gateway copy is locked by the running gateway) without -touching the gateway. Importing pdfengine from the build lib FIRST caches it in sys.modules, so when -_reflow_repro imports pdfengine it reuses this fresh one. Runs the reflow regression gate + a Raw Text -(StreamEditor) smoke for the new TJ-positioning path. -Run: gateway/.venv/Scripts/python.exe tests/edits/_verify_build.py -""" -from __future__ import annotations -import sys -from pathlib import Path - -BUILD_LIB = r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib" -sys.path.insert(0, BUILD_LIB) -import pdfengine # noqa: E402 -- FRESH build, cached in sys.modules before the harness imports it - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, str(Path(__file__).resolve().parent)) -import _reflow_repro as H # noqa: E402 -- reuses the cached fresh pdfengine - -print("pdfengine loaded from:", pdfengine.__file__) -assert BUILD_LIB.lower() in pdfengine.__file__.lower(), "NOT the fresh build .pyd!" - -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" - - -def section(t): - H.report.write(f"\n########## {t} ##########\n") - - -# ---- 1. Reflow regression: multi-edit survival + no-op overlay diff (left/justify) ---- -section("REFLOW stress (multi-edit survival)") -H.stress() - -section("REFLOW no-op overlay (regression: left + justify paragraphs must stay ~baseline)") -H.overlay(0, "Java Backend Developer") # flowing left/justify body -H.overlay(0, "Java Backend Developer", use_lines=True) - -# ---- 2. Center alignment: reflow a centered heading with align=center; assert no crash + survives ---- -section("CENTER align smoke") -try: - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - m = doc.get_page(0).extract_document_model() - i, p = H.find_para(m, "Software Engineer") - if p is None: - H.log(" 'Software Engineer' heading not found (skip center smoke)") - else: - op = H.build_reflow_op(p, 0) - op["data"]["align"] = "center" - # widen the column to the page content box so centering is visible - op["data"]["columnLeft"] = 40.0 - op["data"]["columnRight"] = m.width - 40.0 - doc.apply_edits(__import__("json").dumps({"version": "1.0", "operations": [op]})) - m2 = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "").get_page(0).extract_document_model() - _, pc = H.find_para(m2, "Software") - txt = H.para_text(pc) if pc else "" - H.log(f" center reflow OK; text survived = {('Software' in txt and 'Engineer' in txt)} -> {txt[:60]!r}") -except Exception as exc: - H.log(f" CENTER smoke FAILED: {exc}") - -# ---- 3. Raw Text StreamEditor: extract + same-length replace (P1a TJ preservation) ---- -section("RAW TEXT StreamEditor smoke (P1a)") -try: - import tempfile, os - tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf"); tmp.write(PDF.read_bytes()); tmp.close() - ed = pdfengine.StreamEditor(tmp.name) - objs = ed.extract_text_objects(0) - H.log(f" extract_text_objects(0) -> {len(objs)} objects") - # pick the first object with >=3 ASCII chars; do a SAME-LENGTH edit (exercises TJ redistribution) - target = None - for idx, o in enumerate(objs): - t = o["text"].decode("latin-1") if isinstance(o["text"], bytes) else o["text"] - if len(t) >= 3 and t.strip() and all(32 <= ord(c) < 127 for c in t): - target = (idx, t); break - if target is None: - H.log(" no suitable ASCII object found (skip)") - else: - idx, t = target - # same-length swap: reverse-safe -> replace each alnum char's case-insensitive 'a'->'e' won't change len - new = t[:-1] + ("X" if t[-1] != "X" else "Y") # same length, last char changed - out = tmp.name + ".out.pdf" - ok = ed.replace_text_object(0, idx, new.encode("latin-1"), out) - H.log(f" replace obj#{idx} {t!r} -> {new!r} : success={ok}") - if ok and os.path.exists(out): - ed2 = pdfengine.StreamEditor(out) - objs2 = ed2.extract_text_objects(0) - t2 = objs2[idx]["text"]; t2 = t2.decode("latin-1") if isinstance(t2, bytes) else t2 - H.log(f" re-extract obj#{idx} = {t2!r} (expected {new!r}, match={t2 == new})") - os.remove(out) - os.remove(tmp.name) -except Exception as exc: - H.log(f" RAW TEXT smoke FAILED: {exc}") - -OUT = Path(__file__).resolve().parent / "_verify_build.out.txt" -OUT.write_text(H.report.getvalue(), encoding="utf-8") -print(f"wrote {OUT}") diff --git a/tests/edits/_xpage_real.py b/tests/edits/_xpage_real.py deleted file mode 100644 index 0e09249..0000000 --- a/tests/edits/_xpage_real.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Cross-page reflow on the REAL resume (embedded Calibri/Times subset fonts + cascade). -Verifies that migrating EMBEDDED subset-font text objects across pages keeps glyphs intact. - -Run: gateway/.venv/Scripts/python.exe tests/edits/_xpage_real.py -""" -from __future__ import annotations -import io, json, sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, str(ROOT / "gateway")) -import pdfengine - -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" - - -def ptext(p): - return " ".join("".join(r.text for r in ln.runs) for ln in p.lines) - - -def page_text(doc, i): - m = doc.get_page(i).extract_document_model() - return " ".join(ptext(p) for p in m.paragraphs) - - -def main(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - print(f"loaded: pages={doc.page_count}") - m0 = doc.get_page(0).extract_document_model() - body = [p for p in m0.paragraphs if len(p.lines) >= 1] - para = min(body, key=lambda p: min(ln.baseline_y for ln in p.lines)) - print(f"editing page-0 paragraph (lowest): {ptext(para)[:60]!r} lines={len(para.lines)}") - - obj_idxs = [oi for ln in para.lines for r in ln.runs for oi in r.object_indices] - col_left = min(ln.x for ln in para.lines) - col_right = max(ln.x + ln.w for ln in para.lines) - first_baseline = max(ln.baseline_y for ln in para.lines) - bls = sorted({round(ln.baseline_y, 1) for ln in para.lines}, reverse=True) - leading = abs(bls[0] - bls[1]) if len(bls) > 1 else 14.0 - fid = para.lines[0].runs[0].internal_font_id - - marker = " ".join(f"X{n:03d}" for n in range(60)) - new_text = ptext(para).strip() + " " + marker + " ZZEND" - - op = {"version": "1.0", "operations": [{ - "id": "rg", "type": "reflow_paragraph", "pageIndex": 0, "data": { - "objectIndices": obj_idxs, - "runs": [{"text": new_text, "internalFontId": fid, "fontSize": 11.0, "color": "#000000"}], - "columnLeft": col_left, "columnRight": col_right, "firstBaselineY": first_baseline, - "leading": leading, "oldLineCount": len(para.lines), "align": "left"}}]} - doc.apply_edits(json.dumps(op)) - out = doc.save_full() - r = pdfengine.PdfDocument.load_from_memory(out, "") - print(f"after grow: pages={r.page_count}") - - for i in range(r.page_count): - t = page_text(r, i) - has_marker = any(f"X{n:03d}" in t for n in range(60)) - print(f" page {i}: ZZEND={'ZZEND' in t} markers={has_marker} len={len(t)}") - allt = " ".join(page_text(r, i) for i in range(r.page_count)) - print("Architected preserved:", "Architected" in allt) - print("Education preserved:", "Education" in allt) - nospace = allt.replace(" ", "") - miss_join = [f"X{n:03d}" for n in range(60) if f"X{n:03d}" not in allt] - miss_nospace = [f"X{n:03d}" for n in range(60) if f"X{n:03d}" not in nospace] - print(f"markers intact (space-join): {60-len(miss_join)}/60 missing: {miss_join}") - print(f"markers intact (no-space): {60-len(miss_nospace)}/60 missing: {miss_nospace}") - p0 = page_text(r, 0); p1 = page_text(r, 1) - print("\n--- page0 tail ---\n", p0[-220:]) - print("\n--- page1 head ---\n", p1[:220]) - orig = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - orig_p1 = page_text(orig, 1) - for needle in ["Architected", "Education", "Soft", "Problem-Solving", "Ownership", "Certifications", "Bachelor"]: - print(f" page1-orig token {needle!r}: present_before={needle in orig_p1} present_after={needle in allt}") - for i in range(r.page_count): - mm = r.get_page(i).extract_document_model() - ys = [ln.baseline_y for p in mm.paragraphs for ln in p.lines] - print(f" page {i}: lowest_baseline_y={min(ys):.1f} (page height 792; <0 means off-page)") - - -if __name__ == "__main__": - main() diff --git a/tests/edits/_xpage_reedit.py b/tests/edits/_xpage_reedit.py deleted file mode 100644 index d21c9c2..0000000 --- a/tests/edits/_xpage_reedit.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Reproduce the user's bug: grow a page-0 paragraph so it overflows to page 1, save, THEN edit a -paragraph that now lives on page 1 -> is the result scrambled (leftover glyphs / wrong fonts)? - -Run: gateway/.venv/Scripts/python.exe tests/edits/_xpage_reedit.py -""" -from __future__ import annotations -import json, sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -sys.path.insert(0, str(ROOT / "gateway")) -import pdfengine -PDF = ROOT / "Mo-Faishal-Qureshi.pdf" - - -def ptext(p): - return "".join(r.text for ln in p.lines for r in ln.runs) - - -def find_para(doc, page, needle): - m = doc.get_page(page).extract_document_model() - for p in m.paragraphs: - if needle in ptext(p): - return p - return None - - -def reflow_para(doc, page, para, new_text, align="left"): - obj_idxs = [oi for ln in para.lines for r in ln.runs for oi in r.object_indices] - col_left = min(ln.x for ln in para.lines) - col_right = max(ln.x + ln.w for ln in para.lines) - first_baseline = max(ln.baseline_y for ln in para.lines) - bls = sorted({round(ln.baseline_y, 1) for ln in para.lines}, reverse=True) - leading = abs(bls[0] - bls[1]) if len(bls) > 1 else 14.0 - fid = para.lines[0].runs[0].internal_font_id - op = {"version": "1.0", "operations": [{ - "id": "e", "type": "reflow_paragraph", "pageIndex": page, "data": { - "objectIndices": obj_idxs, - "runs": [{"text": new_text, "internalFontId": fid, "fontSize": 11.0, "color": "#000000"}], - "columnLeft": col_left, "columnRight": col_right, "firstBaselineY": first_baseline, - "leading": leading, "oldLineCount": len(para.lines), "align": align}}]} - doc.apply_edits(json.dumps(op)) - return pdfengine.PdfDocument.load_from_memory(doc.save_full(), "") - - -def main(): - doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "") - tech = find_para(doc, 0, "Technologies") - print("step1: growing page-0 para:", ptext(tech)[:50], "...") - big = ptext(tech).strip() + " " + ("g" * 30 + " ") * 25 - doc = reflow_para(doc, 0, tech, big) - print(" pages after grow:", doc.page_count) - - arch = find_para(doc, 1, "Architected") - if not arch: - print(" !! 'Architected' not found on page 1; pages dump:") - for i in range(doc.page_count): - print(f" page {i}:", " | ".join(ptext(p)[:30] for p in doc.get_page(i).extract_document_model().paragraphs)[:200]) - return - print("step2: editing page-1 para:", ptext(arch)[:50], "...") - before = ptext(arch) - doc2 = reflow_para(doc, 1, arch, "Architected and developed a high-scale ecommerce backend NEWTEXT123 for testing.") - - after = ptext(find_para(doc2, 1, "Architected") or find_para(doc2, 0, "Architected")) - print("\n BEFORE:", before[:90]) - print(" AFTER :", after[:120]) - print(" contains NEWTEXT123:", "NEWTEXT123" in after) - clean = "Architected and developed a high-scale ecommerce backend NEWTEXT123 for testing." - print(" clean match:", after.strip() == clean) - for w in ["product", "catalog", "dynamic", "pricing", "transactions"]: - if w in after: - print(f" !! LEFTOVER old word in edited paragraph: {w!r}") - - -if __name__ == "__main__": - main() diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index 75c7a3c..d1639d4 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -43,7 +43,12 @@ target_include_directories(pdfengine_wasm PRIVATE target_link_libraries(pdfengine_wasm PRIVATE pdfengine::pdfengine) +set(_wasm_font_src "${CMAKE_SOURCE_DIR}/engine/assets/fonts") +set(_wasm_font_dir "${CMAKE_BINARY_DIR}/embed_fonts") +file(COPY "${_wasm_font_src}/" DESTINATION "${_wasm_font_dir}") + target_link_options(pdfengine_wasm PRIVATE + "--embed-file" "${_wasm_font_dir}@/fonts" "-sMODULARIZE=1" "-sEXPORT_ES6=1" "-sENVIRONMENT=node,web"