feat(渲染编辑): 完善渲染压缩及文档版本编辑
build.yaml / build (push) Successful in 1m51s

This commit is contained in:
xiaoqidun committed 2026-10-11 18:45:54 +08:00
1 parent cfbaa8a177
commit 2c06e5ff69
43 files changed
+3211 -397

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
// 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 (
"fmt"
"math"
)
// compressionPatternImageLimit 限制底纹尺寸分析的变换组合数,超限时保留原图尺寸
const compressionPatternImageLimit = 65536
// compressionPatternKey 复用相同底纹的尺寸分析,平移不影响像素需求
type compressionPatternKey struct {
pattern *Pattern
linear [4]float64
}
// objectPatterns 统计文字和路径的填充及描边底纹,保留绘制参数继承关系
// 入参: object 对象样式及坐标, state 继承状态
// 返回: error 底纹分析错误
func (v *compressionImages) objectPatterns(object PathObject, state RenderState) error {
fill, stroke := object.FillColor, (*FillColor)(object.StrokeColor)
if defaults := v.renderer.drawParamDefaults(object.DrawParam, state.Defaults); defaults != nil {
if fill == nil {
fill = defaults.FillColor
}
if stroke == nil {
stroke = (*FillColor)(defaults.StrokeColor)
}
}
if (fill == nil || fill.Pattern == nil) && (stroke == nil || stroke.Pattern == nil) {
return nil
}
matrix, _ := renderObjectMatrix(object.Boundary, NewMatrix(object.CTM), state)
for _, color := range [2]*FillColor{fill, stroke} {
if color != nil && color.Pattern != nil {
if err := v.patternImages(color, matrix); err != nil {
return err
}
}
}
return nil
}
// patternImages 按独立单元坐标汇总底纹图片及缩略图,不展开重复铺排的单元
// 入参: color 底纹颜色, object 对象到页面矩阵
// 返回: error 取消、循环引用或变换错误
func (v *compressionImages) patternImages(color *FillColor, object Matrix) error {
if err := v.ctx.Err(); err != nil {
return err
}
pattern := color.Pattern
if v.patterns[pattern] {
return fmt.Errorf("cyclic pattern image reference")
}
if !finite(pattern.Width) || !finite(pattern.Height) || pattern.Width <= 0 || pattern.Height <= 0 {
return fmt.Errorf("invalid pattern cell size")
}
matrix := NewMatrix(pattern.CTM)
switch pattern.RelativeTo {
case "", "Object":
matrix = object.Multiply(matrix)
case "Page":
if v.patternPage != nil {
matrix = v.patternPage.Multiply(matrix)
}
default:
return fmt.Errorf("invalid pattern coordinate system")
}
if !finite(matrix.a) || !finite(matrix.b) || !finite(matrix.c) || !finite(matrix.d) {
return fmt.Errorf("invalid pattern transform")
}
matrix.e, matrix.f = 0, 0
key := compressionPatternKey{pattern: pattern, linear: [4]float64{matrix.a, matrix.b, matrix.c, matrix.d}}
if v.patternVisits[key] {
return nil
}
if len(v.patternVisits) >= compressionPatternImageLimit {
return fmt.Errorf("pattern image analysis limit exceeded")
}
if v.patterns == nil {
v.patterns = make(map[*Pattern]bool)
v.patternVisits = make(map[compressionPatternKey]bool)
}
v.patternVisits[key] = true
v.patterns[pattern] = true
defer delete(v.patterns, pattern)
if pattern.CellContent.Thumbnail != "" {
id := editorResourceID(pattern.CellContent.Thumbnail)
if id == "" {
return fmt.Errorf("invalid pattern thumbnail resource")
}
size := compressionImageSize(pattern.Width*math.Hypot(matrix.a, matrix.b), pattern.Height*math.Hypot(matrix.c, matrix.d), v.dpi)
if err := v.recordSize([2]string{id}, size); err != nil {
return err
}
}
columns, rows := 1, 1
switch pattern.ReflectMethod {
case "", "Normal":
case "Column":
columns = 2
case "Row":
rows = 2
case "RowAndColumn":
columns, rows = 2, 2
default:
return fmt.Errorf("invalid pattern reflection")
}
parent := v.patternPage
defer func() { v.patternPage = parent }()
solid := FillColor{Value: color.Value, Index: color.Index, ColorSpace: color.ColorSpace}
defaults := &DrawParam{FillColor: &solid, StrokeColor: (*StrokeColor)(&solid)}
for column := range columns {
for row := range rows {
cell := matrix.Multiply(Matrix{a: float64(1 - 2*column), d: float64(1 - 2*row)})
v.patternPage = &cell
for member := range pattern.CellContent.objects() {
if err := v.ctx.Err(); err != nil {
return err
}
if err := v.renderer.WalkObject(&member, RenderState{Defaults: defaults, Parent: &cell, BoundaryInCTM: true}, v); err != nil {
return err
}
}
}
}
return nil
}