mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
1 parent
a3098212eb
commit
c3f6b61dfd
4 files changed
+107
-56
No files matched your search
+12
-46
@@ -17,18 +17,9 @@ package ofdgo
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"maps"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// compressionFontBinding 保存资源索引中的字体标识、文件路径及可裁剪状态
|
|
||||||
type compressionFontBinding struct {
|
|
||||||
id string
|
|
||||||
file string
|
|
||||||
unsafe bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// compressionFontUsage 按文档合并共享字体用字,版本编辑仅统计选中入口
|
// compressionFontUsage 按文档合并共享字体用字,版本编辑仅统计选中入口
|
||||||
// 入参: ctx 取消上下文, parts 输出改动, generated 自产页面引用, documents 文档入口, resources 资源索引, references 各文件引用, read 输出文件读取方法
|
// 入参: ctx 取消上下文, parts 输出改动, generated 自产页面引用, documents 文档入口, resources 资源索引, references 各文件引用, read 输出文件读取方法
|
||||||
// 返回: map[string]*editorFontUsage 各字体文件的用字,无法确定作用域时为空, error 读取或取消错误
|
// 返回: map[string]*editorFontUsage 各字体文件的用字,无法确定作用域时为空, error 读取或取消错误
|
||||||
@@ -41,11 +32,19 @@ func (r *Reader) compressionFontUsage(ctx context.Context, parts map[string][]by
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bindings := make(map[string][]compressionFontBinding)
|
scoped, err := r.resourceReferenceScopes(ctx, parts, generated, documents, resources, references, selected)
|
||||||
|
if err != nil || scoped == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make(map[string]*editorFontUsage)
|
||||||
for _, name := range resources {
|
for _, name := range resources {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
refs := scoped[name]
|
||||||
|
if refs == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
data, err := read(name)
|
data, err := read(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -70,43 +69,10 @@ func (r *Reader) compressionFontUsage(ctx context.Context, parts map[string][]by
|
|||||||
location := editorResourceLocation(name, root.attr("BaseLoc"), value)
|
location := editorResourceLocation(name, root.attr("BaseLoc"), value)
|
||||||
id, charset := editorResourceID(font.attr("ID")), font.attr("Charset")
|
id, charset := editorResourceID(font.attr("ID")), font.attr("Charset")
|
||||||
unsafe := id == "" || charset != "" && !strings.EqualFold(charset, "unicode") || location != cleanPackagePath(resolveResourcePath(name, root.attr("BaseLoc"), value))
|
unsafe := id == "" || charset != "" && !strings.EqualFold(charset, "unicode") || location != cleanPackagePath(resolveResourcePath(name, root.attr("BaseLoc"), value))
|
||||||
bindings[name] = append(bindings[name], compressionFontBinding{id: id, file: location, unsafe: unsafe})
|
if result[location] == nil {
|
||||||
|
result[location] = newEditorFontUsage()
|
||||||
}
|
}
|
||||||
}
|
result[location].merge(refs.fonts[id], unsafe)
|
||||||
used := make(map[string]bool)
|
|
||||||
result := make(map[string]*editorFontUsage)
|
|
||||||
for _, entry := range slices.Sorted(maps.Keys(documents)) {
|
|
||||||
scope, err := r.versionFileDependencies(ctx, entry, parts, generated)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
refs := editorResourceRefs{fonts: make(map[string]*editorFontUsage)}
|
|
||||||
for name := range scope {
|
|
||||||
if source := references[name]; source != nil {
|
|
||||||
if selected == "" || selected == entry {
|
|
||||||
for id, usage := range source.fonts {
|
|
||||||
refs.fontUsage(id).merge(usage, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
used[name] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if selected != "" && selected != entry {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for name := range scope {
|
|
||||||
for _, binding := range bindings[name] {
|
|
||||||
usage := refs.fonts[binding.id]
|
|
||||||
if result[binding.file] == nil {
|
|
||||||
result[binding.file] = newEditorFontUsage()
|
|
||||||
}
|
|
||||||
result[binding.file].merge(usage, binding.unsafe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for name, refs := range references {
|
|
||||||
if !used[name] && (len(refs.fonts) != 0 || len(bindings[name]) != 0) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result, ctx.Err()
|
return result, ctx.Err()
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// scanEditorResources 优先扫描结构依赖,原始数据不按XML解析,孤立文件仍保留引用检查
|
// scanEditorResources 优先扫描结构依赖,原始数据不按XML解析,孤立文件仍保留引用检查
|
||||||
// 入参: ctx 取消上下文, reader 原包, parts 输出改动, generated 自产引用, names 扫描范围, progress 扫描进度, collect 是否保留各文件的字体引用
|
// 入参: ctx 取消上下文, reader 原包, parts 输出改动, generated 自产引用, names 扫描范围, progress 扫描进度, collect 是否保留各文件的资源引用
|
||||||
// 返回: *editorResourceRefs 全部引用, []string 资源索引, map[string]*editorResourceRefs 字体引用, bool 是否可安全优化, error 读取或取消错误
|
// 返回: *editorResourceRefs 全部引用, []string 资源索引, map[string]*editorResourceRefs 文件引用, bool 是否可安全优化, error 读取或取消错误
|
||||||
func scanEditorResources(ctx context.Context, reader *Reader, parts map[string][]byte, generated map[string]editorGeneratedReferences, names map[string]bool, progress editorProgress, collect bool) (*editorResourceRefs, []string, map[string]*editorResourceRefs, bool, error) {
|
func scanEditorResources(ctx context.Context, reader *Reader, parts map[string][]byte, generated map[string]editorGeneratedReferences, names map[string]bool, progress editorProgress, collect bool) (*editorResourceRefs, []string, map[string]*editorResourceRefs, bool, error) {
|
||||||
refs := &editorResourceRefs{ids: make(map[string]bool), files: make(map[string]bool), fonts: make(map[string]*editorFontUsage), roles: make(map[string]int), documents: make(map[string]bool), versions: make(map[string]bool)}
|
refs := &editorResourceRefs{ids: make(map[string]bool), files: make(map[string]bool), fonts: make(map[string]*editorFontUsage), roles: make(map[string]int), documents: make(map[string]bool), versions: make(map[string]bool)}
|
||||||
var resources []string
|
var resources []string
|
||||||
@@ -133,7 +133,7 @@ func scanEditorResources(ctx context.Context, reader *Reader, parts map[string][
|
|||||||
if resource {
|
if resource {
|
||||||
resources = append(resources, name)
|
resources = append(resources, name)
|
||||||
}
|
}
|
||||||
if collect && (resource || len(local.fonts) != 0) {
|
if collect && (resource || len(local.ids) != 0 || len(local.fonts) != 0) {
|
||||||
references[name] = local
|
references[name] = local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resourceReferenceScopes 按文档归集资源标识和字体用字,共享资源索引合并所属文档的引用
|
||||||
|
// 入参: ctx 取消上下文, parts 输出改动, generated 自产页面引用, documents 文档入口, resources 资源索引, references 文件引用, selected 限定的文档入口,空值统计全部文档
|
||||||
|
// 返回: map[string]*editorResourceRefs 各资源索引的引用,存在无法归属的引用时为空, error 读取或取消错误
|
||||||
|
func (r *Reader) resourceReferenceScopes(ctx context.Context, parts map[string][]byte, generated map[string]editorGeneratedReferences, documents map[string]bool, resources []string, references map[string]*editorResourceRefs, selected string) (map[string]*editorResourceRefs, error) {
|
||||||
|
indexes := make(map[string]bool, len(resources))
|
||||||
|
for _, name := range resources {
|
||||||
|
indexes[name] = true
|
||||||
|
}
|
||||||
|
used := make(map[string]bool)
|
||||||
|
result := make(map[string]*editorResourceRefs)
|
||||||
|
for _, entry := range slices.Sorted(maps.Keys(documents)) {
|
||||||
|
scope, err := r.versionFileDependencies(ctx, entry, parts, generated)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
refs := &editorResourceRefs{ids: make(map[string]bool), fonts: make(map[string]*editorFontUsage)}
|
||||||
|
for name := range scope {
|
||||||
|
if source := references[name]; source != nil {
|
||||||
|
used[name] = true
|
||||||
|
if selected == "" || selected == entry {
|
||||||
|
refs.merge(&editorResourceRefs{ids: source.ids, fonts: source.fonts})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if selected != "" && selected != entry {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for name := range scope {
|
||||||
|
if !indexes[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result[name] == nil {
|
||||||
|
result[name] = &editorResourceRefs{ids: make(map[string]bool), fonts: make(map[string]*editorFontUsage)}
|
||||||
|
}
|
||||||
|
result[name].merge(refs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name, refs := range references {
|
||||||
|
if !used[name] && (len(refs.ids) != 0 || len(refs.fonts) != 0 || indexes[name]) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, ctx.Err()
|
||||||
|
}
|
||||||
@@ -47,11 +47,12 @@ type editorResourceRefs struct {
|
|||||||
|
|
||||||
// compactSourceReferences 合并自产页面引用,版本文档按当前版本范围扫描
|
// compactSourceReferences 合并自产页面引用,版本文档按当前版本范围扫描
|
||||||
// 按GB/T33190-2016附录A检查标识和路径引用,保留扫描范围内孤立XML中的引用
|
// 按GB/T33190-2016附录A检查标识和路径引用,保留扫描范围内孤立XML中的引用
|
||||||
// 多文档、未知扩展或无法解析的XML不清理资源,避免误删共享或无法确认用途的条目
|
// 多文档压缩按文档归集引用,未知扩展或无法解析的XML不清理资源
|
||||||
// 入参: parts 输出条目, progress 保存进度回调, generated 自产页面及引用
|
// 入参: parts 输出条目, progress 保存进度回调, generated 自产页面及引用
|
||||||
// 返回: map[string]bool 可移除条目, error 读取或取消错误
|
// 返回: map[string]bool 可移除条目, error 读取或取消错误
|
||||||
func (e *Editor) compactSourceReferences(parts map[string][]byte, progress editorProgress, generated map[string]editorGeneratedReferences) (map[string]bool, error) {
|
func (e *Editor) compactSourceReferences(parts map[string][]byte, progress editorProgress, generated map[string]editorGeneratedReferences) (map[string]bool, error) {
|
||||||
if e.source.reader.DocumentCount() > 1 || e.output != nil && e.output.protected {
|
multiple := e.source.reader.DocumentCount() > 1
|
||||||
|
if e.output != nil && e.output.protected || multiple && (e.output == nil || e.output.options.Mode == CompressionUnchanged) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if len(parts) == 0 && e.revision == 0 && (e.output == nil || e.output.options.Mode == CompressionUnchanged) {
|
if len(parts) == 0 && e.revision == 0 && (e.output == nil || e.output.options.Mode == CompressionUnchanged) {
|
||||||
@@ -84,13 +85,27 @@ func (e *Editor) compactSourceReferences(parts map[string][]byte, progress edito
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refs, resources, _, safe, err := scanEditorResources(ctx, reader, parts, generated, names, progress, false)
|
refs, resources, references, safe, err := scanEditorResources(ctx, reader, parts, generated, names, progress, multiple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !safe {
|
if !safe {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
var scoped map[string]*editorResourceRefs
|
||||||
|
if multiple {
|
||||||
|
selected := ""
|
||||||
|
if reader.OFD.DocBody[reader.documentIndex].versioned {
|
||||||
|
selected, err = reader.DocumentRoot()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scoped, err = reader.resourceReferenceScopes(ctx, parts, generated, refs.documents, resources, references, selected)
|
||||||
|
if err != nil || scoped == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
ordered := slices.Sorted(maps.Keys(names))
|
ordered := slices.Sorted(maps.Keys(names))
|
||||||
removed := maps.Clone(e.source.retiredAttachments)
|
removed := maps.Clone(e.source.retiredAttachments)
|
||||||
if removed == nil {
|
if removed == nil {
|
||||||
@@ -115,7 +130,11 @@ func (e *Editor) compactSourceReferences(parts map[string][]byte, progress edito
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
preserved := refs.versions[cleanPackagePath(name)]
|
usageRefs := refs
|
||||||
|
if multiple {
|
||||||
|
usageRefs = scoped[name]
|
||||||
|
}
|
||||||
|
preserved := refs.versions[cleanPackagePath(name)] || usageRefs == nil
|
||||||
var patches []editorXMLPatch
|
var patches []editorXMLPatch
|
||||||
for _, group := range root.children {
|
for _, group := range root.children {
|
||||||
var members []editorXMLPatch
|
var members []editorXMLPatch
|
||||||
@@ -130,7 +149,7 @@ func (e *Editor) compactSourceReferences(parts map[string][]byte, progress edito
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
id := editorResourceID(node.attr("ID"))
|
id := editorResourceID(node.attr("ID"))
|
||||||
keep := preserved || id == "" || refs.ids[id] || field == "MediaFile" && node.attr("Type") != "Image"
|
keep := preserved || id == "" || usageRefs.ids[id] || field == "MediaFile" && node.attr("Type") != "Image"
|
||||||
if file := node.child(field); file != nil {
|
if file := node.child(field); file != nil {
|
||||||
var value string
|
var value string
|
||||||
if err := xml.Unmarshal(data[file.start:file.end], &value); err != nil {
|
if err := xml.Unmarshal(data[file.start:file.end], &value); err != nil {
|
||||||
@@ -138,8 +157,8 @@ func (e *Editor) compactSourceReferences(parts map[string][]byte, progress edito
|
|||||||
}
|
}
|
||||||
if location := editorResourceLocation(name, root.attr("BaseLoc"), value); location != "" {
|
if location := editorResourceLocation(name, root.attr("BaseLoc"), value); location != "" {
|
||||||
alternate := cleanPackagePath(resolveResourcePath(name, root.attr("BaseLoc"), value))
|
alternate := cleanPackagePath(resolveResourcePath(name, root.attr("BaseLoc"), value))
|
||||||
if field == "FontFile" {
|
if field == "FontFile" && !multiple {
|
||||||
usage := refs.fonts[id]
|
usage := usageRefs.fonts[id]
|
||||||
charset := node.attr("Charset")
|
charset := node.attr("Charset")
|
||||||
unsafe := preserved || id == "" || charset != "" && !strings.EqualFold(charset, "unicode") || location != alternate
|
unsafe := preserved || id == "" || charset != "" && !strings.EqualFold(charset, "unicode") || location != alternate
|
||||||
for _, file := range []string{location, alternate} {
|
for _, file := range []string{location, alternate} {
|
||||||
|
|||||||
Reference in new issue
Block a user