mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
1 parent
341ad7cd7e
commit
53769b65ab
4 files changed
+143
-48
No files matched your search
+73
-24
@@ -56,6 +56,7 @@ type editorPageImport struct {
|
||||
defaultCS string
|
||||
attachments map[string][]byte
|
||||
attachmentIDs map[string]bool
|
||||
signatureIDs *signatureIDSequence
|
||||
copyPage bool
|
||||
strictLinks bool
|
||||
progress editorProgress
|
||||
@@ -961,8 +962,7 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
|
||||
if name == "Attachment" {
|
||||
value, err = m.attachmentID(value)
|
||||
} else if name == "Signature" || name == "StampAnnot" {
|
||||
value = name + ":" + value
|
||||
value = m.id(value)
|
||||
value, err = m.signatureID(entry.name, name, value)
|
||||
} else {
|
||||
value = m.id(value)
|
||||
}
|
||||
@@ -984,7 +984,11 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
|
||||
} else {
|
||||
directory := "Res"
|
||||
if name == "Signature" {
|
||||
directory = path.Join("Signs", "Sign_"+m.id("Signature:"+node.attr("ID")))
|
||||
id, idErr := m.signatureID(entry.name, name, node.attr("ID"))
|
||||
if idErr != nil {
|
||||
return nil, idErr
|
||||
}
|
||||
directory = path.Join("Signs", "Sign_"+id)
|
||||
}
|
||||
value, err = m.copyXML(loc, path.Join(m.directory, directory, path.Base(loc)))
|
||||
}
|
||||
@@ -1236,6 +1240,59 @@ func (m *editorPageImport) attachmentID(id string) (string, error) {
|
||||
return mapped, nil
|
||||
}
|
||||
|
||||
// signatureID 分配导入签章的文本标识,同名签章注释按来源文件区分
|
||||
// 入参: source 来源文件, kind 节点类型, id 来源标识
|
||||
// 返回: string 目标标识, error 签章索引或编号错误
|
||||
func (m *editorPageImport) signatureID(source, kind, id string) (string, error) {
|
||||
key := kind + ":" + id
|
||||
if kind == "StampAnnot" {
|
||||
key = kind + ":" + source + ":" + id
|
||||
}
|
||||
if mapped := m.ids[key]; mapped != "" {
|
||||
return mapped, nil
|
||||
}
|
||||
if m.signatureIDs == nil {
|
||||
var sequence signatureIDSequence
|
||||
doc, err := m.target.Doc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if doc.Signatures != "" {
|
||||
name := m.target.ResPath(doc.Signatures)
|
||||
data, err := m.target.readFile(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var list Signatures
|
||||
if err := xml.Unmarshal(data, &list); err != nil {
|
||||
return "", err
|
||||
}
|
||||
sequence.observe(list.MaxSignID)
|
||||
for _, ref := range list.List {
|
||||
sequence.observe(ref.ID)
|
||||
data, err := m.target.readFile(signatureRefPath(name, ref.BaseLoc))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
file, err := parseSignatureFile(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, stamp := range file.SignedInfo.StampAnnot {
|
||||
sequence.observe(stamp.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.signatureIDs = &sequence
|
||||
}
|
||||
mapped, err := m.signatureIDs.next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m.ids[key] = mapped
|
||||
return mapped, nil
|
||||
}
|
||||
|
||||
// signatures 复制所选页面的签章及原始验证凭据,过滤其他页面的签章位置
|
||||
// 返回: []byte 签名索引条目, error 错误信息
|
||||
func (m *editorPageImport) signatures() ([]byte, error) {
|
||||
@@ -1401,6 +1458,19 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexName := cleanPackagePath(loc)
|
||||
indexData := files[indexName]
|
||||
indexRoot, err := parseEditorXML(indexData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maximum := "s" + strconv.FormatUint(uint64(*m.signatureIDs), 10)
|
||||
value := indexRoot.textXML("MaxSignId", maximum)
|
||||
patch := editorXMLPatch{indexRoot.open, indexRoot.open, value}
|
||||
if node := indexRoot.child("MaxSignId"); node != nil {
|
||||
patch = editorXMLPatch{node.start, node.end, value}
|
||||
}
|
||||
files[indexName] = editorPatchXML(indexData, []editorXMLPatch{patch})
|
||||
if base.document.Signatures == "" {
|
||||
ofd, err := base.reader.readFile("OFD.xml")
|
||||
if err != nil {
|
||||
@@ -1483,27 +1553,6 @@ func mergeImportIndex(reader *Reader, files map[string][]byte, loc, fallback, ro
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rootName == "Signatures" {
|
||||
root, err := parseEditorXML(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
maximum := 0
|
||||
for _, node := range root.children {
|
||||
value := node.attr("ID")
|
||||
if node.name.Local == "MaxSignId" {
|
||||
value = editorImportText(data, node)
|
||||
}
|
||||
id, _ := strconv.Atoi(value)
|
||||
maximum = max(maximum, id)
|
||||
}
|
||||
value := root.textXML("MaxSignId", strconv.Itoa(maximum))
|
||||
patch := editorXMLPatch{root.open, root.open, value}
|
||||
if node := root.child("MaxSignId"); node != nil {
|
||||
patch = editorXMLPatch{node.start, node.end, value}
|
||||
}
|
||||
data = editorPatchXML(data, []editorXMLPatch{patch})
|
||||
}
|
||||
files[name] = data
|
||||
return "/" + name, nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ofdgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// signatureIDSequence 签名及签章注释共用的编号上限,与文档图元编号相互独立
|
||||
type signatureIDSequence uint64
|
||||
|
||||
// observe 记录已有签章标识的编号上限,其他命名形式保持不变
|
||||
// 入参: id 已有标识
|
||||
func (s *signatureIDSequence) observe(id string) {
|
||||
n, err := strconv.ParseUint(strings.TrimPrefix(id, "s"), 10, 64)
|
||||
if err == nil && n > uint64(*s) {
|
||||
*s = signatureIDSequence(n)
|
||||
}
|
||||
}
|
||||
|
||||
// next 分配采用sNNN格式的签章标识
|
||||
// 返回: string 新标识, error 编号耗尽错误
|
||||
func (s *signatureIDSequence) next() (string, error) {
|
||||
if uint64(*s) == ^uint64(0) {
|
||||
return "", fmt.Errorf("signature ID exhausted")
|
||||
}
|
||||
*s++
|
||||
return "s" + strconv.FormatUint(uint64(*s), 10), nil
|
||||
}
|
||||
+6
-19
@@ -471,20 +471,14 @@ func signatureWritePackage(parts map[string][]byte, options SignatureWriteOption
|
||||
// 返回: string 签名标识, []SignatureStamp 外观, error 错误信息
|
||||
func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byte, stamps []SignatureStamp) (string, []SignatureStamp, error) {
|
||||
used := make(map[string]bool)
|
||||
var maximum uint64
|
||||
track := func(id string) {
|
||||
n, err := strconv.ParseUint(strings.TrimPrefix(id, "s"), 10, 64)
|
||||
if err == nil && n > maximum {
|
||||
maximum = n
|
||||
}
|
||||
}
|
||||
track(list.MaxSignID)
|
||||
var sequence signatureIDSequence
|
||||
sequence.observe(list.MaxSignID)
|
||||
add := func(id string) error {
|
||||
if !signatureWriteIDValid(id) || used[id] {
|
||||
return fmt.Errorf("invalid or duplicate signature ID: %s", id)
|
||||
}
|
||||
used[id] = true
|
||||
track(id)
|
||||
sequence.observe(id)
|
||||
return nil
|
||||
}
|
||||
for _, ref := range list.List {
|
||||
@@ -508,26 +502,19 @@ func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byt
|
||||
}
|
||||
}
|
||||
}
|
||||
next := func() (string, error) {
|
||||
if maximum == ^uint64(0) {
|
||||
return "", fmt.Errorf("signature ID exhausted")
|
||||
}
|
||||
maximum++
|
||||
return "s" + strconv.FormatUint(maximum, 10), nil
|
||||
}
|
||||
id, err := next()
|
||||
id, err := sequence.next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
for i := range stamps {
|
||||
if stamps[i].ID == "" {
|
||||
stamps[i].ID, err = next()
|
||||
stamps[i].ID, err = sequence.next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
list.MaxSignID = "s" + strconv.FormatUint(maximum, 10)
|
||||
list.MaxSignID = "s" + strconv.FormatUint(uint64(sequence), 10)
|
||||
return id, stamps, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// SignaturePageStamps 生成所选页面相同毫米坐标的印章位置并检查页内范围
|
||||
@@ -142,13 +142,29 @@ func signatureWriteStamps(r *Reader, stamps []SignatureStamp, seal bool) ([]Sign
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// signatureWriteIDValid 检查可互通的签章标识字符
|
||||
// signatureWriteIDValid 按XML 1.0的NCName规则校验签章标识
|
||||
// 入参: id 标识
|
||||
// 返回: bool 是否有效
|
||||
func signatureWriteIDValid(id string) bool {
|
||||
return id != "" && len(id) <= 128 && strings.IndexFunc(id, func(c rune) bool {
|
||||
return !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.')
|
||||
}) == -1
|
||||
if id == "" || !utf8.ValidString(id) {
|
||||
return false
|
||||
}
|
||||
for i, c := range id {
|
||||
if c == '_' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' ||
|
||||
c >= 0xC0 && c <= 0xD6 || c >= 0xD8 && c <= 0xF6 ||
|
||||
c >= 0xF8 && c <= 0x2FF || c >= 0x370 && c <= 0x37D ||
|
||||
c >= 0x37F && c <= 0x1FFF || c >= 0x200C && c <= 0x200D ||
|
||||
c >= 0x2070 && c <= 0x218F || c >= 0x2C00 && c <= 0x2FEF ||
|
||||
c >= 0x3001 && c <= 0xD7FF || c >= 0xF900 && c <= 0xFDCF ||
|
||||
c >= 0xFDF0 && c <= 0xFFFD || c >= 0x10000 && c <= 0xEFFFF {
|
||||
continue
|
||||
}
|
||||
if i == 0 || !(c == '-' || c == '.' || c >= '0' && c <= '9' ||
|
||||
c == 0xB7 || c >= 0x300 && c <= 0x36F || c >= 0x203F && c <= 0x2040) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// signatureWriteBoxValid 判断印章区域是否有限且尺寸为正
|
||||
|
||||
Reference in new issue
Block a user