mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
This commit is contained in:
1 parent
d6f70de7be
commit
3927929822
30 files changed
+4707
-1161
No files matched your search
+309
-12
@@ -20,6 +20,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -91,6 +92,7 @@ var copiedStyle *ofdgo.GraphicObject
|
||||
// apiResult 浏览器接口返回结果
|
||||
type apiResult struct {
|
||||
OK bool `json:"ok"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Data any `json:"data,omitempty"`
|
||||
MissingGlyphs *ofdgo.MissingGlyphError `json:"missingGlyphs,omitempty"`
|
||||
@@ -102,6 +104,7 @@ func RunWASM() {
|
||||
registerCallback("ofdgoOpen", openDocument)
|
||||
registerCallback("ofdgoConfigure", configureDocument)
|
||||
registerCallback("ofdgoDocumentInfo", documentInfo)
|
||||
registerCallback("ofdgoVerifySignatures", verifySignatures)
|
||||
registerCallback("ofdgoRenderPage", renderPage)
|
||||
registerCallback("ofdgoRenderBackends", renderBackends)
|
||||
registerCallback("ofdgoSVGFontData", svgFontData)
|
||||
@@ -171,6 +174,8 @@ func RunWASM() {
|
||||
registerCallback("ofdgoUndo", func([]js.Value) (any, error) { return restoreEditor(false) })
|
||||
registerCallback("ofdgoRedo", func([]js.Value) (any, error) { return restoreEditor(true) })
|
||||
registerAsyncCallback("ofdgoSaveDocument", saveDocument)
|
||||
registerAsyncCallback("ofdgoSaveEncrypted", saveEncrypted)
|
||||
registerAsyncCallback("ofdgoSaveSigned", saveSigned)
|
||||
select {}
|
||||
}
|
||||
|
||||
@@ -214,6 +219,16 @@ func callbackResult(fn func([]js.Value) (any, error), args []js.Value) any {
|
||||
data, err := safeCall(fn, args)
|
||||
if err != nil {
|
||||
result := apiResult{Error: err.Error()}
|
||||
switch {
|
||||
case errors.Is(err, ofdgo.ErrCredentialsRequired):
|
||||
result.Code, result.Error = "credentialsRequired", "文档已加密"
|
||||
case errors.Is(err, ofdgo.ErrInvalidCredentials):
|
||||
result.Code, result.Error = "invalidCredentials", "凭据无效或文件损坏"
|
||||
case errors.Is(err, ofdgo.ErrUnsupportedEncryption):
|
||||
result.Code, result.Error = "unsupportedEncryption", "不支持此加密方式"
|
||||
case errors.Is(err, ofdgo.ErrEncryptionPolicyRequired):
|
||||
result.Code, result.Error = "encryptionPolicyRequired", "请设置加密方式"
|
||||
}
|
||||
var failure *ofdgo.EditError
|
||||
if errors.As(err, &failure) {
|
||||
result.ReasonCode = failure.Code
|
||||
@@ -257,6 +272,19 @@ func openDocument(args []js.Value) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
renderAnnotations := args[2].Bool()
|
||||
options := OpenOptions{Fonts: fonts, RenderAnnotations: renderAnnotations}
|
||||
if len(args) > 3 && !args[3].IsNull() && !args[3].IsUndefined() {
|
||||
credentials, err := credentialsFromJS(args[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clear(credentials.Password)
|
||||
options.ReaderOptions = append(options.ReaderOptions, ofdgo.WithCredentials(credentials))
|
||||
}
|
||||
session, err := Open(data, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clearImport()
|
||||
currentEditor = nil
|
||||
copiedObjects = nil
|
||||
@@ -265,14 +293,49 @@ func openDocument(args []js.Value) (any, error) {
|
||||
_ = currentSession.Close()
|
||||
currentSession = nil
|
||||
}
|
||||
session, err := Open(data, OpenOptions{Fonts: fonts, RenderAnnotations: renderAnnotations})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentSession = session
|
||||
return currentSession.Summary(), nil
|
||||
}
|
||||
|
||||
// credentialsFromJS 解析本次打开的口令或本机证书私钥
|
||||
// 入参: value 解锁表单数据
|
||||
// 返回: ofdgo.Credentials 会话凭据, error 错误信息
|
||||
func credentialsFromJS(value js.Value) (ofdgo.Credentials, error) {
|
||||
credentials := ofdgo.Credentials{UserName: value.Get("userName").String()}
|
||||
if keyValue := value.Get("key"); !keyValue.IsNull() && !keyValue.IsUndefined() {
|
||||
key, err := bytesFromJS(keyValue)
|
||||
if err != nil {
|
||||
return credentials, err
|
||||
}
|
||||
defer clear(key)
|
||||
certificate, err := bytesFromJS(value.Get("certificate"))
|
||||
if err != nil {
|
||||
return credentials, err
|
||||
}
|
||||
var password []byte
|
||||
if field := value.Get("keyPassword"); !field.IsUndefined() && !field.IsNull() {
|
||||
password, err = bytesFromJS(field)
|
||||
if err != nil {
|
||||
return credentials, err
|
||||
}
|
||||
defer clear(password)
|
||||
}
|
||||
signer, certificate, err := ofdgo.ParseSignatureIdentity(certificate, key, password)
|
||||
if err != nil {
|
||||
return credentials, ofdgo.ErrInvalidCredentials
|
||||
}
|
||||
decrypter, ok := signer.(crypto.Decrypter)
|
||||
if !ok {
|
||||
return credentials, ofdgo.ErrInvalidCredentials
|
||||
}
|
||||
credentials.Certificate, credentials.Decrypter = certificate, decrypter
|
||||
return credentials, nil
|
||||
}
|
||||
var err error
|
||||
credentials.Password, err = bytesFromJS(value.Get("password"))
|
||||
return credentials, err
|
||||
}
|
||||
|
||||
// configureDocument 更新当前会话的字体和注解设置
|
||||
// 入参: args 浏览器参数
|
||||
// 返回: any 文档信息, error 错误信息
|
||||
@@ -315,6 +378,70 @@ func documentInfo(args []js.Value) (any, error) {
|
||||
return currentSession.Info(), nil
|
||||
}
|
||||
|
||||
// securityFilesFromJS 读取会话内的本机证据文件
|
||||
// 入参: value 二进制数组列表
|
||||
// 返回: [][]byte 文件内容, error 错误信息
|
||||
func securityFilesFromJS(value js.Value) ([][]byte, error) {
|
||||
if value.IsUndefined() || value.IsNull() {
|
||||
return nil, nil
|
||||
}
|
||||
files := make([][]byte, value.Length())
|
||||
for i := range files {
|
||||
var err error
|
||||
files[i], err = bytesFromJS(value.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// verifySignatures 按本机信任锚和离线证据重新验签,不设置联网回调
|
||||
// 入参: args 信任根、辅助证书、时间戳及撤销证据
|
||||
// 返回: any 分项报告, error 错误信息
|
||||
func verifySignatures(args []js.Value) (any, error) {
|
||||
if currentSession == nil || len(args) == 0 {
|
||||
return nil, fmt.Errorf("missing signature verification arguments")
|
||||
}
|
||||
input := args[0]
|
||||
files := make(map[string][][]byte)
|
||||
for _, name := range []string{"roots", "certificates", "timestampRoots", "tokens", "crls", "ocsp"} {
|
||||
data, err := securityFilesFromJS(input.Get(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[name] = data
|
||||
}
|
||||
now := time.Now()
|
||||
policy := ofdgo.SignaturePolicy{RejectWeakAlgorithms: true, RequireDocumentCoverage: true,
|
||||
RequireTimestamp: input.Get("requireTimestamp").Bool(), RequireRevocation: input.Get("requireRevocation").Bool()}
|
||||
options := []ofdgo.SignatureVerifyOption{
|
||||
ofdgo.WithSignatureVerifyTime(now), ofdgo.WithSignaturePolicy(policy),
|
||||
ofdgo.WithSignatureTrustCerts(files["roots"]...), ofdgo.WithSignatureCerts(files["certificates"]...),
|
||||
}
|
||||
if policy.RequireTimestamp || len(files["timestampRoots"])+len(files["tokens"]) > 0 {
|
||||
options = append(options, ofdgo.WithSignatureTimestamp(ofdgo.SignatureTimestampOptions{
|
||||
TrustCerts: files["timestampRoots"], Certificates: files["certificates"], Tokens: files["tokens"], Required: policy.RequireTimestamp, VerifyTime: &now,
|
||||
}))
|
||||
}
|
||||
if policy.RequireRevocation || len(files["crls"])+len(files["ocsp"]) > 0 {
|
||||
options = append(options, ofdgo.WithSignatureRevocation(ofdgo.SignatureRevocationOptions{
|
||||
SignatureRevocationEvidence: ofdgo.SignatureRevocationEvidence{CRLs: files["crls"], OCSPResponses: files["ocsp"]},
|
||||
Certificates: append(files["certificates"], files["roots"]...), Required: policy.RequireRevocation, VerifyTime: &now,
|
||||
}))
|
||||
}
|
||||
reports, err := currentSession.Reader.VerifySignatures(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos := make([]SignatureInfo, len(reports))
|
||||
for i, report := range reports {
|
||||
infos[i] = signatureInfo(report)
|
||||
}
|
||||
currentSession.signatures, currentSession.signatureError, currentSession.signaturesRead = infos, nil, true
|
||||
return map[string]any{"signatures": infos, "signatureCount": len(infos), "signatureError": ""}, nil
|
||||
}
|
||||
|
||||
// exportAttachment 分块导出附件
|
||||
// 入参: args 浏览器参数
|
||||
// 返回: any 附件数据, error 错误信息
|
||||
@@ -2686,7 +2813,16 @@ func loadImport(args []js.Value) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader, err := ofdgo.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
var options []ofdgo.ReaderOption
|
||||
if len(args) > 1 && !args[1].IsNull() && !args[1].IsUndefined() {
|
||||
credentials, err := credentialsFromJS(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clear(credentials.Password)
|
||||
options = append(options, ofdgo.WithCredentials(credentials))
|
||||
}
|
||||
reader, err := ofdgo.NewReader(bytes.NewReader(data), int64(len(data)), options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2700,7 +2836,7 @@ func loadImport(args []js.Value) (any, error) {
|
||||
return nil, fmt.Errorf("document has no pages")
|
||||
}
|
||||
pendingImport = reader
|
||||
return map[string]any{"pageCount": len(doc.Pages.Page), "signed": doc.Signatures != ""}, nil
|
||||
return map[string]any{"pageCount": len(doc.Pages.Page), "signed": doc.Signatures != "", "encrypted": reader.Encryption().Encrypted}, nil
|
||||
}
|
||||
|
||||
// importPages 插入指定来源页面并保持目标文档元数据
|
||||
@@ -2727,8 +2863,13 @@ func importPages(args []js.Value) (any, error) {
|
||||
}
|
||||
at := args[1].Int()
|
||||
options := ofdgo.PageImportOptions{Outlines: len(args) > 2 && args[2].Bool()}
|
||||
if len(args) > 3 {
|
||||
options.OnProgress = editorOperationProgress(args[3])
|
||||
progressIndex := 3
|
||||
if len(args) > 3 && args[3].Type() == js.TypeBoolean {
|
||||
options.AllowDecrypted = args[3].Bool()
|
||||
progressIndex = 4
|
||||
}
|
||||
if len(args) > progressIndex {
|
||||
options.OnProgress = editorOperationProgress(args[progressIndex])
|
||||
}
|
||||
if _, err := currentEditor.ImportPagesWithOptions(pendingImport, indexes, at, options); err != nil {
|
||||
return nil, err
|
||||
@@ -3454,16 +3595,172 @@ func saveDocument(args []js.Value) (any, error) {
|
||||
if currentEditor == nil {
|
||||
return nil, fmt.Errorf("no document is being edited")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
currentEditor.OnWriteProgress = editorOperationProgress(args[1])
|
||||
return saveEditor(currentEditor, args)
|
||||
}
|
||||
|
||||
// saveEncrypted 以独立快照加密另存,不修改当前文档的输出策略
|
||||
// 入参: args 加密选项、写出回调、进度回调和可选页面索引
|
||||
// 返回: any 保存结果, error 错误信息
|
||||
func saveEncrypted(args []js.Value) (any, error) {
|
||||
if currentSession == nil || len(args) < 3 {
|
||||
return nil, fmt.Errorf("missing encrypted save arguments")
|
||||
}
|
||||
password, err := bytesFromJS(args[0].Get("password"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clear(password)
|
||||
options := &ofdgo.EncryptionOptions{Password: password, UserName: args[0].Get("userName").String()}
|
||||
if recipients := args[0].Get("recipients"); !recipients.IsUndefined() && !recipients.IsNull() {
|
||||
for i := 0; i < recipients.Length(); i++ {
|
||||
item := recipients.Index(i)
|
||||
data, err := bytesFromJS(item.Get("certificate"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificate, err := ofdgo.ParseEncryptionCertificate(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("接收者证书无效或不支持SM2加密")
|
||||
}
|
||||
options.Recipients = append(options.Recipients, ofdgo.EncryptionRecipient{UserName: item.Get("userName").String(), Certificate: certificate})
|
||||
}
|
||||
}
|
||||
if len(password) == 0 && len(options.Recipients) == 0 {
|
||||
return nil, ofdgo.ErrEncryptionPolicyRequired
|
||||
}
|
||||
reader := currentSession.Reader
|
||||
if currentEditor != nil {
|
||||
reader, err = currentEditor.Reader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
}
|
||||
editor, err := reader.Editor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := editor.SetEncryption(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return saveEditor(editor, args[1:])
|
||||
}
|
||||
|
||||
// saveSigned 调用库层最终包签署并在回验成功后交付浏览器
|
||||
// 入参: args 本机证书私钥、信任根、印章位置、写出回调及进度回调
|
||||
// 返回: any 保存结果, error 错误信息
|
||||
func saveSigned(args []js.Value) (any, error) {
|
||||
if currentSession == nil || len(args) < 3 {
|
||||
return nil, fmt.Errorf("missing signature arguments")
|
||||
}
|
||||
input := args[0]
|
||||
key, err := bytesFromJS(input.Get("key"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clear(key)
|
||||
certificate, err := bytesFromJS(input.Get("certificate"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var password []byte
|
||||
if field := input.Get("keyPassword"); !field.IsUndefined() && !field.IsNull() {
|
||||
password, err = bytesFromJS(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clear(password)
|
||||
}
|
||||
signer, certificate, err := ofdgo.ParseSignatureIdentity(certificate, key, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("证书、私钥或口令无效")
|
||||
}
|
||||
roots, err := securityFilesFromJS(input.Get("roots"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intermediates, err := securityFilesFromJS(input.Get("intermediates"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options := ofdgo.SignatureWriteOptions{Signer: signer, Certificate: certificate, TrustRoots: roots,
|
||||
Intermediates: intermediates, Lock: input.Get("lock").Bool()}
|
||||
switch input.Get("mode").String() {
|
||||
case "append":
|
||||
options.Mode = ofdgo.SignatureAppend
|
||||
case "replace":
|
||||
options.Mode = ofdgo.SignatureReplace
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid signature mode")
|
||||
}
|
||||
if seal := input.Get("seal"); !seal.IsNull() && !seal.IsUndefined() {
|
||||
options.Seal, err = bytesFromJS(seal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count, err := currentSession.Reader.PageCount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indices, err := ofdgo.ParsePageRange(input.Get("pages").String(), count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
box := ofdgo.Box{X: input.Get("x").Float(), Y: input.Get("y").Float(), W: input.Get("width").Float(), H: input.Get("height").Float()}
|
||||
if input.Get("placement").String() == "seam" {
|
||||
pages := make([]int, len(indices))
|
||||
for i, index := range indices {
|
||||
pages[i] = index + 1
|
||||
}
|
||||
options.Stamps, err = currentSession.Reader.SignatureSeamStamps(pages, box)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
for _, index := range indices {
|
||||
options.Stamps = append(options.Stamps, ofdgo.SignatureStamp{PageRef: currentSession.doc.Pages.Page[index].ID,
|
||||
Boundary: fmt.Sprintf("%g %g %g %g", box.X, box.Y, box.W, box.H)})
|
||||
}
|
||||
}
|
||||
}
|
||||
progress := editorOperationProgress(args[2])
|
||||
if err := progress("sign", 0, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output bytes.Buffer
|
||||
if currentEditor != nil {
|
||||
currentEditor.OnWriteProgress = progress
|
||||
defer func() { currentEditor.OnWriteProgress = nil }()
|
||||
_, err = ofdgo.SignEditorTo(&output, currentEditor, options)
|
||||
} else {
|
||||
_, err = currentSession.Reader.SignTo(&output, options)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := progress("sign", 1, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := (exportWriter{write: args[1]}).Write(output.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return successResult(map[string]any{"mime": "application/ofd", "label": "OFD"}), nil
|
||||
}
|
||||
|
||||
// saveEditor 统一分块写出及进度取消,加密策略由库层执行
|
||||
// 入参: editor 待写出文档, args 写出回调、进度回调和可选页面索引
|
||||
// 返回: any 保存结果, error 错误信息
|
||||
func saveEditor(editor *ofdgo.Editor, args []js.Value) (any, error) {
|
||||
if len(args) > 1 {
|
||||
editor.OnWriteProgress = editorOperationProgress(args[1])
|
||||
defer func() { editor.OnWriteProgress = nil }()
|
||||
}
|
||||
writer := bufio.NewWriterSize(exportWriter{write: args[0]}, 1<<20)
|
||||
var err error
|
||||
if len(args) > 2 {
|
||||
_, err = currentEditor.WritePagesTo(writer, indexesFromJS(args[2]))
|
||||
_, err = editor.WritePagesTo(writer, indexesFromJS(args[2]))
|
||||
} else {
|
||||
_, err = currentEditor.WriteTo(writer)
|
||||
_, err = editor.WriteTo(writer)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+115
-7
@@ -30,6 +30,7 @@ import (
|
||||
type OpenOptions struct {
|
||||
Fonts []FontFile
|
||||
RenderAnnotations bool
|
||||
ReaderOptions []ofdgo.ReaderOption
|
||||
}
|
||||
|
||||
// FontFile 字体文件
|
||||
@@ -40,6 +41,23 @@ type FontInfo = ofdgo.FontInfo
|
||||
|
||||
// SignatureInfo 签名验证信息
|
||||
type SignatureInfo struct {
|
||||
DocIndex int `json:"docIndex"`
|
||||
DocRoot string `json:"docRoot,omitempty"`
|
||||
IntegrityValid bool `json:"integrityValid"`
|
||||
TrustedValid bool `json:"trustedValid"`
|
||||
PolicyChecked bool `json:"policyChecked"`
|
||||
PolicyOK bool `json:"policyOK"`
|
||||
PolicyError string `json:"policyError,omitempty"`
|
||||
CoverageChecked bool `json:"coverageChecked"`
|
||||
CoverageOK bool `json:"coverageOK"`
|
||||
CoverageError string `json:"coverageError,omitempty"`
|
||||
UncoveredFiles []string `json:"uncoveredFiles,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"`
|
||||
@@ -72,6 +90,7 @@ type SignatureInfo struct {
|
||||
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"`
|
||||
@@ -85,6 +104,34 @@ type SignatureInfo struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// TimestampInfo 独立展示时间戳绑定、签名、信任和证书时效
|
||||
type TimestampInfo struct {
|
||||
Time string `json:"time,omitempty"`
|
||||
Valid bool `json:"valid"`
|
||||
BindingChecked bool `json:"bindingChecked"`
|
||||
BindingOK bool `json:"bindingOK"`
|
||||
SignedValueChecked bool `json:"signedValueChecked"`
|
||||
SignedValueOK bool `json:"signedValueOK"`
|
||||
CertTrustChecked bool `json:"certTrustChecked"`
|
||||
CertTrustOK bool `json:"certTrustOK"`
|
||||
CertTimeChecked bool `json:"certTimeChecked"`
|
||||
CertTimeOK bool `json:"certTimeOK"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// RevocationInfo 仅表示签者或制章证书的离线撤销状态
|
||||
type RevocationInfo struct {
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Checked bool `json:"checked"`
|
||||
OK bool `json:"ok"`
|
||||
ThisUpdate string `json:"thisUpdate,omitempty"`
|
||||
NextUpdate string `json:"nextUpdate,omitempty"`
|
||||
RevokedAt string `json:"revokedAt,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// SignatureStampInfo 签名外观信息
|
||||
type SignatureStampInfo struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
@@ -119,6 +166,7 @@ type Session struct {
|
||||
|
||||
// DocumentInfo 文档信息
|
||||
type DocumentInfo struct {
|
||||
Encryption EncryptionInfo `json:"encryption"`
|
||||
Version string `json:"version"`
|
||||
DocType string `json:"docType"`
|
||||
Title string `json:"title"`
|
||||
@@ -139,6 +187,21 @@ type DocumentInfo struct {
|
||||
DetailsPending bool `json:"detailsPending,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptionInfo 仅向界面传递非敏感加密状态
|
||||
type EncryptionInfo struct {
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Users []string `json:"users,omitempty"`
|
||||
Layers int `json:"layers,omitempty"`
|
||||
}
|
||||
|
||||
// encryptionInfo 获取当前文档的加密来源,不包含凭据
|
||||
// 返回: EncryptionInfo 加密状态
|
||||
func (s *Session) encryptionInfo() EncryptionInfo {
|
||||
info := s.Reader.Encryption()
|
||||
return EncryptionInfo{Encrypted: info.Encrypted, Method: info.Method, Users: info.Users, Layers: info.Layers}
|
||||
}
|
||||
|
||||
// OutlineInfo 目录节点信息
|
||||
type OutlineInfo = ofdgo.OutlineInfo
|
||||
|
||||
@@ -222,7 +285,7 @@ func Open(data []byte, opts OpenOptions) (*Session, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("empty ofd data")
|
||||
}
|
||||
reader, err := ofdgo.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
reader, err := ofdgo.NewReader(bytes.NewReader(data), int64(len(data)), opts.ReaderOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -381,6 +444,7 @@ func (s *Session) SearchPage(index int, query string) ([]ofdgo.TextMatch, error)
|
||||
// 返回: DocumentInfo 文档信息
|
||||
func (s *Session) Summary() DocumentInfo {
|
||||
info := DocumentInfo{
|
||||
Encryption: s.encryptionInfo(),
|
||||
Version: s.Reader.Version(),
|
||||
DocType: s.Reader.DocType(),
|
||||
PageCount: len(s.doc.Pages.Page),
|
||||
@@ -425,11 +489,12 @@ func (s *Session) Summary() DocumentInfo {
|
||||
// 返回: DocumentInfo 文档信息
|
||||
func (s *Session) Info() DocumentInfo {
|
||||
info := DocumentInfo{
|
||||
Version: s.Reader.Version(),
|
||||
DocType: s.Reader.DocType(),
|
||||
PageCount: len(s.doc.Pages.Page),
|
||||
Pages: make([]PageInfo, 0, len(s.doc.Pages.Page)),
|
||||
Outlines: s.doc.OutlineInfos(),
|
||||
Encryption: s.encryptionInfo(),
|
||||
Version: s.Reader.Version(),
|
||||
DocType: s.Reader.DocType(),
|
||||
PageCount: len(s.doc.Pages.Page),
|
||||
Pages: make([]PageInfo, 0, len(s.doc.Pages.Page)),
|
||||
Outlines: s.doc.OutlineInfos(),
|
||||
}
|
||||
if attachments, err := s.Reader.Attachments(); err == nil {
|
||||
for _, attachment := range attachments {
|
||||
@@ -613,7 +678,22 @@ func signatureInfo(report ofdgo.SignatureVerifyReport) SignatureInfo {
|
||||
dateTime = report.SignatureTime.Format(time.RFC3339Nano)
|
||||
}
|
||||
checked, passed := signatureReferenceCounts(report.References)
|
||||
return 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,
|
||||
@@ -646,6 +726,7 @@ func signatureInfo(report ofdgo.SignatureVerifyReport) SignatureInfo {
|
||||
CertTimeOK: report.CertTimeOK,
|
||||
CertTrustChecked: report.CertTrustChecked,
|
||||
CertTrustOK: report.CertTrustOK,
|
||||
CertTrustError: report.CertTrustError,
|
||||
ReferenceCount: len(report.References),
|
||||
ReferenceChecked: checked,
|
||||
ReferencePassed: passed,
|
||||
@@ -658,6 +739,33 @@ func signatureInfo(report ofdgo.SignatureVerifyReport) SignatureInfo {
|
||||
Stamps: signatureStampInfos(report.StampPositions),
|
||||
Error: signatureReportError(report),
|
||||
}
|
||||
for _, stamp := range report.Timestamps {
|
||||
info.Timestamps = append(info.Timestamps, TimestampInfo{
|
||||
Time: securityTime(stamp.Time), Valid: stamp.Valid,
|
||||
BindingChecked: stamp.BindingChecked, BindingOK: stamp.BindingOK,
|
||||
SignedValueChecked: stamp.SignedValueChecked, SignedValueOK: stamp.SignedValueOK,
|
||||
CertTrustChecked: stamp.CertTrustChecked, CertTrustOK: stamp.CertTrustOK,
|
||||
CertTimeChecked: stamp.CertTimeChecked, CertTimeOK: stamp.CertTimeOK, Error: stamp.Error,
|
||||
})
|
||||
}
|
||||
for _, revocation := range report.Revocations {
|
||||
info.Revocations = append(info.Revocations, RevocationInfo{
|
||||
Subject: revocation.Certificate.Subject, Source: revocation.Source, Status: revocation.Status,
|
||||
Checked: revocation.Checked, OK: revocation.OK, ThisUpdate: securityTime(revocation.ThisUpdate),
|
||||
NextUpdate: securityTime(revocation.NextUpdate), RevokedAt: securityTime(revocation.RevokedAt), Error: revocation.Error,
|
||||
})
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// securityTime 格式化已提供的安全证据时间,零值不展示
|
||||
// 入参: value 时间
|
||||
// 返回: string 标准时间文本
|
||||
func securityTime(value time.Time) string {
|
||||
if value.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return value.Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
// signatureSigner 获取签名人名称
|
||||
|
||||
Reference in new issue
Block a user