From 197521edb875e592fc13caaed537a8c5c1b01f3b Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Mon, 28 Sep 2026 12:16:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=BD=AC=E6=8D=A2=E8=83=BD=E5=8A=9B):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DPDF=E8=BD=AC=E6=8D=A2=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=AD=97=E4=BD=93=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/webui/ofdgo.js | 21 ++++++++++++++++++++- assets/webui/ofdgo_wasm.js | 31 +++++++++++++++++-------------- internal/webui/wasm.go | 14 +++++++++----- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/assets/webui/ofdgo.js b/assets/webui/ofdgo.js index 3412455..bdef776 100644 --- a/assets/webui/ofdgo.js +++ b/assets/webui/ofdgo.js @@ -3253,10 +3253,23 @@ async function loadWASM() { }; worker.onerror = (event) => fail(new Error(event.message || "引擎加载失败")); worker.onmessageerror = () => fail(new Error("引擎通信失败")); - worker.onmessage = ({ data }) => { + worker.onmessage = async ({ data }) => { if (worker !== wasmWorker) { return; } + if (data.type === "font") { + try { + const request = wasmRequests.get(data.id); + const font = request?.localFonts?.get(data.name); + if (!font || request.openSeq !== state.openSeq) throw new Error("系统字体不可用"); + if (!request.fontData.has(font)) request.fontData.set(font, fontManager.read(font)); + const bytes = await request.fontData.get(font); + if (worker === wasmWorker) worker.postMessage({ type: "font", request: data.request, bytes }); + } catch (err) { + if (worker === wasmWorker) worker.postMessage({ type: "font", request: data.request, error: err.message }); + } + return; + } if (data.type === "progress") { setProgress(data.text, data.percent); } else if (data.type === "conversion") { @@ -7939,6 +7952,12 @@ async function callWASM(name, ...args) { return new Promise((resolve, reject) => { const id = ++wasmRequestID; wasmRequests.set(id, { resolve, reject, openSeq: state.openSeq }); + if (name === "ofdgoConvertPDF") { + const request = wasmRequests.get(id); + request.localFonts = fontManager.localEntries(); + request.fontData = new Map(); + args.push([...request.localFonts.keys()]); + } if (name === "ofdgoConvertPDF" || name === "ofdgoExportPage" || name === "ofdgoExportDocument" || name === "ofdgoExportAttachment" || name === "ofdgoSaveDocument" || name === "ofdgoSaveEncrypted" || name === "ofdgoSaveSigned" || name === "ofdgoImportPages") { state.exportRequestID = id; el.cancelExportButton.hidden = false; diff --git a/assets/webui/ofdgo_wasm.js b/assets/webui/ofdgo_wasm.js index dd06975..1a36a02 100644 --- a/assets/webui/ofdgo_wasm.js +++ b/assets/webui/ofdgo_wasm.js @@ -4,6 +4,21 @@ let pending = Promise.resolve(); const operations = new Map(); const fontRequests = new Map(); let fontSequence = 0; + +function requestFont(id, signal, name, done) { + if (signal.aborted) { done(null, "", true); return; } + const request = ++fontSequence; + const cancel = () => { + fontRequests.delete(request); + done(null, "", true); + }; + signal.addEventListener("abort", cancel, { once: true }); + fontRequests.set(request, result => { + signal.removeEventListener("abort", cancel); + done(result.bytes, result.error || "", signal.aborted); + }); + self.postMessage({ id, type: "font", request, name }); +} self.onmessage = ({ data }) => { if (data.type === "font") { fontRequests.get(data.request)?.(data); @@ -103,20 +118,7 @@ async function handleBatchMessage({ id, name, args }) { finish(done); }, (action, pageName, done) => boundary(action, pageName).then(() => finish(done), err => finish(done, err)), - done => finish(done), fonts, (name, done) => { - if (signal.aborted) { done(null, "", true); return; } - const request = ++fontSequence; - const cancel = () => { - fontRequests.delete(request); - done(null, "", true); - }; - signal.addEventListener("abort", cancel, { once: true }); - fontRequests.set(request, result => { - signal.removeEventListener("abort", cancel); - done(result.bytes, result.error || "", signal.aborted); - }); - self.postMessage({ id, type: "font", request, name }); - }); + done => finish(done), fonts, (name, done) => requestFont(id, signal, name, done)); } else { const [entries, handle] = args; if (handle) output = await handle.createWritable(); @@ -187,6 +189,7 @@ async function handleMessage({ id, name, args }) { self.postMessage({ id, type: importing ? "import" : "conversion", phase, completed, total }); finish(done, null, importing && phase === "commit"); }); + if (converting) args.push((name, done) => requestFont(id, signal, name, done)); } else { const file = args.pop(); const indices = saving ? args.shift() : null; diff --git a/internal/webui/wasm.go b/internal/webui/wasm.go index 26f0edb..5608e48 100644 --- a/internal/webui/wasm.go +++ b/internal/webui/wasm.go @@ -371,10 +371,10 @@ func safeCall(fn func([]js.Value) (any, error), args []js.Value) (data any, err } // convertPDFDocument 通过库转换PDF,不替换当前文档会话 -// 入参: args PDF数据、外部字体、UTF-8密码及进度回调 +// 入参: args PDF数据、外部字体、UTF-8密码、字体目录、进度及字体读取回调 // 返回: any OFD数据及转换警告, error 错误信息 func convertPDFDocument(args []js.Value) (any, error) { - if len(args) != 4 { + if len(args) != 6 { return nil, fmt.Errorf("missing PDF data") } data, err := bytesFromJS(args[0]) @@ -386,7 +386,7 @@ func convertPDFDocument(args []js.Value) (any, error) { return nil, err } var output bytes.Buffer - options := ofdgo.PDFImportOptions{OnProgress: editorOperationProgress(args[3]), PasswordUTF8: true} + options := ofdgo.PDFImportOptions{OnProgress: editorOperationProgress(args[4]), PasswordUTF8: true} if !args[2].IsNull() && !args[2].IsUndefined() { options.Password, err = bytesFromJS(args[2]) if err != nil { @@ -394,9 +394,13 @@ func convertPDFDocument(args []js.Value) (any, error) { } defer clear(options.Password) } - if len(fonts) > 0 { - options.RendererOptions = []ofdgo.RendererOption{ofdgo.WithFontFS(ofdgo.NewFontFS(fonts))} + fontSources := []fs.FS{ofdgo.NewFontFS(fonts)} + if names := stringsFromJS(args[3]); len(names) > 0 { + fontSources = append(fontSources, ofdgo.NewLazyFontFS(names, func(name string) ([]byte, error) { + return loadConversionFont(args[5], name) + })) } + options.RendererOptions = []ofdgo.RendererOption{ofdgo.WithFontFS(fontSources...)} report, err := ofdgo.ConvertPDF(context.Background(), bytes.NewReader(data), int64(len(data)), &output, options) if err != nil { return nil, err