mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
This commit is contained in:
1 parent
3927929822
commit
e3908c1149
8 files changed
+317
-145
No files matched your search
@@ -353,8 +353,9 @@
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="signPanel" class="form-dialog security-dialog" aria-label="签章">
|
||||
<dialog id="signPanel" class="form-dialog security-dialog" aria-labelledby="signTitle">
|
||||
<form id="signForm" autocomplete="off">
|
||||
<h2 id="signTitle" class="form-title">签署文档</h2>
|
||||
<div class="form-row"><label for="signCertificate">证书</label><input id="signCertificate" type="file" accept=".pem,.cer,.crt,.der" required></div>
|
||||
<div class="form-row"><label for="signKey">私钥</label><input id="signKey" type="file" accept=".pem,.key,.der" required></div>
|
||||
<div class="form-row"><label for="signKeyPassword">私钥口令</label><input id="signKeyPassword" type="password" autocomplete="off"></div>
|
||||
@@ -374,8 +375,9 @@
|
||||
<div class="form-actions"><div id="signStatus" class="form-status" role="status" aria-live="polite"></div><button id="signCancel" class="button" type="button">取消</button><button class="button" type="submit" title="签署另存">签署</button></div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="verifyPanel" class="form-dialog security-dialog" aria-label="验签">
|
||||
<dialog id="verifyPanel" class="form-dialog security-dialog" aria-labelledby="verifyTitle">
|
||||
<form id="verifyForm">
|
||||
<h2 id="verifyTitle" class="form-title">验证签名</h2>
|
||||
<div class="form-row"><label for="verifyRoots">信任根</label><input id="verifyRoots" type="file" accept=".pem,.cer,.crt,.der" multiple></div>
|
||||
<div class="form-row"><label for="verifyCerts">证书</label><input id="verifyCerts" type="file" accept=".pem,.cer,.crt,.der" multiple></div>
|
||||
<div class="form-row"><label for="verifyTimeRoots">时间戳根</label><input id="verifyTimeRoots" type="file" accept=".pem,.cer,.crt,.der" multiple></div>
|
||||
@@ -386,8 +388,9 @@
|
||||
<div class="form-actions"><div id="verifyStatus" class="form-status" role="status" aria-live="polite"></div><button id="verifyCancel" class="button" type="button">取消</button><button class="button" type="submit">验签</button></div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="credentialsPanel" class="form-dialog security-dialog" aria-label="解锁文档">
|
||||
<dialog id="credentialsPanel" class="form-dialog security-dialog" aria-labelledby="credentialsTitle">
|
||||
<form id="credentialsForm" autocomplete="off">
|
||||
<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 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>
|
||||
@@ -397,8 +400,9 @@
|
||||
<div class="form-actions"><div id="credentialsStatus" class="form-status" role="status" aria-live="polite"></div><button id="credentialsCancel" class="button" type="button">取消</button><button class="button" type="submit">解锁</button></div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="exportPanel" class="form-dialog" aria-label="导出文档">
|
||||
<dialog id="exportPanel" class="form-dialog" aria-labelledby="exportTitle">
|
||||
<form id="exportForm">
|
||||
<h2 id="exportTitle" class="form-title">导出文档</h2>
|
||||
<div id="encryptionFields" hidden>
|
||||
<div class="form-row"><label for="encryptionType">方式</label><select id="encryptionType"><option value="password">口令</option><option value="certificate">证书</option></select></div>
|
||||
<div id="encryptionUserRow" class="form-row"><label for="encryptionUser">用户</label><input id="encryptionUser" type="text" autocomplete="off" spellcheck="false"></div>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+21
-20
@@ -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);
|
||||
}
|
||||
|
||||
+45
-2
@@ -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 错误信息
|
||||
|
||||
+86
-86
@@ -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{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+32
-32
@@ -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 判断签名完整性是否有效
|
||||
|
||||
Reference in new issue
Block a user