mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
1 parent
444c01b976
commit
e9caaa4193
31 files changed
+639
-182
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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})})
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user