mirror of https://github.com/VERT-sh/VERT.git
feat: add PDF support via mupdf WASM
This commit is contained in:
parent
3f4a42b907
commit
cce3b887b6
|
|
@ -22,3 +22,5 @@ vite.config.ts.timestamp-*
|
|||
|
||||
# IDE
|
||||
.idea
|
||||
|
||||
static/mupdf-wasm.wasm
|
||||
|
|
|
|||
3
bun.lock
3
bun.lock
|
|
@ -17,6 +17,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"fflate": "^0.8.2",
|
||||
"lucide-svelte": "^0.554.0",
|
||||
"mupdf": "^1.28.0",
|
||||
"music-metadata": "^11.10.1",
|
||||
"overlayscrollbars": "^2.12.0",
|
||||
"overlayscrollbars-svelte": "^0.5.5",
|
||||
|
|
@ -642,6 +643,8 @@
|
|||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"mupdf": ["mupdf@1.28.0", "", {}, "sha512-ACUnbpECaQ5JLq04pwd89lS+0IGMest5qL5tb08g9TAR7bDtfqflHEkb2Xm3o4rvC/szguLiV+WEbW9kstj8Sg=="],
|
||||
|
||||
"music-metadata": ["music-metadata@11.10.3", "", { "dependencies": { "@borewit/text-codec": "^0.2.0", "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.3", "file-type": "^21.1.1", "media-typer": "^1.1.0", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.5.0" } }, "sha512-j0g/x4cNNZW6I5gdcPAY+GFkJY9WHTpkFDMBJKQLxJQyvSfQbXm57fTE3haGFFuOzCgtsTd4Plwc49Sn9RacDQ=="],
|
||||
|
||||
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check . && eslint ."
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"postinstall": "cp node_modules/mupdf/dist/mupdf-wasm.wasm static/mupdf-wasm.wasm"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "^2.5.0",
|
||||
|
|
@ -51,6 +52,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"fflate": "^0.8.2",
|
||||
"lucide-svelte": "^0.554.0",
|
||||
"mupdf": "^1.28.0",
|
||||
"music-metadata": "^11.10.3",
|
||||
"overlayscrollbars": "^2.12.0",
|
||||
"overlayscrollbars-svelte": "^0.5.5",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { FFmpegConverter } from "./ffmpeg.svelte";
|
|||
import { PandocConverter } from "./pandoc.svelte";
|
||||
import { VertdConverter } from "./vertd.svelte";
|
||||
import { MagickConverter } from "./magick.svelte";
|
||||
import { MuPDFConverter } from "./mupdf.svelte";
|
||||
import { DISABLE_ALL_EXTERNAL_REQUESTS } from "$lib/util/consts";
|
||||
|
||||
const getConverters = (): Converter[] => {
|
||||
|
|
@ -17,6 +18,7 @@ const getConverters = (): Converter[] => {
|
|||
}
|
||||
|
||||
converters.push(new PandocConverter());
|
||||
converters.push(new MuPDFConverter());
|
||||
return converters;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { VertFile } from "$lib/types";
|
||||
import { Converter, FormatInfo } from "./converter.svelte";
|
||||
import { browser } from "$app/environment";
|
||||
import MuPDFWorker from "$lib/workers/mupdf?worker&url";
|
||||
|
||||
export class MuPDFConverter extends Converter {
|
||||
public name = "mupdf";
|
||||
|
||||
private activeConversions = new Map<string, Worker>();
|
||||
|
||||
constructor() {
|
||||
super(60);
|
||||
if (!browser) return;
|
||||
this.status = "ready";
|
||||
this.clearTimeout();
|
||||
}
|
||||
|
||||
public async convert(file: VertFile, to: string): Promise<VertFile> {
|
||||
const worker = new Worker(MuPDFWorker, { type: "module" });
|
||||
this.activeConversions.set(file.id, worker);
|
||||
|
||||
worker.postMessage({ file: file.file, to, id: file.id });
|
||||
|
||||
const result = await new Promise<{ type: string; output?: Uint8Array; error?: string }>(
|
||||
(resolve) => {
|
||||
worker.onmessage = (e) => resolve(e.data);
|
||||
worker.onerror = (e) => resolve({ type: "error", error: e.message });
|
||||
},
|
||||
);
|
||||
|
||||
worker.terminate();
|
||||
this.activeConversions.delete(file.id);
|
||||
|
||||
if (result.type === "error") throw new Error(result.error);
|
||||
|
||||
if (!to.startsWith(".")) to = `.${to}`;
|
||||
return new VertFile(new File([result.output! as BlobPart], file.name), to);
|
||||
}
|
||||
|
||||
public async cancel(input: VertFile): Promise<void> {
|
||||
const worker = this.activeConversions.get(input.id);
|
||||
if (worker) {
|
||||
worker.terminate();
|
||||
this.activeConversions.delete(input.id);
|
||||
}
|
||||
}
|
||||
|
||||
public supportedFormats = [
|
||||
new FormatInfo("pdf", true, false),
|
||||
new FormatInfo("md", false, true),
|
||||
new FormatInfo("txt", false, true),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).$libmupdf_wasm_Module = {
|
||||
locateFile: () => "/mupdf-wasm.wasm",
|
||||
};
|
||||
|
||||
let mupdf: typeof import("mupdf") | null = null;
|
||||
|
||||
self.onmessage = async (e: MessageEvent) => {
|
||||
const { file, to, id } = e.data as { file: File; to: string; id: string };
|
||||
try {
|
||||
if (!mupdf) mupdf = await import("mupdf");
|
||||
|
||||
const buf = new Uint8Array(await file.arrayBuffer());
|
||||
const doc = mupdf.Document.openDocument(buf, "application/pdf");
|
||||
const pageCount = doc.countPages();
|
||||
const parts: string[] = [];
|
||||
|
||||
for (let i = 0; i < pageCount; i++) {
|
||||
const page = doc.loadPage(i);
|
||||
const st = page.toStructuredText("preserve-whitespace");
|
||||
parts.push(st.asText());
|
||||
page.destroy();
|
||||
}
|
||||
doc.destroy();
|
||||
|
||||
self.postMessage({
|
||||
type: "finished",
|
||||
output: new TextEncoder().encode(parts.join("\n\n")),
|
||||
id,
|
||||
});
|
||||
} catch (err) {
|
||||
self.postMessage({ type: "error", error: String(err), id });
|
||||
}
|
||||
};
|
||||
|
|
@ -53,7 +53,7 @@ export default defineConfig(({ command }) => {
|
|||
format: "es",
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ["@ffmpeg/core-mt", "@ffmpeg/ffmpeg", "@ffmpeg/util"],
|
||||
exclude: ["@ffmpeg/core-mt", "@ffmpeg/ffmpeg", "@ffmpeg/util", "mupdf"],
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue