mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
34e17383f1
commit
52d7b6aa64
2 files changed
+78
-66
No files matched your search
@@ -235,6 +235,13 @@ func (r *Reader) documentDependencies(ctx context.Context, indexes ...int) (map[
|
|||||||
// 入参: ctx 取消上下文, name 文件路径, rejectUnknown 是否拒绝无法识别的扩展节点, add 接收引用路径及类别
|
// 入参: ctx 取消上下文, name 文件路径, rejectUnknown 是否拒绝无法识别的扩展节点, add 接收引用路径及类别
|
||||||
// 返回: error 读取或结构错误
|
// 返回: error 读取或结构错误
|
||||||
func (r *Reader) documentFileReferences(ctx context.Context, name string, rejectUnknown bool, add func(string, int)) error {
|
func (r *Reader) documentFileReferences(ctx context.Context, name string, rejectUnknown bool, add func(string, int)) error {
|
||||||
|
return r.scanDocumentFileReferences(ctx, name, rejectUnknown, add, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanDocumentFileReferences 流式扫描文件依赖,按需记录引用节点位置,不构建完整节点树
|
||||||
|
// 入参: ctx 取消上下文, name 文件路径, rejectUnknown 是否拒绝未知扩展, add 接收依赖, record 接收引用位置,可为nil
|
||||||
|
// 返回: error 读取、结构或取消错误
|
||||||
|
func (r *Reader) scanDocumentFileReferences(ctx context.Context, name string, rejectUnknown bool, add func(string, int), record func(*editorXML, string, string)) error {
|
||||||
input, err := r.openFile(name)
|
input, err := r.openFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -242,12 +249,14 @@ func (r *Reader) documentFileReferences(ctx context.Context, name string, reject
|
|||||||
defer input.Close()
|
defer input.Close()
|
||||||
decoder := xml.NewDecoder(imageInput{ReadCloser: input, context: ctx})
|
decoder := xml.NewDecoder(imageInput{ReadCloser: input, context: ctx})
|
||||||
var stack []xml.Name
|
var stack []xml.Name
|
||||||
|
var positions []*editorXML
|
||||||
root, base := "", ""
|
root, base := "", ""
|
||||||
var text strings.Builder
|
var text strings.Builder
|
||||||
for {
|
for {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
start := int(decoder.InputOffset())
|
||||||
token, err := decoder.Token()
|
token, err := decoder.Token()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if root == "" {
|
if root == "" {
|
||||||
@@ -285,6 +294,7 @@ func (r *Reader) documentFileReferences(ctx context.Context, name string, reject
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
var position *editorXML
|
||||||
for _, attr := range node.Attr {
|
for _, attr := range node.Attr {
|
||||||
if attr.Name.Space != "" || strings.TrimSpace(attr.Value) == "" {
|
if attr.Name.Space != "" || strings.TrimSpace(attr.Value) == "" {
|
||||||
continue
|
continue
|
||||||
@@ -292,8 +302,21 @@ func (r *Reader) documentFileReferences(ctx context.Context, name string, reject
|
|||||||
if len(stack) == 0 && root == "Res" && attr.Name.Local == "BaseLoc" {
|
if len(stack) == 0 && root == "Res" && attr.Name.Local == "BaseLoc" {
|
||||||
base = attr.Value
|
base = attr.Value
|
||||||
} else if kind := packageReference(root, parent, node.Name.Local, attr.Name.Local); kind != packageReferenceNone {
|
} else if kind := packageReference(root, parent, node.Name.Local, attr.Name.Local); kind != packageReferenceNone {
|
||||||
add(resolveResourcePath(name, base, attr.Value), kind)
|
target := resolveResourcePath(name, base, attr.Value)
|
||||||
|
add(target, kind)
|
||||||
|
if record != nil {
|
||||||
|
if position == nil {
|
||||||
|
position = &editorXML{name: node.Name, attrs: node.Attr, start: start, open: int(decoder.InputOffset())}
|
||||||
}
|
}
|
||||||
|
record(position, attr.Name.Local, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if record != nil {
|
||||||
|
if position == nil && packageReference(root, parent, node.Name.Local, "") != packageReferenceNone {
|
||||||
|
position = &editorXML{name: node.Name, attrs: node.Attr, start: start, open: int(decoder.InputOffset())}
|
||||||
|
}
|
||||||
|
positions = append(positions, position)
|
||||||
}
|
}
|
||||||
stack = append(stack, node.Name)
|
stack = append(stack, node.Name)
|
||||||
text.Reset()
|
text.Reset()
|
||||||
@@ -302,10 +325,22 @@ func (r *Reader) documentFileReferences(ctx context.Context, name string, reject
|
|||||||
text.Write(node)
|
text.Write(node)
|
||||||
}
|
}
|
||||||
case xml.EndElement:
|
case xml.EndElement:
|
||||||
|
var position *editorXML
|
||||||
|
if record != nil {
|
||||||
|
position = positions[len(positions)-1]
|
||||||
|
if position != nil {
|
||||||
|
position.close, position.end = start, int(decoder.InputOffset())
|
||||||
|
}
|
||||||
|
positions = positions[:len(positions)-1]
|
||||||
|
}
|
||||||
if len(stack) >= 2 {
|
if len(stack) >= 2 {
|
||||||
kind := packageReference(root, stack[len(stack)-2].Local, node.Name.Local, "")
|
kind := packageReference(root, stack[len(stack)-2].Local, node.Name.Local, "")
|
||||||
if value := strings.TrimSpace(text.String()); kind != packageReferenceNone && value != "" {
|
if value := strings.TrimSpace(text.String()); kind != packageReferenceNone && value != "" {
|
||||||
add(resolveResourcePath(name, base, value), kind)
|
target := resolveResourcePath(name, base, value)
|
||||||
|
add(target, kind)
|
||||||
|
if record != nil {
|
||||||
|
record(position, "", target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack = stack[:len(stack)-1]
|
stack = stack[:len(stack)-1]
|
||||||
|
|||||||
+41
-64
@@ -18,7 +18,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -26,8 +25,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// versionEditFile 保存版本内容XML及标准文件引用,不解析字体、图片或附件数据
|
// versionEditFile 保存结构文件及引用位置,仅在改写时读取尚未加载的原文
|
||||||
type versionEditFile struct {
|
type versionEditFile struct {
|
||||||
|
reader *Reader
|
||||||
|
name string
|
||||||
data []byte
|
data []byte
|
||||||
links []versionEditLink
|
links []versionEditLink
|
||||||
}
|
}
|
||||||
@@ -83,10 +84,18 @@ func (r *Reader) versionFileDependencies(ctx context.Context, entry string, part
|
|||||||
return used, ctx.Err()
|
return used, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// versionEditGraph 沿标准文件引用建立版本依赖图,二进制资源只记录路径
|
// versionEditGraph 流式建立标准文件依赖图,不保留完整XML节点树或解压后的原包文件
|
||||||
// 入参: ctx 取消上下文, entry 文档入口, parts 待修改条目, generated 本次自产页面及引用
|
// 入参: ctx 取消上下文, entry 文档入口, parts 待修改条目, generated 本次自产页面及引用
|
||||||
// 返回: *versionEditGraph 依赖图, error 读取、结构或取消错误
|
// 返回: *versionEditGraph 依赖图, error 读取、结构或取消错误
|
||||||
func (r *Reader) versionEditGraph(ctx context.Context, entry string, parts map[string][]byte, generated map[string]editorGeneratedReferences) (*versionEditGraph, error) {
|
func (r *Reader) versionEditGraph(ctx context.Context, entry string, parts map[string][]byte, generated map[string]editorGeneratedReferences) (*versionEditGraph, error) {
|
||||||
|
view := *r
|
||||||
|
if len(parts) != 0 {
|
||||||
|
view.files = maps.Clone(r.files)
|
||||||
|
if view.files == nil {
|
||||||
|
view.files = make(map[string][]byte, len(parts))
|
||||||
|
}
|
||||||
|
maps.Copy(view.files, parts)
|
||||||
|
}
|
||||||
graph := &versionEditGraph{files: make(map[string]*versionEditFile), used: make(map[string]bool)}
|
graph := &versionEditGraph{files: make(map[string]*versionEditFile), used: make(map[string]bool)}
|
||||||
queue := []string{entry}
|
queue := []string{entry}
|
||||||
queued := map[string]bool{entry: true}
|
queued := map[string]bool{entry: true}
|
||||||
@@ -101,69 +110,19 @@ func (r *Reader) versionEditGraph(ctx context.Context, entry string, parts map[s
|
|||||||
graph.files[name], graph.used[name] = &versionEditFile{}, true
|
graph.files[name], graph.used[name] = &versionEditFile{}, true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !ok {
|
file := &versionEditFile{reader: &view, name: name, data: view.files[name]}
|
||||||
var err error
|
|
||||||
data, err = r.readFileViewContext(ctx, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root, err := parseEditorXMLContext(ctx, data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("version file %s: %w", name, err)
|
|
||||||
}
|
|
||||||
file := &versionEditFile{data: data}
|
|
||||||
graph.files[name], graph.used[name] = file, true
|
graph.files[name], graph.used[name] = file, true
|
||||||
base := ""
|
add := func(target string, kind int) {
|
||||||
if root.name.Local == "Res" {
|
|
||||||
base = root.attr("BaseLoc")
|
|
||||||
}
|
|
||||||
var visit func(*editorXML) error
|
|
||||||
visit = func(node *editorXML) error {
|
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if node != root && !node.matchesOFD(node.name.Local) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
parent := ""
|
|
||||||
if node.parent != nil {
|
|
||||||
parent = node.parent.name.Local
|
|
||||||
}
|
|
||||||
if parent == "Extension" && node.name.Local == "Data" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
add := func(attribute, value string, kind int) {
|
|
||||||
if strings.TrimSpace(value) == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
target := resolveResourcePath(name, base, value)
|
|
||||||
file.links = append(file.links, versionEditLink{node: node, attribute: attribute, target: target})
|
|
||||||
graph.used[target] = true
|
graph.used[target] = true
|
||||||
if kind == packageReferenceXML && !queued[target] {
|
if kind == packageReferenceXML && !queued[target] {
|
||||||
queued[target] = true
|
queued[target] = true
|
||||||
queue = append(queue, target)
|
queue = append(queue, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if kind := packageReference(root.name.Local, parent, node.name.Local, ""); kind != packageReferenceNone {
|
record := func(node *editorXML, attribute, target string) {
|
||||||
add("", editorImportText(data, node), kind)
|
file.links = append(file.links, versionEditLink{node: node, attribute: attribute, target: target})
|
||||||
}
|
}
|
||||||
for _, attr := range node.attrs {
|
if err := view.scanDocumentFileReferences(ctx, name, false, add, record); err != nil {
|
||||||
if attr.Name.Space != "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if kind := packageReference(root.name.Local, parent, node.name.Local, attr.Name.Local); kind != packageReferenceNone {
|
|
||||||
add(attr.Name.Local, attr.Value, kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, child := range node.children {
|
|
||||||
if err := visit(child); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := visit(root); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,16 +443,34 @@ func (r *Reader) versionOutputChanges(ctx context.Context, changes map[string][]
|
|||||||
return parts, nil
|
return parts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// rewrite 更新已隔离文件的引用,保留其余XML及相对路径基准
|
// rewrite 按需读取并更新已隔离文件的引用,保留其余XML及相对路径基准
|
||||||
// 入参: ctx 取消上下文, paths 原路径到新路径的映射
|
// 入参: ctx 取消上下文, paths 原路径到新路径的映射
|
||||||
// 返回: []byte 更新后的XML,未改动时借用原文, error 属性编码错误
|
// 返回: []byte 更新后的XML,未改动时返回已加载原文或nil, error 读取或属性编码错误
|
||||||
func (file *versionEditFile) rewrite(ctx context.Context, paths map[string]string) ([]byte, error) {
|
func (file *versionEditFile) rewrite(ctx context.Context, paths map[string]string) ([]byte, error) {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(paths) == 0 {
|
needed := false
|
||||||
|
for _, link := range file.links {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if paths[link.target] != "" {
|
||||||
|
needed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !needed {
|
||||||
return file.data, nil
|
return file.data, nil
|
||||||
}
|
}
|
||||||
|
source := file.data
|
||||||
|
if source == nil {
|
||||||
|
var err error
|
||||||
|
source, err = file.reader.readFileViewContext(ctx, file.name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
var patches []editorXMLPatch
|
var patches []editorXMLPatch
|
||||||
headers := make(map[*editorXML][]versionEditLink)
|
headers := make(map[*editorXML][]versionEditLink)
|
||||||
for _, link := range file.links {
|
for _, link := range file.links {
|
||||||
@@ -504,13 +481,13 @@ func (file *versionEditFile) rewrite(ctx context.Context, paths map[string]strin
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if link.attribute == "" {
|
if link.attribute == "" {
|
||||||
patches = append(patches, editorXMLContent(file.data, link.node, editorFontXMLText("/"+paths[link.target])))
|
patches = append(patches, editorXMLContent(source, link.node, editorFontXMLText("/"+paths[link.target])))
|
||||||
} else {
|
} else {
|
||||||
headers[link.node] = append(headers[link.node], link)
|
headers[link.node] = append(headers[link.node], link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for node, links := range headers {
|
for node, links := range headers {
|
||||||
data := file.data[node.start:node.end]
|
data := source[node.start:node.end]
|
||||||
for _, link := range links {
|
for _, link := range links {
|
||||||
root, err := parseEditorXMLContext(ctx, data)
|
root, err := parseEditorXMLContext(ctx, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -534,7 +511,7 @@ func (file *versionEditFile) rewrite(ctx context.Context, paths map[string]strin
|
|||||||
if len(patches) == 0 {
|
if len(patches) == 0 {
|
||||||
return file.data, nil
|
return file.data, nil
|
||||||
}
|
}
|
||||||
return editorPatchXML(file.data, patches), nil
|
return editorPatchXML(source, patches), ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// versionManifest 更新所选版本的入口与文件清单,保留已有文件标识及扩展字段
|
// versionManifest 更新所选版本的入口与文件清单,保留已有文件标识及扩展字段
|
||||||
|
|||||||
Reference in new issue
Block a user