mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:44:23 +08:00
1 parent
5842522edf
commit
6dceeee194
8 files changed
+82
-19
No files matched your search
@@ -472,8 +472,8 @@
|
|||||||
<dialog id="credentialsPanel" class="form-dialog security-dialog" aria-labelledby="credentialsTitle">
|
<dialog id="credentialsPanel" class="form-dialog security-dialog" aria-labelledby="credentialsTitle">
|
||||||
<form id="credentialsForm" autocomplete="off">
|
<form id="credentialsForm" autocomplete="off">
|
||||||
<h2 id="credentialsTitle" class="form-title">解锁文档</h2>
|
<h2 id="credentialsTitle" class="form-title">解锁文档</h2>
|
||||||
<div class="form-row"><label for="credentialsType">方式</label><select id="credentialsType"><option value="password">口令</option><option value="certificate">证书</option></select></div>
|
<div id="credentialsTypeRow" class="form-row"><label for="credentialsType">方式</label><select id="credentialsType"><option value="password">口令</option><option value="certificate">证书</option></select></div>
|
||||||
<div class="form-row"><label for="credentialsUser">用户</label><input id="credentialsUser" type="text" autocomplete="off" spellcheck="false"></div>
|
<div id="credentialsUserRow" class="form-row"><label for="credentialsUser">用户</label><input id="credentialsUser" type="text" autocomplete="off" spellcheck="false"></div>
|
||||||
<div id="credentialsPasswordRow" class="form-row"><label for="credentialsPassword">口令</label><input id="credentialsPassword" type="password" autocomplete="off" required autofocus></div>
|
<div id="credentialsPasswordRow" class="form-row"><label for="credentialsPassword">口令</label><input id="credentialsPassword" type="password" autocomplete="off" required autofocus></div>
|
||||||
<div id="credentialsCertificateRow" class="form-row" hidden><label for="credentialsCertificate">证书</label><input id="credentialsCertificate" type="file" accept=".pem,.cer,.crt,.der"></div>
|
<div id="credentialsCertificateRow" class="form-row" hidden><label for="credentialsCertificate">证书</label><input id="credentialsCertificate" type="file" accept=".pem,.cer,.crt,.der"></div>
|
||||||
<div id="credentialsKeyRow" class="form-row" hidden><label for="credentialsKey">私钥</label><input id="credentialsKey" type="file" accept=".pem,.key,.der"></div>
|
<div id="credentialsKeyRow" class="form-row" hidden><label for="credentialsKey">私钥</label><input id="credentialsKey" type="file" accept=".pem,.key,.der"></div>
|
||||||
|
|||||||
+25
-7
@@ -161,6 +161,8 @@ const el = {
|
|||||||
encryptSaveButton: document.querySelector("#encryptSaveButton"),
|
encryptSaveButton: document.querySelector("#encryptSaveButton"),
|
||||||
credentialsPanel: document.querySelector("#credentialsPanel"),
|
credentialsPanel: document.querySelector("#credentialsPanel"),
|
||||||
credentialsType: document.querySelector("#credentialsType"),
|
credentialsType: document.querySelector("#credentialsType"),
|
||||||
|
credentialsTypeRow: document.querySelector("#credentialsTypeRow"),
|
||||||
|
credentialsUserRow: document.querySelector("#credentialsUserRow"),
|
||||||
credentialsPasswordRow: document.querySelector("#credentialsPasswordRow"),
|
credentialsPasswordRow: document.querySelector("#credentialsPasswordRow"),
|
||||||
credentialsCertificateRow: document.querySelector("#credentialsCertificateRow"),
|
credentialsCertificateRow: document.querySelector("#credentialsCertificateRow"),
|
||||||
credentialsKeyRow: document.querySelector("#credentialsKeyRow"),
|
credentialsKeyRow: document.querySelector("#credentialsKeyRow"),
|
||||||
@@ -3387,7 +3389,7 @@ async function openOFD(file) {
|
|||||||
if (openSeq !== state.openSeq) return;
|
if (openSeq !== state.openSeq) return;
|
||||||
let converted;
|
let converted;
|
||||||
try {
|
try {
|
||||||
converted = await callWASM("ofdgoConvertPDF", bytes, await fontManager.files(fontManager.records()));
|
converted = await openWithCredentials(await fontManager.files(fontManager.records()), openSeq, bytes, false, true);
|
||||||
} finally {
|
} finally {
|
||||||
if (openSeq === state.openSeq) {
|
if (openSeq === state.openSeq) {
|
||||||
state.exportRequestID = 0;
|
state.exportRequestID = 0;
|
||||||
@@ -3881,8 +3883,14 @@ function finishCredentials(credentials) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestCredentials(code, openSeq) {
|
function requestCredentials(code, openSeq, pdf = false) {
|
||||||
finishCredentials(null);
|
finishCredentials(null);
|
||||||
|
el.credentialsTypeRow.hidden = pdf;
|
||||||
|
el.credentialsUserRow.hidden = pdf;
|
||||||
|
if (pdf) {
|
||||||
|
el.credentialsType.value = "password";
|
||||||
|
updateCredentialsType();
|
||||||
|
}
|
||||||
el.credentialsStatus.textContent = code === "invalidCredentials" ? "解锁失败,请重试" : "";
|
el.credentialsStatus.textContent = code === "invalidCredentials" ? "解锁失败,请重试" : "";
|
||||||
el.progressPanel.hidden = true;
|
el.progressPanel.hidden = true;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@@ -3892,19 +3900,21 @@ function requestCredentials(code, openSeq) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openWithCredentials(fonts, openSeq, data = state.ofdBytes, importing = false) {
|
async function openWithCredentials(fonts, openSeq, data = state.ofdBytes, importing = false, pdf = false) {
|
||||||
let credentials = null;
|
let credentials = null;
|
||||||
try {
|
try {
|
||||||
while (openSeq === state.openSeq) {
|
while (openSeq === state.openSeq) {
|
||||||
try {
|
try {
|
||||||
return importing ? await callWASM("ofdgoLoadImport", data.slice(), credentials)
|
return pdf ? await callWASM("ofdgoConvertPDF", data, fonts, credentials?.password || null)
|
||||||
|
: importing ? await callWASM("ofdgoLoadImport", data.slice(), credentials)
|
||||||
: await callWASM("ofdgoOpen", data, fonts, state.renderAnnotations, credentials);
|
: await callWASM("ofdgoOpen", data, fonts, state.renderAnnotations, credentials);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (openSeq !== state.openSeq || !["credentialsRequired", "invalidCredentials"].includes(err.code)) throw err;
|
if (openSeq !== state.openSeq || !(pdf ? err.code === "pdfPassword" : ["credentialsRequired", "invalidCredentials"].includes(err.code))) throw err;
|
||||||
|
const code = pdf ? credentials ? "invalidCredentials" : "credentialsRequired" : err.code;
|
||||||
credentials?.password.fill(0);
|
credentials?.password.fill(0);
|
||||||
credentials?.key?.fill(0);
|
credentials?.key?.fill(0);
|
||||||
credentials?.keyPassword?.fill(0);
|
credentials?.keyPassword?.fill(0);
|
||||||
credentials = await requestCredentials(err.code, openSeq);
|
credentials = await requestCredentials(code, openSeq, pdf);
|
||||||
if (!credentials || openSeq !== state.openSeq) {
|
if (!credentials || openSeq !== state.openSeq) {
|
||||||
const canceled = new Error("打开已取消");
|
const canceled = new Error("打开已取消");
|
||||||
canceled.name = "AbortError";
|
canceled.name = "AbortError";
|
||||||
@@ -4283,6 +4293,7 @@ async function openSealPosition() {
|
|||||||
const file = el.signSeal.files?.[0];
|
const file = el.signSeal.files?.[0];
|
||||||
if (!file || !state.doc || document.body.hasAttribute("aria-busy")) return;
|
if (!file || !state.doc || document.body.hasAttribute("aria-busy")) return;
|
||||||
const sequence = state.openSeq;
|
const sequence = state.openSeq;
|
||||||
|
let preview;
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
try {
|
try {
|
||||||
const range = el.signPages.value.trim(), mode = el.signPlacement.value;
|
const range = el.signPages.value.trim(), mode = el.signPlacement.value;
|
||||||
@@ -4291,8 +4302,10 @@ async function openSealPosition() {
|
|||||||
const result = await callWASM("ofdgoPreviewSignatureSeal", new Uint8Array(await file.arrayBuffer()), `1-${state.doc.pageCount}`);
|
const result = await callWASM("ofdgoPreviewSignatureSeal", new Uint8Array(await file.arrayBuffer()), `1-${state.doc.pageCount}`);
|
||||||
if (sequence !== state.openSeq || !el.signPanel.open || file !== el.signSeal.files?.[0]) return;
|
if (sequence !== state.openSeq || !el.signPanel.open || file !== el.signSeal.files?.[0]) return;
|
||||||
sealPreview = {pages:result.pages, selected, range, mode, ratio:document.querySelector("#signKeepRatio").checked ? result.width / result.height : 0, sequence, enabled:canvasEditor.enabled, status:el.statusText.textContent, url:URL.createObjectURL(new Blob([result.svg], {type:"image/svg+xml"}))};
|
sealPreview = {pages:result.pages, selected, range, mode, ratio:document.querySelector("#signKeepRatio").checked ? result.width / result.height : 0, sequence, enabled:canvasEditor.enabled, status:el.statusText.textContent, url:URL.createObjectURL(new Blob([result.svg], {type:"image/svg+xml"}))};
|
||||||
|
preview = sealPreview;
|
||||||
sealPosition.Image.src = sealPreview.url;
|
sealPosition.Image.src = sealPreview.url;
|
||||||
await sealPosition.Image.decode();
|
await sealPosition.Image.decode();
|
||||||
|
if (sealPreview !== preview) return;
|
||||||
if (sequence !== state.openSeq || !el.signPanel.open) { finishSealPosition(false); return; }
|
if (sequence !== state.openSeq || !el.signPanel.open) { finishSealPosition(false); return; }
|
||||||
canvasEditor.enabled = false;
|
canvasEditor.enabled = false;
|
||||||
if (COMPACT_LAYOUT.matches) {
|
if (COMPACT_LAYOUT.matches) {
|
||||||
@@ -4305,7 +4318,12 @@ async function openSealPosition() {
|
|||||||
el.signStatus.textContent = "";
|
el.signStatus.textContent = "";
|
||||||
setStatus("拖动选取签章区域,Esc 取消");
|
setStatus("拖动选取签章区域,Esc 取消");
|
||||||
el.viewerPanel.focus({preventScroll:true});
|
el.viewerPanel.focus({preventScroll:true});
|
||||||
} catch (error) { finishSealPosition(); el.signStatus.textContent = error.message; }
|
} catch (error) {
|
||||||
|
if (sequence === state.openSeq && (!preview || sealPreview === preview)) {
|
||||||
|
finishSealPosition();
|
||||||
|
el.signStatus.textContent = error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
finally { if (sequence === state.openSeq) setBusy(false); }
|
finally { if (sequence === state.openSeq) setBusy(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ async function handleMessage({ id, name, args }) {
|
|||||||
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;
|
||||||
const password = name === "ofdgoOpen" ? args[3]?.password : name === "ofdgoSaveEncrypted" || name === "ofdgoLoadImport" ? args[1]?.password : null;
|
const password = converting ? args[2] : name === "ofdgoOpen" ? args[3]?.password : name === "ofdgoSaveEncrypted" || name === "ofdgoLoadImport" ? args[1]?.password : null;
|
||||||
try {
|
try {
|
||||||
signal?.throwIfAborted();
|
signal?.throwIfAborted();
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
||||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1
|
github.com/xiaoqidun/jbig2 v1.1.1
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927150509-ae8390858589
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927160712-be47f27aa7ac
|
||||||
golang.org/x/crypto v0.57.0
|
golang.org/x/crypto v0.57.0
|
||||||
golang.org/x/image v0.46.0
|
golang.org/x/image v0.46.0
|
||||||
golang.org/x/text v0.42.0
|
golang.org/x/text v0.42.0
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
|||||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927150509-ae8390858589 h1:dHH5wnrIyOKRpx3Dw4IiMgMEPMrNFdatfkoMKpX3Khk=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927160712-be47f27aa7ac h1:RLwc4JpZm5PU9vBrMffyvdubmy7u3hknjsAId9gMvB4=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927150509-ae8390858589/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927160712-be47f27aa7ac/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||||
|
|||||||
+12
-3
@@ -332,6 +332,8 @@ func callbackResult(fn func([]js.Value) (any, error), args []js.Value) any {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
result := apiResult{Error: err.Error()}
|
result := apiResult{Error: err.Error()}
|
||||||
switch {
|
switch {
|
||||||
|
case errors.Is(err, ofdgo.ErrPDFPassword):
|
||||||
|
result.Code, result.Error = "pdfPassword", "PDF密码缺失或错误"
|
||||||
case errors.Is(err, ofdgo.ErrCredentialsRequired):
|
case errors.Is(err, ofdgo.ErrCredentialsRequired):
|
||||||
result.Code, result.Error = "credentialsRequired", "文档已加密"
|
result.Code, result.Error = "credentialsRequired", "文档已加密"
|
||||||
case errors.Is(err, ofdgo.ErrInvalidCredentials):
|
case errors.Is(err, ofdgo.ErrInvalidCredentials):
|
||||||
@@ -369,10 +371,10 @@ func safeCall(fn func([]js.Value) (any, error), args []js.Value) (data any, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convertPDFDocument 通过库转换PDF,不替换当前文档会话
|
// convertPDFDocument 通过库转换PDF,不替换当前文档会话
|
||||||
// 入参: args PDF数据、外部字体及进度回调
|
// 入参: args PDF数据、外部字体、UTF-8密码及进度回调
|
||||||
// 返回: any OFD数据及转换警告, error 错误信息
|
// 返回: any OFD数据及转换警告, error 错误信息
|
||||||
func convertPDFDocument(args []js.Value) (any, error) {
|
func convertPDFDocument(args []js.Value) (any, error) {
|
||||||
if len(args) != 3 {
|
if len(args) != 4 {
|
||||||
return nil, fmt.Errorf("missing PDF data")
|
return nil, fmt.Errorf("missing PDF data")
|
||||||
}
|
}
|
||||||
data, err := bytesFromJS(args[0])
|
data, err := bytesFromJS(args[0])
|
||||||
@@ -384,7 +386,14 @@ func convertPDFDocument(args []js.Value) (any, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
options := ofdgo.PDFImportOptions{OnProgress: editorOperationProgress(args[2])}
|
options := ofdgo.PDFImportOptions{OnProgress: editorOperationProgress(args[3]), PasswordUTF8: true}
|
||||||
|
if !args[2].IsNull() && !args[2].IsUndefined() {
|
||||||
|
options.Password, err = bytesFromJS(args[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer clear(options.Password)
|
||||||
|
}
|
||||||
if len(fonts) > 0 {
|
if len(fonts) > 0 {
|
||||||
options.RendererOptions = []ofdgo.RendererOption{ofdgo.WithFontFS(ofdgo.NewFontFS(fonts))}
|
options.RendererOptions = []ofdgo.RendererOption{ofdgo.WithFontFS(ofdgo.NewFontFS(fonts))}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-1
@@ -397,8 +397,18 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
|||||||
var finalPrivData []byte
|
var finalPrivData []byte
|
||||||
if len(privDictData) > 0 {
|
if len(privDictData) > 0 {
|
||||||
pDict := parseCFFDict(privDictData)
|
pDict := parseCFFDict(privDictData)
|
||||||
|
if err := normalizeCFFPrivate(pDict); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if _, ok := pDict[19]; ok || len(localSubrData) > 0 {
|
if _, ok := pDict[19]; ok || len(localSubrData) > 0 {
|
||||||
pDict[19] = []float64{float64(privateLen)}
|
pDict[19] = []float64{0}
|
||||||
|
for {
|
||||||
|
length := len(encodeCFFDict(pDict))
|
||||||
|
if pDict[19][0] == float64(length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pDict[19][0] = float64(length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finalPrivData = encodeCFFDict(pDict)
|
finalPrivData = encodeCFFDict(pDict)
|
||||||
privateLen = len(finalPrivData)
|
privateLen = len(finalPrivData)
|
||||||
@@ -438,6 +448,21 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
|||||||
return newCFF.Bytes(), nil
|
return newCFF.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeCFFPrivate 移除旧版CFF中不生效的缺省字段,拒绝需要额外解释的取值
|
||||||
|
// 入参: dict 私有字典
|
||||||
|
// 返回: error 不支持的旧版行为
|
||||||
|
func normalizeCFFPrivate(dict cffDict) error {
|
||||||
|
for op, value := range map[int]float64{1215: 0, 1216: -1} {
|
||||||
|
if values, ok := dict[op]; ok {
|
||||||
|
if len(values) != 1 || values[0] != value {
|
||||||
|
return fmt.Errorf("unsupported legacy CFF private operator %d", op)
|
||||||
|
}
|
||||||
|
delete(dict, op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// sanitizeMultiFDCFF 清洗多FD的CID CFF数据
|
// sanitizeMultiFDCFF 清洗多FD的CID CFF数据
|
||||||
// 入参: data 原始CFF数据, hdrSize 头部大小, nameIndexData 名称索引, topDict 顶层字典
|
// 入参: data 原始CFF数据, hdrSize 头部大小, nameIndexData 名称索引, topDict 顶层字典
|
||||||
// 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量
|
// 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量
|
||||||
@@ -479,6 +504,9 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(privateDict, 19)
|
delete(privateDict, 19)
|
||||||
|
if err := normalizeCFFPrivate(privateDict); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
finalPrivData := encodeCFFDict(privateDict)
|
finalPrivData := encodeCFFDict(privateDict)
|
||||||
privateLen := len(finalPrivData)
|
privateLen := len(finalPrivData)
|
||||||
charStringsData := encodeCFFIndex(inlined)
|
charStringsData := encodeCFFIndex(inlined)
|
||||||
|
|||||||
+10
-2
@@ -26,14 +26,18 @@ import (
|
|||||||
"github.com/xiaoqidun/pdfgo"
|
"github.com/xiaoqidun/pdfgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrPDFPassword 表示PDF需要正确的用户或所有者密码
|
||||||
|
var ErrPDFPassword = pdfgo.ErrPassword
|
||||||
|
|
||||||
// PDFImportOptions 指定PDF转换的密码、运行时后端、进度通知与检查策略
|
// PDFImportOptions 指定PDF转换的密码、运行时后端、进度通知与检查策略
|
||||||
// Password按PDFDocEncoding字节传入,空值尝试默认空密码
|
// Password默认使用PDF安全处理器编码,PasswordUTF8启用UTF-8输入及标准密码准备,空值尝试空密码
|
||||||
// Strict禁止恢复缺失资源或丢失内容语义,默认在报告中记录警告
|
// Strict禁止恢复缺失资源或丢失内容语义,默认在报告中记录警告
|
||||||
// RendererOptions复用渲染器字体来源和后端配置,Backends优先于其中的后端设置
|
// RendererOptions复用渲染器字体来源和后端配置,Backends优先于其中的后端设置
|
||||||
// RasterDPI指定局部透明效果合成精度,0沿用渲染器DPI,默认300dpi,Strict禁用局部合成
|
// RasterDPI指定局部透明效果合成精度,0沿用渲染器DPI,默认300dpi,Strict禁用局部合成
|
||||||
// OnProgress按open、pages、convert及write.*阶段报告进度,total为0表示总量未知
|
// OnProgress按open、pages、convert及write.*阶段报告进度,total为0表示总量未知
|
||||||
type PDFImportOptions struct {
|
type PDFImportOptions struct {
|
||||||
Password []byte
|
Password []byte
|
||||||
|
PasswordUTF8 bool
|
||||||
Backends *RenderBackends
|
Backends *RenderBackends
|
||||||
RendererOptions []RendererOption
|
RendererOptions []RendererOption
|
||||||
Progress func(int) error
|
Progress func(int) error
|
||||||
@@ -108,7 +112,11 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
return nil, PDFImportReport{}, err
|
return nil, PDFImportReport{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reader, err := pdfgo.NewReaderWithPassword(source, size, options.Password)
|
open := pdfgo.NewReaderWithPassword
|
||||||
|
if options.PasswordUTF8 {
|
||||||
|
open = pdfgo.NewReaderWithUTF8Password
|
||||||
|
}
|
||||||
|
reader, err := open(source, size, options.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, PDFImportReport{}, err
|
return nil, PDFImportReport{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user