feat(编辑能力): 修复字体选择与裁剪边界问题
build.yaml / build (push) Successful in 35s

This commit is contained in:
xiaoqidun committed 2026-09-16 18:32:20 +08:00
1 parent 71e67be9c1
commit 1914b5329a
25 files changed
+747 -253

No files matched your search

+30 -30
View File
@@ -28,7 +28,7 @@ import (
"github.com/tdewolff/font"
)
// editorSource 保留输入包及按需加载的原始页面。
// editorSource 保留输入包及按需加载的原始页面
type editorSource struct {
reader *Reader
document *Document
@@ -39,7 +39,7 @@ type editorSource struct {
idsReady bool
}
// editorObjectOrigin 保留原对象的XML语义,供复制和局部更新复用。
// editorObjectOrigin 保留原对象的XML语义,供复制和局部更新复用
type editorObjectOrigin struct {
page *editorSourcePage
node *editorXML
@@ -47,7 +47,7 @@ type editorObjectOrigin struct {
reason string
}
// editorSourcePage 保存页面原文、解析快照及图层和页块内的对象位置。
// editorSourcePage 保存页面原文、解析快照及图层和页块内的对象位置
type editorSourcePage struct {
ref Page
data []byte
@@ -56,9 +56,9 @@ type editorSourcePage struct {
nodes map[string]*editorXML
}
// ObjectCapabilities 已有对象可执行的操作,Reason说明受限原因。
// Update表示完整替换内容,ReplaceFont表示可显式替换文字字体,Transform表示保留文字定位的几何变换,Order仅限同一图层或末级页块。
// Arrange表示可准确度量对齐与旋转范围;Reflow表示可重排文字,LayoutKnown表示段落选项可恢复。
// ObjectCapabilities 已有对象可执行的操作,Reason说明受限原因
// Update表示完整替换内容,ReplaceFont表示可显式替换文字字体,Transform表示保留文字定位的几何变换,Order仅限同一图层或末级页块
// Arrange表示可准确度量对齐与旋转范围;Reflow表示可重排文字,LayoutKnown表示段落选项可恢复
type ObjectCapabilities struct {
Update bool
ReplaceFont bool
@@ -72,7 +72,7 @@ type ObjectCapabilities struct {
Reason string
}
// PageCapabilities 页面可执行的操作,Insert表示可以添加RGB对象;新增空白页面始终可用。
// PageCapabilities 页面可执行的操作,Insert表示可以添加RGB对象;新增空白页面始终可用
type PageCapabilities struct {
Insert bool
Copy bool
@@ -81,9 +81,9 @@ type PageCapabilities struct {
Resize bool
}
// Editor 打开主文档进行保真编辑,不修改输入包,不以权限声明或签名限制编辑。
// 原权限和签名数据保留,修改受保护内容会使原签名失效,不重新签名。
// 页面按需解析,未修改条目直接保留;编辑器及其Reader快照使用期间不得关闭输入Reader。
// Editor 打开主文档进行保真编辑,不修改输入包,不以权限声明或签名限制编辑
// 原权限和签名数据保留,修改受保护内容会使原签名失效,不重新签名
// 页面按需解析,未修改条目直接保留;编辑器及其Reader快照使用期间不得关闭输入Reader
// 返回: *Editor 编辑器, error 错误信息
func (r *Reader) Editor() (*Editor, error) {
if r.Zip != nil {
@@ -129,7 +129,7 @@ func (r *Reader) Editor() (*Editor, error) {
return e, nil
}
// editorDirectoryExists 判断包内目录是否已经使用,避免覆盖原始资源。
// editorDirectoryExists 判断包内目录是否已经使用,避免覆盖原始资源
// 入参: reader 输入包, directory 目录路径
// 返回: bool 是否存在
func editorDirectoryExists(reader *Reader, directory string) bool {
@@ -146,7 +146,7 @@ func editorDirectoryExists(reader *Reader, directory string) bool {
return false
}
// prepareSourceIDs 首次新增内容前流式核对原包标识,不依赖可能缺失或过期的MaxUnitID。
// prepareSourceIDs 首次新增内容前流式核对原包标识,不依赖可能缺失或过期的MaxUnitID
// 返回: error 错误信息
func (e *Editor) prepareSourceIDs() error {
if e.source == nil || e.source.idsReady {
@@ -180,7 +180,7 @@ func (e *Editor) prepareSourceIDs() error {
return nil
}
// editorXMLMaxID 流式读取OFD结构中的最大标识,跳过非OFD的XML附件。
// editorXMLMaxID 流式读取OFD结构中的最大标识,跳过非OFD的XML附件
// 入参: input XML输入流
// 返回: int 最大标识, error 错误信息
func editorXMLMaxID(input io.Reader) (int, error) {
@@ -217,7 +217,7 @@ func editorXMLMaxID(input io.Reader) (int, error) {
}
}
// resourceDirectory 获取新增二进制资源的目录。
// resourceDirectory 获取新增二进制资源的目录
// 返回: string 包内路径
func (e *Editor) resourceDirectory() string {
if e.source != nil {
@@ -226,14 +226,14 @@ func (e *Editor) resourceDirectory() string {
return "Doc_0/Res"
}
// originalPage 判断索引是否指向原文档页面。
// originalPage 判断索引是否指向原文档页面
// 入参: index 页面索引
// 返回: bool 是否为原始页面
func (e *Editor) originalPage(index int) bool {
return e.source != nil && index >= 0 && index < len(e.pages) && e.source.pages[e.pages[index].ID] != nil
}
// PageCapabilities 获取页面操作范围;原始页面暂不复制、删除,避免破坏模板与外部引用。
// PageCapabilities 获取页面操作范围;原始页面暂不复制、删除,避免破坏模板与外部引用
// 入参: index 页面索引
// 返回: PageCapabilities 操作能力, error 错误信息
func (e *Editor) PageCapabilities(index int) (PageCapabilities, error) {
@@ -244,7 +244,7 @@ func (e *Editor) PageCapabilities(index int) (PageCapabilities, error) {
return PageCapabilities{Insert: e.sourceRGB(), Copy: !original, Delete: !original, Move: true, Resize: true}, nil
}
// sourceRGB 判断新建RGB对象能否直接使用文档默认颜色空间。
// sourceRGB 判断新建RGB对象能否直接使用文档默认颜色空间
// 返回: bool 是否为8位RGB文档
func (e *Editor) sourceRGB() bool {
if e.source == nil || e.source.document.CommonData.DefaultCS == 0 {
@@ -254,7 +254,7 @@ func (e *Editor) sourceRGB() bool {
return space != nil && space.Type == "RGB" && (space.BitsPerComponent == 0 || space.BitsPerComponent == 8) && len(space.Palette) == 0
}
// loadSourcePage 首次访问时解析原始页面,不加载图片或字体二进制数据。
// loadSourcePage 首次访问时解析原始页面,不加载图片或字体二进制数据
// 入参: index 页面索引
// 返回: error 错误信息
func (e *Editor) loadSourcePage(index int) error {
@@ -310,9 +310,9 @@ func (e *Editor) loadSourcePage(index int) error {
return nil
}
// ObjectCapabilities 获取对象可执行的操作,不支持的内容保持原文,不强制转换。
// 字体缺失或子集不包含原文时仍可移动、删除,不允许重新排版或复制。
// 显式提供可用字体后可通过UpdateObject替换;原文布局未知时不自动推断段落选项。
// ObjectCapabilities 获取对象可执行的操作,不支持的内容保持原文,不强制转换
// 字体缺失或子集不包含原文时仍可移动、删除,不允许重新排版或复制
// 显式提供可用字体后可通过UpdateObject替换;原文布局未知时不自动推断段落选项
// 入参: page 页面索引, id 对象标识
// 返回: ObjectCapabilities 操作能力, error 错误信息
func (e *Editor) ObjectCapabilities(page int, id string) (ObjectCapabilities, error) {
@@ -365,7 +365,7 @@ func (e *Editor) ObjectCapabilities(page int, id string) (ObjectCapabilities, er
return all, nil
}
// objectOrigin 获取原对象或其副本的保真来源。
// objectOrigin 获取原对象或其副本的保真来源
// 入参: id 对象标识
// 返回: *editorObjectOrigin 原始对象,无来源时为nil
func (e *Editor) objectOrigin(id string) *editorObjectOrigin {
@@ -375,7 +375,7 @@ func (e *Editor) objectOrigin(id string) *editorObjectOrigin {
return e.source.origins[id]
}
// editorContainerOrderable 判断容器的直接对象能否在保留页块的前提下排序。
// editorContainerOrderable 判断容器的直接对象能否在保留页块的前提下排序
// 入参: container 图层或页块
// 返回: bool 是否可排序
func editorContainerOrderable(container *editorXML) bool {
@@ -387,7 +387,7 @@ func editorContainerOrderable(container *editorXML) bool {
return true
}
// editorGeometry 获取基本对象的边界与变换。
// editorGeometry 获取基本对象的边界与变换
// 入参: object 对象
// 返回: string 边界, string 变换矩阵
func editorGeometry(object GraphicObject) (string, string) {
@@ -402,7 +402,7 @@ func editorGeometry(object GraphicObject) (string, string) {
return "", ""
}
// editorFont 按需解析原文档内嵌字体,不用系统回退替换原字体。
// editorFont 按需解析原文档内嵌字体,不用系统回退替换原字体
// 入参: id 字体资源标识
// 返回: *font.SFNT 字体, error 错误信息
func (e *Editor) editorFont(id string) (*font.SFNT, error) {
@@ -431,7 +431,7 @@ func (e *Editor) editorFont(id string) (*font.SFNT, error) {
return nil, fmt.Errorf("embedded font %q not found", id)
}
// editorImage 按需读取原图片尺寸,不解码完整像素。
// editorImage 按需读取原图片尺寸,不解码完整像素
// 入参: id 图片资源标识
// 返回: image.Point 图片尺寸, error 错误信息
func (e *Editor) editorImage(id string) (image.Point, error) {
@@ -456,14 +456,14 @@ func (e *Editor) editorImage(id string) (image.Point, error) {
return size, nil
}
// cloneEditorData 深复制编辑快照,未导出的排版信息保持不可变共享。
// cloneEditorData 深复制编辑快照,未导出的排版信息保持不可变共享
// 入参: value 原值
// 返回: T 独立副本
func cloneEditorData[T any](value T) T {
return cloneEditorValue(reflect.ValueOf(value)).Interface().(T)
}
// cloneEditorValue 复制结构体中的可变指针和切片。
// cloneEditorValue 复制结构体中的可变指针和切片
// 入参: value 原值
// 返回: reflect.Value 独立副本
func cloneEditorValue(value reflect.Value) reflect.Value {
@@ -495,7 +495,7 @@ func cloneEditorValue(value reflect.Value) reflect.Value {
return value
}
// editorObjectXML 编码可编辑对象,包含独立命名空间声明。
// editorObjectXML 编码可编辑对象,包含独立命名空间声明
// 入参: object 对象
// 返回: []byte XML片段, error 错误信息
func editorObjectXML(object GraphicObject) ([]byte, error) {
@@ -503,7 +503,7 @@ func editorObjectXML(object GraphicObject) ([]byte, error) {
return bytes.TrimPrefix(data, []byte(xml.Header)), err
}
// validateEditorGeometry 校验几何操作产生的数值,不重新排版或替换字体。
// validateEditorGeometry 校验几何操作产生的数值,不重新排版或替换字体
// 入参: object 变换后的对象
// 返回: error 错误信息
func validateEditorGeometry(object GraphicObject) error {