mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
786b1a41c2
commit
b85b3bc27d
4 files changed
+65
-13
No files matched your search
+21
-4
@@ -3215,6 +3215,14 @@ async function loadWASM() {
|
|||||||
}
|
}
|
||||||
if (data.type === "progress") {
|
if (data.type === "progress") {
|
||||||
setProgress(data.text, data.percent);
|
setProgress(data.text, data.percent);
|
||||||
|
} else if (data.type === "conversion") {
|
||||||
|
if (wasmRequests.get(data.id)?.openSeq === state.openSeq && !el.cancelExportButton.disabled) {
|
||||||
|
if (data.phase === "open") setProgress("正在读取 PDF", 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("正在生成 OFD", null);
|
||||||
|
}
|
||||||
} else if (data.type === "export") {
|
} else if (data.type === "export") {
|
||||||
if (wasmRequests.get(data.id)?.openSeq === state.openSeq && state.exporting) {
|
if (wasmRequests.get(data.id)?.openSeq === state.openSeq && state.exporting) {
|
||||||
if (data.stage === "save") {
|
if (data.stage === "save") {
|
||||||
@@ -3344,7 +3352,15 @@ async function openOFD(file) {
|
|||||||
setProgress("正在转换 PDF", 25);
|
setProgress("正在转换 PDF", 25);
|
||||||
await ensureWASM();
|
await ensureWASM();
|
||||||
if (openSeq !== state.openSeq) return;
|
if (openSeq !== state.openSeq) return;
|
||||||
const converted = await callWASM("ofdgoConvertPDF", bytes);
|
let converted;
|
||||||
|
try {
|
||||||
|
converted = await callWASM("ofdgoConvertPDF", bytes);
|
||||||
|
} finally {
|
||||||
|
if (openSeq === state.openSeq) {
|
||||||
|
state.exportRequestID = 0;
|
||||||
|
el.cancelExportButton.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (openSeq !== state.openSeq) return;
|
if (openSeq !== state.openSeq) return;
|
||||||
bytes = converted.bytes;
|
bytes = converted.bytes;
|
||||||
warnings = JSON.parse(converted.warnings || "null") || [];
|
warnings = JSON.parse(converted.warnings || "null") || [];
|
||||||
@@ -3384,7 +3400,8 @@ async function openOFD(file) {
|
|||||||
await openDocument({ pageIndex: 0, resetScroll: true, openSeq });
|
await openDocument({ pageIndex: 0, resetScroll: true, openSeq });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (openSeq === state.openSeq) {
|
if (openSeq === state.openSeq) {
|
||||||
showError(err, !state.doc);
|
if (err.name === "AbortError") setStatus("转换已取消");
|
||||||
|
else showError(err, !state.doc);
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7299,11 +7316,11 @@ async function callWASM(name, ...args) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = ++wasmRequestID;
|
const id = ++wasmRequestID;
|
||||||
wasmRequests.set(id, { resolve, reject, openSeq: state.openSeq });
|
wasmRequests.set(id, { resolve, reject, openSeq: state.openSeq });
|
||||||
if (name === "ofdgoExportPage" || name === "ofdgoExportDocument" || name === "ofdgoExportAttachment" || name === "ofdgoSaveDocument" || name === "ofdgoSaveEncrypted" || name === "ofdgoSaveSigned" || name === "ofdgoImportPages") {
|
if (name === "ofdgoConvertPDF" || name === "ofdgoExportPage" || name === "ofdgoExportDocument" || name === "ofdgoExportAttachment" || name === "ofdgoSaveDocument" || name === "ofdgoSaveEncrypted" || name === "ofdgoSaveSigned" || name === "ofdgoImportPages") {
|
||||||
state.exportRequestID = id;
|
state.exportRequestID = id;
|
||||||
el.cancelExportButton.hidden = false;
|
el.cancelExportButton.hidden = false;
|
||||||
el.cancelExportButton.disabled = false;
|
el.cancelExportButton.disabled = false;
|
||||||
el.cancelExportButton.title = { ofdgoSaveDocument: "取消保存", ofdgoSaveEncrypted: "取消保存", ofdgoSaveSigned: "取消签署", ofdgoImportPages: "取消导入", ofdgoExportAttachment: "取消下载" }[name] || "取消导出";
|
el.cancelExportButton.title = { ofdgoConvertPDF: "取消转换", ofdgoSaveDocument: "取消保存", ofdgoSaveEncrypted: "取消保存", ofdgoSaveSigned: "取消签署", ofdgoImportPages: "取消导入", ofdgoExportAttachment: "取消下载" }[name] || "取消导出";
|
||||||
el.cancelExportButton.setAttribute("aria-label", el.cancelExportButton.title);
|
el.cancelExportButton.setAttribute("aria-label", el.cancelExportButton.title);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ self.onmessage = ({ data }) => {
|
|||||||
operations.get(data.id)?.abort();
|
operations.get(data.id)?.abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.name === "ofdgoExportPage" || data.name === "ofdgoExportDocument" || data.name === "ofdgoExportAttachment" || data.name === "ofdgoSaveDocument" || data.name === "ofdgoSaveEncrypted" || data.name === "ofdgoSaveSigned" || data.name === "ofdgoImportPages") {
|
if (data.name === "ofdgoConvertPDF" || data.name === "ofdgoExportPage" || data.name === "ofdgoExportDocument" || data.name === "ofdgoExportAttachment" || data.name === "ofdgoSaveDocument" || data.name === "ofdgoSaveEncrypted" || data.name === "ofdgoSaveSigned" || data.name === "ofdgoImportPages") {
|
||||||
operations.set(data.id, new AbortController());
|
operations.set(data.id, new AbortController());
|
||||||
}
|
}
|
||||||
pending = pending.then(() => handleMessage(data));
|
pending = pending.then(() => handleMessage(data));
|
||||||
@@ -17,6 +17,7 @@ async function handleMessage({ id, name, args }) {
|
|||||||
let channel;
|
let channel;
|
||||||
const signal = operations.get(id)?.signal;
|
const signal = operations.get(id)?.signal;
|
||||||
const importing = name === "ofdgoImportPages";
|
const importing = name === "ofdgoImportPages";
|
||||||
|
const converting = name === "ofdgoConvertPDF";
|
||||||
const saving = name === "ofdgoSaveDocument" || name === "ofdgoSaveEncrypted" || name === "ofdgoSaveSigned";
|
const saving = name === "ofdgoSaveDocument" || name === "ofdgoSaveEncrypted" || name === "ofdgoSaveSigned";
|
||||||
const key = name === "ofdgoOpen" ? args[3]?.key : name === "ofdgoSaveSigned" || name === "ofdgoLoadImport" ? args[1]?.key : null;
|
const key = name === "ofdgoOpen" ? args[3]?.key : name === "ofdgoSaveSigned" || name === "ofdgoLoadImport" ? args[1]?.key : null;
|
||||||
const keyPassword = name === "ofdgoOpen" ? args[3]?.keyPassword : name === "ofdgoSaveSigned" || name === "ofdgoLoadImport" ? args[1]?.keyPassword : null;
|
const keyPassword = name === "ofdgoOpen" ? args[3]?.keyPassword : name === "ofdgoSaveSigned" || name === "ofdgoLoadImport" ? args[1]?.keyPassword : null;
|
||||||
@@ -34,10 +35,10 @@ async function handleMessage({ id, name, args }) {
|
|||||||
};
|
};
|
||||||
channel.port2.postMessage(null);
|
channel.port2.postMessage(null);
|
||||||
};
|
};
|
||||||
if (importing) {
|
if (importing || converting) {
|
||||||
args.push((phase, completed, total, done) => {
|
args.push((phase, completed, total, done) => {
|
||||||
self.postMessage({ id, type: "import", phase, completed, total });
|
self.postMessage({ id, type: importing ? "import" : "conversion", phase, completed, total });
|
||||||
finish(done, null, phase === "commit");
|
finish(done, null, importing && phase === "commit");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const file = args.pop();
|
const file = args.pop();
|
||||||
@@ -74,7 +75,7 @@ async function handleMessage({ id, name, args }) {
|
|||||||
const payload = await globalThis[name](...args);
|
const payload = await globalThis[name](...args);
|
||||||
signal?.throwIfAborted();
|
signal?.throwIfAborted();
|
||||||
const result = typeof payload === "string" ? JSON.parse(payload) : payload;
|
const result = typeof payload === "string" ? JSON.parse(payload) : payload;
|
||||||
if (signal && !importing && result.ok) {
|
if (signal && !importing && !converting && result.ok) {
|
||||||
operations.delete(id);
|
operations.delete(id);
|
||||||
result.data.size = size;
|
result.data.size = size;
|
||||||
self.postMessage({ id, type: "export", stage: "save" });
|
self.postMessage({ id, type: "export", stage: "save" });
|
||||||
@@ -93,7 +94,7 @@ async function handleMessage({ id, name, args }) {
|
|||||||
await output?.abort().catch(() => {});
|
await output?.abort().catch(() => {});
|
||||||
const result = { id, ok: false, error: err.message };
|
const result = { id, ok: false, error: err.message };
|
||||||
if (signal?.aborted) {
|
if (signal?.aborted) {
|
||||||
result.error = importing ? "导入已取消" : "导出已取消";
|
result.error = importing ? "导入已取消" : converting ? "转换已取消" : "导出已取消";
|
||||||
result.canceled = true;
|
result.canceled = true;
|
||||||
}
|
}
|
||||||
self.postMessage(result);
|
self.postMessage(result);
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func awaitExport(fn js.Value, args ...any) error {
|
|||||||
// RunWASM 注册浏览器WASM接口并阻塞运行
|
// RunWASM 注册浏览器WASM接口并阻塞运行
|
||||||
func RunWASM() {
|
func RunWASM() {
|
||||||
registerCallback("ofdgoOpen", openDocument)
|
registerCallback("ofdgoOpen", openDocument)
|
||||||
registerCallback("ofdgoConvertPDF", convertPDFDocument)
|
registerAsyncCallback("ofdgoConvertPDF", convertPDFDocument)
|
||||||
registerCallback("ofdgoConfigure", configureDocument)
|
registerCallback("ofdgoConfigure", configureDocument)
|
||||||
registerCallback("ofdgoDocumentInfo", documentInfo)
|
registerCallback("ofdgoDocumentInfo", documentInfo)
|
||||||
registerCallback("ofdgoVerifySignatures", verifySignatures)
|
registerCallback("ofdgoVerifySignatures", verifySignatures)
|
||||||
@@ -316,7 +316,7 @@ func safeCall(fn func([]js.Value) (any, error), args []js.Value) (data any, err
|
|||||||
// 入参: args PDF数据
|
// 入参: args PDF数据
|
||||||
// 返回: any OFD数据及转换警告, error 错误信息
|
// 返回: any OFD数据及转换警告, error 错误信息
|
||||||
func convertPDFDocument(args []js.Value) (any, error) {
|
func convertPDFDocument(args []js.Value) (any, error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 2 {
|
||||||
return nil, fmt.Errorf("missing PDF data")
|
return nil, fmt.Errorf("missing PDF data")
|
||||||
}
|
}
|
||||||
data, err := bytesFromJS(args[0])
|
data, err := bytesFromJS(args[0])
|
||||||
@@ -324,7 +324,8 @@ func convertPDFDocument(args []js.Value) (any, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
report, err := ofdgo.ConvertPDF(context.Background(), bytes.NewReader(data), int64(len(data)), &output, ofdgo.PDFImportOptions{})
|
options := ofdgo.PDFImportOptions{OnProgress: editorOperationProgress(args[1])}
|
||||||
|
report, err := ofdgo.ConvertPDF(context.Background(), bytes.NewReader(data), int64(len(data)), &output, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import (
|
|||||||
|
|
||||||
// PDFImportOptions 指定PDF转换的运行时后端、进度通知与检查策略
|
// PDFImportOptions 指定PDF转换的运行时后端、进度通知与检查策略
|
||||||
// Strict禁止恢复缺失资源或丢失内容语义,默认在报告中记录警告
|
// Strict禁止恢复缺失资源或丢失内容语义,默认在报告中记录警告
|
||||||
|
// OnProgress按open、pages、convert及write.*阶段报告进度,total为0表示总量未知
|
||||||
type PDFImportOptions struct {
|
type PDFImportOptions struct {
|
||||||
Backends *RenderBackends
|
Backends *RenderBackends
|
||||||
Progress func(int) error
|
Progress func(int) error
|
||||||
|
OnProgress func(stage string, completed, total int) error
|
||||||
Strict bool
|
Strict bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,11 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, PDFImportReport{}, err
|
return nil, PDFImportReport{}, err
|
||||||
}
|
}
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
if err := options.OnProgress("open", 0, 0); err != nil {
|
||||||
|
return nil, PDFImportReport{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
reader, err := pdfgo.NewReader(source, size)
|
reader, err := pdfgo.NewReader(source, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, PDFImportReport{}, err
|
return nil, PDFImportReport{}, err
|
||||||
@@ -82,6 +89,11 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
editor.SetRenderBackends(*options.Backends)
|
editor.SetRenderBackends(*options.Backends)
|
||||||
}
|
}
|
||||||
importer := pdfImporter{ctx: ctx, reader: reader, editor: editor, fontIDs: map[*pdfgo.Font]string{}, fontMetrics: map[string]FontMetrics{}, pages: map[pdfgo.Reference]*pdfgo.Page{}, pageIDs: map[pdfgo.Reference]string{}}
|
importer := pdfImporter{ctx: ctx, reader: reader, editor: editor, fontIDs: map[*pdfgo.Font]string{}, fontMetrics: map[string]FontMetrics{}, pages: map[pdfgo.Reference]*pdfgo.Page{}, pageIDs: map[pdfgo.Reference]string{}}
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
if err := options.OnProgress("pages", 0, 0); err != nil {
|
||||||
|
return nil, PDFImportReport{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
err = reader.WalkPages(ctx, func(index int, page *pdfgo.Page) error {
|
err = reader.WalkPages(ctx, func(index int, page *pdfgo.Page) error {
|
||||||
_, width, height := pdfPageMatrix(page)
|
_, width, height := pdfPageMatrix(page)
|
||||||
if _, err := editor.AddPage(width, height); err != nil {
|
if _, err := editor.AddPage(width, height); err != nil {
|
||||||
@@ -89,11 +101,19 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
}
|
}
|
||||||
importer.pages[page.Reference] = page
|
importer.pages[page.Reference] = page
|
||||||
importer.pageIDs[page.Reference] = editor.pages[index].ID
|
importer.pageIDs[page.Reference] = editor.pages[index].ID
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
return options.OnProgress("pages", index+1, 0)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, PDFImportReport{}, err
|
return nil, PDFImportReport{}, err
|
||||||
}
|
}
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
if err := options.OnProgress("convert", 0, len(editor.pages)); err != nil {
|
||||||
|
return nil, PDFImportReport{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
err = reader.WalkPages(ctx, func(index int, page *pdfgo.Page) error {
|
err = reader.WalkPages(ctx, func(index int, page *pdfgo.Page) error {
|
||||||
matrix, width, height := pdfPageMatrix(page)
|
matrix, width, height := pdfPageMatrix(page)
|
||||||
importer.matrix = matrix
|
importer.matrix = matrix
|
||||||
@@ -125,6 +145,11 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
return fmt.Errorf("import PDF page %d: %w", index+1, err)
|
return fmt.Errorf("import PDF page %d: %w", index+1, err)
|
||||||
}
|
}
|
||||||
importer.report.Pages++
|
importer.report.Pages++
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
if err := options.OnProgress("convert", index+1, len(editor.pages)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
if options.Progress != nil {
|
if options.Progress != nil {
|
||||||
return options.Progress(index + 1)
|
return options.Progress(index + 1)
|
||||||
}
|
}
|
||||||
@@ -163,6 +188,14 @@ func ConvertPDF(ctx context.Context, source io.ReaderAt, size int64, output io.W
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return PDFImportReport{}, err
|
return PDFImportReport{}, err
|
||||||
}
|
}
|
||||||
|
if options.OnProgress != nil {
|
||||||
|
if err := options.OnProgress("write", 0, 0); err != nil {
|
||||||
|
return PDFImportReport{}, err
|
||||||
|
}
|
||||||
|
editor.OnWriteProgress = func(stage string, completed, total int) error {
|
||||||
|
return options.OnProgress("write."+stage, completed, total)
|
||||||
|
}
|
||||||
|
}
|
||||||
_, err = editor.WriteTo(output)
|
_, err = editor.WriteTo(output)
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user