diff --git a/assets/webui/ofdgo.css b/assets/webui/ofdgo.css
index bacb25c..ce688f1 100644
--- a/assets/webui/ofdgo.css
+++ b/assets/webui/ofdgo.css
@@ -645,6 +645,16 @@ button:not(:disabled):hover {
padding: 12px;
}
+.form-title {
+ margin: -12px -12px 12px;
+ padding: 10px 12px;
+ border-bottom: 1px solid var(--line);
+ background: #eef1f4;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.5;
+}
+
.security-dialog {
width: 360px;
}
diff --git a/assets/webui/ofdgo.js b/assets/webui/ofdgo.js
index b06b433..9037a36 100644
--- a/assets/webui/ofdgo.js
+++ b/assets/webui/ofdgo.js
@@ -350,6 +350,7 @@ const el = {
exportPageButton: document.querySelector("#exportPageButton"),
exportButton: document.querySelector("#exportButton"),
exportPanel: document.querySelector("#exportPanel"),
+ exportTitle: document.querySelector("#exportTitle"),
exportForm: document.querySelector("#exportForm"),
exportAll: document.querySelector("#exportAll"),
exportSpecified: document.querySelector("#exportSpecified"),
@@ -1602,7 +1603,7 @@ async function toggleEditor() {
const openSeq = ++state.openSeq;
setBusy(true, "正在准备编辑", null, "正在准备编辑");
try {
- const doc = await callWASM("ofdgoEditDocument");
+ const doc = await callWASM("ofdgoEditDocument", pageIndex);
if (openSeq !== state.openSeq) return;
state.savedRevision = doc.revision;
state.editorViews.clear();
@@ -3961,7 +3962,7 @@ function openExportPanel(encrypted = false) {
el.encryptionFields.hidden = !encrypted;
el.encryptionPassword.required = el.encryptionConfirm.required = encrypted;
updateEncryptionType();
- el.exportPanel.setAttribute("aria-label", encrypted ? "加密另存" : "导出文档");
+ el.exportTitle.textContent = encrypted ? "加密另存" : "导出文档";
el.exportSubmit.textContent = encrypted ? "保存" : "导出";
if (state.editing && state.pageSelection.size) {
el.exportSpecified.checked = true;
@@ -5851,9 +5852,9 @@ function renderSignatures() {
head.append(badges, name);
row.append(head);
- appendInfoLine(row, "章名", signature.sealName);
appendInfoLine(row, "签署人", signature.signer);
appendInfoLine(row, "时间", formatDocumentTime(signature.signatureDateTime));
+ appendInfoLine(row, "章名", signature.sealName);
appendInfoLine(row, "机构", signatureAgency(signature));
appendSignatureCheck(row, "信任", signature.certTrustChecked, signature.certTrustOK);
appendInfoLine(row, "保护文件", signatureReferenceText(signature), signatureReferenceStatus(signature));
@@ -5864,25 +5865,25 @@ function renderSignatures() {
checks.append(checksTitle);
appendSignatureCheck(checks, "原文", signature.dataHashChecked, signature.dataHashOK);
appendSignatureCheck(checks, "签名", signature.signedValueChecked, signature.signedValueOK);
+ appendSignatureCheck(checks, "证书", signature.certChecked, signature.certOK);
+ appendSignaturePolicy(checks, "证书期限", signature.certTimeChecked, signature.certTimeOK);
+ appendInfoLine(checks, "信任错误", signature.certTrustError, "fail");
+ appendSignaturePolicy(checks, "签署时间", signature.signatureTimeChecked, signature.signatureTimeOK);
if (signature.type !== "Sign") {
appendSignatureCheck(checks, "印章", signature.sealChecked, signature.sealOK);
appendSignaturePolicy(checks, "印章匹配", signature.sealMatchChecked, signature.sealMatchOK);
+ appendSignaturePolicy(checks, "印章期限", signature.sealTimeChecked, signature.sealTimeOK);
+ if (signature.sealCertTimeChecked) {
+ appendInfoLine(checks, "制章证书", signature.sealCertTimeOK ? "有效" : "失效", signature.sealCertTimeOK ? "ok" : "fail");
+ }
}
- appendSignatureCheck(checks, "证书", signature.certChecked, signature.certOK);
- appendSignaturePolicy(checks, "签署时间", signature.signatureTimeChecked, signature.signatureTimeOK);
- appendSignaturePolicy(checks, "印章期限", signature.sealTimeChecked, signature.sealTimeOK);
- if (signature.sealCertTimeChecked) {
- appendInfoLine(checks, "制章证书", signature.sealCertTimeOK ? "有效" : "失效", signature.sealCertTimeOK ? "ok" : "fail");
- }
- appendSignaturePolicy(checks, "证书期限", signature.certTimeChecked, signature.certTimeOK);
- appendSignatureCheck(checks, "策略", signature.policyChecked, signature.policyOK);
appendSignatureCheck(checks, "覆盖", signature.coverageChecked, signature.coverageOK);
- appendSignatureCheck(checks, "时间戳", signature.timestampChecked, signature.timestampOK);
- appendSignatureCheck(checks, "撤销", signature.revocationChecked, signature.revocationOK);
- appendInfoLine(checks, "策略错误", signature.policyError, "fail");
- appendInfoLine(checks, "信任错误", signature.certTrustError, "fail");
appendInfoLine(checks, "覆盖错误", signature.coverageError, "fail");
appendInfoLine(checks, "未保护", signature.uncoveredFiles?.join("、"), "fail");
+ appendSignatureCheck(checks, "策略", signature.policyChecked, signature.policyOK);
+ appendInfoLine(checks, "策略错误", signature.policyError, "fail");
+ appendSignatureCheck(checks, "时间戳", signature.timestampChecked, signature.timestampOK);
+ appendSignatureCheck(checks, "撤销", signature.revocationChecked, signature.revocationOK);
row.append(checks);
for (const timestamp of signature.timestamps || []) {
const details = document.createElement("details");
@@ -5917,17 +5918,17 @@ function renderSignatures() {
title.textContent = "签章信息";
details.append(title);
appendInfoLine(details, "编号", signature.id);
- appendInfoLine(details, "版本", signature.version);
- appendInfoLine(details, "章图", signature.sealType);
- appendInfoLine(details, "章号", signature.sealId);
- appendInfoLine(details, "厂商", signature.sealVendor);
appendInfoLine(details, "文档", signature.docRoot);
appendInfoLine(details, "算法", signature.signatureMethod);
appendInfoLine(details, "摘要", signature.digestMethod);
- appendInfoLine(details, "序列号", signature.signSerial);
appendInfoLine(details, "证书主体", signature.signSubject);
appendInfoLine(details, "颁发者", signature.signIssuer);
+ appendInfoLine(details, "序列号", signature.signSerial);
+ appendInfoLine(details, "章号", signature.sealId);
+ appendInfoLine(details, "章图", signature.sealType);
appendInfoLine(details, "制章证书", signature.sealSubject);
+ appendInfoLine(details, "印章厂商", signature.sealVendor);
+ appendInfoLine(details, "组件版本", signature.version);
row.append(details);
fragment.append(row);
}
diff --git a/internal/webui/wasm.go b/internal/webui/wasm.go
index 1780d4e..3d9d0e5 100644
--- a/internal/webui/wasm.go
+++ b/internal/webui/wasm.go
@@ -498,6 +498,15 @@ func renderPage(args []js.Value) (any, error) {
dpi = args[2].Float()
}
raster := len(args) > 3 && args[3].Bool()
+ if currentEditor != nil {
+ page, err := currentEditor.Page(args[0].Int())
+ if err != nil {
+ return nil, err
+ }
+ if err := syncEditorPageIDs(args[0].Int(), page); err != nil {
+ return nil, err
+ }
+ }
page, err := currentSession.RenderPage(args[0].Int(), backend, dpi, raster)
if err != nil {
return nil, err
@@ -569,7 +578,7 @@ func renderPage(args []js.Value) (any, error) {
}
// pageTextString 提取OFD页面原文
-// 入参: args 浏览器参数
+// 入参: args 页面索引
// 返回: any 页面原文, error 错误信息
func pageTextString(args []js.Value) (any, error) {
if currentSession == nil {
@@ -2906,7 +2915,7 @@ func updateInfo(args []js.Value) (any, error) {
}
// editDocument 将已打开文档接入编辑器,沿用页面、资源及字体配置
-// 入参: args 浏览器参数
+// 入参: args 当前页索引,省略时使用首页
// 返回: any 编辑状态, error 错误信息
func editDocument(args []js.Value) (any, error) {
if currentSession == nil {
@@ -2924,6 +2933,17 @@ func editDocument(args []js.Value) (any, error) {
editor.SetFontFS(currentSession.fontFS)
}
editor.SetRenderBackends(currentSession.Renderer.Backends())
+ page := 0
+ if len(args) != 0 {
+ page = args[0].Int()
+ }
+ content, err := editor.Page(page)
+ if err != nil {
+ return nil, err
+ }
+ if err := syncEditorPageIDs(page, content); err != nil {
+ return nil, err
+ }
currentEditor = editor
currentSession.editing = true
copiedObjects = nil
@@ -2931,6 +2951,29 @@ func editDocument(args []js.Value) (any, error) {
return editorSummary(), nil
}
+// syncEditorPageIDs 同步编辑副本的对象编号,不改变原渲染属性或丢弃正常页面缓存
+// 入参: index 页面索引, content 编辑页面
+// 返回: error 页面读取错误
+func syncEditorPageIDs(index int, content *ofdgo.PageContent) error {
+ page, err := currentSession.pageContent(index)
+ if err != nil {
+ return err
+ }
+ for i, layer := range content.Content.Layer {
+ for j, object := range layer.Objects {
+ before := &page.Content.Layer[i].Objects[j]
+ if before.TextObject.ID != object.TextObject.ID || before.PathObject.ID != object.PathObject.ID || before.ImageObject.ID != object.ImageObject.ID || before.CompositeGraphicUnit.ID != object.CompositeGraphicUnit.ID {
+ before.TextObject.ID = object.TextObject.ID
+ before.PathObject.ID = object.PathObject.ID
+ before.ImageObject.ID = object.ImageObject.ID
+ before.CompositeGraphicUnit.ID = object.CompositeGraphicUnit.ID
+ delete(currentSession.textCache, index)
+ }
+ }
+ }
+ return nil
+}
+
// previewEditor 通过内存快照更新预览,不生成中间压缩包
// 入参: editor 编辑文档, annotations 是否显示注解
// 返回: editorInfo 文档信息, error 错误信息
diff --git a/internal/webui/webui.go b/internal/webui/webui.go
index ed06739..71e1837 100644
--- a/internal/webui/webui.go
+++ b/internal/webui/webui.go
@@ -43,65 +43,65 @@ type FontInfo = ofdgo.FontInfo
type SignatureInfo struct {
DocIndex int `json:"docIndex"`
DocRoot string `json:"docRoot,omitempty"`
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Status string `json:"status"`
IntegrityValid bool `json:"integrityValid"`
TrustedValid bool `json:"trustedValid"`
- PolicyChecked bool `json:"policyChecked"`
- PolicyOK bool `json:"policyOK"`
- PolicyError string `json:"policyError,omitempty"`
+ Error string `json:"error,omitempty"`
+ Signer string `json:"signer,omitempty"`
+ SignatureDateTime string `json:"signatureDateTime,omitempty"`
+ SealName string `json:"sealName,omitempty"`
+ Provider string `json:"provider,omitempty"`
+ Company string `json:"company,omitempty"`
+ SealType string `json:"sealType,omitempty"`
+ SealID string `json:"sealId,omitempty"`
+ SealVendor string `json:"sealVendor,omitempty"`
+ Version string `json:"version,omitempty"`
+ SignSubject string `json:"signSubject,omitempty"`
+ SignIssuer string `json:"signIssuer,omitempty"`
+ SignSerial string `json:"signSerial,omitempty"`
+ SealSubject string `json:"sealSubject,omitempty"`
+ SignatureMethod string `json:"signatureMethod,omitempty"`
+ DigestMethod string `json:"digestMethod,omitempty"`
+ ReferenceCount int `json:"referenceCount"`
+ ReferenceChecked int `json:"referenceChecked"`
+ ReferencePassed int `json:"referencePassed"`
+ Stamps []SignatureStampInfo `json:"stamps,omitempty"`
+ DataHashChecked bool `json:"dataHashChecked"`
+ DataHashOK bool `json:"dataHashOK"`
+ SignedValueChecked bool `json:"signedValueChecked"`
+ SignedValueOK bool `json:"signedValueOK"`
+ CertChecked bool `json:"certChecked"`
+ CertOK bool `json:"certOK"`
+ CertTimeChecked bool `json:"certTimeChecked,omitempty"`
+ CertTimeOK bool `json:"certTimeOK,omitempty"`
+ CertTrustChecked bool `json:"certTrustChecked,omitempty"`
+ CertTrustOK bool `json:"certTrustOK,omitempty"`
+ CertTrustError string `json:"certTrustError,omitempty"`
+ SignatureTimeChecked bool `json:"signatureTimeChecked,omitempty"`
+ SignatureTimeOK bool `json:"signatureTimeOK,omitempty"`
+ SealChecked bool `json:"sealChecked"`
+ SealOK bool `json:"sealOK"`
+ SealMatchChecked bool `json:"sealMatchChecked"`
+ SealMatchOK bool `json:"sealMatchOK"`
+ SealTimeChecked bool `json:"sealTimeChecked,omitempty"`
+ SealTimeOK bool `json:"sealTimeOK,omitempty"`
+ SealCertTimeChecked bool `json:"sealCertTimeChecked,omitempty"`
+ SealCertTimeOK bool `json:"sealCertTimeOK,omitempty"`
CoverageChecked bool `json:"coverageChecked"`
CoverageOK bool `json:"coverageOK"`
CoverageError string `json:"coverageError,omitempty"`
UncoveredFiles []string `json:"uncoveredFiles,omitempty"`
+ PolicyChecked bool `json:"policyChecked"`
+ PolicyOK bool `json:"policyOK"`
+ PolicyError string `json:"policyError,omitempty"`
TimestampChecked bool `json:"timestampChecked"`
TimestampOK bool `json:"timestampOK"`
Timestamps []TimestampInfo `json:"timestamps,omitempty"`
RevocationChecked bool `json:"revocationChecked"`
RevocationOK bool `json:"revocationOK"`
Revocations []RevocationInfo `json:"revocations,omitempty"`
- ID string `json:"id"`
- Type string `json:"type"`
- Status string `json:"status"`
- Version string `json:"version,omitempty"`
- SealType string `json:"sealType,omitempty"`
- SealID string `json:"sealId,omitempty"`
- SealName string `json:"sealName,omitempty"`
- SealVendor string `json:"sealVendor,omitempty"`
- Signer string `json:"signer,omitempty"`
- SignatureDateTime string `json:"signatureDateTime,omitempty"`
- Provider string `json:"provider,omitempty"`
- Company string `json:"company,omitempty"`
- DataHashChecked bool `json:"dataHashChecked"`
- DataHashOK bool `json:"dataHashOK"`
- SignedValueChecked bool `json:"signedValueChecked"`
- SignedValueOK bool `json:"signedValueOK"`
- SealChecked bool `json:"sealChecked"`
- SealOK bool `json:"sealOK"`
- SealMatchChecked bool `json:"sealMatchChecked"`
- SealMatchOK bool `json:"sealMatchOK"`
- CertChecked bool `json:"certChecked"`
- CertOK bool `json:"certOK"`
- SignatureTimeChecked bool `json:"signatureTimeChecked,omitempty"`
- SignatureTimeOK bool `json:"signatureTimeOK,omitempty"`
- SealTimeChecked bool `json:"sealTimeChecked,omitempty"`
- SealTimeOK bool `json:"sealTimeOK,omitempty"`
- SealCertTimeChecked bool `json:"sealCertTimeChecked,omitempty"`
- SealCertTimeOK bool `json:"sealCertTimeOK,omitempty"`
- CertTimeChecked bool `json:"certTimeChecked,omitempty"`
- CertTimeOK bool `json:"certTimeOK,omitempty"`
- CertTrustChecked bool `json:"certTrustChecked,omitempty"`
- CertTrustOK bool `json:"certTrustOK,omitempty"`
- CertTrustError string `json:"certTrustError,omitempty"`
- ReferenceCount int `json:"referenceCount"`
- ReferenceChecked int `json:"referenceChecked"`
- ReferencePassed int `json:"referencePassed"`
- SignatureMethod string `json:"signatureMethod,omitempty"`
- DigestMethod string `json:"digestMethod,omitempty"`
- SignSerial string `json:"signSerial,omitempty"`
- SignSubject string `json:"signSubject,omitempty"`
- SignIssuer string `json:"signIssuer,omitempty"`
- SealSubject string `json:"sealSubject,omitempty"`
- Stamps []SignatureStampInfo `json:"stamps,omitempty"`
- Error string `json:"error,omitempty"`
}
// TimestampInfo 独立展示时间戳绑定、签名、信任和证书时效
@@ -681,63 +681,63 @@ func signatureInfo(report ofdgo.SignatureVerifyReport) SignatureInfo {
info := SignatureInfo{
DocIndex: report.DocIndex,
DocRoot: report.DocRoot,
- IntegrityValid: report.IntegrityValid(),
- TrustedValid: report.TrustedValid(),
- PolicyChecked: report.PolicyChecked,
- PolicyOK: report.PolicyOK,
- PolicyError: report.PolicyError,
- CoverageChecked: report.CoverageChecked,
- CoverageOK: report.CoverageOK,
- CoverageError: report.CoverageError,
- UncoveredFiles: report.UncoveredFiles,
- TimestampChecked: report.TimestampChecked,
- TimestampOK: report.TimestampOK,
- RevocationChecked: report.RevocationChecked,
- RevocationOK: report.RevocationOK,
ID: report.ID,
Type: string(report.Type),
Status: status,
- Version: report.Provider.Version,
- SealType: report.SealType,
- SealID: report.SealInfo.ID,
- SealName: report.SealInfo.Name,
- SealVendor: report.SealInfo.VendorID,
+ IntegrityValid: report.IntegrityValid(),
+ TrustedValid: report.TrustedValid(),
+ Error: signatureReportError(report),
Signer: signatureSigner(report),
SignatureDateTime: dateTime,
+ SealName: report.SealInfo.Name,
Provider: report.Provider.ProviderName,
Company: report.Provider.Company,
+ SealType: report.SealType,
+ SealID: report.SealInfo.ID,
+ SealVendor: report.SealInfo.VendorID,
+ Version: report.Provider.Version,
+ SignSubject: report.SignCert.Subject,
+ SignIssuer: report.SignCert.Issuer,
+ SignSerial: report.SignCert.SerialNumber,
+ SealSubject: report.SealCert.Subject,
+ SignatureMethod: report.SignatureMethod,
+ DigestMethod: report.DigestMethod,
+ ReferenceCount: len(report.References),
+ ReferenceChecked: checked,
+ ReferencePassed: passed,
+ Stamps: signatureStampInfos(report.StampPositions),
DataHashChecked: report.DataHashChecked,
DataHashOK: report.DataHashOK,
SignedValueChecked: report.SignedValueChecked,
SignedValueOK: report.SignedValueOK,
- SealChecked: report.SealChecked,
- SealOK: report.SealOK,
- SealMatchChecked: report.SealMatchChecked,
- SealMatchOK: report.SealMatchOK,
CertChecked: report.CertChecked,
CertOK: report.CertOK,
- SignatureTimeChecked: report.SignatureTimeChecked,
- SignatureTimeOK: report.SignatureTimeOK,
- SealTimeChecked: report.SealTimeChecked,
- SealTimeOK: report.SealTimeOK,
- SealCertTimeChecked: report.SealCertTimeChecked,
- SealCertTimeOK: report.SealCertTimeOK,
CertTimeChecked: report.CertTimeChecked,
CertTimeOK: report.CertTimeOK,
CertTrustChecked: report.CertTrustChecked,
CertTrustOK: report.CertTrustOK,
CertTrustError: report.CertTrustError,
- ReferenceCount: len(report.References),
- ReferenceChecked: checked,
- ReferencePassed: passed,
- SignatureMethod: report.SignatureMethod,
- DigestMethod: report.DigestMethod,
- SignSerial: report.SignCert.SerialNumber,
- SignSubject: report.SignCert.Subject,
- SignIssuer: report.SignCert.Issuer,
- SealSubject: report.SealCert.Subject,
- Stamps: signatureStampInfos(report.StampPositions),
- Error: signatureReportError(report),
+ SignatureTimeChecked: report.SignatureTimeChecked,
+ SignatureTimeOK: report.SignatureTimeOK,
+ SealChecked: report.SealChecked,
+ SealOK: report.SealOK,
+ SealMatchChecked: report.SealMatchChecked,
+ SealMatchOK: report.SealMatchOK,
+ SealTimeChecked: report.SealTimeChecked,
+ SealTimeOK: report.SealTimeOK,
+ SealCertTimeChecked: report.SealCertTimeChecked,
+ SealCertTimeOK: report.SealCertTimeOK,
+ CoverageChecked: report.CoverageChecked,
+ CoverageOK: report.CoverageOK,
+ CoverageError: report.CoverageError,
+ UncoveredFiles: report.UncoveredFiles,
+ PolicyChecked: report.PolicyChecked,
+ PolicyOK: report.PolicyOK,
+ PolicyError: report.PolicyError,
+ TimestampChecked: report.TimestampChecked,
+ TimestampOK: report.TimestampOK,
+ RevocationChecked: report.RevocationChecked,
+ RevocationOK: report.RevocationOK,
}
for _, stamp := range report.Timestamps {
info.Timestamps = append(info.Timestamps, TimestampInfo{
diff --git a/ofdgo_editor_open.go b/ofdgo_editor_open.go
index 01cc277..88fbdac 100644
--- a/ofdgo_editor_open.go
+++ b/ofdgo_editor_open.go
@@ -57,6 +57,7 @@ type editorSourcePage struct {
root *editorXML
original *PageContent
nodes map[string]*editorXML
+ repaired bool
}
// ObjectCapabilities 已有对象可执行的操作,Reason说明受限原因
@@ -260,6 +261,103 @@ func (e *Editor) sourceRGB() bool {
return space != nil && space.Type == "RGB" && (space.BitsPerComponent == 0 || space.BitsPerComponent == 8) && len(space.Palette) == 0
}
+// repairSourceObjectIDs 按实际节点区分重复对象,只修复没有引用歧义的编号
+// 入参: data 页面原文, root 页面节点
+// 返回: []byte 编辑副本, bool 是否修复, error 错误信息
+func (e *Editor) repairSourceObjectIDs(data []byte, root *editorXML) ([]byte, bool, error) {
+ seen := make(map[string]bool)
+ duplicates := make(map[string]bool)
+ var nodes []*editorXML
+ var visit func(*editorXML) error
+ visit = func(parent *editorXML) error {
+ for _, node := range parent.children {
+ id := node.attr("ID")
+ if id != "" && seen[id] {
+ switch node.name.Local {
+ case "TextObject", "PathObject", "ImageObject", "CompositeObject":
+ nodes = append(nodes, node)
+ duplicates[id] = true
+ default:
+ return fmt.Errorf("duplicate container ID %q", id)
+ }
+ }
+ seen[id] = true
+ if node.name.Local == "PageBlock" {
+ if err := visit(node); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }
+ if content := root.child("Content"); content != nil {
+ for _, layer := range content.children {
+ if layer.name.Local == "Layer" {
+ if err := visit(layer); err != nil {
+ return nil, false, err
+ }
+ }
+ }
+ }
+ if len(nodes) == 0 {
+ return data, false, nil
+ }
+ if err := e.checkDuplicateObjectReferences(duplicates); err != nil {
+ return nil, false, err
+ }
+ if err := e.prepareSourceIDs(); err != nil {
+ return nil, false, err
+ }
+ maximum := e.maxID
+ var patches []editorXMLPatch
+ for _, node := range nodes {
+ maximum++
+ encoded, err := editorXMLAttribute(data, node, "ID", strconv.Itoa(maximum))
+ if err != nil {
+ return nil, false, err
+ }
+ patches = append(patches, editorXMLPatch{node.start, node.end, encoded})
+ }
+ e.maxID = maximum
+ return editorPatchXML(data, patches), true, nil
+}
+
+// checkDuplicateObjectReferences 拒绝无法判定目标的编号引用,不猜测其所属对象
+// 入参: duplicates 重复编号
+// 返回: error 引用歧义或读取错误
+func (e *Editor) checkDuplicateObjectReferences(duplicates map[string]bool) error {
+ reader := e.source.reader
+ for _, name := range reader.fileNamesFold {
+ if !strings.EqualFold(path.Ext(name), ".xml") {
+ continue
+ }
+ input, err := reader.openFile(name)
+ if err != nil {
+ return err
+ }
+ decoder := xml.NewDecoder(input)
+ for {
+ token, readErr := decoder.Token()
+ if readErr != nil {
+ input.Close()
+ if readErr != io.EOF {
+ return readErr
+ }
+ break
+ }
+ if node, ok := token.(xml.StartElement); ok {
+ for _, attr := range node.Attr {
+ if editorObjectReference(attr.Name.Local) && duplicates[editorResourceID(attr.Value)] {
+ input.Close()
+ return fmt.Errorf("ambiguous reference to duplicate object ID %q in %s", attr.Value, name)
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
// loadSourcePage 首次访问时解析原始页面,不加载图片或字体二进制数据
// 入参: index 页面索引
// 返回: error 错误信息
@@ -281,6 +379,21 @@ func (e *Editor) loadSourcePage(index int) error {
if err != nil {
return err
}
+ data, repaired, err := e.repairSourceObjectIDs(data, root)
+ if err != nil {
+ return err
+ }
+ if repaired {
+ root, err = parseEditorXML(data)
+ if err != nil {
+ return err
+ }
+ var updated PageContent
+ if err := xml.Unmarshal(data, &updated); err != nil {
+ return err
+ }
+ page.Content = updated.Content
+ }
nodes := make(map[string]*editorXML)
if content := root.child("Content"); content != nil {
for _, layer := range content.children {
@@ -293,6 +406,7 @@ func (e *Editor) loadSourcePage(index int) error {
}
}
source.data, source.root, source.nodes = data, root, nodes
+ source.repaired = repaired
for i := range page.Content.Layer {
layer := &page.Content.Layer[i]
for j, object := range layer.Objects {
diff --git a/ofdgo_editor_package.go b/ofdgo_editor_package.go
index 726b496..62282e6 100644
--- a/ofdgo_editor_package.go
+++ b/ofdgo_editor_package.go
@@ -49,7 +49,7 @@ func (e *Editor) sourceParts(progress editorProgress) (map[string][]byte, error)
if err != nil {
return nil, err
}
- if !bytes.Equal(data, original.data) {
+ if original.repaired || !bytes.Equal(data, original.data) {
parts[reader.ResPath(original.ref.BaseLoc)] = data
}
} else {
diff --git a/ofdgo_sign_verify.go b/ofdgo_sign_verify.go
index 747860d..8d80d3e 100644
--- a/ofdgo_sign_verify.go
+++ b/ofdgo_sign_verify.go
@@ -46,32 +46,20 @@ import (
type SignatureVerifyReport struct {
DocIndex int
DocRoot string
- PolicyChecked bool
- PolicyOK bool
- PolicyError string
- CoverageChecked bool
- CoverageOK bool
- CoverageError string
- CoveredFiles []string
- UncoveredFiles []string
- TimestampChecked bool
- TimestampOK bool
- Timestamps []SignatureTimestampReport
- RevocationChecked bool
- RevocationOK bool
- Revocations []SignatureRevocationReport
ID string
BaseLoc string
Type SignType
- Provider SignatureProvider
+ Valid bool
+ Error string
Signer string
- SignCert SignatureCertInfo
- SealCert SignatureCertInfo
- SealInfo SignatureSealInfo
- SealType string
- SignatureMethod string
SignatureDateTime string
SignatureTime time.Time
+ Provider SignatureProvider
+ SealInfo SignatureSealInfo
+ SealType string
+ SignCert SignatureCertInfo
+ SealCert SignatureCertInfo
+ SignatureMethod string
DigestMethod string
References []SignatureReferenceVerify
Stamps []SignatureStamp
@@ -82,25 +70,37 @@ type SignatureVerifyReport struct {
DataHashOK bool
SignedValueChecked bool
SignedValueOK bool
- SealChecked bool
- SealOK bool
- SealMatchChecked bool
- SealMatchOK bool
CertChecked bool
CertOK bool
- SignatureTimeChecked bool
- SignatureTimeOK bool
- SealCertTimeChecked bool
- SealCertTimeOK bool
- SealTimeChecked bool
- SealTimeOK bool
CertTimeChecked bool
CertTimeOK bool
CertTrustChecked bool
CertTrustOK bool
CertTrustError string
- Valid bool
- Error string
+ SignatureTimeChecked bool
+ SignatureTimeOK bool
+ SealChecked bool
+ SealOK bool
+ SealMatchChecked bool
+ SealMatchOK bool
+ SealTimeChecked bool
+ SealTimeOK bool
+ SealCertTimeChecked bool
+ SealCertTimeOK bool
+ CoverageChecked bool
+ CoverageOK bool
+ CoverageError string
+ CoveredFiles []string
+ UncoveredFiles []string
+ PolicyChecked bool
+ PolicyOK bool
+ PolicyError string
+ TimestampChecked bool
+ TimestampOK bool
+ Timestamps []SignatureTimestampReport
+ RevocationChecked bool
+ RevocationOK bool
+ Revocations []SignatureRevocationReport
}
// IntegrityValid 判断签名完整性是否有效