mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +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
|
defaultCS string
|
||||||
attachments map[string][]byte
|
attachments map[string][]byte
|
||||||
attachmentIDs map[string]bool
|
attachmentIDs map[string]bool
|
||||||
|
signatureIDs *signatureIDSequence
|
||||||
copyPage bool
|
copyPage bool
|
||||||
strictLinks bool
|
strictLinks bool
|
||||||
progress editorProgress
|
progress editorProgress
|
||||||
@@ -961,8 +962,7 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
|
|||||||
if name == "Attachment" {
|
if name == "Attachment" {
|
||||||
value, err = m.attachmentID(value)
|
value, err = m.attachmentID(value)
|
||||||
} else if name == "Signature" || name == "StampAnnot" {
|
} else if name == "Signature" || name == "StampAnnot" {
|
||||||
value = name + ":" + value
|
value, err = m.signatureID(entry.name, name, value)
|
||||||
value = m.id(value)
|
|
||||||
} else {
|
} else {
|
||||||
value = m.id(value)
|
value = m.id(value)
|
||||||
}
|
}
|
||||||
@@ -984,7 +984,11 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
|
|||||||
} else {
|
} else {
|
||||||
directory := "Res"
|
directory := "Res"
|
||||||
if name == "Signature" {
|
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)))
|
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
|
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 复制所选页面的签章及原始验证凭据,过滤其他页面的签章位置
|
// signatures 复制所选页面的签章及原始验证凭据,过滤其他页面的签章位置
|
||||||
// 返回: []byte 签名索引条目, error 错误信息
|
// 返回: []byte 签名索引条目, error 错误信息
|
||||||
func (m *editorPageImport) 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 == "" {
|
if base.document.Signatures == "" {
|
||||||
ofd, err := base.reader.readFile("OFD.xml")
|
ofd, err := base.reader.readFile("OFD.xml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1483,27 +1553,6 @@ func mergeImportIndex(reader *Reader, files map[string][]byte, loc, fallback, ro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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
|
files[name] = data
|
||||||
return "/" + name, nil
|
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 错误信息
|
// 返回: string 签名标识, []SignatureStamp 外观, error 错误信息
|
||||||
func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byte, stamps []SignatureStamp) (string, []SignatureStamp, error) {
|
func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byte, stamps []SignatureStamp) (string, []SignatureStamp, error) {
|
||||||
used := make(map[string]bool)
|
used := make(map[string]bool)
|
||||||
var maximum uint64
|
var sequence signatureIDSequence
|
||||||
track := func(id string) {
|
sequence.observe(list.MaxSignID)
|
||||||
n, err := strconv.ParseUint(strings.TrimPrefix(id, "s"), 10, 64)
|
|
||||||
if err == nil && n > maximum {
|
|
||||||
maximum = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
track(list.MaxSignID)
|
|
||||||
add := func(id string) error {
|
add := func(id string) error {
|
||||||
if !signatureWriteIDValid(id) || used[id] {
|
if !signatureWriteIDValid(id) || used[id] {
|
||||||
return fmt.Errorf("invalid or duplicate signature ID: %s", id)
|
return fmt.Errorf("invalid or duplicate signature ID: %s", id)
|
||||||
}
|
}
|
||||||
used[id] = true
|
used[id] = true
|
||||||
track(id)
|
sequence.observe(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, ref := range list.List {
|
for _, ref := range list.List {
|
||||||
@@ -508,26 +502,19 @@ func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next := func() (string, error) {
|
id, err := sequence.next()
|
||||||
if maximum == ^uint64(0) {
|
|
||||||
return "", fmt.Errorf("signature ID exhausted")
|
|
||||||
}
|
|
||||||
maximum++
|
|
||||||
return "s" + strconv.FormatUint(maximum, 10), nil
|
|
||||||
}
|
|
||||||
id, err := next()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
for i := range stamps {
|
for i := range stamps {
|
||||||
if stamps[i].ID == "" {
|
if stamps[i].ID == "" {
|
||||||
stamps[i].ID, err = next()
|
stamps[i].ID, err = sequence.next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.MaxSignID = "s" + strconv.FormatUint(maximum, 10)
|
list.MaxSignID = "s" + strconv.FormatUint(uint64(sequence), 10)
|
||||||
return id, stamps, nil
|
return id, stamps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignaturePageStamps 生成所选页面相同毫米坐标的印章位置并检查页内范围
|
// SignaturePageStamps 生成所选页面相同毫米坐标的印章位置并检查页内范围
|
||||||
@@ -142,13 +142,29 @@ func signatureWriteStamps(r *Reader, stamps []SignatureStamp, seal bool) ([]Sign
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// signatureWriteIDValid 检查可互通的签章标识字符
|
// signatureWriteIDValid 按XML 1.0的NCName规则校验签章标识
|
||||||
// 入参: id 标识
|
// 入参: id 标识
|
||||||
// 返回: bool 是否有效
|
// 返回: bool 是否有效
|
||||||
func signatureWriteIDValid(id string) bool {
|
func signatureWriteIDValid(id string) bool {
|
||||||
return id != "" && len(id) <= 128 && strings.IndexFunc(id, func(c rune) bool {
|
if id == "" || !utf8.ValidString(id) {
|
||||||
return !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.')
|
return false
|
||||||
}) == -1
|
}
|
||||||
|
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 判断印章区域是否有限且尺寸为正
|
// signatureWriteBoxValid 判断印章区域是否有限且尺寸为正
|
||||||
|
|||||||
Reference in new issue
Block a user