mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
+1
-29
@@ -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)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "github.com/xiaoqidun/jbig2"
|
||||
_ "golang.org/x/image/bmp"
|
||||
)
|
||||
|
||||
// decodeImageData 解码图片数据
|
||||
|
||||
@@ -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 颜色对象
|
||||
|
||||
+32
-93
@@ -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
|
||||
}
|
||||
|
||||
+56
-4
@@ -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 获取文本偏移量
|
||||
|
||||
Reference in New Issue
Block a user