feat(文字编辑): 修复文字编辑字形映射
build.yaml / build (push) Successful in 2m55s

This commit is contained in:
xiaoqidun committed 2026-10-10 13:11:03 +08:00
1 parent 0f2fb7a842
commit e2c74894f0
4 files changed
+59 -4

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ require (
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6 github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893
github.com/xiaoqidun/jbig2 v1.1.4 github.com/xiaoqidun/jbig2 v1.1.4
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031 github.com/xiaoqidun/pdfgo v0.0.0-20261010050917-6c0a77a9f79a
golang.org/x/crypto v0.58.0 golang.org/x/crypto v0.58.0
golang.org/x/image v0.47.0 golang.org/x/image v0.47.0
golang.org/x/text v0.43.0 golang.org/x/text v0.43.0
+2 -2
View File
@@ -62,8 +62,8 @@ github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 h1:jmQlBO7R+bcimOM
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA= github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
github.com/xiaoqidun/jbig2 v1.1.4 h1:LF6J1si4JY+TszTh9+m4KAg9/ba0JjfXoGtQTQC2S8c= github.com/xiaoqidun/jbig2 v1.1.4 h1:LF6J1si4JY+TszTh9+m4KAg9/ba0JjfXoGtQTQC2S8c=
github.com/xiaoqidun/jbig2 v1.1.4/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA= github.com/xiaoqidun/jbig2 v1.1.4/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA=
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031 h1:FV9MPGAQ08+AGYMPKesJWl42eLL5TPU+4niOlY1sVTY= github.com/xiaoqidun/pdfgo v0.0.0-20261010050917-6c0a77a9f79a h1:6zWvNSCI6VSW7DIwFtlfrUe8r57dtrykyO9ShXMOssI=
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031/go.mod h1:fXz1emtF1YCpcIv+fglDZstjkHxjS2l3G4DVMgUzVnk= github.com/xiaoqidun/pdfgo v0.0.0-20261010050917-6c0a77a9f79a/go.mod h1:fXz1emtF1YCpcIv+fglDZstjkHxjS2l3G4DVMgUzVnk=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
+9 -1
View File
@@ -29,7 +29,7 @@ func (e *Editor) UpdateCompositeText(page int, path ObjectPath, index int, value
} }
// StyleCompositeText 批量修改内部文字字体、字号或颜色,换字体保留原文字定位 // StyleCompositeText 批量修改内部文字字体、字号或颜色,换字体保留原文字定位
// 字体缺字或复杂字形映射使整批操作失败,不使用回退字体代替原文 // 更换字体时同步字形索引,无法逐字映射时需显式重排,缺字使整批操作失败
// 入参: page 页面索引, path 父复合路径, indexes 成员序号, style 文字样式增量 // 入参: page 页面索引, path 父复合路径, indexes 成员序号, style 文字样式增量
// 返回: error 错误信息 // 返回: error 错误信息
func (e *Editor) StyleCompositeText(page int, path ObjectPath, indexes []int, style TextStyle) error { func (e *Editor) StyleCompositeText(page int, path ObjectPath, indexes []int, style TextStyle) error {
@@ -78,8 +78,14 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite
content, layout := text.TextLayout() content, layout := text.TextLayout()
original := content original := content
if style.Font != "" { if style.Font != "" {
if state.layout == nil && options == nil && (style.Size == 0 || style.Size == text.Size) {
if err := e.replaceTextFont(&text, style.Font); err != nil {
return err
}
} else {
text.Font = style.Font text.Font = style.Font
} }
}
if value != nil { if value != nil {
content = *value content = *value
} }
@@ -105,6 +111,7 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite
return err return err
} }
object.TextObject.TextCode = text.TextCode object.TextObject.TextCode = text.TextCode
object.TextObject.CGTransform = text.CGTransform
} else if options != nil || content != original || style.Size != 0 && style.Size != text.Size || state.layout != nil && state.layout.options.Shape && style.Font != "" && style.Font != object.TextObject.Font { } else if options != nil || content != original || style.Size != 0 && style.Size != text.Size || state.layout != nil && state.layout.options.Shape && style.Font != "" && style.Font != object.TextObject.Font {
if style.Size != 0 { if style.Size != 0 {
text.Size = style.Size text.Size = style.Size
@@ -131,6 +138,7 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite
return err return err
} }
object.TextObject.Font = style.Font object.TextObject.Font = style.Font
object.TextObject.CGTransform = text.CGTransform
} }
if style.Color != "" { if style.Color != "" {
if err := creationColor(&FillColor{Value: style.Color}); err != nil { if err := creationColor(&FillColor{Value: style.Color}); err != nil {
+47
View File
@@ -361,7 +361,11 @@ func (e *Editor) styleTextObjects(objects []GraphicObject, style TextStyle) ([]G
return nil, &EditError{Code: EditUnsupportedObject, Err: fmt.Errorf("object %q requires explicit paragraph layout before changing size", id)} return nil, &EditError{Code: EditUnsupportedObject, Err: fmt.Errorf("object %q requires explicit paragraph layout before changing size", id)}
} }
if fontChanged { if fontChanged {
if known {
text.Font = style.Font text.Font = style.Font
} else if err := e.replaceTextFont(text, style.Font); err != nil {
return nil, err
}
} }
if sizeChanged { if sizeChanged {
text.Size = style.Size text.Size = style.Size
@@ -390,6 +394,49 @@ func (e *Editor) styleTextObjects(objects []GraphicObject, style TextStyle) ([]G
return updates, nil return updates, nil
} }
// replaceTextFont 更换字体并重建字形索引,保留原文字定位
// 入参: text 文字对象, id 目标字体标识
// 返回: error 错误信息
func (e *Editor) replaceTextFont(text *TextObject, id string) error {
if text.Font == id {
return nil
}
if len(text.CGTransform) == 0 {
text.Font = id
return nil
}
if err := validateTextGlyphs(*text, 1<<16); err != nil {
return err
}
font, err := e.editorFont(id)
if err != nil {
return err
}
var chars []rune
for _, code := range text.TextCode {
chars = append(chars, textCodeRunes(code.Value)...)
}
transforms := append([]CGTransform(nil), text.CGTransform...)
var missing []rune
for i := range transforms {
transform := &transforms[i]
if transform.CodeCount != 1 || transform.GlyphCount != 1 {
return &EditError{Code: EditLayoutRequired, Err: fmt.Errorf("changing the font of combined glyphs requires explicit layout")}
}
char := chars[transform.CodePosition]
glyph := font.GlyphIndex(char)
if glyph == 0 {
missing = append(missing, char)
}
transform.Glyphs = strconv.Itoa(int(glyph))
}
if err := missingGlyphError(id, missing); err != nil {
return err
}
text.Font, text.CGTransform = id, transforms
return nil
}
// breakTextLines 优先使用Unicode断行机会,过长词仅在字素边界折行,不丢弃空白 // breakTextLines 优先使用Unicode断行机会,过长词仅在字素边界折行,不丢弃空白
// 入参: runes 段落字符, advances 含字距的字符步进, width 本地排版宽度, wrap 是否自动折行, spacing 附加字距, indent 首行缩进 // 入参: runes 段落字符, advances 含字距的字符步进, width 本地排版宽度, wrap 是否自动折行, spacing 附加字距, indent 首行缩进
// 返回: [][2]int 各行的字符起止索引,左闭右开 // 返回: [][2]int 各行的字符起止索引,左闭右开