diff --git a/client-tauri/index.html b/client-tauri/index.html
index 2d851d5b9..92c0a3c96 100644
--- a/client-tauri/index.html
+++ b/client-tauri/index.html
@@ -5,8 +5,6 @@
Tauri + React + TS
-
-
diff --git a/client-tauri/package.json b/client-tauri/package.json
index 869ab4021..eb95ccaa3 100644
--- a/client-tauri/package.json
+++ b/client-tauri/package.json
@@ -25,6 +25,7 @@
"react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.18.0",
+ "vite-plugin-node-polyfills": "^0.21.0",
"vite-plugin-top-level-await": "^1.3.1"
},
"devDependencies": {
diff --git a/client-tauri/src/App.tsx b/client-tauri/src/App.tsx
index cbab0b925..28d489cd9 100644
--- a/client-tauri/src/App.tsx
+++ b/client-tauri/src/App.tsx
@@ -21,7 +21,7 @@ import resourcesToBackend from "i18next-resources-to-backend";
i18next.use(LanguageDetector).use(initReactI18next).use(resourcesToBackend((language: string, namespace: string) => import(`../../shared-operations/public/locales/${namespace}/${language}.json`)))
.init({
- debug: true,
+ debug: false,
ns: ["common"], // Preload this namespace, no need to add the others, they will load once their module is loaded
defaultNS: "common",
fallbackLng: "en",
diff --git a/client-tauri/src/pages/Dynamic.tsx b/client-tauri/src/pages/Dynamic.tsx
index 2ca5d19e2..81abcdd66 100644
--- a/client-tauri/src/pages/Dynamic.tsx
+++ b/client-tauri/src/pages/Dynamic.tsx
@@ -38,24 +38,15 @@ function Dynamic() {
});
}
- function formDataToObject(formData: FormData): Record {
- const result: Record = {};
-
- formData.forEach((value, key) => {
- result[key] = value.toString();
- });
-
- return result;
- }
-
async function handleSubmit(e: BaseSyntheticEvent) {
+ console.clear();
if(!activeOperator.current) {
throw new Error("Please select an Operator in the Dropdown");
}
const formData = new FormData(e.target);
-
- const action: Action = {type: activeOperator.current.constructor.name, values: formDataToObject(formData)};
+ const values = Object.fromEntries(formData.entries());
+ let action: Action = {type: activeOperator.current.type, values: values};
// Validate PDF File
@@ -68,7 +59,6 @@ function Dynamic() {
for (let i = 0; i < files.length; i++) {
const file = filesArray[i];
if(file) {
- console.log(new Uint8Array(await file.arrayBuffer()));
inputs.push(new PdfFile(
file.name.replace(/\.[^/.]+$/, ""), // Strip Extension
new Uint8Array(await file.arrayBuffer()),
@@ -80,26 +70,25 @@ function Dynamic() {
}
}
- const pdfValidationResults = await JoiPDFFileSchema.validate(inputs);
- if(pdfValidationResults.error) {
- console.log({error: "PDF validation failed", details: pdfValidationResults.error.message});
- }
- const pdfFiles: PdfFile[] = pdfValidationResults.value;
+ const validationResults = activeOperator.current.schema.validate({input: inputs, values: action.values});
- // Validate Action Values
- const actionValidationResults = activeOperator.current.schema.validate({input: pdfFiles, values: action.values});
-
- if(actionValidationResults.error) {
- console.log({error: "Value validation failed", details: actionValidationResults.error.message});
- return;
+ if(validationResults.error) {
+ console.log({error: "Validation failed", details: validationResults.error.message});
+ }
+ else {
+ action.values = validationResults.value.values;
+ const operation = new activeOperator.current(action);
+ operation.run(validationResults.value.input, (progress) => {}).then(async pdfFiles => {
+ console.log("Done");
+ console.log(pdfFiles);
+
+ for await (const pdfFile of (pdfFiles as PdfFile[])) {
+ var blob = new Blob([await pdfFile.uint8Array], {type: "application/pdf"});
+ var objectUrl = URL.createObjectURL(blob);
+ window.open(objectUrl);
+ }
+ });
}
-
- action.values = pdfValidationResults.value.values;
- const operation = new activeOperator.current(action);
-
- operation.run(pdfValidationResults.value, (progress) => {}).then(pdfFiles => {
- console.log("Done");
- });
};
function capitalizeFirstLetter(string: String) {
diff --git a/client-tauri/vite.config.ts b/client-tauri/vite.config.ts
index c37ca4b1f..78dfb6118 100644
--- a/client-tauri/vite.config.ts
+++ b/client-tauri/vite.config.ts
@@ -1,14 +1,27 @@
import { defineConfig } from "vite";
+import { nodePolyfills } from 'vite-plugin-node-polyfills'
import react from "@vitejs/plugin-react";
import topLevelAwait from "vite-plugin-top-level-await";
-import dynamicImport from 'vite-plugin-dynamic-import'
-import compileTime from "vite-plugin-compile-time"
-import { fileURLToPath, URL } from 'node:url'
+import dynamicImport from 'vite-plugin-dynamic-import';
+import compileTime from "vite-plugin-compile-time";
+import { fileURLToPath, URL } from 'node:url';
// https://vitejs.dev/config/
export default defineConfig(async () => ({
plugins: [
+ // Thanks: https://stackoverflow.com/questions/74417822/how-can-i-use-buffer-process-in-vite-app
+ nodePolyfills({
+ include: [],
+
+ globals: {
+ Buffer: true, // can also be 'build', 'dev', or false
+ global: false,
+ process: true,
+ },
+ // Whether to polyfill `node:` protocol imports.
+ protocolImports: false,
+ }),
react(),
topLevelAwait({
// The export name of top-level await promise for each chunk module
diff --git a/package-lock.json b/package-lock.json
index 11699920d..2b3094c37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,6 +39,7 @@
"react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.18.0",
+ "vite-plugin-node-polyfills": "^0.21.0",
"vite-plugin-top-level-await": "^1.3.1"
},
"devDependencies": {
@@ -75,6 +76,21 @@
}
}
},
+ "client-tauri/node_modules/vite-plugin-node-polyfills": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.21.0.tgz",
+ "integrity": "sha512-Sk4DiKnmxN8E0vhgEhzLudfJQfaT8k4/gJ25xvUPG54KjLJ6HAmDKbr4rzDD/QWEY+Lwg80KE85fGYBQihEPQA==",
+ "dependencies": {
+ "@rollup/plugin-inject": "^5.0.5",
+ "node-stdlib-browser": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/davidmyersdev"
+ },
+ "peerDependencies": {
+ "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
+ }
+ },
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
@@ -1157,6 +1173,57 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@jsonjoy.com/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/@jsonjoy.com/json-pack": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz",
+ "integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==",
+ "dependencies": {
+ "@jsonjoy.com/base64": "^1.1.1",
+ "@jsonjoy.com/util": "^1.1.2",
+ "hyperdyperid": "^1.2.0",
+ "thingies": "^1.20.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/@jsonjoy.com/util": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.1.3.tgz",
+ "integrity": "sha512-g//kkF4kOwUjemValCtOc/xiYzmwMRmWq3Bn+YnzOzuZLHq2PpMOxxIayN3cKbo7Ko2Np65t6D9H81IvXbXhqg==",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
@@ -5617,6 +5684,14 @@
"node": ">= 6"
}
},
+ "node_modules/hyperdyperid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
+ "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
+ "engines": {
+ "node": ">=10.18"
+ }
+ },
"node_modules/i18next": {
"version": "23.7.16",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.7.16.tgz",
@@ -8317,6 +8392,24 @@
"node": ">=8"
}
},
+ "node_modules/sonic-forest": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sonic-forest/-/sonic-forest-1.0.3.tgz",
+ "integrity": "sha512-dtwajos6IWMEWXdEbW1IkEkyL2gztCAgDplRIX+OT5aRKnEd5e7r7YCxRgXZdhRP1FBdOBf8axeTPhzDv8T4wQ==",
+ "dependencies": {
+ "tree-dump": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -8573,6 +8666,17 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/thingies": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
+ "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
+ "engines": {
+ "node": ">=10.18"
+ },
+ "peerDependencies": {
+ "tslib": "^2"
+ }
+ },
"node_modules/through2": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@@ -8672,6 +8776,21 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"optional": true
},
+ "node_modules/tree-dump": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.1.tgz",
+ "integrity": "sha512-WCkcRBVPSlHHq1dc/px9iOfqklvzCbdRwvlNfxGZsrHqf6aZttfPrd7DJTt6oR10dwUfpFFQeVTkPbBIZxX/YA==",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -10525,12 +10644,55 @@
"license": "ISC",
"dependencies": {
"@stirling-tools/joi": "github:Stirling-Tools/joi",
+ "buffer": "^6.0.3",
"i18next-resources-to-backend": "^1.2.0",
"image-js": "^0.35.5",
+ "memfs": "^4.9.2",
"next-i18next": "^15.1.1",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.0.269"
}
+ },
+ "shared-operations/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "shared-operations/node_modules/memfs": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.2.tgz",
+ "integrity": "sha512-f16coDZlTG1jskq3mxarwB+fGRrd0uXWt+o1WIhRfOwbXQZqUDsTVxQBFK9JjRQHblg8eAG2JSbprDXKjc7ijQ==",
+ "dependencies": {
+ "@jsonjoy.com/json-pack": "^1.0.3",
+ "@jsonjoy.com/util": "^1.1.2",
+ "sonic-forest": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ }
}
}
}
diff --git a/shared-operations/package.json b/shared-operations/package.json
index c568d1343..7dce373a6 100644
--- a/shared-operations/package.json
+++ b/shared-operations/package.json
@@ -10,8 +10,10 @@
"license": "ISC",
"dependencies": {
"@stirling-tools/joi": "github:Stirling-Tools/joi",
+ "buffer": "^6.0.3",
"i18next-resources-to-backend": "^1.2.0",
"image-js": "^0.35.5",
+ "memfs": "^4.9.2",
"next-i18next": "^15.1.1",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.0.269"
diff --git a/shared-operations/public/wasm/pdfcpu/pdfcpu.wasm b/shared-operations/public/wasm/pdfcpu/pdfcpu.wasm
index fc7855d3f..b860b423f 100644
Binary files a/shared-operations/public/wasm/pdfcpu/pdfcpu.wasm and b/shared-operations/public/wasm/pdfcpu/pdfcpu.wasm differ
diff --git a/shared-operations/src/functions/impose.ts b/shared-operations/src/functions/impose.ts
index 8b715dafb..0b3f7a885 100644
--- a/shared-operations/src/functions/impose.ts
+++ b/shared-operations/src/functions/impose.ts
@@ -87,12 +87,10 @@ export class Impose extends Operator {
[
"pdfcpu.wasm",
"nup",
- "-c",
- "disable",
- "f:" + this.actionValues.format,
- "/output.pdf",
- String(this.actionValues.nup),
- "input.pdf",
+ "formsize:" + this.actionValues.format, // configuration string
+ "/output.pdf", // outFile
+ String(this.actionValues.nup), // nupvalue
+ "/input.pdf" // infile
],
await input.uint8Array
);
diff --git a/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.client.js b/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.client.js
index 104eb58d5..da9bf4478 100644
--- a/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.client.js
+++ b/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.client.js
@@ -1,89 +1,32 @@
-// imports browserfs via index.html script-tag
-import wasmUrl from '../../../public/wasm/pdfcpu/pdfcpu.wasm?url'
+import "./wasm_exec_memfs.js";
-let wasmLocation = "/wasm/pdfcpu/";
-
-let fs;
-let Buffer;
-
-// TODO: This can later be defered to load asynchronously
-configureFs();
-loadWasm();
-
-function configureFs() {
- BrowserFS.configure(
- {
- fs: "InMemory",
- },
- function (e) {
- if (e) {
- // An error happened!
- throw e;
- }
- fs = BrowserFS.BFSRequire("fs");
- Buffer = BrowserFS.BFSRequire("buffer").Buffer;
-
- window.fs = fs;
- window.Buffer = Buffer;
- }
- );
-}
-
-function loadWasm() {
- import("./wasm_exec.js");
-}
-
-const runWasm = async (param) => {
- if (window.cachedWasmResponse === undefined) {
- const response = await fetch(wasmUrl);
- const buffer = await response.arrayBuffer();
- window.cachedWasmResponse = buffer;
- window.go = new Go();
- }
- const { instance } = await WebAssembly.instantiate(
- window.cachedWasmResponse,
- window.go.importObject
- );
- window.go.argv = param;
- await window.go.run(instance);
- return window.go.exitCode;
-};
-
-async function loadFileAsync(data) {
- console.log(`Writing file to MemoryFS`);
- await fs.writeFile(`/input.pdf`, data);
- console.log(`Write done. Validating...`);
- let exitcode = await runWasm([
- "pdfcpu.wasm",
- "validate",
- "-c",
- "disable",
- `/input.pdf`,
- ]);
-
- console.log("Exit Code: " + exitcode);
- if (exitcode !== 0)
- throw new Error("There was an error validating your PDFs");
-
- console.log(`File is Valid`);
-}
+import wasmUrl from '../../../public/wasm/pdfcpu/pdfcpu.wasm?url';
export async function oneToOne(wasmArray, snapshot) {
- await loadFileAsync(Buffer.from(snapshot));
-
- console.log("Nuping File");
- let exitcode = await runWasm(wasmArray);
-
- if (exitcode !== 0) {
- console.error("There was an error nuping your PDFs");
- return;
+ if (!WebAssembly.instantiateStreaming) { // polyfill
+ WebAssembly.instantiateStreaming = async (resp, importObject) => {
+ const source = await (await resp).arrayBuffer();
+ return await WebAssembly.instantiate(source, importObject);
+ };
}
- await fs.unlink("input.pdf");
- const contents = fs.readFileSync("output.pdf");
- fs.unlink("output.pdf");
- console.log("Your File ist Ready!");
- return new Uint8Array(contents);
+ const go = new Go();
+ go.argv = wasmArray;
+
+ const webAssemblyInstantiatedSource = await WebAssembly.instantiateStreaming(fetch(wasmUrl), go.importObject);
+ let inst = webAssemblyInstantiatedSource.instance
+
+ await globalThis.fs.promises.writeFile("/input.pdf", Buffer.from(snapshot));
+
+ await go.run(inst);
+ inst = await WebAssembly.instantiate(webAssemblyInstantiatedSource.module, go.importObject); // reset instance
+
+ globalThis.fs.promises.unlink("/input.pdf");
+ const result = await globalThis.fs.promises.readFile("/output.pdf");
+
+ globalThis.fs.promises.unlink("/output.pdf");
+
+ return result;
}
export async function manyToOne() {
diff --git a/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server.js b/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server.js
index e37414612..1ae8e4d9a 100644
--- a/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server.js
+++ b/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server.js
@@ -1,112 +1,32 @@
-import { WasmFs } from '@wasmer/wasmfs';
+import "./wasm_exec_memfs.js";
+import fs from "node:fs";
+
import path from "path";
import { fileURLToPath } from 'url';
-let nodeWasmLocation = "./dist/public/wasm/pdfcpu/"; // TODO: Replace with __dirname
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
-let fs;
-const wasmfs = new WasmFs();
-
-// TODO: This can later be defered to load asynchronously
-(async () => {
- configureFs();
- await loadWasm();
-})();
-
-function configureFs() {
- // Can't use BrowserFS: https://github.com/jvilk/BrowserFS/issues/271
- fs = wasmfs.fs;
- global.fs = fs;
-
- console.log("InMemoryFs configured");
-}
-
-async function loadWasm() {
- // global.crypto = (await import("crypto")).webcrypto; // wasm dependecy
- await import("./wasm_exec.js");
-}
-
-const runWasm = async (param) => {
- if (global.cachedWasmResponse === undefined) {
- const buffer = (await import("fs")).readFileSync(nodeWasmLocation + "/pdfcpu.wasm");
- global.cachedWasmResponse = buffer;
- global.go = new Go();
- }
- const { instance } = await WebAssembly.instantiate(
- global.cachedWasmResponse,
- global.go.importObject
- );
- global.go.argv = param;
- await global.go.run(instance);
- return global.go.exitCode;
-};
-
-async function loadFileAsync(data) {
- console.log(`Writing file to Disk`);
- if(fs === undefined) {
- throw new Error("FS hasn't loaded, this should never happen.")
- }
- fs.writeFileSync(`input.pdf`, data);
- console.log(`Write done. Validating...`);
- let exitcode = await runWasm([
- "pdfcpu.wasm",
- "validate",
- "-c",
- "disable",
- `input.pdf`,
- ]);
- if (exitcode !== 0)
- throw new Error("There was an error validating your PDFs");
-
- // // Get logs of command
- // wasmfs.getStdOut().then(response => {
- // console.log(response);
- // });
-
- console.log(`File is Valid`);
-}
+const nodeWasmLocation = path.join(__dirname, "../../../public/wasm/pdfcpu/", "pdfcpu.wasm");
export async function oneToOne(wasmArray, snapshot) {
- await loadFileAsync(Buffer.from(snapshot));
+ const go = new Go();
+ go.argv = wasmArray;
- console.log("Nuping File");
+ const wasmFile = fs.readFileSync(nodeWasmLocation);
+ const webAssemblyInstantiatedSource = await WebAssembly.instantiate(wasmFile, go.importObject);
- let exitcode = await runWasm(wasmArray);
- if (exitcode !== 0) {
- console.error("There was an error nuping your PDFs");
- return;
- }
- console.log("Nuping Done");
+ await globalThis.fs.promises.writeFile("/input.pdf", Buffer.from(snapshot));
+
+ await go.run(webAssemblyInstantiatedSource.instance);
+
+ globalThis.fs.promises.unlink("/input.pdf");
+
+ const pdfcpu_result = await globalThis.fs.promises.readFile("/output.pdf");
+
+ globalThis.fs.promises.unlink("/output.pdf");
- /* TODO:
- * Make this more elegant, this waits for the write to finish.
- * Maybe replace wasmfs with https://github.com/streamich/memfs
- */
- await checkExistsWithTimeout("/output.pdf", 1000);
- console.log("Write started...");
- let fileSize;
- while (true) {
- fileSize = fs.statSync("/output.pdf").size;
- await new Promise((resolve, reject) => {
- setTimeout(() => {
- resolve();
- }, 50);
- });
- if(fileSize > 0 && fileSize == fs.statSync("/output.pdf").size) // Wait for file Size not changing anymore.
- break;
- }
-
- console.log("Could be done?");
-
- fs.unlinkSync("input.pdf");
-
- const data = fs.readFileSync("/output.pdf");
- if(data.length == 0) {
- throw Error("File Size 0 that should not happen. The write probably didn't finish in time.");
- }
- fs.unlinkSync("output.pdf");
- console.log("Your File ist Ready!");
- return new Uint8Array(data);
+ return new Uint8Array(pdfcpu_result);
}
export async function manyToOne() {
@@ -119,30 +39,4 @@ export async function oneToMany() {
export async function manyToMany() {
//TODO: Do this if necessary for some pdfcpu operations
-}
-
-// THX: https://stackoverflow.com/questions/26165725/nodejs-check-file-exists-if-not-wait-till-it-exist
-function checkExistsWithTimeout(filePath, timeout) {
- return new Promise(function (resolve, reject) {
-
- var timer = setTimeout(function () {
- watcher.close();
- reject(new Error('File did not exists and was not created during the timeout.'));
- }, timeout);
-
- fs.access(filePath, fs.constants.R_OK, function (err) {
- if (!err) {
- clearTimeout(timer);
- watcher.close();
- resolve();
- }
- });
-
- var dir = path.dirname(filePath);
- var watcher = fs.watch(dir, function (eventType, filename) {
- clearTimeout(timer);
- watcher.close();
- resolve();
- });
- });
}
\ No newline at end of file
diff --git a/shared-operations/src/wasm/pdfcpu/wasm_exec.js b/shared-operations/src/wasm/pdfcpu/wasm_exec.js
index 92a7bdaf3..15ee8320f 100644
--- a/shared-operations/src/wasm/pdfcpu/wasm_exec.js
+++ b/shared-operations/src/wasm/pdfcpu/wasm_exec.js
@@ -1,92 +1,27 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+
+"use strict";
+
(() => {
- // Map multiple JavaScript environments to a single common API,
- // preferring web standards over Node.js API.
- //
- // Environments considered:
- // - Browsers
- // - Node.js
- // - Electron
- // - Parcel
- // - Webpack
-
- console.log("pdfcpu wasm_exec imported")
- if (typeof global !== "undefined") {
- // global already exists
- } else if (typeof window !== "undefined") {
- window.global = window;
- } else if (typeof self !== "undefined") {
- self.global = self;
- } else {
- throw new Error("cannot export Go (neither global, window nor self is defined)");
- }
-
- let logFS = false
- var handler = {
- get: function (target, property) {
- if (property in target && target[property] instanceof Function) {
- return function () {
- if (logFS) {
- console.log(property, 'called', arguments);
- }
- // 将callback替换
- if (arguments[arguments.length - 1] instanceof Function) {
- var origCB = arguments[arguments.length - 1];
- var newCB = function () {
- if (logFS) {
- console.log('callback for', property, 'get called with args:', arguments);
- }
- return Reflect.apply(origCB, arguments.callee, arguments);
- }
- arguments[arguments.length - 1] = newCB;
- }
- return Reflect.apply(target[property], target, arguments);
- }
- } else {
- return target[property]
- }
- }
- }
-
- if (!global.require && typeof require !== "undefined") {
- global.require = require;
- }
-
-
- if (!global.fs && global.require) {
-
- //const fs = require("fs");
- if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
- global.fs = fs;
- }
-
- }
-
const enosys = () => {
- const err = new Error("not implemented");
+ const err = new Error("enosys not implemented");
+ console.error(err.stack);
err.code = "ENOSYS";
return err;
};
- if (!global.fs) {
+ if (!globalThis.fs) {
let outputBuf = "";
- global.fs = {
- constants: {
- O_WRONLY: -1,
- O_RDWR: -1,
- O_CREAT: -1,
- O_TRUNC: -1,
- O_APPEND: -1,
- O_EXCL: -1
- }, // unused
+ globalThis.fs = {
+ constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
- console.log(outputBuf.substr(0, nl));
- outputBuf = outputBuf.substr(nl + 1);
+ console.log(outputBuf.substring(0, nl));
+ outputBuf = outputBuf.substring(nl + 1);
}
return buf.length;
},
@@ -98,296 +33,71 @@
const n = this.writeSync(fd, buf);
callback(null, n);
},
- chmod(path, mode, callback) {
- callback(enosys());
- },
- chown(path, uid, gid, callback) {
- callback(enosys());
- },
- close(fd, callback) {
- callback(enosys());
- },
- fchmod(fd, mode, callback) {
- callback(enosys());
- },
- fchown(fd, uid, gid, callback) {
- callback(enosys());
- },
- fstat(fd, callback) {
- callback(enosys());
- },
- fsync(fd, callback) {
- callback(null);
- },
- ftruncate(fd, length, callback) {
- callback(enosys());
- },
- lchown(path, uid, gid, callback) {
- callback(enosys());
- },
- link(path, link, callback) {
- callback(enosys());
- },
- lstat(path, callback) {
- callback(enosys());
- },
- mkdir(path, perm, callback) {
- callback(enosys());
- },
- open(path, flags, mode, callback) {
- callback(enosys());
- },
- read(fd, buffer, offset, length, position, callback) {
- callback(enosys());
- },
- readdir(path, callback) {
- callback(enosys());
- },
- readlink(path, callback) {
- callback(enosys());
- },
- rename(from, to, callback) {
- callback(enosys());
- },
- rmdir(path, callback) {
- callback(enosys());
- },
- stat(path, callback) {
- callback(enosys());
- },
- symlink(path, link, callback) {
- callback(enosys());
- },
- truncate(path, length, callback) {
- callback(enosys());
- },
- unlink(path, callback) {
- callback(enosys());
- },
- utimes(path, atime, mtime, callback) {
- callback(enosys());
- },
+ chmod(path, mode, callback) { callback(enosys()); },
+ chown(path, uid, gid, callback) { callback(enosys()); },
+ close(fd, callback) { callback(enosys()); },
+ fchmod(fd, mode, callback) { callback(enosys()); },
+ fchown(fd, uid, gid, callback) { callback(enosys()); },
+ fstat(fd, callback) { callback(enosys()); },
+ fsync(fd, callback) { callback(null); },
+ ftruncate(fd, length, callback) { callback(enosys()); },
+ lchown(path, uid, gid, callback) { callback(enosys()); },
+ link(path, link, callback) { callback(enosys()); },
+ lstat(path, callback) { callback(enosys()); },
+ mkdir(path, perm, callback) { callback(enosys()); },
+ open(path, flags, mode, callback) { callback(enosys()); },
+ read(fd, buffer, offset, length, position, callback) {callback(enosys()); },
+ readdir(path, callback) { callback(enosys()); },
+ readlink(path, callback) { callback(enosys()); },
+ rename(from, to, callback) { callback(enosys()); },
+ rmdir(path, callback) { callback(enosys()); },
+ stat(path, callback) { callback(enosys()); },
+ symlink(path, link, callback) { callback(enosys()); },
+ truncate(path, length, callback) { callback(enosys()); },
+ unlink(path, callback) { callback(enosys()); },
+ utimes(path, atime, mtime, callback) { callback(enosys()); },
};
}
- if (!global.process) {
- global.process = {
- getuid() {
- return -1;
- },
- getgid() {
- return -1;
- },
- geteuid() {
- return -1;
- },
- getegid() {
- return -1;
- },
- getgroups() {
- throw enosys();
- },
+ if (!globalThis.process) {
+ globalThis.process = {
+ getuid() { return -1; },
+ getgid() { return -1; },
+ geteuid() { return -1; },
+ getegid() { return -1; },
+ getgroups() { throw enosys(); },
pid: -1,
ppid: -1,
- umask() {
- throw enosys();
- },
- cwd() {
- throw enosys();
- },
- chdir() {
- throw enosys();
- },
+ umask() { throw enosys(); },
+ cwd() { throw enosys(); },
+ chdir() { throw enosys(); },
}
}
- // if (!global.crypto && global.require) {
- // const nodeCrypto = require("crypto");
- // global.crypto = {
- // getRandomValues(b) {
- // nodeCrypto.randomFillSync(b);
- // },
- // };
- // }
- if (!global.crypto) {
- throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
- }
- if (!global.crypto.getRandomValues) {
- throw new Error("global.crypto.getRandomValues is not available, polyfill required (getRandomValues only)");
+ if (!globalThis.crypto) {
+ throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
}
- if (!global.performance) {
- global.performance = {
- now() {
- const [sec, nsec] = process.hrtime();
- return sec * 1000 + nsec / 1000000;
- },
- };
+ if (!globalThis.performance) {
+ throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
}
- if (!global.TextEncoder && global.require) {
- global.TextEncoder = require("util").TextEncoder;
- }
- if (!global.TextEncoder) {
- throw new Error("global.TextEncoder is not available, polyfill required");
+ if (!globalThis.TextEncoder) {
+ throw new Error("globalThis.TextEncoder is not available, polyfill required");
}
- if (!global.TextDecoder && global.require) {
- global.TextDecoder = require("util").TextDecoder;
+ if (!globalThis.TextDecoder) {
+ throw new Error("globalThis.TextDecoder is not available, polyfill required");
}
- if (!global.TextDecoder) {
- throw new Error("global.TextDecoder is not available, polyfill required");
- }
-
-
- const isNodeJS = global.process && global.process.title === "node";
-
- if (!isNodeJS) {
- // console.log("ini browser fs")
- // var myfs = global.BrowserFS.BFSRequire('fs');
- // global.Buffer = global.BrowserFS.BFSRequire('buffer').Buffer;
- // global.fs = myfs;
-
- global.fs.constants = {
- O_RDONLY: 0,
- O_WRONLY: 1,
- O_RDWR: 2,
- O_CREAT: 64,
- O_CREATE: 64,
- O_EXCL: 128,
- O_NOCTTY: 256,
- O_TRUNC: 512,
- O_APPEND: 1024,
- O_DIRECTORY: 65536,
- O_NOATIME: 262144,
- O_NOFOLLOW: 131072,
- O_SYNC: 1052672,
- O_DIRECT: 16384,
- O_NONBLOCK: 2048,
- };
-
- let outputBuf = "";
-
- global.fs.writeSyncOriginal = global.fs.writeSync
- global.fs.writeSync = function (fd, buf) {
- if (fd === 1 || fd === 2) {
- outputBuf += decoder.decode(buf);
- const nl = outputBuf.lastIndexOf("\n");
- if (nl != -1) {
- console.log(outputBuf.substr(0, nl));
- outputBuf = outputBuf.substr(nl + 1);
- }
- return buf.length;
- } else {
- return global.fs.writeSyncOriginal(...arguments);
- }
- };
-
- global.fs.writeOriginal = global.fs.write
- global.fs.write = function (fd, buf, offset, length, position, callback) {
- // (corresponding to STDOUT/STDERR)
- if (fd === 1 || fd === 2) {
- if (offset !== 0 || length !== buf.length || position !== null) {
- throw new Error("not implemented");
- }
- const n = this.writeSync(fd, buf);
- callback(null, n, buf);
- } else {
- // buf: read buf first
- arguments[1] = global.Buffer.from(arguments[1]);
- return global.fs.writeOriginal(...arguments);
- }
- };
-
-
-
- global.fs.openOriginal = global.fs.open
- global.fs.open = function (path, flags, mode, callback) {
- var myflags = 'r';
- var O = global.fs.constants;
-
- // Convert numeric flags to string flags
- // FIXME: maybe wrong...
- console.log("open dir?", path, 'flag', flags, myflags)
- if (flags & O.O_WRONLY) { // 'w'
- myflags = 'w';
- if (flags & O.O_EXCL) {
- myflags = 'wx';
- }
- } else if (flags & O.O_RDWR) { // 'r+' or 'w+'
- if (flags & O.O_CREAT && flags & O.O_TRUNC) { // w+
- if (flags & O.O_EXCL) {
- myflags = 'wx+';
- } else {
- myflags = 'w+';
- }
- } else { // r+
- myflags = 'r+';
- }
- } else if (flags & O.O_APPEND) { // 'a'
- console.log("append error")
- throw new Error("Not implmented");
- } else {
- // 打开文件
- myflags = 'r+';
- console.log("open dir?", path, 'flag', flags, myflags)
- }
-
-
- return global.fs.openOriginal(path, myflags, mode, callback);
- };
-
- global.fs.fstatOriginal = global.fs.fstat;
- global.fs.fstat = function (fd, callback) {
- return global.fs.fstatOriginal(fd, function () {
- var retStat = arguments[1];
- delete retStat['fileData'];
- retStat.atimeMs = retStat.atime.getTime();
- retStat.mtimeMs = retStat.mtime.getTime();
- retStat.ctimeMs = retStat.ctime.getTime();
- retStat.birthtimeMs = retStat.birthtime.getTime();
- return callback(arguments[0], retStat);
-
- });
- };
-
-
-
- global.fs.closeOriginal = global.fs.close;
- global.fs.close = function (fd, callback) {
- return global.fs.closeOriginal(fd, function () {
- if (typeof arguments[0] === 'undefined') arguments[0] = null;
- return callback(...arguments);
- });
- }
-
- // global.fs.renameOriginal = global.fs.rename
- // global.fs.rename = function (from, to, callback) {
- // console.log("rename a0", arguments[0])
- // global.fs.renameOriginal(from, to);
- // callback(arguments[0])
- // }
-
- // global.fs.renameSyncOriginal = global.fs.renameSync
- // global.fs.renameSync = function(fd, options) {
- // console.log("Sync")
- // }
-
-
- global.fs = new Proxy(global.fs, handler);
- }
-
- // End of polyfills for common API.
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
- global.Go = class {
+ globalThis.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
- this.exitCode = code;
if (code !== 0) {
console.warn("exit code:", code);
}
@@ -404,6 +114,10 @@
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
}
+ const setInt32 = (addr, v) => {
+ this.mem.setUint32(addr + 0, v, true);
+ }
+
const getInt64 = (addr) => {
const low = this.mem.getUint32(addr + 0, true);
const high = this.mem.getInt32(addr + 4, true);
@@ -497,7 +211,10 @@
const timeOrigin = Date.now() - performance.now();
this.importObject = {
- go: {
+ _gotest: {
+ add: (a, b) => a + b,
+ },
+ gojs: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
@@ -537,8 +254,8 @@
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
- // func walltime1() (sec int64, nsec int32)
- "runtime.walltime1": (sp) => {
+ // func walltime() (sec int64, nsec int32)
+ "runtime.walltime": (sp) => {
sp >>>= 0;
const msec = (new Date).getTime();
setInt64(sp + 8, msec / 1000);
@@ -560,7 +277,7 @@
this._resume();
}
},
- getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
+ getInt64(sp + 8),
));
this.mem.setInt32(sp + 16, id, true);
},
@@ -642,6 +359,7 @@
storeValue(sp + 56, result);
this.mem.setUint8(sp + 64, 1);
} catch (err) {
+ sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, err);
this.mem.setUint8(sp + 64, 0);
}
@@ -658,6 +376,7 @@
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
+ sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
@@ -674,6 +393,7 @@
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
+ sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
@@ -755,7 +475,7 @@
null,
true,
false,
- global,
+ globalThis,
this,
];
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
@@ -764,10 +484,10 @@
[null, 2],
[true, 3],
[false, 4],
- [global, 5],
+ [globalThis, 5],
[this, 6],
]);
- this._idPool = []; // unused ids that have been garbage collected
+ this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
@@ -805,6 +525,13 @@
offset += 8;
});
+ // The linker guarantees global data starts from at least wasmMinDataAddr.
+ // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
+ const wasmMinDataAddr = 4096 + 8192;
+ if (offset >= wasmMinDataAddr) {
+ throw new Error("total length of command line and environment variables exceeds limit");
+ }
+
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
@@ -814,7 +541,7 @@
_resume() {
if (this.exited) {
- throw new Error("Go program has already exited");
+ console.warn("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
@@ -825,51 +552,11 @@
_makeFuncWrapper(id) {
const go = this;
return function () {
- const event = {
- id: id,
- this: this,
- args: arguments
- };
+ const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
-
- if (
- typeof module !== "undefined" &&
- global.require &&
- global.require.main === module &&
- global.process &&
- global.process.versions &&
- !global.process.versions.electron
- ) {
- if (process.argv.length < 3) {
- console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
- process.exit(1);
- }
-
- const go = new Go();
- go.argv = process.argv.slice(2);
- go.env = Object.assign({
- TMPDIR: require("os").tmpdir()
- }, process.env);
- go.exit = process.exit;
- WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
- process.on("exit", (code) => { // Node.js exits if no event handler is pending
- if (code === 0 && !go.exited) {
- // deadlock, make Go print error and stack traces
- go._pendingEvent = {
- id: 0
- };
- go._resume();
- }
- });
- return go.run(result.instance);
- }).catch((err) => {
- console.error(err);
- process.exit(1);
- });
- }
-})();
\ No newline at end of file
+})();
diff --git a/shared-operations/src/wasm/pdfcpu/wasm_exec_memfs.js b/shared-operations/src/wasm/pdfcpu/wasm_exec_memfs.js
new file mode 100644
index 000000000..cee161194
--- /dev/null
+++ b/shared-operations/src/wasm/pdfcpu/wasm_exec_memfs.js
@@ -0,0 +1,37 @@
+import memfs from 'memfs';
+
+globalThis.fs = memfs.fs;
+
+import "./wasm_exec.js";
+
+const encoder = new TextEncoder("utf-8");
+const decoder = new TextDecoder("utf-8");
+let outputBuf = "";
+
+globalThis.fs.writeSyncOriginal = globalThis.fs.writeSync;
+globalThis.fs.writeSync = function(fd, buf) {
+ if (fd === 1 || fd === 2) {
+ outputBuf += decoder.decode(buf);
+ const nl = outputBuf.lastIndexOf("\n");
+ if (nl != -1) {
+ console.log(outputBuf.substr(0, nl));
+ outputBuf = outputBuf.substr(nl + 1);
+ }
+ return buf.length;
+ } else {
+ return globalThis.fs.writeSyncOriginal(...arguments);
+ }
+};
+
+globalThis.fs.writeOriginal = globalThis.fs.write;
+globalThis.fs.write = function(fd, buf, offset, length, position, callback) {
+ if (fd === 1 || fd === 2) {
+ if (offset !== 0 || length !== buf.length || position !== null) {
+ throw new Error("fs func not implemented");
+ }
+ const n = this.writeSync(fd, buf);
+ callback(null, n, buf);
+ } else {
+ return globalThis.fs.writeOriginal(...arguments);
+ }
+};
\ No newline at end of file