feat(标准版本): 修复裁剪字形并完善版本兼容
build.yaml / build (push) Successful in 47s

This commit is contained in:
xiaoqidun committed 2026-10-09 23:15:06 +08:00
1 parent 444c01b976
commit e9caaa4193
31 files changed
+639 -182

No files matched your search

+1 -1
View File
@@ -233,7 +233,7 @@ func (r *Reader) compressionImageSafety(ctx context.Context, images []ImageInfo)
break
}
if element, ok := token.(xml.StartElement); ok {
if element.Name.Space != "" && element.Name.Space != ofdNamespace && element.Name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(element.Name.Space) == ofdXMLUnknown {
safe = false
break
}
+2 -2
View File
@@ -272,7 +272,7 @@ func (r *Reader) decryptPackage(settings readerOptions) error {
func decodeEncryptionEntries(data, key, iv []byte, provider CryptoProvider) (encryptionEntries, error) {
var entries encryptionEntries
valid := func() bool {
return entries.XMLName.Space == ofdNamespace && (entries.XMLName.Local == "EncryptEntries" || entries.XMLName.Local == "EncryptedEntries")
return entries.XMLName.Space == ofdNamespace2016 && (entries.XMLName.Local == "EncryptEntries" || entries.XMLName.Local == "EncryptedEntries")
}
if err := xml.Unmarshal(data, &entries); err == nil && valid() {
return entries, nil
@@ -468,7 +468,7 @@ func encryptPackageParts(parts map[string][]byte, options EncryptionOptions, pro
seed.Users = append(seed.Users, encryptionUser{Name: recipient.UserName, Role: recipient.UserType, Certificate: base64.StdEncoding.EncodeToString(certificate), Key: base64.StdEncoding.EncodeToString(wrapped), IV: base64.StdEncoding.EncodeToString(iv)})
}
}
entries := encryptionEntries{XMLName: xml.Name{Space: ofdNamespace, Local: "EncryptEntries"}, ID: id}
entries := encryptionEntries{XMLName: xml.Name{Space: ofdNamespace2016, Local: "EncryptEntries"}, ID: id}
cipherParts := make(map[string][]byte, len(parts)+3)
for i, name := range slices.Sorted(maps.Keys(parts)) {
if err := progress.report("encrypt", i, len(parts)); err != nil {
+1 -1
View File
@@ -199,7 +199,7 @@ func (e *Editor) Page(index int) (*PageContent, error) {
return nil, err
}
result := cloneEditorData(*page)
result.XMLName = xml.Name{Space: ofdNamespace, Local: "Page"}
result.XMLName = xml.Name{Space: ofdNamespace2016, Local: "Page"}
for i := range result.Content.Layer {
layer := &result.Content.Layer[i]
layer.TextObject, layer.PathObject, layer.ImageObject, layer.CompositeGraphicUnit = nil, nil, nil, nil
+6
View File
@@ -62,8 +62,10 @@ func (e *Editor) SetDocumentActions(actions []Action) error {
return err
}
patch := editorXMLPatch{root.close, root.close, encoded}
context := root
if node := root.child("Actions"); node != nil {
patch.start, patch.end = node.start, node.end
context = node
} else {
for _, child := range root.children {
if packageOFDNode(child, child.name.Local) && slices.Contains([]string{"VPreferences", "Bookmarks", "Annotations", "CustomTags", "Attachments", "Extensions"}, child.name.Local) {
@@ -72,6 +74,10 @@ func (e *Editor) SetDocumentActions(actions []Action) error {
}
}
}
patch.data, err = editorXMLGenerated(encoded, context.name.Space)
if err != nil {
return err
}
return e.commitAnnotationParts(base, map[string][]byte{name: editorPatchXML(data, []editorXMLPatch{patch})})
}
+6 -3
View File
@@ -69,9 +69,9 @@ func (e *Editor) UpdateAnnotation(index int, id, remark, creator string) error {
if child := root.child("Remark"); child != nil {
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, child, escaped.Bytes())})
} else if root.open == root.end {
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, root, editorXMLText("Remark", remark))})
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, root, root.textXML("Remark", remark))})
} else {
data = editorPatchXML(data, []editorXMLPatch{{root.open, root.open, editorXMLText("Remark", remark)}})
data = editorPatchXML(data, []editorXMLPatch{{root.open, root.open, root.textXML("Remark", remark)}})
}
}
root, err = parseEditorXML(data)
@@ -280,7 +280,10 @@ func (e *Editor) editAnnotations(index int, ids []string, edit func([]byte, *edi
if err != nil {
return err
}
parts[docName] = editorXMLSetText(docData, doc, [][2]string{{"Annotations", ""}})
parts[docName], err = editorXMLSetText(docData, doc, [][2]string{{"Annotations", ""}})
if err != nil {
return err
}
}
}
if len(parts) == 0 {
+11 -5
View File
@@ -38,7 +38,7 @@ func (e *Editor) AddAnnotation(index int, annotation Annotation) (string, error)
if err != nil {
return err
}
return next.appendAnnotations(index, data)
return next.appendAnnotations(index, data, true)
})
if err != nil {
return "", err
@@ -211,9 +211,9 @@ func (e *Editor) annotationXML(index int, id string) ([]byte, error) {
}
// appendAnnotations 在独立页面注解文件末尾追加原文,保留原索引及未知内容
// 入参: index 页面索引, annotations 标准注解XML片段
// 入参: index 页面索引, annotations 注解XML片段, generated 是否为自产内容
// 返回: error 错误信息
func (e *Editor) appendAnnotations(index int, annotations []byte) error {
func (e *Editor) appendAnnotations(index int, annotations []byte, generated bool) error {
base := e.source
var err error
if base == nil {
@@ -271,6 +271,12 @@ func (e *Editor) appendAnnotations(index int, annotations []byte) error {
return err
}
added := bytes.TrimPrefix(annotations, []byte(xml.Header))
if generated {
added, err = editorXMLGenerated(added, page.name.Space)
if err != nil {
return err
}
}
if page.open == page.end {
content = editorPatchXML(content, []editorXMLPatch{editorXMLContent(content, page, added)})
} else {
@@ -300,7 +306,7 @@ func (e *Editor) appendAnnotations(index int, annotations []byte) error {
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, loc, value.Bytes())})
}
} else {
entry, err := editorXMLContainer("Page", ofdAttrs{{Name: xml.Name{Local: "PageID"}, Value: e.pages[index].ID}}, editorXMLText("FileLoc", "/"+file))
entry, err := root.containerXML("Page", ofdAttrs{{Name: xml.Name{Local: "PageID"}, Value: e.pages[index].ID}}, root.textXML("FileLoc", "/"+file))
if err != nil {
return err
}
@@ -329,7 +335,7 @@ func (e *Editor) appendAnnotations(index int, annotations []byte) error {
break
}
}
patch := editorXMLPatch{position, position, editorXMLText("Annotations", "/"+name)}
patch := editorXMLPatch{position, position, doc.textXML("Annotations", "/"+name)}
if node := doc.child("Annotations"); node != nil {
var value bytes.Buffer
_ = xml.EscapeText(&value, []byte("/"+name))
+2 -2
View File
@@ -134,7 +134,7 @@ func (e *Editor) PasteAnnotations(page int, selection *AnnotationSelection, dx,
content = append(content, bytes.TrimPrefix(data, []byte(xml.Header))...)
result = append(result, id)
}
if err := edit.appendAnnotations(page, content); err != nil {
if err := edit.appendAnnotations(page, content, false); err != nil {
return err
}
edit.source.annotationStates = states
@@ -160,7 +160,7 @@ func (e *Editor) PasteAnnotations(page int, selection *AnnotationSelection, dx,
var added []byte
for _, name := range selection.resources {
if !registered[name] {
added = append(added, editorXMLText("DocumentRes", "/"+name)...)
added = append(added, common.textXML("DocumentRes", "/"+name)...)
registered[name] = true
}
}
+11 -3
View File
@@ -86,12 +86,20 @@ func (e *Editor) AddAttachment(name string, data []byte) (string, error) {
if err != nil {
return "", err
}
entry, err = editorXMLGenerated(entry, root.name.Space)
if err != nil {
return "", err
}
if root.open == root.end {
parts[name] = editorPatchXML(existing, []editorXMLPatch{editorXMLContent(existing, root, entry)})
} else {
parts[name] = editorPatchXML(existing, []editorXMLPatch{{root.close, root.close, entry}})
}
} else if node != nil {
entry, err = editorXMLGenerated(entry, node.name.Space)
if err != nil {
return "", err
}
if node.open == node.end {
parts[docName] = editorPatchXML(docData, []editorXMLPatch{editorXMLContent(docData, node, entry)})
} else {
@@ -108,7 +116,7 @@ func (e *Editor) AddAttachment(name string, data []byte) (string, error) {
if next := doc.child("Extensions"); next != nil {
position = next.start
}
parts[docName] = editorPatchXML(docData, []editorXMLPatch{{position, position, editorXMLText("Attachments", "/"+name)}})
parts[docName] = editorPatchXML(docData, []editorXMLPatch{{position, position, doc.textXML("Attachments", "/"+name)}})
}
if err := e.commitAnnotationParts(base, parts); err != nil {
return "", err
@@ -151,9 +159,9 @@ func (e *Editor) ReplaceAttachment(id string, data []byte) error {
if child := node.child("FileLoc"); child != nil {
fragment = editorPatchXML(fragment, []editorXMLPatch{editorXMLContent(fragment, child, location.Bytes())})
} else if node.open == node.end {
fragment = editorPatchXML(fragment, []editorXMLPatch{editorXMLContent(fragment, node, editorXMLText("FileLoc", "/"+file))})
fragment = editorPatchXML(fragment, []editorXMLPatch{editorXMLContent(fragment, node, node.textXML("FileLoc", "/"+file))})
} else {
fragment = editorPatchXML(fragment, []editorXMLPatch{{node.open, node.open, editorXMLText("FileLoc", "/"+file)}})
fragment = editorPatchXML(fragment, []editorXMLPatch{{node.open, node.open, node.textXML("FileLoc", "/"+file)}})
}
root, err := parseEditorXML(fragment)
if err != nil {
+2 -2
View File
@@ -543,7 +543,7 @@ func (e *Editor) compositeMembers(n *editorCompositeNode, reader *Reader, render
var collect func(*editorXML) error
collect = func(container *editorXML) error {
for _, child := range container.children {
if child.name.Space != "" && child.name.Space != ofdNamespace && child.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(child.name.Space) == ofdXMLUnknown {
continue
}
switch child.name.Local {
@@ -1055,7 +1055,7 @@ func editorResourceReferences(data []byte) ([]string, error) {
refs := make(map[string]bool)
var collect func(*editorXML)
collect = func(node *editorXML) {
if node.name.Space == "" || node.name.Space == ofdNamespace || node.name.Space == "http://www.ofdspec.org" {
if classifyOFDNamespace(node.name.Space) != ofdXMLUnknown {
if node.name.Local == "Thumbnail" || node.name.Local == "Substitution" {
if id := editorResourceID(editorImportText(data, node)); id != "" {
refs[id] = true
+3 -3
View File
@@ -347,7 +347,7 @@ func (n *editorCompositeNode) preserveActionRegions(matrix Matrix) (bool, error)
}
var targets []*editorXML
for _, action := range actions.children {
if action.name.Local != "Action" || action.name.Space != "" && action.name.Space != actions.name.Space && action.name.Space != ofdNamespace {
if action.name.Local != "Action" || action.name.Space != "" && action.name.Space != actions.name.Space && action.name.Space != ofdNamespace2016 {
continue
}
if action.attr("Event") == "CLICK" && action.child("Region") == nil {
@@ -425,7 +425,7 @@ func (n *editorCompositeNode) convertCoordinates(parent Matrix, inward bool) err
}
}
for _, child := range node.children {
if child.name.Space != "" && child.name.Space != ofdNamespace && child.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(child.name.Space) == ofdXMLUnknown {
continue
}
switch child.name.Local {
@@ -487,7 +487,7 @@ func (n *editorCompositeNode) loadedScope(path []int) *editorCompositeNode {
func compositeContainers(node *editorXML) []*editorXML {
var result []*editorXML
for _, child := range node.children {
if (child.name.Local == "Content" || child.name.Local == "PageBlock") && (child.name.Space == "" || child.name.Space == ofdNamespace || child.name.Space == "http://www.ofdspec.org") {
if (child.name.Local == "Content" || child.name.Local == "PageBlock") && classifyOFDNamespace(child.name.Space) != ofdXMLUnknown {
result = append(result, child)
}
}
+8 -2
View File
@@ -204,11 +204,17 @@ func appendCompositeClip(node *editorCompositeNode, shape PathObject, matrix Mat
}
var patch editorXMLPatch
if original := node.node.child("Clips"); original != nil {
clip := editorXMLEncodedFragment(data, root.children[0])
clip, err := editorXMLEncodedFragment(data, root.children[0], original.name.Space)
if err != nil {
return err
}
content := append(bytes.Clone(node.data[original.open:original.close]), clip...)
patch = editorXMLContent(node.data, original, content)
} else {
data = editorXMLEncodedFragment(data, root)
data, err = editorXMLEncodedFragment(data, root, node.node.name.Space)
if err != nil {
return err
}
if actions := node.node.child("Actions"); actions != nil {
patch = editorXMLPatch{actions.end, actions.end, data}
} else {
+1 -1
View File
@@ -135,7 +135,7 @@ func editorXMLRemapIDs(data []byte, root *editorXML, ids map[string]string) ([]b
var patches []editorXMLPatch
var visit func(*editorXML) error
visit = func(node *editorXML) error {
if node.name.Space == "" || node.name.Space == ofdNamespace || node.name.Space == "http://www.ofdspec.org" {
if classifyOFDNamespace(node.name.Space) != ofdXMLUnknown {
decoder := xml.NewDecoder(bytes.NewReader(data[node.start:node.open]))
token, err := decoder.RawToken()
if err != nil {
+1 -1
View File
@@ -43,7 +43,7 @@ func (e *Editor) drawParamXML(id string) ([]byte, error) {
}
if params := root.child("DrawParams"); params != nil {
for _, node := range params.children {
if node.name.Local == "DrawParam" && node.attr("ID") == id && (node.name.Space == "" || node.name.Space == ofdNamespace || node.name.Space == "http://www.ofdspec.org") {
if node.name.Local == "DrawParam" && node.attr("ID") == id && classifyOFDNamespace(node.name.Space) != ofdXMLUnknown {
return editorXMLStandalone(data[node.start:node.end], node)
}
}
+1 -1
View File
@@ -202,7 +202,7 @@ func editorUngroupable(node *editorXML) bool {
return false
}
for _, member := range content.children {
if member.name.Space != "" && member.name.Space != ofdNamespace && member.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(member.name.Space) == ofdXMLUnknown {
return false
}
if member.name.Local == "PageBlock" {
+59 -11
View File
@@ -644,6 +644,10 @@ func (e *Editor) appendImportedOutlines(imported []byte) ([]byte, error) {
if err != nil {
return nil, err
}
value, err = editorXMLGenerated(value, root.name.Space)
if err != nil {
return nil, err
}
content = append(content, value...)
}
preview := *e
@@ -882,11 +886,11 @@ func editorImportText(data []byte, node *editorXML) string {
return value.Text
}
// encode 编码标准OFD节点,保留文字数据和几何值,仅改写资源、标识及路径
// encode 编码标准OFD节点,重映射引用并将旧式裁剪换算到标准坐标
// 入参: entry 来源上下文, node 当前节点
// 返回: []byte 独立XML片段, error 错误信息
func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]byte, error) {
if node.name.Space != "" && node.name.Space != ofdNamespace && node.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(node.name.Space) == ofdXMLUnknown {
return nil, fmt.Errorf("cannot remap extension namespace %q", node.name.Space)
}
name := node.name.Local
@@ -931,6 +935,9 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
continue
}
key, value := attr.Name.Local, attr.Value
if name == "Clips" && key == "TransFlag" {
continue
}
var err error
switch key {
case "ID":
@@ -974,6 +981,22 @@ func (m *editorPageImport) encode(entry editorImportEntry, node *editorXML) ([]b
}
attrs = append(attrs, xml.Attr{Name: xml.Name{Local: key}, Value: value})
}
if name == "Area" && node.parent != nil && node.parent.name.Local == "Clip" && node.parent.parent != nil && node.parent.parent.name.Local == "Clips" {
clips := node.parent.parent
transformed := classifyOFDNamespace(clips.name.Space) == ofdXMLLegacy
if clips.hasAttr("TransFlag") {
var err error
transformed, err = strconv.ParseBool(clips.attr("TransFlag"))
if err != nil {
return nil, err
}
}
if transformed && clips.parent != nil {
matrix := NewMatrix(clips.parent.attr("CTM")).Multiply(NewMatrix(node.attr("CTM")))
attrs = slices.DeleteFunc(attrs, func(attr xml.Attr) bool { return attr.Name.Space == "" && attr.Name.Local == "CTM" })
attrs.add("CTM", matrix.String())
}
}
if m.defaultCS != "" && (name == "FillColor" || name == "StrokeColor" || name == "Color") && node.attr("ColorSpace") == "" {
attrs = append(attrs, xml.Attr{Name: xml.Name{Local: "ColorSpace"}, Value: m.defaultCS})
}
@@ -1251,14 +1274,18 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
return nil, err
}
if added {
content = editorXMLText("DocumentRes", "/"+strings.TrimPrefix(name, "/"))
content = common.textXML("DocumentRes", "/"+strings.TrimPrefix(name, "/"))
}
}
for _, id := range slices.Sorted(maps.Keys(m.templateParts)) {
content = append(content, m.templateParts[id]...)
entry, err := editorXMLGenerated(m.templateParts[id], common.name.Space)
if err != nil {
return nil, err
}
content = append(content, entry...)
}
patches := []editorXMLPatch{{position, position, content}}
patch := editorXMLPatch{common.open, common.open, editorXMLText("MaxUnitID", strconv.Itoa(m.maximum))}
patch := editorXMLPatch{common.open, common.open, common.textXML("MaxUnitID", strconv.Itoa(m.maximum))}
if maximum := common.child("MaxUnitID"); maximum != nil {
patch = editorXMLContent(data, maximum, []byte(strconv.Itoa(m.maximum)))
}
@@ -1268,10 +1295,14 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
return nil, fmt.Errorf("document has no Pages")
}
if len(m.pageParts) != 0 {
entries, err := editorXMLGenerated(m.pageParts, pages.name.Space)
if err != nil {
return nil, err
}
if pages.open == pages.end {
patches = append(patches, editorXMLContent(data, pages, m.pageParts))
patches = append(patches, editorXMLContent(data, pages, entries))
} else {
patches = append(patches, editorXMLPatch{pages.close, pages.close, m.pageParts})
patches = append(patches, editorXMLPatch{pages.close, pages.close, entries})
}
}
data = editorPatchXML(data, patches)
@@ -1282,7 +1313,10 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
}
if base.document.Annotations == "" {
root, _ = parseEditorXML(data)
data = editorXMLSetText(data, root, [][2]string{{"Annotations", loc}})
data, err = editorXMLSetText(data, root, [][2]string{{"Annotations", loc}})
if err != nil {
return nil, err
}
}
}
if len(m.attachments) != 0 {
@@ -1293,6 +1327,10 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
root, _ = parseEditorXML(data)
node := root.child("Attachments")
if node != nil && len(node.children) != 0 {
entries, err = editorXMLGenerated(entries, node.name.Space)
if err != nil {
return nil, err
}
content := append(bytes.Clone(data[node.open:node.close]), entries...)
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, node, content)})
} else {
@@ -1301,7 +1339,10 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
return nil, err
}
if base.document.Attachments.Path == "" {
data = editorXMLSetText(data, root, [][2]string{{"Attachments", loc}})
data, err = editorXMLSetText(data, root, [][2]string{{"Attachments", loc}})
if err != nil {
return nil, err
}
}
}
}
@@ -1324,7 +1365,10 @@ func (m *editorPageImport) merge(base *editorSource, refs []Page, annotations, s
if file, ok := base.reader.packageFile(name); ok {
name = cleanPackagePath(file.Name)
}
files[name] = editorXMLSetText(ofd, root.childAt("DocBody", base.reader.documentIndex), [][2]string{{"Signatures", loc}})
files[name], err = editorXMLSetText(ofd, root.childAt("DocBody", base.reader.documentIndex), [][2]string{{"Signatures", loc}})
if err != nil {
return nil, err
}
}
}
reader := &Reader{Zip: base.reader.Zip, files: files, encryption: base.reader.encryption, documentIndex: base.reader.documentIndex}
@@ -1377,6 +1421,10 @@ func mergeImportIndex(reader *Reader, files map[string][]byte, loc, fallback, ro
if parseErr != nil {
return "", parseErr
}
entries, err = editorXMLGenerated(entries, root.name.Space)
if err != nil {
return "", err
}
if root.open == root.end {
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, root, entries)})
} else {
@@ -1400,7 +1448,7 @@ func mergeImportIndex(reader *Reader, files map[string][]byte, loc, fallback, ro
id, _ := strconv.Atoi(value)
maximum = max(maximum, id)
}
value := editorXMLText("MaxSignId", strconv.Itoa(maximum))
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}
+12 -1
View File
@@ -199,7 +199,10 @@ func (e *Editor) UpdateAnnotationLink(page int, id string, target AnnotationLink
}
if len(targets) == 1 {
n := targets[0]
updated := link
updated, err := editorXMLGenerated(link, n.name.Space)
if err != nil {
return nil, err
}
if n.name.Local == "URI" && target.URI != nil {
var old URI
if err := xml.Unmarshal(data[n.start:n.end], &old); err != nil {
@@ -261,12 +264,20 @@ func (e *Editor) UpdateAnnotationLink(page int, id string, target AnnotationLink
}
action = bytes.TrimPrefix(action, []byte(xml.Header))
if actions := objects[0].child("Actions"); actions != nil {
action, err = editorXMLGenerated(action, actions.name.Space)
if err != nil {
return nil, err
}
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, actions, append(bytes.Clone(data[actions.open:actions.close]), action...))})
} else {
actions, err := editorXMLContainer("Actions", nil, action)
if err != nil {
return nil, err
}
actions, err = editorXMLGenerated(actions, objects[0].name.Space)
if err != nil {
return nil, err
}
position := objects[0].open
data = editorPatchXML(data, []editorXMLPatch{{position, position, bytes.TrimPrefix(actions, []byte(xml.Header))}})
}
+25 -56
View File
@@ -18,7 +18,6 @@ import (
"bytes"
"encoding/xml"
"fmt"
"io"
"reflect"
)
@@ -51,7 +50,10 @@ func editorUpdateInfoXML(data []byte, docID string, index int, before, after Doc
values = append(values, [2]string{field[0], field[2]})
}
}
data = editorXMLSetText(data, info, values)
data, err = editorXMLSetText(data, info, values)
if err != nil {
return nil, err
}
if !reflect.DeepEqual(before.CustomDatas, after.CustomDatas) {
root, err := parseEditorXML(data)
if err != nil {
@@ -70,7 +72,7 @@ func editorCreatorXML(data []byte, docID string, index int) ([]byte, error) {
if err != nil {
return nil, err
}
return editorXMLSetText(data, info, [][2]string{{"Creator", ofdCreator}, {"CreatorVersion", ""}}), nil
return editorXMLSetText(data, info, [][2]string{{"Creator", ofdCreator}, {"CreatorVersion", ""}})
}
// editorDocInfoXML 获取文档描述,修改缺少描述的源文件时补建标准容器及标识
@@ -88,11 +90,15 @@ func editorDocInfoXML(data []byte, docID string, index int) ([]byte, *editorXML,
if info := body.child("DocInfo"); info != nil {
return data, info, nil
}
info, err := editorXMLContainer("DocInfo", nil, editorXMLText("DocID", docID))
info, err := body.containerXML("DocInfo", nil, body.textXML("DocID", docID))
if err != nil {
return nil, nil, err
}
data = editorPatchXML(data, []editorXMLPatch{{body.open, body.open, info}})
if body.open == body.end {
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, body, info)})
} else {
data = editorPatchXML(data, []editorXMLPatch{{body.open, body.open, info}})
}
root, err = parseEditorXML(data)
if err != nil {
return nil, nil, err
@@ -105,6 +111,10 @@ func editorDocInfoXML(data []byte, docID string, index int) ([]byte, *editorXML,
// 返回: []byte 修改后的XML, error 编码错误
func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byte, error) {
container := info.child("CustomDatas")
context := info
if container != nil {
context = container
}
var items []CustomData
if values != nil {
items = values.CustomData
@@ -113,7 +123,7 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
var nodes []*editorXML
if container != nil {
for _, node := range container.children {
if node.name.Local == "CustomData" && (node.name.Space == ofdNamespace || node.name.Space == container.name.Space) {
if node.name.Local == "CustomData" && (node.name.Space == ofdNamespace2016 || node.name.Space == container.name.Space) {
nodes = append(nodes, node)
byName[node.attr("Name")] = append(byName[node.attr("Name")], node)
}
@@ -140,10 +150,9 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
}
}
if node == nil {
entry, err := xml.Marshal(struct {
XMLName xml.Name
CustomData
}{xml.Name{Space: ofdNamespace, Local: "CustomData"}, item})
var value bytes.Buffer
_ = xml.EscapeText(&value, []byte(item.Value))
entry, err := context.containerXML("CustomData", ofdAttrs{{Name: xml.Name{Local: "Name"}, Value: item.Name}}, value.Bytes())
if err != nil {
return nil, err
}
@@ -163,14 +172,14 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
if err != nil {
return nil, err
}
updated, err := editorCustomDataValue(entry, renamed, original.Value, item.Value)
updated, err := editorXMLTextValue(entry, renamed, original.Value, item.Value)
if err != nil {
return nil, err
}
encoded = append(encoded, updated)
continue
}
entry, err := editorCustomDataValue(data, node, original.Value, item.Value)
entry, err := editorXMLTextValue(data, node, original.Value, item.Value)
if err != nil {
return nil, err
}
@@ -180,9 +189,10 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
if len(encoded) == 0 {
return data, nil
}
content := []byte("<ofd:CustomDatas xmlns:ofd=\"" + ofdNamespace + "\">")
content = append(content, bytes.Join(encoded, nil)...)
content = append(content, []byte("</ofd:CustomDatas>")...)
content, err := info.containerXML("CustomDatas", nil, bytes.Join(encoded, nil))
if err != nil {
return nil, err
}
if info.open == info.end {
return editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, info, content)}), nil
}
@@ -206,44 +216,3 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
}
return editorPatchXML(data, patches), nil
}
// editorCustomDataValue 修改字段文本,保留扩展子节点和注释
// 入参: data 原XML, node 字段节点, before 原值, after 新值
// 返回: []byte 字段XML, error 编码错误
func editorCustomDataValue(data []byte, node *editorXML, before, after string) ([]byte, error) {
if before == after {
return data[node.start:node.end], nil
}
var content bytes.Buffer
if err := xml.EscapeText(&content, []byte(after)); err != nil {
return nil, err
}
if node.open < node.close {
inner := data[node.open:node.close]
decoder := xml.NewDecoder(bytes.NewReader(inner))
depth := 0
for {
start := decoder.InputOffset()
token, err := decoder.RawToken()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
_, text := token.(xml.CharData)
if !text || depth != 0 {
content.Write(inner[start:decoder.InputOffset()])
}
switch token.(type) {
case xml.StartElement:
depth++
case xml.EndElement:
depth--
}
}
}
patch := editorXMLContent(data, node, content.Bytes())
entry := append(bytes.Clone(data[node.start:patch.start]), patch.data...)
return append(entry, data[patch.end:node.end]...), nil
}
+169
View File
@@ -0,0 +1,169 @@
// 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 (
"bytes"
"encoding/xml"
"strconv"
)
// textXML 按所在节点的命名空间编码文本节点
// 入参: name 节点名称, value 文本
// 返回: []byte XML片段
func (n *editorXML) textXML(name, value string) []byte {
var content bytes.Buffer
_ = xml.EscapeText(&content, []byte(value))
data, _ := n.containerXML(name, nil, content.Bytes())
return data
}
// containerXML 按所在节点的命名空间封装子节点,不改写子节点原文
// 入参: name 节点名称, attrs 属性, content 子节点XML
// 返回: []byte XML片段, error 编码错误
func (n *editorXML) containerXML(name string, attrs ofdAttrs, content []byte) ([]byte, error) {
var output bytes.Buffer
if n.name.Space == "" {
attrs = append(attrs, xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: ""})
} else {
name = "ofd:" + name
attrs.add("xmlns:ofd", n.name.Space)
}
value := struct {
Content []byte `xml:",innerxml"`
}{content}
err := xml.NewEncoder(&output).EncodeElement(value, xml.StartElement{Name: xml.Name{Local: name}, Attr: attrs})
return output.Bytes(), err
}
// editorXMLGenerated 将自产标准片段用于指定命名空间,已标准化的旧版裁剪保持2016语义
// 入参: data 自产XML片段,不含待保留的源文件原文, namespace 目标命名空间
// 返回: []byte XML片段, error 解析错误
func editorXMLGenerated(data []byte, namespace string) ([]byte, error) {
if namespace == ofdNamespace2016 || len(data) == 0 {
return data, nil
}
header := []byte(`<context xmlns:ofd="` + ofdNamespace2016 + `">`)
wrapped := append(bytes.Clone(header), bytes.TrimPrefix(data, []byte(xml.Header))...)
end := len(wrapped)
wrapped = append(wrapped, []byte(`</context>`)...)
root, err := parseEditorXML(wrapped)
if err != nil {
return nil, err
}
prefix := editorXMLNamespacePrefix(root)
var patches []editorXMLPatch
var visit func(*editorXML, bool) error
visit = func(node *editorXML, declare bool) error {
if node.name.Space != ofdNamespace2016 || namespace == ofdNamespaceLegacy && node.name.Local == "Clips" {
value, err := editorXMLStandalone(wrapped[node.start:node.end], node)
if err != nil {
return err
}
patches = append(patches, editorXMLPatch{node.start, node.end, value})
return nil
}
decoder := xml.NewDecoder(bytes.NewReader(wrapped[node.start:node.open]))
token, err := decoder.RawToken()
if err != nil {
return err
}
start := token.(xml.StartElement)
name := node.name.Local
if namespace != "" {
name = prefix + ":" + name
}
var output bytes.Buffer
output.WriteString("<" + name)
ofdDeclared := false
for _, attr := range start.Attr {
if attr.Name.Space == "xmlns" && attr.Name.Local == "ofd" {
ofdDeclared = true
}
if namespace == "" && attr.Name.Space == "" && attr.Name.Local == "xmlns" {
continue
}
key := attr.Name.Local
if attr.Name.Space != "" {
key = attr.Name.Space + ":" + key
}
output.WriteString(" " + key + `="`)
_ = xml.EscapeText(&output, []byte(attr.Value))
output.WriteByte('"')
}
if declare && !ofdDeclared {
output.WriteString(` xmlns:ofd="` + ofdNamespace2016 + `"`)
}
if declare || namespace == "" {
key := "xmlns"
if namespace != "" {
key = "xmlns:" + prefix
}
output.WriteString(" " + key + `="`)
_ = xml.EscapeText(&output, []byte(namespace))
output.WriteByte('"')
}
if node.open == node.end {
output.WriteString("/>")
} else {
output.WriteByte('>')
patches = append(patches, editorXMLPatch{node.close, node.end, []byte("</" + name + ">")})
}
patches = append(patches, editorXMLPatch{node.start, node.open, output.Bytes()})
for _, child := range node.children {
if err := visit(child, false); err != nil {
return err
}
}
return nil
}
for _, node := range root.children {
if err := visit(node, true); err != nil {
return nil, err
}
}
patches = append(patches, editorXMLPatch{0, len(header), nil}, editorXMLPatch{end, len(wrapped), nil})
return editorPatchXML(wrapped, patches), nil
}
// editorXMLNamespacePrefix 选择未被当前作用域及子树占用的命名空间前缀
// 入参: node 节点
// 返回: string 可用前缀
func editorXMLNamespacePrefix(node *editorXML) string {
used := make(map[string]bool)
collect := func(current *editorXML) {
for _, attr := range current.attrs {
if attr.Name.Space == "xmlns" {
used[attr.Name.Local] = true
}
}
}
for parent := node.parent; parent != nil; parent = parent.parent {
collect(parent)
}
var visit func(*editorXML)
visit = func(current *editorXML) {
collect(current)
for _, child := range current.children {
visit(child)
}
}
visit(node)
prefix := "ofd"
for index := 1; used[prefix]; index++ {
prefix = "ofd" + strconv.Itoa(index)
}
return prefix
}
+16 -6
View File
@@ -68,6 +68,10 @@ func (e *Editor) AddOutline(parent []int, title string, page int) ([]int, error)
if err != nil {
return nil, err
}
item, err = editorXMLGenerated(item, node.name.Space)
if err != nil {
return nil, err
}
var content []byte
if node.open != node.end {
content = bytes.Clone(data[node.open:node.close])
@@ -115,7 +119,7 @@ func (e *Editor) UpdateOutline(path []int, title string, page int) error {
if actions != nil {
var removals []editorXMLPatch
for _, action := range actions.children {
if action.name.Space != actions.name.Space && action.name.Space != ofdNamespace && action.name.Space != "" {
if action.name.Space != actions.name.Space && action.name.Space != ofdNamespace2016 && action.name.Space != "" {
continue
}
if action.name.Local == "Action" && action.child("Goto") != nil {
@@ -126,7 +130,10 @@ func (e *Editor) UpdateOutline(path []int, title string, page int) error {
if actions.open != actions.end {
content = editorPatchXML(data[actions.open:actions.close], removals)
}
added := e.outlineAction(page)
added, err := editorXMLGenerated(e.outlineAction(page), actions.name.Space)
if err != nil {
return err
}
if len(added) > 0 {
addRoot, err := parseEditorXML(added)
if err != nil {
@@ -141,8 +148,10 @@ func (e *Editor) UpdateOutline(path []int, title string, page int) error {
}
patches = append(patches, editorXMLContent(data, actions, content))
} else if page >= 0 {
var content []byte
content = append(content, e.outlineAction(page)...)
content, err := editorXMLGenerated(e.outlineAction(page), node.name.Space)
if err != nil {
return err
}
if node.open != node.end {
content = append(content, data[node.open:node.close]...)
}
@@ -275,6 +284,7 @@ func (e *Editor) outlineXML() ([]byte, error) {
if node := root.child("Outlines"); node != nil {
return editorXMLStandalone(data[node.start:node.end], node)
}
return root.containerXML("Outlines", nil, nil)
}
return editorXMLContainer("Outlines", nil, nil)
}
@@ -305,7 +315,7 @@ func (e *Editor) outlineAction(page int) []byte {
func editorOutlineChildren(node *editorXML) []*editorXML {
var children []*editorXML
for _, child := range node.children {
if child.name.Local == "OutlineElem" && (child.name.Space == node.name.Space || child.name.Space == ofdNamespace || child.name.Space == "") {
if child.name.Local == "OutlineElem" && (child.name.Space == node.name.Space || child.name.Space == ofdNamespace2016 || child.name.Space == "") {
children = append(children, child)
}
}
@@ -415,7 +425,7 @@ func (e *Editor) withOutlines(data []byte) ([]byte, error) {
walk = func(node *editorXML) {
if actions := node.child("Actions"); actions != nil {
for _, action := range actions.children {
if action.name.Local != "Action" || action.name.Space != actions.name.Space && action.name.Space != ofdNamespace && action.name.Space != "" {
if action.name.Local != "Action" || action.name.Space != actions.name.Space && action.name.Space != ofdNamespace2016 && action.name.Space != "" {
continue
}
if target := action.child("Goto"); target != nil {
+42 -20
View File
@@ -149,8 +149,9 @@ func (e *Editor) sourcePartsPrepared(progress editorProgress, generated map[stri
}
var patches []editorXMLPatch
if e.maxID != source.document.CommonData.MaxUnitID {
value := editorXMLText("MaxUnitID", strconv.Itoa(e.maxID))
value := common.textXML("MaxUnitID", strconv.Itoa(e.maxID))
if maxID := common.child("MaxUnitID"); maxID != nil {
value = maxID.textXML("MaxUnitID", strconv.Itoa(e.maxID))
patches = append(patches, editorXMLPatch{maxID.start, maxID.end, value})
} else {
patches = append(patches, editorXMLPatch{common.open, common.open, value})
@@ -166,7 +167,7 @@ func (e *Editor) sourcePartsPrepared(progress editorProgress, generated map[stri
}
var references []byte
for _, name := range resourceFiles {
references = append(references, editorXMLText("DocumentRes", "/"+name)...)
references = append(references, common.textXML("DocumentRes", "/"+name)...)
}
patches = append(patches, editorXMLPatch{position, position, references})
}
@@ -190,10 +191,7 @@ func (e *Editor) sourcePartsPrepared(progress editorProgress, generated map[stri
if original != nil {
entries = append(entries, data[original.start:original.end])
} else {
item, err := e.encodeXML(func(x *ofdXML) {
x.root("Page", ofdAttrs{{Name: xml.Name{Local: "ID"}, Value: page.ID}, {Name: xml.Name{Local: "BaseLoc"}, Value: page.BaseLoc}})
x.end("Page")
})
item, err := pages.containerXML("Page", ofdAttrs{{Name: xml.Name{Local: "ID"}, Value: page.ID}, {Name: xml.Name{Local: "BaseLoc"}, Value: page.BaseLoc}}, nil)
if err != nil {
return nil, err
}
@@ -308,17 +306,24 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
if page.Area.PhysicalBox != source.original.Area.PhysicalBox {
area := source.root.child("Area")
if area != nil {
areaData := data[area.start:area.end]
areaData, err := editorXMLStandalone(data[area.start:area.end], area)
if err != nil {
return nil, err
}
areaRoot, err := parseEditorXML(areaData)
if err != nil {
return nil, err
}
updated := editorXMLSetText(areaData, areaRoot, [][2]string{{"PhysicalBox", page.Area.PhysicalBox}})
updated, err := editorXMLSetText(areaData, areaRoot, [][2]string{{"PhysicalBox", page.Area.PhysicalBox}})
if err != nil {
return nil, err
}
patches = append(patches, editorXMLPatch{area.start, area.end, updated})
} else {
value := []byte("<ofd:Area xmlns:ofd=\"" + ofdNamespace + "\">")
value = append(value, editorXMLText("PhysicalBox", page.Area.PhysicalBox)...)
value = append(value, []byte("</ofd:Area>")...)
value, err := source.root.containerXML("Area", nil, source.root.textXML("PhysicalBox", page.Area.PhysicalBox))
if err != nil {
return nil, err
}
patches = append(patches, editorXMLPatch{source.root.open, source.root.open, value})
}
}
@@ -400,11 +405,15 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
}
}
var added []byte
context := source.root.child("Content")
if context == nil {
context = source.root
}
for _, layer := range page.Content.Layer[len(source.original.Content.Layer):] {
if len(layer.Objects) == 0 {
continue
}
encoded, err := e.sourceLayerXML(layer)
encoded, err := e.sourceLayerXML(layer, context.name.Space)
if err != nil {
return nil, err
}
@@ -413,9 +422,10 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
if len(added) != 0 {
content := source.root.child("Content")
if content == nil {
value := []byte("<ofd:Content xmlns:ofd=\"" + ofdNamespace + "\">")
value = append(value, added...)
value = append(value, []byte("</ofd:Content>")...)
value, err := context.containerXML("Content", nil, added)
if err != nil {
return nil, err
}
position := source.root.close
if actions := source.root.child("Actions"); actions != nil {
position = actions.start
@@ -433,8 +443,14 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
return nil, err
}
patch := editorXMLPatch{source.root.close, source.root.close, encoded}
context := source.root
if node := source.root.child("Actions"); node != nil {
patch.start, patch.end = node.start, node.end
context = node
}
patch.data, err = editorXMLGenerated(encoded, context.name.Space)
if err != nil {
return nil, err
}
patches = append(patches, patch)
}
@@ -442,9 +458,9 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
}
// sourceLayerXML 写出新增图层,对复制对象保留原始定位和显式默认值
// 入参: layer 图层
// 入参: layer 图层, namespace 所在容器的命名空间
// 返回: []byte 图层XML, error 错误信息
func (e *Editor) sourceLayerXML(layer Layer) ([]byte, error) {
func (e *Editor) sourceLayerXML(layer Layer, namespace string) ([]byte, error) {
if !slices.ContainsFunc(layer.Objects, func(object GraphicObject) bool { return e.objectOrigin(editorObjectID(object)) != nil }) {
data, err := e.encodeXML(func(x *ofdXML) {
attrs := ofdAttrs{{Name: xml.Name{Local: "ID"}, Value: layer.ID}, {Name: xml.Name{Local: "Type"}, Value: layer.Type}}
@@ -458,7 +474,10 @@ func (e *Editor) sourceLayerXML(layer Layer) ([]byte, error) {
}
x.end("Layer")
})
return bytes.TrimPrefix(data, []byte(xml.Header)), err
if err != nil {
return nil, err
}
return editorXMLGenerated(bytes.TrimPrefix(data, []byte(xml.Header)), namespace)
}
var content []byte
for _, object := range layer.Objects {
@@ -471,6 +490,9 @@ func (e *Editor) sourceLayerXML(layer Layer) ([]byte, error) {
}
} else {
data, err = editorObjectXML(object)
if err == nil {
data, err = editorXMLGenerated(data, namespace)
}
}
if err != nil {
return nil, err
@@ -479,7 +501,7 @@ func (e *Editor) sourceLayerXML(layer Layer) ([]byte, error) {
}
attrs := ofdAttrs{{Name: xml.Name{Local: "ID"}, Value: layer.ID}, {Name: xml.Name{Local: "Type"}, Value: layer.Type}}
attrs.add("DrawParam", layer.DrawParam)
return editorXMLContainer("Layer", attrs, content)
return (&editorXML{name: xml.Name{Space: namespace}}).containerXML("Layer", attrs, content)
}
// sourceNewPageXML 编码新增页面,同时保留复制对象的原文语义
@@ -510,7 +532,7 @@ func (e *Editor) sourcePageData(page PageContent, references *editorReferenceSca
}
var layers []byte
for _, layer := range page.Content.Layer {
data, err := e.sourceLayerXML(layer)
data, err := e.sourceLayerXML(layer, ofdNamespace2016)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -97,7 +97,7 @@ func (e *Editor) prunePageReferences(parts map[string][]byte, generated map[stri
return err
}
}
if node.name.Space != "" && node.name.Space != ofdNamespace && node.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(node.name.Space) == ofdXMLUnknown {
return nil
}
remove := false
@@ -192,7 +192,7 @@ func pruneCreatedPageReferences(data []byte, removed map[string]bool) ([]byte, e
var patches []editorXMLPatch
var walk func(*editorXML)
walk = func(node *editorXML) {
if node.name.Space != ofdNamespace {
if node.name.Space != ofdNamespace2016 {
return
}
if node.name.Local == "Action" {
+3 -3
View File
@@ -126,7 +126,7 @@ func (s *editorReferenceScan) accept(token xml.Token) bool {
}
switch token := token.(type) {
case xml.StartElement:
if token.Name.Space != "" && token.Name.Space != ofdNamespace {
if token.Name.Space != "" && token.Name.Space != ofdNamespace2016 {
s.safe = false
return false
}
@@ -233,7 +233,7 @@ func (x *ofdXML) referenceToken(token xml.Token) {
s := x.references
switch value := token.(type) {
case xml.StartElement:
value.Name = xml.Name{Space: ofdNamespace, Local: strings.TrimPrefix(value.Name.Local, "ofd:")}
value.Name = xml.Name{Space: ofdNamespace2016, Local: strings.TrimPrefix(value.Name.Local, "ofd:")}
for _, attr := range value.Attr {
if editorResourceReferenceKind(attr.Name.Local, s.resource) != editorReferenceNone && !editorReferenceTextSafe(attr.Value) {
s.safe = false
@@ -241,7 +241,7 @@ func (x *ofdXML) referenceToken(token xml.Token) {
}
token = value
case xml.EndElement:
value.Name = xml.Name{Space: ofdNamespace, Local: strings.TrimPrefix(value.Name.Local, "ofd:")}
value.Name = xml.Name{Space: ofdNamespace2016, Local: strings.TrimPrefix(value.Name.Local, "ofd:")}
token = value
case xml.CharData:
if s.capture && !editorReferenceTextSafe(string(value)) {
+137 -40
View File
@@ -92,7 +92,7 @@ func (n *editorXML) child(name string) *editorXML {
// 返回: *editorXML 节点,不存在时为nil
func (n *editorXML) childAt(name string, index int) *editorXML {
for _, child := range n.children {
if child.name.Local == name && (child.name.Space == n.name.Space || child.name.Space == ofdNamespace || child.name.Space == "") {
if child.name.Local == name && (child.name.Space == n.name.Space || child.name.Space == ofdNamespace2016 || child.name.Space == "") {
if index == 0 {
return child
}
@@ -162,15 +162,13 @@ func editorXMLContent(data []byte, node *editorXML, content []byte) editorXMLPat
// 入参: name 节点名称, value 文本
// 返回: []byte XML片段
func editorXMLText(name, value string) []byte {
var escaped bytes.Buffer
_ = xml.EscapeText(&escaped, []byte(value))
return []byte("<ofd:" + name + " xmlns:ofd=\"" + ofdNamespace + "\">" + escaped.String() + "</ofd:" + name + ">")
return (&editorXML{name: xml.Name{Space: ofdNamespace2016}}).textXML(name, value)
}
// editorXMLSetText 更新或追加直接子节点,保留其他节点
// 入参: data 原文, node 父节点, values 需要修改的字段
// 返回: []byte 修改后的XML
func editorXMLSetText(data []byte, node *editorXML, values [][2]string) []byte {
// 返回: []byte 修改后的XML, error 文本编码错误
func editorXMLSetText(data []byte, node *editorXML, values [][2]string) ([]byte, error) {
var patches []editorXMLPatch
var added []byte
order := []string{"PhysicalBox", "ApplicationBox", "ContentBox", "BleedBox"}
@@ -181,10 +179,18 @@ func editorXMLSetText(data []byte, node *editorXML, values [][2]string) []byte {
child := node.child(value[0])
var encoded []byte
if value[1] != "" {
encoded = editorXMLText(value[0], value[1])
encoded = node.textXML(value[0], value[1])
}
if child != nil {
patches = append(patches, editorXMLPatch{child.start, child.end, encoded})
if value[1] == "" {
patches = append(patches, editorXMLPatch{child.start, child.end, nil})
} else {
encoded, err := editorXMLTextValue(data, child, editorImportText(data, child), value[1])
if err != nil {
return nil, err
}
patches = append(patches, editorXMLPatch{child.start, child.end, encoded})
}
} else if node.open == node.end {
added = append(added, encoded...)
} else if len(encoded) != 0 {
@@ -201,7 +207,48 @@ func editorXMLSetText(data []byte, node *editorXML, values [][2]string) []byte {
if len(added) != 0 {
patches = append(patches, editorXMLContent(data, node, added))
}
return editorPatchXML(data, patches)
return editorPatchXML(data, patches), nil
}
// editorXMLTextValue 修改字段文本,保留命名空间、属性、扩展子节点和注释
// 入参: data 原XML, node 字段节点, before 原值, after 新值
// 返回: []byte 字段XML, error 编码错误
func editorXMLTextValue(data []byte, node *editorXML, before, after string) ([]byte, error) {
if before == after {
return data[node.start:node.end], nil
}
var content bytes.Buffer
if err := xml.EscapeText(&content, []byte(after)); err != nil {
return nil, err
}
if node.open < node.close {
inner := data[node.open:node.close]
decoder := xml.NewDecoder(bytes.NewReader(inner))
depth := 0
for {
start := decoder.InputOffset()
token, err := decoder.RawToken()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
_, text := token.(xml.CharData)
if !text || depth != 0 {
content.Write(inner[start:decoder.InputOffset()])
}
switch token.(type) {
case xml.StartElement:
depth++
case xml.EndElement:
depth--
}
}
}
patch := editorXMLContent(data, node, content.Bytes())
entry := append(bytes.Clone(data[node.start:patch.start]), patch.data...)
return append(entry, data[patch.end:node.end]...), nil
}
// editorXMLObjects 递归登记图层和页块内的对象位置,保留原容器层级
@@ -228,7 +275,7 @@ func editorXMLObjects(container *editorXML, nodes map[string]*editorXML) error {
// 入参: node 节点, allowed 允许的属性名称
// 返回: bool 是否支持
func editorXMLAttributes(node *editorXML, allowed string) bool {
if node.name.Space != "" && node.name.Space != ofdNamespace && node.name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(node.name.Space) == ofdXMLUnknown {
return false
}
for _, attr := range node.attrs {
@@ -326,7 +373,7 @@ func editorXMLContainersSupported(node *editorXML) bool {
// 入参: node 原始对象节点
// 返回: bool 是否支持保真复制
func editorXMLCopyable(node *editorXML) bool {
if node.attr("ID") != "" && (editorResourceID(node.attr("ID")) == "" || node.name.Space != "" && node.name.Space != ofdNamespace && node.name.Space != "http://www.ofdspec.org") {
if node.attr("ID") != "" && (editorResourceID(node.attr("ID")) == "" || classifyOFDNamespace(node.name.Space) == ofdXMLUnknown) {
return false
}
for _, child := range node.children {
@@ -343,7 +390,11 @@ func editorXMLCopyable(node *editorXML) bool {
// 返回: []byte 对象XML, error 错误信息
func editorXMLObject(data []byte, node *editorXML, before, after GraphicObject) ([]byte, error) {
if before.Type != after.Type {
return editorObjectXML(after)
encoded, err := editorObjectXML(after)
if err != nil {
return nil, err
}
return editorXMLGenerated(encoded, node.name.Space)
}
if node.attr("DrawParam") != "" && (before.Type == "TextObject" && before.TextObject.DrawParam == "" || before.Type == "PathObject" && before.PathObject.DrawParam == "") {
before = GraphicObject{Type: before.Type}
@@ -373,8 +424,16 @@ func editorXMLObject(data []byte, node *editorXML, before, after GraphicObject)
// 入参: data 原文, node 原节点, oldXML 修改前编码, newXML 修改后编码
// 返回: []byte 更新节点, error 错误信息
func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte, error) {
legacyClip := node.name.Local == "Clips" && node.hasAttr("TransFlag")
return editorXMLMergeNamespace(data, node, oldXML, newXML, node.name.Space)
}
// editorXMLMergeNamespace 合并节点差异,并向已换算的裁剪子树传递目标命名空间
// 入参: data 原文, node 原节点, oldXML 修改前编码, newXML 修改后编码, namespace 目标命名空间
// 返回: []byte 更新节点, error 解析错误
func editorXMLMergeNamespace(data []byte, node *editorXML, oldXML, newXML []byte, namespace string) ([]byte, error) {
legacyClip := node.name.Local == "Clips" && (node.hasAttr("TransFlag") || classifyOFDNamespace(node.name.Space) == ofdXMLLegacy)
if legacyClip {
namespace = ofdNamespace2016
var clips Clips
if err := xml.Unmarshal(data[node.start:node.end], &clips); err != nil {
return nil, err
@@ -404,7 +463,7 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
}
}
contentChanged := !bytes.Equal(oldXML[oldNode.open:oldNode.close], newXML[newNode.open:newNode.close])
if len(changes) == 0 && !contentChanged {
if len(changes) == 0 && !contentChanged && namespace == node.name.Space {
return bytes.Clone(data[node.start:node.end]), nil
}
d := xml.NewDecoder(bytes.NewReader(data[node.start:node.open]))
@@ -413,6 +472,10 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
return nil, err
}
start := token.(xml.StartElement)
if legacyClip || namespace != node.name.Space {
start.Name.Space = editorXMLNamespacePrefix(node)
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: "xmlns", Local: start.Name.Space}, Value: namespace})
}
var result bytes.Buffer
name := start.Name.Local
if start.Name.Space != "" {
@@ -445,11 +508,11 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
}
}
result.WriteByte('>')
if contentChanged {
if contentChanged || namespace != node.name.Space {
matching := len(node.children) > 0 && len(node.children) == len(oldNode.children) && len(node.children) == len(newNode.children)
if matching {
for i, child := range node.children {
if child.name.Space != "" && child.name.Space != ofdNamespace && child.name.Space != "http://www.ofdspec.org" || child.name.Local != oldNode.children[i].name.Local || child.name.Local != newNode.children[i].name.Local {
if !packageOFDNode(child, oldNode.children[i].name.Local) || child.name.Local != newNode.children[i].name.Local {
matching = false
break
}
@@ -459,7 +522,11 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
position := node.open
for i, child := range node.children {
before, after := oldNode.children[i], newNode.children[i]
updated, err := editorXMLMerge(data, child, oldXML[before.start:before.end], newXML[after.start:after.end])
childNamespace := child.name.Space
if childNamespace == node.name.Space {
childNamespace = namespace
}
updated, err := editorXMLMergeNamespace(data, child, oldXML[before.start:before.end], newXML[after.start:after.end], childNamespace)
if err != nil {
return nil, err
}
@@ -469,7 +536,7 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
}
result.Write(data[position:node.close])
} else {
content, err := editorXMLMergeContent(data, node, oldXML, oldNode, newXML, newNode)
content, err := editorXMLMergeContent(data, node, oldXML, oldNode, newXML, newNode, namespace)
if err != nil {
return nil, err
}
@@ -483,16 +550,24 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
}
// editorXMLMergeContent 按字段合并子节点,保留未修改字段、扩展节点和注释
// 入参: data 原文数据, node 原文节点, oldXML 修改前数据, oldNode 修改前节点, newXML 修改后数据, newNode 修改后节点
// 入参: data 原文数据, node 原文节点, oldXML 修改前数据, oldNode 修改前节点, newXML 修改后数据, newNode 修改后节点, namespace 目标命名空间
// 返回: []byte 子节点内容, error 错误信息
func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode *editorXML, newXML []byte, newNode *editorXML) ([]byte, error) {
func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode *editorXML, newXML []byte, newNode *editorXML, namespace string) ([]byte, error) {
if len(oldNode.children) == 0 && len(newNode.children) == 0 {
return newXML[newNode.open:newNode.close], nil
updated, err := editorXMLTextValue(data, node, editorImportText(oldXML, oldNode), editorImportText(newXML, newNode))
if err != nil {
return nil, err
}
root, err := parseEditorXML(updated)
if err != nil {
return nil, err
}
return updated[root.open:root.close], nil
}
groups := func(root *editorXML, encoded bool) map[string][]*editorXML {
result := make(map[string][]*editorXML)
for _, child := range root.children {
if encoded || child.name.Space == "" || child.name.Space == ofdNamespace || child.name.Space == "http://www.ofdspec.org" {
if encoded || packageOFDNode(child, child.name.Local) {
result[child.name.Local] = append(result[child.name.Local], child)
}
}
@@ -515,7 +590,7 @@ func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode
for _, child := range next {
newData = append(newData, newXML[child.start:child.end]...)
}
if bytes.Equal(oldData, newData) {
if bytes.Equal(oldData, newData) && namespace == node.name.Space {
continue
}
if len(source) > 0 && len(source) == len(old) {
@@ -523,7 +598,11 @@ func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode
var encoded []byte
if i < len(next) {
var err error
encoded, err = editorXMLMerge(data, child, oldXML[old[i].start:old[i].end], newXML[next[i].start:next[i].end])
childNamespace := child.name.Space
if childNamespace == node.name.Space {
childNamespace = namespace
}
encoded, err = editorXMLMergeNamespace(data, child, oldXML[old[i].start:old[i].end], newXML[next[i].start:next[i].end], childNamespace)
if err != nil {
return nil, err
}
@@ -533,7 +612,11 @@ func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode
if len(next) > len(source) {
var added []byte
for _, child := range next[len(source):] {
added = append(added, editorXMLEncodedFragment(newXML, child)...)
fragment, err := editorXMLEncodedFragment(newXML, child, namespace)
if err != nil {
return nil, err
}
added = append(added, fragment...)
}
position := source[len(source)-1].end - node.open
patches = append(patches, editorXMLPatch{position, position, added})
@@ -542,7 +625,11 @@ func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode
}
newData = nil
for _, child := range next {
newData = append(newData, editorXMLEncodedFragment(newXML, child)...)
fragment, err := editorXMLEncodedFragment(newXML, child, namespace)
if err != nil {
return nil, err
}
newData = append(newData, fragment...)
}
if len(source) > 0 {
for i, child := range source {
@@ -566,13 +653,26 @@ func editorXMLMergeContent(data []byte, node *editorXML, oldXML []byte, oldNode
return editorPatchXML(data[node.open:node.close], patches), nil
}
// editorXMLEncodedFragment 为新增的标准编码节点局部声明前缀,不覆盖原文命名空间
// 入参: data 标准编码内容, node 待插入的子节点
// 返回: []byte 独立片段
func editorXMLEncodedFragment(data []byte, node *editorXML) []byte {
position := node.open - node.start - 1
fragment := data[node.start:node.end]
return editorPatchXML(fragment, []editorXMLPatch{{position, position, []byte(" xmlns:ofd=\"" + ofdNamespace + "\"")}})
// editorXMLEncodedFragment 补齐自产片段的继承声明,再按目标命名空间编码
// 入参: data 标准编码内容, node 待插入节点, namespace 目标命名空间
// 返回: []byte 独立片段, error 解析错误
func editorXMLEncodedFragment(data []byte, node *editorXML, namespace string) ([]byte, error) {
fragment, err := editorXMLStandalone(data[node.start:node.end], node)
if err != nil {
return nil, err
}
if node.name.Space == "ofd" {
root, err := parseEditorXML(fragment)
if err != nil {
return nil, err
}
position := root.open - 1
if fragment[position-1] == '/' {
position--
}
fragment = editorPatchXML(fragment, []editorXMLPatch{{position, position, []byte(" xmlns:ofd=\"" + ofdNamespace2016 + "\"")}})
}
return editorXMLGenerated(fragment, namespace)
}
// editorXMLAttribute 修改单个标准属性,保留节点其余原文
@@ -594,13 +694,7 @@ func editorXMLAttribute(data []byte, node *editorXML, name, value string) ([]byt
// 入参: name 节点名称, attrs 属性, content 子节点XML
// 返回: []byte XML片段, error 错误信息
func editorXMLContainer(name string, attrs ofdAttrs, content []byte) ([]byte, error) {
var output bytes.Buffer
attrs.add("xmlns:ofd", ofdNamespace)
value := struct {
Content []byte `xml:",innerxml"`
}{content}
err := xml.NewEncoder(&output).EncodeElement(value, xml.StartElement{Name: xml.Name{Local: "ofd:" + name}, Attr: attrs})
return output.Bytes(), err
return (&editorXML{name: xml.Name{Space: ofdNamespace2016}}).containerXML(name, attrs, content)
}
// editorXMLStandalone 补齐原对象继承的命名空间,使跨页复制不依赖原父节点
@@ -628,6 +722,9 @@ func editorXMLStandalone(data []byte, source *editorXML) ([]byte, error) {
}
}
}
if _, exists := namespaces["xmlns"]; !exists {
namespaces["xmlns"] = ""
}
for _, attr := range root.attrs {
if attr.Name.Space == "xmlns" {
delete(namespaces, "xmlns:"+attr.Name.Local)
+17
View File
@@ -53,6 +53,23 @@ func (clips *Clips) usesObjectMatrix() bool {
return clips != nil && clips.TransFlag != nil && *clips.TransFlag
}
// UnmarshalXML 兼容旧命名空间的对象变换裁剪,显式TransFlag优先
// 入参: d XML解码器, start 起始节点
// 返回: error 解码错误
func (clips *Clips) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type plain Clips
var value plain
if classifyOFDNamespace(start.Name.Space) == ofdXMLLegacy {
transform := true
value.TransFlag = &transform
}
if err := d.DecodeElement(&value, &start); err != nil {
return err
}
*clips = Clips(value)
return nil
}
// UnmarshalXML 解析路径并区分省略样式、显式实线与零线宽
// 入参: d XML解码器, start 起始节点
// 返回: error 错误信息
+12 -4
View File
@@ -38,11 +38,11 @@ func packagePagePath(directory, id string) string {
return path.Join(directory, "Pages", "Page_"+id, "Content.xml")
}
// packageOFDNode 判断包索引中的标准节点,兼容旧版及省略命名空间的文档
// packageOFDNode 识别包索引中的OFD节点,兼容与父节点同命名空间的已知字段
// 入参: node XML节点, name 节点名称
// 返回: bool 是否为标准节点
// 返回: bool 是否为OFD节点
func packageOFDNode(node *editorXML, name string) bool {
return node.name.Local == name && (node.name.Space == "" || node.name.Space == ofdNamespace || node.name.Space == "http://www.ofdspec.org")
return node.name.Local == name && (classifyOFDNamespace(node.name.Space) != ofdXMLUnknown || node.parent != nil && node.name.Space == node.parent.name.Space)
}
// packageName 为新增条目分配文档内路径,不覆盖原文件或本次资源
@@ -118,6 +118,10 @@ func mergePackageResources(reader *Reader, parts map[string][]byte, doc *Documen
return "", false, err
}
target := root.child(group.name.Local)
context := root
if target != nil {
context = target
}
var content []byte
for _, node := range group.children {
if target != nil && slices.ContainsFunc(target.children, func(old *editorXML) bool {
@@ -129,6 +133,10 @@ func mergePackageResources(reader *Reader, parts map[string][]byte, doc *Documen
if err != nil {
return "", false, err
}
entry, err = editorXMLGenerated(entry, context.name.Space)
if err != nil {
return "", false, err
}
content = append(content, entry...)
}
if len(content) == 0 {
@@ -139,7 +147,7 @@ func mergePackageResources(reader *Reader, parts map[string][]byte, doc *Documen
data = editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, target, content)})
continue
}
content, err = editorXMLContainer(group.name.Local, nil, content)
content, err = root.containerXML(group.name.Local, nil, content)
if err != nil {
return "", false, err
}
+1 -1
View File
@@ -257,7 +257,7 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
return fmt.Errorf("import PDF page %d: %w", index+1, err)
}
if len(importer.annotationData) != 0 {
if err := editor.appendAnnotations(index, importer.annotationData); err != nil {
if err := editor.appendAnnotations(index, importer.annotationData, true); err != nil {
return fmt.Errorf("import PDF page %d: %w", index+1, err)
}
importer.annotationData = nil
+2 -2
View File
@@ -109,11 +109,11 @@ func (r *Reader) documentFiles(ctx context.Context, index int) (map[string]bool,
}
switch node := token.(type) {
case xml.StartElement:
if depth == 0 && node.Name.Space != "" && node.Name.Space != ofdNamespace && node.Name.Space != "http://www.ofdspec.org" {
if depth == 0 && classifyOFDNamespace(node.Name.Space) == ofdXMLUnknown {
input.Close()
goto nextFile
}
if node.Name.Space != "" && node.Name.Space != ofdNamespace && node.Name.Space != "http://www.ofdspec.org" {
if classifyOFDNamespace(node.Name.Space) == ofdXMLUnknown {
input.Close()
return nil, fmt.Errorf("cannot determine document dependencies in %s", actual)
}
+22 -3
View File
@@ -312,6 +312,10 @@ func signatureWritePackage(parts map[string][]byte, options SignatureWriteOption
}
}
oldCount := len(list.List)
previousList, err := signatureWriteXML("Signatures", list)
if err != nil {
return nil, err
}
verifyOptions := append([]SignatureVerifyOption(nil), options.ExistingVerifyOptions...)
verifyOptions = append(verifyOptions, WithSignatureCert(identity.cert.Raw))
if options.Mode == SignatureAppend && oldCount > 0 {
@@ -342,7 +346,10 @@ func signatureWritePackage(parts map[string][]byte, options SignatureWriteOption
if body == nil {
return nil, fmt.Errorf("missing document body")
}
parts["OFD.xml"] = editorXMLSetText(parts["OFD.xml"], body, [][2]string{{"Signatures", "/" + listPath}})
parts["OFD.xml"], err = editorXMLSetText(parts["OFD.xml"], body, [][2]string{{"Signatures", "/" + listPath}})
if err != nil {
return nil, err
}
}
if options.Mode == SignatureReplace {
list.List = nil
@@ -373,10 +380,22 @@ func signatureWritePackage(parts map[string][]byte, options SignatureWriteOption
parts[sealPath] = bytes.Clone(options.Seal)
}
list.List = append(list.List, Signature{ID: id, Type: kind, BaseLoc: "/" + sigPath})
parts[listPath], err = signatureWriteXML("Signatures", list)
updatedList, err := signatureWriteXML("Signatures", list)
if err != nil {
return nil, err
}
if original := parts[listPath]; len(original) != 0 {
root, err := parseEditorXML(original)
if err != nil {
return nil, err
}
updated, err := editorXMLMerge(original, root, previousList, updatedList)
if err != nil {
return nil, err
}
updatedList = editorPatchXML(original, []editorXMLPatch{{root.start, root.end, updated}})
}
parts[listPath] = updatedList
refs := SignatureReferences{CheckMethod: identity.digest}
names := make([]string, 0, len(parts))
for name := range parts {
@@ -518,7 +537,7 @@ func signatureWriteIDs(list *Signatures, listPath string, parts map[string][]byt
func signatureWriteXML(name string, value any) ([]byte, error) {
var out bytes.Buffer
out.WriteString(xml.Header)
err := xml.NewEncoder(&out).EncodeElement(value, xml.StartElement{Name: xml.Name{Space: ofdNamespace, Local: name}})
err := xml.NewEncoder(&out).EncodeElement(value, xml.StartElement{Name: xml.Name{Space: ofdNamespace2016, Local: name}})
return out.Bytes(), err
}
+13
View File
@@ -24,6 +24,19 @@ type textObjectTokens struct {
transforms []int
}
// UnmarshalXML 解析字形变换,省略的字符数和字形数均取1
// 入参: decoder XML解码器, start 变换起始节点
// 返回: error 解码错误
func (transform *CGTransform) UnmarshalXML(decoder *xml.Decoder, start xml.StartElement) error {
type plain CGTransform
value := plain{CodeCount: 1, GlyphCount: 1}
if err := decoder.DecodeElement(&value, &start); err != nil {
return err
}
*transform = CGTransform(value)
return nil
}
// Token 转发XML令牌并记录直接子节点顺序
// 返回: xml.Token XML令牌, error 解码错误
func (s *textObjectTokens) Token() (xml.Token, error) {
+3 -6
View File
@@ -31,9 +31,6 @@ import (
"time"
)
// ofdNamespace 为OFD文档的XML命名空间
const ofdNamespace = "http://www.ofdspec.org/2016"
// editorProgress 保存准备阶段的进度检查点,内存预览使用nil
type editorProgress func(stage string, completed, total int) error
@@ -675,7 +672,7 @@ func (x *ofdXML) checkContext() bool {
// root 写入声明命名空间的根节点
// 入参: name 节点名, attrs 属性
func (x *ofdXML) root(name string, attrs ofdAttrs) {
attrs.add("xmlns:ofd", ofdNamespace)
attrs.add("xmlns:ofd", ofdNamespace2016)
x.start(name, attrs)
}
@@ -785,7 +782,7 @@ func (x *ofdXML) object(object GraphicObject, root bool) {
var fill, stroke *FillColor
var actions []Action
if root {
attrs.add("xmlns:ofd", ofdNamespace)
attrs.add("xmlns:ofd", ofdNamespace2016)
}
switch object.Type {
case "TextObject", "Text":
@@ -1112,7 +1109,7 @@ func (x *ofdXML) shading(name string, attrs ofdAttrs, segments []ShdSegment) {
// 入参: name 元素名称, value 元素内容
func (x *ofdXML) element(name string, value any) {
if x.checkContext() {
x.err = x.encoder.EncodeElement(value, xml.StartElement{Name: xml.Name{Space: ofdNamespace, Local: name}})
x.err = x.encoder.EncodeElement(value, xml.StartElement{Name: xml.Name{Space: ofdNamespace2016, Local: name}})
x.referenceElement(name, value)
}
}
+48
View File
@@ -0,0 +1,48 @@
// 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
// 旧版与2016版OFD命名空间
const (
ofdNamespaceLegacy = "http://www.ofdspec.org"
ofdNamespace2016 = "http://www.ofdspec.org/2016"
)
// 未声明、未知、旧版及2016版OFD命名空间,不限制已知内容的读取
const (
ofdXMLUnqualified ofdXMLNamespace = iota
ofdXMLUnknown
ofdXMLLegacy
ofdXML2016
)
// ofdXMLNamespace 标识XML节点采用的OFD命名空间
type ofdXMLNamespace uint8
// classifyOFDNamespace 识别OFD命名空间,未声明与未知版本分别处理
// 入参: namespace XML命名空间
// 返回: ofdXMLNamespace 命名空间类别
func classifyOFDNamespace(namespace string) ofdXMLNamespace {
switch namespace {
case "":
return ofdXMLUnqualified
case ofdNamespaceLegacy:
return ofdXMLLegacy
case ofdNamespace2016:
return ofdXML2016
default:
return ofdXMLUnknown
}
}