mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
1 parent
f71000befd
commit
6a9d2a12eb
3 files changed
+157
-10
No files matched your search
@@ -530,7 +530,7 @@ func (e *Editor) restoreFontSnapshot(state Editor) {
|
|||||||
*e = state
|
*e = state
|
||||||
}
|
}
|
||||||
|
|
||||||
// pruneExternalFontFiles 清理全包不再引用的字体文件,遇到无法识别的扩展时保留资源
|
// pruneExternalFontFiles 清理全包无引用字体并同步版本清单,保留附件及无法确认的扩展引用
|
||||||
// 入参: ctx 取消上下文, reader 包快照, candidates 待清理的字体路径
|
// 入参: ctx 取消上下文, reader 包快照, candidates 待清理的字体路径
|
||||||
// 返回: error 取消或读取错误
|
// 返回: error 取消或读取错误
|
||||||
func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...string) error {
|
func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...string) error {
|
||||||
@@ -546,26 +546,29 @@ func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...s
|
|||||||
if len(removed) == 0 {
|
if len(removed) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
files, structures := make(map[string]bool), map[string]bool{"OFD.xml": true}
|
||||||
for i := range reader.DocumentCount() {
|
for i := range reader.DocumentCount() {
|
||||||
files, err := reader.documentFiles(ctx, i)
|
_, xmlFiles, err := reader.documentDependencies(ctx, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for name := range files {
|
maps.Copy(structures, xmlFiles)
|
||||||
delete(removed, name)
|
}
|
||||||
}
|
versions := make(map[string]*editorFontXML)
|
||||||
if len(removed) == 0 {
|
names := slices.Sorted(maps.Keys(structures))
|
||||||
return nil
|
for _, name := range slices.Sorted(maps.Keys(reader.fileNames)) {
|
||||||
|
if !structures[name] && strings.HasSuffix(strings.ToLower(name), ".xml") {
|
||||||
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name := range reader.fileNames {
|
for _, name := range names {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !strings.HasSuffix(strings.ToLower(name), ".xml") {
|
if files[name] && !structures[name] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data, err := reader.readFile(name)
|
data, err := reader.readFile(name)
|
||||||
@@ -580,19 +583,36 @@ func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...s
|
|||||||
if root.matchesOFD("Res") {
|
if root.matchesOFD("Res") {
|
||||||
base = root.attr("BaseLoc")
|
base = root.attr("BaseLoc")
|
||||||
}
|
}
|
||||||
|
if root.matchesOFD("DocVersion") {
|
||||||
|
versions[name] = &editorFontXML{name: name, data: data, root: root}
|
||||||
|
}
|
||||||
var retains func(*editorXML) bool
|
var retains func(*editorXML) bool
|
||||||
retains = func(node *editorXML) bool {
|
retains = func(node *editorXML) bool {
|
||||||
if classifyOFDNamespace(node.name.Space) == ofdXMLUnknown || slices.Contains([]string{"CustomTags", "Extensions", "ExtendData", "Data"}, node.name.Local) {
|
if classifyOFDNamespace(node.name.Space) == ofdXMLUnknown || slices.Contains([]string{"CustomTags", "Extensions", "ExtendData", "Data"}, node.name.Local) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
parent := ""
|
||||||
|
if node.parent != nil {
|
||||||
|
parent = node.parent.name.Local
|
||||||
|
}
|
||||||
for _, attr := range node.attrs {
|
for _, attr := range node.attrs {
|
||||||
if attr.Name.Space != "" && attr.Name.Space != "xmlns" && attr.Name.Space != "http://www.w3.org/XML/1998/namespace" {
|
if attr.Name.Space != "" && attr.Name.Space != "xmlns" && attr.Name.Space != "http://www.w3.org/XML/1998/namespace" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if kind := packageReference(root.name.Local, parent, node.name.Local, attr.Name.Local); attr.Name.Space == "" && kind != packageReferenceNone {
|
||||||
|
location := cleanPackagePath(resolveResourcePath(name, base, attr.Value))
|
||||||
|
delete(removed, location)
|
||||||
|
if kind == packageReferenceData {
|
||||||
|
files[location] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if node.matchesOFD("FontFile") {
|
if kind := packageReference(root.name.Local, parent, node.name.Local, ""); kind != packageReferenceNone && !(root.matchesOFD("DocVersion") && parent == "FileList" && node.matchesOFD("File")) {
|
||||||
location := resolveResourcePath(name, base, strings.TrimSpace(editorImportText(data, node)))
|
location := resolveResourcePath(name, base, strings.TrimSpace(editorImportText(data, node)))
|
||||||
delete(removed, cleanPackagePath(location))
|
delete(removed, cleanPackagePath(location))
|
||||||
|
if kind == packageReferenceData {
|
||||||
|
files[cleanPackagePath(location)] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return slices.ContainsFunc(node.children, retains)
|
return slices.ContainsFunc(node.children, retains)
|
||||||
}
|
}
|
||||||
@@ -603,6 +623,27 @@ func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...s
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
updates := make(map[string][]byte)
|
||||||
|
for name, file := range versions {
|
||||||
|
var patches []editorXMLPatch
|
||||||
|
if list := file.root.child("FileList"); list != nil {
|
||||||
|
for _, node := range list.children {
|
||||||
|
if node.matchesOFD("File") && removed[resolveResourcePath(name, "", strings.TrimSpace(editorImportText(file.data, node)))] {
|
||||||
|
patches = append(patches, editorXMLPatch{start: node.start, end: node.end})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(patches) != 0 {
|
||||||
|
updates[name] = editorPatchXML(file.data, patches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reader.files == nil {
|
||||||
|
reader.files = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
maps.Copy(reader.files, updates)
|
||||||
for name := range reader.files {
|
for name := range reader.files {
|
||||||
if removed[cleanPackagePath(name)] {
|
if removed[cleanPackagePath(name)] {
|
||||||
delete(reader.files, name)
|
delete(reader.files, name)
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// 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"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compactVersionFonts 清理仅由版本清单保留的无引用字体,同步清单且不修改编辑器快照
|
||||||
|
// 入参: ctx 取消上下文, parts 输出修改, removed 待删除条目
|
||||||
|
// 返回: error 读取、结构或取消错误
|
||||||
|
func (e *Editor) compactVersionFonts(ctx context.Context, parts map[string][]byte, removed map[string]bool) error {
|
||||||
|
reader := e.source.reader
|
||||||
|
versioned := false
|
||||||
|
for _, body := range reader.OFD.DocBody {
|
||||||
|
versioned = versioned || body.versioned
|
||||||
|
}
|
||||||
|
if !versioned {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
protected, err := reader.hasOutputSignatures()
|
||||||
|
if err != nil || protected {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
view := &Reader{Zip: reader.Zip, files: maps.Clone(reader.files)}
|
||||||
|
if view.files == nil {
|
||||||
|
view.files = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
maps.Copy(view.files, parts)
|
||||||
|
if err := view.initRoot(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
candidates, seen := make(map[string]bool), make(map[string]bool)
|
||||||
|
for index := range view.DocumentCount() {
|
||||||
|
view.documentIndex = index
|
||||||
|
versions, err := view.DocumentVersions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, version := range versions {
|
||||||
|
for _, file := range version.Files {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := file.Location
|
||||||
|
if seen[name] || removed[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[name] = true
|
||||||
|
input, err := view.openFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var header [4]byte
|
||||||
|
n, err := io.ReadFull(input, header[:])
|
||||||
|
input.Close()
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == len(header) {
|
||||||
|
switch string(header[:]) {
|
||||||
|
case "\x00\x01\x00\x00", "OTTO", "ttcf":
|
||||||
|
candidates[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(candidates) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := pruneExternalFontFiles(ctx, view, slices.Collect(maps.Keys(candidates))...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for name, data := range view.files {
|
||||||
|
if original, ok := reader.files[name]; !ok || !bytes.Equal(original, data) {
|
||||||
|
parts[name] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name := range candidates {
|
||||||
|
if !view.fileNames[name] {
|
||||||
|
removed[name] = true
|
||||||
|
delete(parts, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -620,6 +620,9 @@ func (e *Editor) writeSource(writer io.Writer, fonts map[string][]byte, progress
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := e.compactVersionFonts(ctx, parts, removed); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
if len(parts) != 0 || len(removed) != 0 || e.output != nil && e.output.options.Mode != CompressionUnchanged && !e.output.protected {
|
if len(parts) != 0 || len(removed) != 0 || e.output != nil && e.output.options.Mode != CompressionUnchanged && !e.output.protected {
|
||||||
name := "OFD.xml"
|
name := "OFD.xml"
|
||||||
if file, ok := reader.packageFile(name); ok {
|
if file, ok := reader.packageFile(name); ok {
|
||||||
|
|||||||
Reference in new issue
Block a user