Files
ofdgo/ofdgo_editor_resource_graph.go
T
2026-10-14 08:22:31 +08:00

109 lines
3.5 KiB
Go

// 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 (
"bufio"
"bytes"
"context"
"encoding/xml"
"io"
)
// editorResourceElement 返回标准资源集合中的声明元素名称
// 入参: group 资源集合名称
// 返回: string 声明元素名称,未知集合返回空值
func editorResourceElement(group string) string {
switch group {
case "Fonts":
return "Font"
case "MultiMedias":
return "MultiMedia"
case "ColorSpaces":
return "ColorSpace"
case "DrawParams":
return "DrawParam"
case "CompositeGraphicUnits":
return "CompositeGraphicUnit"
}
return ""
}
// editorResourceDefinitions 单次流式读取资源声明依赖,保留历史索引和非图像媒体
// 入参: ctx 取消上下文, data 资源索引内容, name 文件路径, source 扫描结果, preserved 是否保留全部声明
// 返回: *editorResourceRefs 含声明依赖的引用,无法安全分析时为空, error 解析或取消错误
func editorResourceDefinitions(ctx context.Context, data []byte, name string, source *editorResourceRefs, preserved bool) (*editorResourceRefs, error) {
result := *source
result.ids, result.fonts = make(map[string]bool), nil
result.definitions = make(map[string]*editorResourceRefs)
outer := &editorResourceRefs{ids: make(map[string]bool), files: make(map[string]bool), fonts: make(map[string]*editorFontUsage)}
scan := editorReferenceScan{ctx: ctx, refs: outer, name: name, safe: true}
input := bufio.NewReader(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
if found, err := editorXMLPrefix(input); err != nil || !found {
return nil, err
}
decoder := xml.NewDecoder(input)
for {
if err := ctx.Err(); err != nil {
return nil, err
}
token, err := decoder.Token()
if err == io.EOF {
if !scan.seen || len(scan.stack) != 0 {
return nil, nil
}
return &result, ctx.Err()
}
if err != nil {
return nil, err
}
depth := len(scan.stack)
if node, ok := token.(xml.StartElement); ok {
if depth == 0 && node.Name.Local != "Res" || depth == 1 && editorResourceElement(node.Name.Local) == "" {
return nil, nil
}
if depth == 2 {
kind := editorResourceElement(scan.stack[1])
var id, mediaType string
for _, attr := range node.Attr {
if attr.Name.Space == "" {
switch attr.Name.Local {
case "ID":
id = editorResourceID(attr.Value)
case "Type":
mediaType = attr.Value
}
}
}
if node.Name.Local != kind || id == "" || result.definitions[id] != nil {
return nil, nil
}
refs := &editorResourceRefs{ids: make(map[string]bool), files: make(map[string]bool), fonts: make(map[string]*editorFontUsage)}
result.definitions[id] = refs
scan.refs = refs
if preserved || kind == "MultiMedia" && mediaType != "Image" {
result.ids[id] = true
}
}
}
if !scan.accept(token) {
return nil, ctx.Err()
}
if _, end := token.(xml.EndElement); end && depth == 3 {
scan.refs = outer
}
}
}