diff --git a/ofdgo_font_info.go b/ofdgo_font_info.go index c95e2d5..f00d84b 100644 --- a/ofdgo_font_info.go +++ b/ofdgo_font_info.go @@ -331,35 +331,7 @@ func (r *Renderer) countCompositeFonts(cgu CompositeGraphicUnit, usage map[strin // countTextFont 统计文本字体使用次数 // 入参: text 文本对象, usage 字体使用次数 func (r *Renderer) countTextFont(text TextObject, usage map[string]int) { - fontID := text.Font - if fontID == "" && text.DrawParam != "" { - fontID = r.drawParamFont(text.DrawParam, nil) - } - if fontID != "" { + if fontID := r.textObjectFontID(text); fontID != "" { usage[fontID]++ } } - -// drawParamFont 获取绘制参数字体 -// 入参: id 绘制参数ID, visited 已访问绘制参数 -// 返回: string 字体ID -func (r *Renderer) drawParamFont(id string, visited map[string]bool) string { - if id == "" { - return "" - } - if visited == nil { - visited = make(map[string]bool) - } - if visited[id] { - return "" - } - visited[id] = true - dp := r.DrawParams[id] - if dp == nil { - return "" - } - if dp.Font != "" { - return dp.Font - } - return r.drawParamFont(dp.Relative, visited) -} diff --git a/ofdgo_image.go b/ofdgo_image.go index f28f68f..ed7dd67 100644 --- a/ofdgo_image.go +++ b/ofdgo_image.go @@ -20,6 +20,9 @@ import ( "fmt" "image" "image/color" + + _ "github.com/xiaoqidun/jbig2" + _ "golang.org/x/image/bmp" ) // decodeImageData 解码图片数据 diff --git a/ofdgo_paint.go b/ofdgo_paint.go index 6c49483..0cf40ab 100644 --- a/ofdgo_paint.go +++ b/ofdgo_paint.go @@ -225,6 +225,16 @@ func parseStrokePaint(strokeColor *StrokeColor, x, y, pageH, originX, originY fl return nil } +// patternColor 获取图案单元默认颜色 +// 入参: fillColor 填充颜色节点 +// 返回: color.Color 默认颜色 +func patternColor(fillColor *FillColor) color.Color { + if fillColor == nil || strings.TrimSpace(fillColor.Value) == "" { + return nil + } + return parseColorWithAlpha(fillColor.Value, fillColor.Alpha) +} + // parseAxialShdColor 解析轴向渐变颜色 // 入参: axialShd 轴向渐变节点, alpha 透明度 // 返回: color.Color 颜色对象 diff --git a/ofdgo_renderer.go b/ofdgo_renderer.go index e3cbe05..611785b 100644 --- a/ofdgo_renderer.go +++ b/ofdgo_renderer.go @@ -29,8 +29,6 @@ import ( "github.com/tdewolff/canvas/renderers" "github.com/tdewolff/canvas/renderers/pdf" "github.com/tdewolff/canvas/renderers/rasterizer" - _ "github.com/xiaoqidun/jbig2" - _ "golang.org/x/image/bmp" ) // Renderer 渲染器实现 @@ -306,19 +304,7 @@ func (r *Renderer) renderTemplate(ctx *canvas.Context, templateID string, pageH // renderLayer 渲染图层 // 入参: ctx 画布上下文, layer 图层对象, pageH 页面高度, defaultFill 默认填充色, defaultStroke 默认描边色, defaultLW 默认线宽, parentCTM 父级CTM func (r *Renderer) renderLayer(ctx *canvas.Context, layer Layer, pageH float64, defaultFill, defaultStroke color.Color, defaultLW float64, parentCTM *Matrix) { - if layer.DrawParam != "" { - if dp := r.getDrawParam(layer.DrawParam, nil); dp != nil { - if dp.LineWidth > 0 { - defaultLW = dp.LineWidth - } - if dp.FillColor != nil { - defaultFill = parseFillColor(dp.FillColor) - } - if dp.StrokeColor != nil { - defaultStroke = parseStrokeColor(dp.StrokeColor) - } - } - } + defaultFill, defaultStroke, defaultLW = r.drawParamDefaults(layer.DrawParam, defaultFill, defaultStroke, defaultLW) if len(layer.Objects) > 0 { for _, obj := range layer.Objects { r.renderObject(ctx, obj, pageH, defaultFill, defaultStroke, defaultLW, parentCTM, false) @@ -357,19 +343,7 @@ func (r *Renderer) renderCompositeGraphicUnit(ctx *canvas.Context, cgu Composite r.renderCompositeGraphicUnit(ctx, refCopy, pageH, defaultFill, defaultStroke, defaultLW, ¤tCTM, true) } } - if cgu.DrawParam != "" { - if dp := r.getDrawParam(cgu.DrawParam, nil); dp != nil { - if dp.LineWidth > 0 { - defaultLW = dp.LineWidth - } - if dp.FillColor != nil { - defaultFill = parseFillColor(dp.FillColor) - } - if dp.StrokeColor != nil { - defaultStroke = parseStrokeColor(dp.StrokeColor) - } - } - } + defaultFill, defaultStroke, defaultLW = r.drawParamDefaults(cgu.DrawParam, defaultFill, defaultStroke, defaultLW) if len(cgu.Objects) > 0 { for _, obj := range cgu.Objects { obj = mergeGraphicObjectAlpha(obj, cgu.Alpha) @@ -432,6 +406,29 @@ func mergeGraphicObjectAlpha(obj GraphicObject, alpha *int) GraphicObject { return obj } +// drawParamDefaults 合并绘制参数默认样式 +// 入参: id 绘制参数ID, defaultFill 默认填充色, defaultStroke 默认描边色, defaultLW 默认线宽 +// 返回: color.Color 默认填充色, color.Color 默认描边色, float64 默认线宽 +func (r *Renderer) drawParamDefaults(id string, defaultFill, defaultStroke color.Color, defaultLW float64) (color.Color, color.Color, float64) { + if id == "" { + return defaultFill, defaultStroke, defaultLW + } + dp := r.getDrawParam(id, nil) + if dp == nil { + return defaultFill, defaultStroke, defaultLW + } + if dp.LineWidth > 0 { + defaultLW = dp.LineWidth + } + if dp.FillColor != nil { + defaultFill = parseFillColor(dp.FillColor) + } + if dp.StrokeColor != nil { + defaultStroke = parseStrokeColor(dp.StrokeColor) + } + return defaultFill, defaultStroke, defaultLW +} + // getDrawParam 获取绘制参数逻辑 // 入参: id 参数ID, visited 访问记录 // 返回: *DrawParam 绘制参数 @@ -744,16 +741,6 @@ func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *Pattern, defaultC } } -// patternColor 获取图案单元默认颜色 -// 入参: fillColor 填充颜色节点 -// 返回: color.Color 默认颜色 -func patternColor(fillColor *FillColor) color.Color { - if fillColor == nil || strings.TrimSpace(fillColor.Value) == "" { - return nil - } - return parseColorWithAlpha(fillColor.Value, fillColor.Alpha) -} - // renderText 渲染文本 // 入参: ctx 画布上下文, obj 文本对象, pageH 页面高度, defaultFill 默认填充色, defaultStroke 默认描边色, parentCTM 父级CTM, boundaryInCTM 边界是否参与父级CTM func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64, defaultFill, defaultStroke color.Color, parentCTM *Matrix, boundaryInCTM bool) { @@ -850,6 +837,8 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 } face := ff.Face(sizePt, fillPaint, fontStyle, canvas.FontNormal) glyphTransforms := r.textObjectGlyphTransforms(fontID, obj) + hasUnderline := strings.Contains(obj.Decoration, "Underline") + useGlyphFillPaint := fillColorNode != nil && fillColorNode.AxialShd != nil codePos := 0 for _, tc := range obj.TextCode { var runes []rune @@ -863,7 +852,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 } dxs, dys := parseFloats(tc.DeltaX), parseFloats(tc.DeltaY) xs, ys := parseFloats(tc.X), parseFloats(tc.Y) - drawAsPath := embeddedFont || textCodePositioned(tc) + drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) cx, cy := 0.0, 0.0 if len(xs) > 0 { cx = xs[0] @@ -900,7 +889,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 } textWidth := textGlyphWidth(face, glyph) * hScale glyphFillPaint := fillPaint - if fillColorNode != nil && fillColorNode.AxialShd != nil { + if useGlyphFillPaint { glyphFillPaint = parseFillPaint(fillColorNode, bx, by, pageH, canvasX, canvasY) } advanceLimit := textGlyphAdvanceLimit(dxs, dys, xs, i, len(glyphs), cx) @@ -932,7 +921,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 x, y = 0, 0 } textFace := face - if fillColorNode != nil && fillColorNode.AxialShd != nil { + if useGlyphFillPaint { textFace = ff.Face(sizePt, glyphFillPaint, fontStyle, canvas.FontNormal) } text := canvas.NewTextLine(textFace, str, canvas.Left) @@ -947,7 +936,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 ctx.Translate(canvasX, canvasY) ctx.ComposeView(textMatrix(ctm)) drawGlyph(0, 0) - if strings.Contains(obj.Decoration, "Underline") { + if hasUnderline { uw := sizeMM * 0.05 ctx.SetStrokeWidth(uw) ctx.SetStrokeColor(fillColor) @@ -961,7 +950,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 } drawGlyph(canvasX, canvasY) } - if strings.Contains(obj.Decoration, "Underline") { + if hasUnderline { uw := sizeMM * 0.05 ctx.SetStrokeWidth(uw) ctx.SetStrokeColor(fillColor) @@ -976,24 +965,6 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64 ctx.Pop() } -// hasTextMatrix 判断文本是否需要应用字形变换 -// 入参: ctm 变换矩阵 -// 返回: bool 是否需要变换 -func hasTextMatrix(ctm Matrix) bool { - const eps = 1e-9 - return math.Abs(ctm.a-1) > eps || math.Abs(ctm.b) > eps || math.Abs(ctm.c) > eps || math.Abs(ctm.d-1) > eps -} - -// textMatrix 获取文本字形变换矩阵 -// 入参: ctm OFD变换矩阵 -// 返回: canvas.Matrix 画布变换矩阵 -func textMatrix(ctm Matrix) canvas.Matrix { - return canvas.Matrix{ - {ctm.a, -ctm.c, 0}, - {-ctm.b, ctm.d, 0}, - } -} - // renderStamp 渲染印章 // 入参: ctx 画布上下文, s 印章对象, pageH 页面高度 func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) { @@ -1239,35 +1210,3 @@ func (r *Renderer) buildTinyFillRectPath(obj PathObject, pageH float64, ctm Matr p.Close() return p } - -// parseIndexRunes 解析索引字形 -// 入参: indexStr 索引字符串, fontID 字体ID -// 返回: []rune 字形列表 -func (r *Renderer) parseIndexRunes(indexStr string, fontID string) []rune { - var gids []int - parts := strings.Fields(indexStr) - for _, p := range parts { - if strings.Contains(p, "-") { - sub := strings.Split(p, "-") - if len(sub) == 2 { - start, _ := strconv.Atoi(sub[0]) - end, _ := strconv.Atoi(sub[1]) - for k := start; k <= end; k++ { - gids = append(gids, k) - } - } - } else { - val, _ := strconv.Atoi(p) - gids = append(gids, val) - } - } - var res []rune - for _, gid := range gids { - if rVal, ok := r.fontGlyphRune(fontID, gid); ok { - res = append(res, rVal) - continue - } - res = append(res, rune(gid)) - } - return res -} diff --git a/ofdgo_text.go b/ofdgo_text.go index 862e76e..293fea2 100644 --- a/ofdgo_text.go +++ b/ofdgo_text.go @@ -15,6 +15,8 @@ package ofdgo import ( + "math" + "strconv" "strings" "github.com/tdewolff/canvas" @@ -159,6 +161,24 @@ func textGlyphPath(face *canvas.FontFace, glyph textGlyph) (*canvas.Path, float6 return p, face.MmPerEm * float64(face.Font.GlyphAdvance(glyphID)) } +// hasTextMatrix 判断文本是否需要应用字形变换 +// 入参: ctm 变换矩阵 +// 返回: bool 是否需要变换 +func hasTextMatrix(ctm Matrix) bool { + const eps = 1e-9 + return math.Abs(ctm.a-1) > eps || math.Abs(ctm.b) > eps || math.Abs(ctm.c) > eps || math.Abs(ctm.d-1) > eps +} + +// textMatrix 获取文本字形变换矩阵 +// 入参: ctm OFD变换矩阵 +// 返回: canvas.Matrix 画布变换矩阵 +func textMatrix(ctm Matrix) canvas.Matrix { + return canvas.Matrix{ + {ctm.a, -ctm.c, 0}, + {-ctm.b, ctm.d, 0}, + } +} + // fontGlyphRune 获取字形ID对应的包装字体字符 // 入参: fontID 字体ID, glyphID 字形ID或CID // 返回: rune 包装字体字符, bool 是否存在 @@ -184,6 +204,38 @@ func (r *Renderer) fontGlyphRune(fontID string, glyphID int) (rune, bool) { return 0, false } +// parseIndexRunes 解析索引字形 +// 入参: indexStr 索引字符串, fontID 字体ID +// 返回: []rune 字形列表 +func (r *Renderer) parseIndexRunes(indexStr string, fontID string) []rune { + var gids []int + parts := strings.Fields(indexStr) + for _, p := range parts { + if strings.Contains(p, "-") { + sub := strings.Split(p, "-") + if len(sub) == 2 { + start, _ := strconv.Atoi(sub[0]) + end, _ := strconv.Atoi(sub[1]) + for k := start; k <= end; k++ { + gids = append(gids, k) + } + } + } else { + val, _ := strconv.Atoi(p) + gids = append(gids, val) + } + } + var res []rune + for _, gid := range gids { + if rVal, ok := r.fontGlyphRune(fontID, gid); ok { + res = append(res, rVal) + continue + } + res = append(res, rune(gid)) + } + return res +} + // textGlyphAdvanceLimit 获取显式字形推进宽度 // 入参: dxs X方向偏移, dys Y方向偏移, xs X坐标列表, index 字形索引, count 字形数量, currentX 当前X坐标 // 返回: float64 推进宽度 @@ -203,13 +255,13 @@ func textGlyphAdvanceLimit(dxs, dys, xs []float64, index int, count int, current } // textCodePositioned 判断文本编码是否带显式定位 -// 入参: textCode 文本编码 +// 入参: textCode 文本编码, xs X坐标列表, ys Y坐标列表 // 返回: bool 是否带显式定位 -func textCodePositioned(textCode TextCode) bool { +func textCodePositioned(textCode TextCode, xs, ys []float64) bool { return strings.TrimSpace(textCode.DeltaX) != "" || strings.TrimSpace(textCode.DeltaY) != "" || - len(parseFloats(textCode.X)) > 1 || - len(parseFloats(textCode.Y)) > 1 + len(xs) > 1 || + len(ys) > 1 } // textDelta 获取文本偏移量