feat(强化加载): 支持PDF按页加载与增量转换
build.yaml / build (push) Successful in 44s

This commit is contained in:
xiaoqidun committed 2026-10-26 17:21:15 +08:00
1 parent 04d7d2d672
commit fb4f03465b
11 files changed
+647 -154

No files matched your search

+50 -28
View File
@@ -116,6 +116,8 @@ function createDocumentState() {
textFontID: null,
textDefaults: { type: "TextObject", size: 16 * 25.4 / 72, color: "#000000", wrap: true, align: "left", paragraphHeight: 0, letterSpacing: 0 },
ofdBytes: null,
sourcePDF: false,
pdfComplete: false,
fileName: "ofdgo.ofd",
openSeq: 0,
pageSeq: 0,
@@ -5503,11 +5505,10 @@ async function loadWASM(tab = documentTab()) {
if (data.type === "progress") {
if (visible) setProgress(data.text, data.percent);
} else if (data.type === "conversion") {
if (visible && wasmRequests.get(data.id)?.openSeq === state.openSeq && !el.cancelExportButton.disabled) {
if (visible && wasmRequests.get(data.id)?.name !== "ofdgoLoadPDF" && wasmRequests.get(data.id)?.openSeq === state.openSeq && !el.cancelExportButton.disabled) {
if (data.phase === "open") setProgress("正在读取文档", null);
else if (data.phase === "pages") setProgress(data.completed ? `正在整理页面 · ${data.completed} 页` : "正在整理页面", null);
else if (data.phase === "convert") setProgress(`正在转换页面 · ${data.completed} / ${data.total} 页`, data.total ? 28 + data.completed / data.total * 22 : null);
else if (data.phase === "write.fonts") setProgress("正在处理字体", null);
else setProgress("正在生成文档", null);
}
} else if (data.type === "export") {
@@ -5723,7 +5724,6 @@ async function openOFD(file, remote = null, handle = file?.handle) {
workspace.tabs.push(tab);
const current = () => !tab.closed && tab.opening === request;
let accepted = false;
let originalSource = null;
try {
setBusy(true, "正在读取文档", 10, STATUS.opening, tab);
await activateDocumentTab(tab);
@@ -5742,11 +5742,10 @@ async function openOFD(file, remote = null, handle = file?.handle) {
const source = downloaded ? downloaded.file : file;
const format = await callTabWASM(tab, "ofdgoDetectFormat", source);
if (!current()) return;
let bytes = await documentBytes(source);
originalSource = bytes;
const bytes = await documentBytes(source);
tab.state.ofdBytes = bytes;
tab.state.sourcePDF = format === "pdf";
const name = (downloaded ? downloaded.name : file.name) || `ofdgo.${format}`;
let warnings = [];
let convertedDoc;
if (!current()) {
return;
@@ -5765,10 +5764,7 @@ async function openOFD(file, remote = null, handle = file?.handle) {
}
}
if (!current()) return;
bytes = converted.bytes;
tab.state.ofdBytes = bytes;
convertedDoc = converted.doc;
warnings = JSON.parse(converted.warnings || "null") || [];
convertedDoc = converted;
}
await ensureTabEngine(tab);
if (!current()) return;
@@ -5776,7 +5772,7 @@ async function openOFD(file, remote = null, handle = file?.handle) {
if (!current()) return;
tab.state.saveHandle = format === "ofd" && !remote ? handle || null : null;
tab.state.saveStamp = tab.state.saveHandle && !nativeHost ? {size: file.size, modified: file.lastModified} : null;
tab.state.conversionWarnings = warnings;
tab.state.conversionWarnings = [];
tab.state.fileName = (name || "ofdgo.ofd").replace(/\.pdf$/i, ".ofd");
if (remote && !/\.ofd$/i.test(tab.state.fileName)) tab.state.fileName += ".ofd";
tab.state.doc = doc;
@@ -5791,6 +5787,7 @@ async function openOFD(file, remote = null, handle = file?.handle) {
if (isForegroundDocument(tab) && openSeq === tab.state.openSeq) tab.initialLoad = false;
}
else tab.status = "文档已打开";
if (tab.state.sourcePDF) completePDF(tab);
} catch (err) {
if (current()) {
tab.error = err.name === "AbortError" ? "打开已取消" : err.message;
@@ -5807,7 +5804,6 @@ async function openOFD(file, remote = null, handle = file?.handle) {
if (nativeHost) await nativeHost.release(source);
tab.state.ofdBytes = null;
}
if (nativeHost && originalSource !== source) await nativeHost.release(originalSource);
}
return accepted;
}
@@ -6313,22 +6309,15 @@ function requestCredentials(code, openSeq, pdf = false, tab = documentTab()) {
async function openWithCredentials(fonts, openSeq, data = state.ofdBytes, importing = false, pdf = false, tab = documentTab()) {
const state = tab.state;
const callWASM = (name, ...args) => callTabWASM(tab, name, ...args);
pdf ||= !importing && state.sourcePDF;
const current = () => !tab.closed && (Boolean(tab.opening) || openSeq === state.openSeq);
let credentials = null;
try {
while (current()) {
try {
if (pdf) {
const converted = await callWASM("ofdgoConvertPDF", data, fonts, credentials?.password || null);
let accepted = false;
try {
if (!current()) return converted;
converted.doc = await callWASM("ofdgoOpen", converted.bytes, fonts, state.renderAnnotations, credentials);
accepted = current();
return converted;
} finally {
if (!accepted && nativeHost) await nativeHost.release(converted.bytes);
}
state.pdfComplete = false;
return await callWASM("ofdgoOpenPDF", data, fonts, credentials?.password || null, state.renderAnnotations);
}
return importing ? await callWASM("ofdgoLoadImport", data?.nativeFile ? data : data.slice(), credentials)
: await callWASM("ofdgoOpen", data, fonts, state.renderAnnotations, credentials);
@@ -6444,8 +6433,10 @@ async function openDocument(options = {}) {
if (openSeq !== state.openSeq) {
return;
}
wasmWorker.configurePages?.(!state.editorInfo && !doc.encrypted && state.ofdBytes ? {
file: state.ofdBytes, fonts, annotations: state.renderAnnotations, document: doc.documentIndex, version: doc.documentVersion
const pageFonts = state.sourcePDF && !nativeHost && !state.editorInfo ? await fontManager.files(fontManager.records()) : fonts;
if (openSeq !== state.openSeq) return;
tab.worker.configurePages?.(!state.editorInfo && !doc.encrypted && state.ofdBytes ? {
file: state.ofdBytes, fonts: pageFonts, pdf: state.sourcePDF, fontNames: state.sourcePDF ? [...fontManager.localEntries().keys()] : [], annotations: state.renderAnnotations, document: doc.documentIndex, version: doc.documentVersion
} : null);
if (options.keepPreview) {
pageView.resetPageLoading();
@@ -6535,10 +6526,40 @@ async function openDocument(options = {}) {
}
}
async function completePDF(tab) {
const state = tab.state, source = state.ofdBytes, wasmSeq = state.wasmSeq;
if (tab.pdfTask?.wasmSeq === wasmSeq || state.pdfComplete || !state.sourcePDF || !source || state.editorInfo) return;
const task = {wasmSeq};
tab.pdfTask = task;
const current = () => !tab.closed && state.ofdBytes === source && state.wasmSeq === wasmSeq;
try {
while (current()) {
await new Promise(resolve => setTimeout(resolve, 0));
if (!current()) return;
const result = await callTabWASM(tab, "ofdgoLoadPDF");
if (!current()) return;
if (!result.done) continue;
state.pdfComplete = true;
state.conversionWarnings = JSON.parse(result.warnings || "null") || [];
if (!state.editorInfo) Object.assign(state.doc, result.doc);
if (isForegroundDocument(tab)) loadDocumentDetails(state.openSeq);
return;
}
} catch (err) {
if (current() && !err.canceled) {
tab.status = err.message;
if (isForegroundDocument(tab)) showError(err, false);
}
} finally {
if (tab.pdfTask === task) tab.pdfTask = null;
}
}
async function loadDocumentDetails(openSeq) {
const tab = documentTab(), state = tab.state;
const callWASM = (name, ...args) => callTabWASM(tab, name, ...args);
if (openSeq !== state.openSeq || !state.doc) return;
if (state.sourcePDF && !state.pdfComplete && !state.editorInfo) { completePDF(tab); return; }
try {
const details = await callWASM("ofdgoDocumentInfo");
if (openSeq !== state.openSeq) return;
@@ -9930,7 +9951,7 @@ async function exportFile(whole, indices = null, value = el.exportFormat.value,
if (tab.closed || wasmSeq !== state.wasmSeq) return;
setBusy(true, "正在生成文件", null, status, tab);
}
const readingSave = saving && !state.editorInfo && indices === null && compression.mode === 0;
const readingSave = saving && !state.sourcePDF && !state.editorInfo && indices === null && compression.mode === 0;
const destination = !nativeHost && file && file === state.saveHandle && state.saveStamp ? {fileHandle: file, saveStamp: state.saveStamp} : file;
if (saving && indices === null && !readingSave) {
snapshotPrepared = true;
@@ -12350,13 +12371,13 @@ async function callTabWASM(tab, name, ...args) {
if (name === "ofdgoResources" && workspace.active === tab && resourceElements.Panel.open) resourceManager.requestID = id;
wasmRequests.set(id, { name, resolve, reject, openSeq: state.openSeq, tab });
if (name === "ofdgoExportMedia") wasmRequests.get(id).mediaID = args[0];
if (name === "ofdgoConvertPDF") {
if (name === "ofdgoOpenPDF" || state.sourcePDF && !state.editorInfo) {
const request = wasmRequests.get(id);
request.localFonts = fontManager.localEntries();
request.fontData = new Map();
args.push([...request.localFonts.keys()]);
if (name === "ofdgoOpenPDF") args.push([...request.localFonts.keys()]);
}
if (name === "ofdgoImportDocuments" || name === "ofdgoImportImages" || name === "ofdgoExportImages" || name === "ofdgoConvertPDF" || name === "ofdgoExportPage" || name === "ofdgoExportDocument" || name === "ofdgoExportAttachment" || name === "ofdgoExportResource" || name === "ofdgoExportVersion" || name === "ofdgoSaveDocument" || name === "ofdgoSaveSigned" || name === "ofdgoImportPages") {
if (name === "ofdgoImportDocuments" || name === "ofdgoImportImages" || name === "ofdgoExportImages" || name === "ofdgoOpenPDF" || name === "ofdgoExportPage" || name === "ofdgoExportDocument" || name === "ofdgoExportAttachment" || name === "ofdgoExportResource" || name === "ofdgoExportVersion" || name === "ofdgoSaveDocument" || name === "ofdgoSaveSigned" || name === "ofdgoImportPages") {
state.exportRequestID = id;
if (visible) {
el.cancelExportButton.hidden = false;
@@ -12371,6 +12392,7 @@ async function callTabWASM(tab, name, ...args) {
}
if (name === "ofdgoLoadImport" && ArrayBuffer.isView(args[0]) && !nativeHost) transfer.push(args[0].buffer);
const message = {id, name, args};
if (name === "ofdgoLoadPDF") message.background = true;
if (name === "ofdgoRenderPage" && [...state.pageInFlight.values()].some(task => task.index === args[0] && pageView.pageRenderPriority(task, state) >= 6)) message.preload = true;
tab.worker.postMessage(message, transfer);
} catch (err) {
+41 -11
View File
@@ -35,7 +35,7 @@ class BrowserScheduler {
}
priority(task) {
return Number(task.worker.batch || this.owner(task.worker, task.data)?.foreground === false) + Number(!!task.data.preload);
return Number(task.worker.batch || this.owner(task.worker, task.data)?.foreground === false) + Number(!!task.data.preload || !!task.data.background);
}
submit(worker, data, transfer) {
@@ -92,8 +92,8 @@ class BrowserScheduler {
const busy = [...task.worker.operations.values()].some(data => data.engineID === task.data.engineID && data.name !== "ofdgoSaveSnapshot");
const snapshot = task.data.name === "ofdgoSaveSnapshot" && [...task.worker.operations.values()].some(data => data.engineID === task.data.engineID && data.name === "ofdgoSaveSnapshot");
const active = this.active();
const interactive = task.worker.primary && this.owner(task.worker, task.data)?.foreground === true && !task.data.preload;
const foregroundActive = [...this.workers].some(worker => [...worker.operations.values()].some(data => data.name !== "ofdgoSaveSnapshot" && worker.primary && this.owner(worker, data)?.foreground === true && !data.preload));
const interactive = task.worker.primary && this.owner(task.worker, task.data)?.foreground === true && !task.data.preload && !task.data.background;
const foregroundActive = [...this.workers].some(worker => [...worker.operations.values()].some(data => data.name !== "ofdgoSaveSnapshot" && worker.primary && this.owner(worker, data)?.foreground === true && !data.preload && !data.background));
const continuing = task.worker.primary && [...task.worker.operations.values()].some(data => data.engineID === task.data.engineID);
const available = continuing || active < this.limit || interactive && !foregroundActive && active < this.limit + 1;
if (busy || snapshot || !available || !task.worker.primary && active && this.memory + (64 << 20) > this.budget) { i++; continue; }
@@ -307,6 +307,8 @@ class BrowserEngine {
this.primary.onerror = event => this.onerror?.(event);
this.primary.onmessageerror = event => this.onmessageerror?.(event);
this.requests = new Map();
this.fontRequests = new Map();
this.primaryPages = new Set();
this.slots = new Set();
this.sequence = 0;
this.revision = 0;
@@ -323,8 +325,9 @@ class BrowserEngine {
configurePages(source) {
const current = this.pageSource;
const fonts = (source?.fonts || []).map(font => font?.data !== undefined ? {name:font.name, data:font.data} : font);
const sameNames = JSON.stringify(source?.fontNames || []) === JSON.stringify(current?.fontNames || []);
const sameFonts = current && fonts.length === current.fonts.length && fonts.every((font, index) => font === current.fonts[index] || font?.data !== undefined && font.name === current.fonts[index]?.name && font.data === current.fonts[index]?.data);
if (source && current && source.file === current.file && (source.document || 0) === current.document && (source.version || "") === current.version && source.annotations === current.annotations && source.revision === current.revision && sameFonts) return;
if (source && current && source.file === current.file && !!source.pdf === !!current.pdf && (source.document || 0) === current.document && (source.version || "") === current.version && source.annotations === current.annotations && source.revision === current.revision && sameFonts && sameNames) return;
this.invalidatePages();
if (!source || this.closed) return;
this.pageSource = {...source, fonts, document:source.document || 0, version:source.version || ""};
@@ -347,6 +350,7 @@ class BrowserEngine {
invalidatePages() {
this.revision++;
this.primaryPages.clear();
this.source = null;
this.pageSource = null;
this.parallelism = 1;
@@ -358,6 +362,7 @@ class BrowserEngine {
closeSlot(slot, error = new DOMException("页面加载已取消", "AbortError")) {
if (!this.slots.delete(slot)) return;
slot.worker.terminate();
for (const [id, request] of this.fontRequests) if (request.worker === slot.worker) this.fontRequests.delete(id);
slot.rejectReady(error);
for (const pending of slot.pending.values()) pending.reject(error);
slot.pending.clear();
@@ -378,6 +383,12 @@ class BrowserEngine {
if (this.source) this.parallelism = engineScheduler.limit;
if (data.type === "ready") { resolve(); return; }
if (data.type === "exit") { this.closeSlot(slot, new Error("页面加载失败")); return; }
if (data.type === "font") {
const request = `${slot.worker.engineID}:${data.request}`;
this.fontRequests.set(request, {worker:slot.worker, request:data.request});
this.onmessage?.({data:{...data, id:slot.requestID, request}});
return;
}
if (data.type) return;
const request = slot.pending.get(data.id);
if (!request) return;
@@ -389,7 +400,9 @@ class BrowserEngine {
});
this.slots.add(slot);
slot.ready = ready.then(async () => {
let opened = await this.callSlot(slot, "ofdgoOpen", [source.file, source.fonts, source.annotations]);
let opened = source.pdf
? await this.callSlot(slot, "ofdgoOpenPDF", [source.file, source.fonts, null, source.annotations, source.fontNames])
: await this.callSlot(slot, "ofdgoOpen", [source.file, source.fonts, source.annotations]);
if ((source.document || 0) !== (opened.data.documentIndex || 0)) opened = await this.callSlot(slot, "ofdgoSelectDocument", [source.document]);
if ((source.version || "") !== (opened.data.documentVersion || "")) await this.callSlot(slot, "ofdgoSelectVersion", [source.version || ""]);
if (this.slots.has(slot)) this.startupTime = performance.now() - started;
@@ -409,6 +422,7 @@ class BrowserEngine {
}
pageSlot(data) {
if (this.primaryPages.has(data.args[0])) return null;
const preferred = [...this.slots].find(slot => !slot.busy && slot.pages.has(data.args[0]));
const pending = [...this.requests.values()].filter(slot => slot === null).length - Number(this.requests.has(data.id));
if (!preferred && pending <= 0) return null;
@@ -440,6 +454,14 @@ class BrowserEngine {
postMessage(data, transfer) {
if (this.closed) return;
if (data.type === "font" && typeof data.request === "string") {
const request = this.fontRequests.get(data.request);
if (request) {
this.fontRequests.delete(data.request);
request.worker.postMessage({...data, request:request.request}, transfer);
}
return;
}
if (!data.type && data.preload && this.pagesPaused) {
this.onmessage?.({data:{engineID:this.engineID, id:data.id, ok:false, canceled:true, error:"预加载已暂停"}});
return;
@@ -451,7 +473,7 @@ class BrowserEngine {
return;
}
if (!data.type && data.name !== "ofdgoRenderPage" && ![
"ofdgoDocumentInfo", "ofdgoDocumentVersions", "ofdgoDocumentNavigation", "ofdgoOpenLinks", "ofdgoOutlineLinks", "ofdgoRenderBackends", "ofdgoSVGFontData",
"ofdgoLoadPDF", "ofdgoDocumentInfo", "ofdgoDocumentVersions", "ofdgoDocumentNavigation", "ofdgoOpenLinks", "ofdgoOutlineLinks", "ofdgoRenderBackends", "ofdgoSVGFontData",
"ofdgoSearchPage", "ofdgoPageTextString", "ofdgoResources", "ofdgoResourceReferences", "ofdgoResourceTargets",
"ofdgoResourceLocations", "ofdgoFontPreview", "ofdgoFontFaces", "ofdgoFontFace", "ofdgoMatchFontFiles",
"ofdgoImages", "ofdgoStructureTargets", "ofdgoStructureObjects", "ofdgoPackageDocuments", "ofdgoVerifySignatures",
@@ -472,11 +494,19 @@ class BrowserEngine {
renderSlot(slot, data, transfer) {
slot.busy = true;
slot.requestID = data.id;
this.requests.set(data.id, slot);
const revision = this.revision;
let fallback = false;
slot.ready.then(() => this.callSlot(slot, data.name, data.args, !!data.preload)).then(result => {
if (revision !== this.revision) throw new DOMException("页面加载已取消", "AbortError");
if (this.source.pdf && ((JSON.parse(result.data.annotations || "null") || []).length || result.data.links?.length)) {
this.primaryPages.add(data.args[0]);
this.requests.set(data.id, this.primary);
fallback = true;
this.primary.postMessage(data, transfer);
return;
}
slot.lastPage = data.args[0];
slot.pages.delete(slot.lastPage);
slot.pages.add(slot.lastPage);
@@ -612,13 +642,13 @@ class NativeHost {
}
}
async decode(value, name = "", key = "", signal) {
async decode(value, signal) {
const reads = [];
const visit = (item, field, replace) => {
if (!item || typeof item !== "object" || item instanceof Blob || item instanceof ArrayBuffer || ArrayBuffer.isView(item)) return;
if (item.nativeFile) {
const file = new NativeFile(item);
if (field === "file" || name === "ofdgoConvertPDF" && field === "bytes") replace(file);
if (field === "file") replace(file);
else reads.push(this.readResource(file, signal).then(bytes => replace(field === "blob" ? new Blob([bytes], {type:item.type}) : new Uint8Array(bytes))));
return;
}
@@ -626,7 +656,7 @@ class NativeHost {
if (content && typeof content === "object") visit(content, Array.isArray(item) ? "" : child, result => item[child] = result);
}
};
visit(value, key, result => value = result);
visit(value, "", result => value = result);
const results = await Promise.allSettled(reads);
const failed = results.find(result => result.status === "rejected");
if (failed) throw failed.reason;
@@ -688,7 +718,7 @@ class NativeHost {
return;
}
if (data.type === "font") return;
if (["ofdgoDetectFormat", "ofdgoOpen", "ofdgoConvertPDF"].includes(data.name)) {
if (["ofdgoDetectFormat", "ofdgoOpen", "ofdgoOpenPDF"].includes(data.name)) {
const input = data.args?.[0];
if (input?.nativeFile && !input.temporary) inputs.add(input.nativeFile);
}
@@ -721,7 +751,7 @@ class NativeHost {
let decoded;
try {
controller.signal.throwIfAborted();
decoded = await host.decode(result, data.name, "", controller.signal);
decoded = await host.decode(result, controller.signal);
controller.signal.throwIfAborted();
}
catch (error) { await host.release(result); throw error; }
+1 -1
View File
@@ -229,7 +229,7 @@ export function createPageView(context) {
for (const task of context.state.pagePresentQueue.values()) task.resolve(null);
context.state.pagePresentQueue.clear();
for (const [id, request] of context.wasmRequests) {
if (request.tab === context.documentTab() && request.name === "ofdgoRenderPage") context.wasmWorker?.postMessage({ type: "cancel", id });
if (request.tab === context.documentTab() && request.name === "ofdgoRenderPage" && (!context.state.sourcePDF || context.state.pdfComplete || context.state.editorInfo)) context.wasmWorker?.postMessage({ type: "cancel", id });
}
for (const task of context.state.pageInFlight.values()) {
task.resolve(null);
+14 -3
View File
@@ -235,9 +235,9 @@ async function handleMessage(engine, { id, name, args }) {
let saveStamp;
let channel;
const signal = operations.get(id)?.signal;
const cancellable = ["ofdgoPreparePrint", "ofdgoLayoutPrint", "ofdgoPrintSide", "ofdgoPrintPDF", "ofdgoResources", "ofdgoPrepareSave", "ofdgoSaveSnapshot", "ofdgoImportDocuments", "ofdgoImportImages", "ofdgoExportImages", "ofdgoConvertPDF", "ofdgoExportPage", "ofdgoExportDocument", "ofdgoExportAttachment", "ofdgoExportResource", "ofdgoExportMedia", "ofdgoExportVersion", "ofdgoSaveDocument", "ofdgoSaveSigned", "ofdgoImportPages"].includes(name);
const cancellable = ["ofdgoPreparePrint", "ofdgoLayoutPrint", "ofdgoPrintSide", "ofdgoPrintPDF", "ofdgoResources", "ofdgoPrepareSave", "ofdgoSaveSnapshot", "ofdgoImportDocuments", "ofdgoImportImages", "ofdgoExportImages", "ofdgoOpenPDF", "ofdgoExportPage", "ofdgoExportDocument", "ofdgoExportAttachment", "ofdgoExportResource", "ofdgoExportMedia", "ofdgoExportVersion", "ofdgoSaveDocument", "ofdgoSaveSigned", "ofdgoImportPages"].includes(name);
const importing = name === "ofdgoImportPages" || name === "ofdgoImportImages" || name === "ofdgoImportDocuments";
const converting = name === "ofdgoConvertPDF";
const converting = name === "ofdgoOpenPDF";
const saving = name === "ofdgoSaveDocument" || name === "ofdgoSaveSnapshot" || name === "ofdgoSaveSigned";
const resources = ["ofdgoPreparePrint", "ofdgoLayoutPrint", "ofdgoPrintSide", "ofdgoPrintPDF", "ofdgoResources", "ofdgoPrepareSave"].includes(name);
const key = name === "ofdgoOpen" ? args[3]?.key : name === "ofdgoSaveSigned" || name === "ofdgoLoadImport" ? args[1]?.key : null;
@@ -245,7 +245,7 @@ async function handleMessage(engine, { id, name, args }) {
const password = converting ? args[2] : name === "ofdgoSetEncryption" ? args[0]?.password : name === "ofdgoOpen" ? args[3]?.password : name === "ofdgoLoadImport" ? args[1]?.password : null;
try {
signal?.throwIfAborted();
if (["ofdgoOpen", "ofdgoLoadImport", "ofdgoConvertPDF"].includes(name) && args[0] instanceof Blob) {
if (["ofdgoOpen", "ofdgoOpenPDF", "ofdgoLoadImport"].includes(name) && args[0] instanceof Blob) {
const file = args[0], reader = new FileReaderSync();
args[0] = {size: file.size, read: (offset, size) => new Uint8Array(reader.readAsArrayBuffer(file.slice(offset, offset + size)))};
}
@@ -337,10 +337,21 @@ async function handleMessage(engine, { id, name, args }) {
if (!saving && args.some(value => value?.mode !== undefined)) args.push(done => finish(done));
}
}
if (engine.pdf && name !== "ofdgoOpenPDF") {
channel ||= new MessageChannel();
const context = await globalThis.ofdgoEngineCall(engine.id, "ofdgoPDFContext", (phase, completed, total, done) => {
postMessage({id, type: "conversion", phase, completed, total});
channel.port1.onmessage = () => done("", signal.aborted);
channel.port2.postMessage(null);
}, (name, done) => requestFont(engine, id, signal, name, done));
engine.pdf = JSON.parse(context).data === true;
}
const started = name === "ofdgoOpen" || name === "ofdgoRenderPage" ? performance.now() : null;
const payload = await globalThis.ofdgoEngineCall(engine.id, name, ...args);
signal?.throwIfAborted();
const result = typeof payload === "string" ? JSON.parse(payload) : payload;
if (result.ok && name === "ofdgoOpenPDF") engine.pdf = true;
if (result.ok && (name === "ofdgoOpen" || name === "ofdgoLoadPDF" && result.data.done)) engine.pdf = false;
if (started !== null && result.ok) result.elapsed = performance.now() - started;
if (cancellable && !importing && !converting && !resources && result.ok) {
result.data.size = size;