diff --git a/go.mod b/go.mod index db2b0b6..db1680c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6 github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 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/image v0.47.0 golang.org/x/text v0.43.0 diff --git a/go.sum b/go.sum index 3292087..1179e7f 100644 --- a/go.sum +++ b/go.sum @@ -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/jbig2 v1.1.4 h1:LF6J1si4JY+TszTh9+m4KAg9/ba0JjfXoGtQTQC2S8c= 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-20261010042215-c8c66c83f031/go.mod h1:fXz1emtF1YCpcIv+fglDZstjkHxjS2l3G4DVMgUzVnk= +github.com/xiaoqidun/pdfgo v0.0.0-20261010050917-6c0a77a9f79a h1:6zWvNSCI6VSW7DIwFtlfrUe8r57dtrykyO9ShXMOssI= +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/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= diff --git a/ofdgo_editor_composite_text.go b/ofdgo_editor_composite_text.go index 03a750f..80f9d0d 100644 --- a/ofdgo_editor_composite_text.go +++ b/ofdgo_editor_composite_text.go @@ -29,7 +29,7 @@ func (e *Editor) UpdateCompositeText(page int, path ObjectPath, index int, value } // StyleCompositeText 批量修改内部文字字体、字号或颜色,换字体保留原文字定位 -// 字体缺字或复杂字形映射使整批操作失败,不使用回退字体代替原文 +// 更换字体时同步字形索引,无法逐字映射时需显式重排,缺字使整批操作失败 // 入参: page 页面索引, path 父复合路径, indexes 成员序号, style 文字样式增量 // 返回: error 错误信息 func (e *Editor) StyleCompositeText(page int, path ObjectPath, indexes []int, style TextStyle) error { @@ -78,7 +78,13 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite content, layout := text.TextLayout() original := content if style.Font != "" { - text.Font = 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 + } } if value != nil { content = *value @@ -105,6 +111,7 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite return err } 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 { if style.Size != 0 { text.Size = style.Size @@ -131,6 +138,7 @@ func (e *Editor) updateCompositeText(node *editorCompositeNode, member Composite return err } object.TextObject.Font = style.Font + object.TextObject.CGTransform = text.CGTransform } if style.Color != "" { if err := creationColor(&FillColor{Value: style.Color}); err != nil { diff --git a/ofdgo_editor_text.go b/ofdgo_editor_text.go index 1fc36f9..d11a859 100644 --- a/ofdgo_editor_text.go +++ b/ofdgo_editor_text.go @@ -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)} } if fontChanged { - text.Font = style.Font + if known { + text.Font = style.Font + } else if err := e.replaceTextFont(text, style.Font); err != nil { + return nil, err + } } if sizeChanged { text.Size = style.Size @@ -390,6 +394,49 @@ func (e *Editor) styleTextObjects(objects []GraphicObject, style TextStyle) ([]G 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断行机会,过长词仅在字素边界折行,不丢弃空白 // 入参: runes 段落字符, advances 含字距的字符步进, width 本地排版宽度, wrap 是否自动折行, spacing 附加字距, indent 首行缩进 // 返回: [][2]int 各行的字符起止索引,左闭右开