feat(版本编辑): 优化版本读取与多文档排序
build.yaml / build (push) Successful in 51s

This commit is contained in:
xiaoqidun committed 2026-10-12 10:57:18 +08:00
1 parent a710bc5f41
commit 17a9a03361
2 files changed
+87 -38

No files matched your search

+11 -5
View File
@@ -239,13 +239,19 @@ func (e *Editor) MoveDocumentContext(ctx context.Context, from, to int) error {
return nil
}
return e.editDocumentsContext(ctx, func(reader *Reader, data []byte, root *editorXML) (int, error) {
bodies := make([][]byte, reader.DocumentCount())
for index := range bodies {
nodes := make([]*editorXML, 0, reader.DocumentCount())
bodies := make([][]byte, 0, reader.DocumentCount())
for _, body := range root.children {
if err := ctx.Err(); err != nil {
return 0, err
}
body := root.childAt("DocBody", index)
bodies[index] = data[body.start:body.end]
if body.matchesOFD("DocBody") {
nodes = append(nodes, body)
bodies = append(bodies, data[body.start:body.end])
}
}
if len(nodes) != reader.DocumentCount() {
return 0, fmt.Errorf("document entry count mismatch")
}
moveEditorItem(bodies, from, to)
var patches []editorXMLPatch
@@ -253,7 +259,7 @@ func (e *Editor) MoveDocumentContext(ctx context.Context, from, to int) error {
if err := ctx.Err(); err != nil {
return 0, err
}
body := root.childAt("DocBody", index)
body := nodes[index]
patches = append(patches, editorXMLPatch{body.start, body.end, content})
}
reader.files["OFD.xml"] = editorPatchXML(data, patches)
+76 -33
View File
@@ -16,7 +16,9 @@ package ofdgo
import (
"context"
"encoding/xml"
"fmt"
"io"
"strconv"
"strings"
)
@@ -166,59 +168,100 @@ func parseVersionEntries(ctx context.Context, body *editorXML) ([]DocumentVersio
return result, nil
}
// readDocumentVersionContext 读取版本描述,按描述文件位置解析清单和文档入口
// readDocumentVersionContext 流式读取版本描述,按描述文件位置解析清单和文档入口
// 入参: ctx 取消上下文, id 入口中的版本标识, location 描述文件路径
// 返回: DocumentVersionInfo 版本信息, error 读取、描述或取消错误
func (r *Reader) readDocumentVersionContext(ctx context.Context, id, location string) (DocumentVersionInfo, error) {
var info DocumentVersionInfo
data, err := r.readFileViewContext(ctx, location)
if err != nil {
return info, err
}
root, err := parseEditorXMLContext(ctx, data)
if err != nil {
return info, err
}
if root.name.Local != "DocVersion" || strings.TrimSpace(root.attr("ID")) != id {
return info, fmt.Errorf("invalid version descriptor %s", location)
}
list, docRoot := root.child("FileList"), root.child("DocRoot")
if list == nil || docRoot == nil {
return info, fmt.Errorf("missing version FileList or DocRoot in %s", location)
}
if root.childAt("FileList", 1) != nil || root.childAt("DocRoot", 1) != nil {
return info, fmt.Errorf("duplicate version FileList or DocRoot in %s", location)
}
entry, err := versionFileLocation(location, editorImportText(data, docRoot))
if err != nil {
return info, err
}
info = DocumentVersionInfo{ID: id, Location: location, Version: root.attr("Version"), Name: root.attr("Name"), CreationDate: root.attr("CreationDate"), DocRoot: entry}
ids := map[string]bool{id: true}
found := false
for _, node := range list.children {
if err := ctx.Err(); err != nil {
return DocumentVersionInfo{}, err
}
if !node.matchesOFD("File") {
input, err := r.openFile(location)
if err != nil {
return DocumentVersionInfo{}, err
}
defer input.Close()
decoder := xml.NewDecoder(imageInput{ReadCloser: input, context: ctx})
var info DocumentVersionInfo
var root, parent *editorXML
var files, entry bool
ids := map[string]bool{id: true}
for {
if err := ctx.Err(); err != nil {
return DocumentVersionInfo{}, err
}
token, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
return DocumentVersionInfo{}, err
}
switch start := token.(type) {
case xml.StartElement:
node := &editorXML{name: start.Name, attrs: start.Attr, parent: parent}
if parent == nil {
if root != nil || start.Name.Local != "DocVersion" || strings.TrimSpace(node.attr("ID")) != id {
return DocumentVersionInfo{}, fmt.Errorf("invalid version descriptor %s", location)
}
root, parent = node, node
info = DocumentVersionInfo{ID: id, Location: location, Version: node.attr("Version"), Name: node.attr("Name"), CreationDate: node.attr("CreationDate")}
continue
}
switch {
case parent == root && node.matchesOFD("FileList"):
if files {
return DocumentVersionInfo{}, fmt.Errorf("duplicate version FileList in %s", location)
}
files, parent = true, node
case parent == root && node.matchesOFD("DocRoot"):
if entry {
return DocumentVersionInfo{}, fmt.Errorf("duplicate version DocRoot in %s", location)
}
var value string
if err := decoder.DecodeElement(&value, &start); err != nil {
return DocumentVersionInfo{}, err
}
info.DocRoot, err = versionFileLocation(location, value)
if err != nil {
return DocumentVersionInfo{}, err
}
entry = true
case parent != root && node.matchesOFD("File"):
fileID := strings.TrimSpace(node.attr("ID"))
if !ofdXMLIDValid(fileID) || ids[fileID] {
return DocumentVersionInfo{}, fmt.Errorf("invalid or duplicate version file ID %q", fileID)
}
ids[fileID] = true
name, err := versionFileLocation(location, editorImportText(data, node))
var value string
if err := decoder.DecodeElement(&value, &start); err != nil {
return DocumentVersionInfo{}, err
}
name, err := versionFileLocation(location, value)
if err != nil {
return DocumentVersionInfo{}, err
}
found = found || name == entry
info.Files = append(info.Files, VersionFile{ID: fileID, Location: name})
default:
if err := decoder.Skip(); err != nil {
return DocumentVersionInfo{}, err
}
if !found {
return DocumentVersionInfo{}, fmt.Errorf("version DocRoot absent from FileList: %s", entry)
}
case xml.EndElement:
parent = parent.parent
}
}
if !files || !entry {
return DocumentVersionInfo{}, fmt.Errorf("missing version FileList or DocRoot in %s", location)
}
for _, file := range info.Files {
if err := ctx.Err(); err != nil {
return DocumentVersionInfo{}, err
}
if file.Location == info.DocRoot {
return info, nil
}
}
return DocumentVersionInfo{}, fmt.Errorf("version DocRoot absent from FileList: %s", info.DocRoot)
}
// versionFileLocation 解析版本描述中的ST_Loc,拒绝空路径和包外路径