mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
feat(字体渲染): 支持渐变字体渲染
This commit is contained in:
+12
-5
@@ -803,10 +803,9 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
if gid == 0 {
|
||||
continue
|
||||
}
|
||||
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||
if r, ok := adobeGB1CIDToUnicode(cid); ok {
|
||||
mapping[r] = uint16(gid)
|
||||
} else {
|
||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
||||
}
|
||||
}
|
||||
return mapping
|
||||
@@ -816,7 +815,7 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
if gid == 0 {
|
||||
continue
|
||||
}
|
||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
||||
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
@@ -838,8 +837,9 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
r = getUnicodeFromName(name)
|
||||
}
|
||||
if r == 0 {
|
||||
r = 0xE000 + rune(gid)
|
||||
r = packedGlyphRune(uint16(gid))
|
||||
}
|
||||
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||
mapping[r] = uint16(gid)
|
||||
}
|
||||
return mapping
|
||||
@@ -867,7 +867,14 @@ func getCFFCIDRuneMap(data []byte) map[uint16]rune {
|
||||
}
|
||||
gidRunes := make(map[uint16]rune)
|
||||
for run, gid := range mapping {
|
||||
gidRunes[gid] = run
|
||||
if run != packedGlyphRune(gid) {
|
||||
gidRunes[gid] = run
|
||||
}
|
||||
}
|
||||
for run, gid := range mapping {
|
||||
if _, ok := gidRunes[gid]; !ok {
|
||||
gidRunes[gid] = run
|
||||
}
|
||||
}
|
||||
result := make(map[uint16]rune)
|
||||
for gid, cid := range sids {
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ func fixTrueType(data []byte, fixCmap, fixName bool) (bool, []byte, map[rune]uin
|
||||
if len(mapping) == 0 {
|
||||
mapping = make(map[rune]uint16)
|
||||
for i := uint16(0); i < numGlyphs; i++ {
|
||||
mapping[rune(i)] = i
|
||||
mapping[packedGlyphRune(i)] = i
|
||||
}
|
||||
}
|
||||
newTables["cmap"] = buildCmapTable(numGlyphs, mapping)
|
||||
|
||||
@@ -27,6 +27,13 @@ func align4(n uint32) uint32 {
|
||||
return (n + 3) & ^uint32(3)
|
||||
}
|
||||
|
||||
// packedGlyphRune 获取包装字体字符
|
||||
// 入参: gid 字形ID
|
||||
// 返回: rune 包装字体字符
|
||||
func packedGlyphRune(gid uint16) rune {
|
||||
return 0xF0000 + rune(gid)
|
||||
}
|
||||
|
||||
// calcTableChecksum 计算字体表校验和
|
||||
// 入参: data 表数据
|
||||
// 返回: uint32 校验和
|
||||
|
||||
+29
-5
@@ -602,11 +602,20 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
if fillColor == nil {
|
||||
fillColor = canvas.Black
|
||||
}
|
||||
var fillPaint any = fillColor
|
||||
var fillColorNode *FillColor
|
||||
if dp != nil && dp.FillColor != nil {
|
||||
fillColor = parseFillColor(dp.FillColor)
|
||||
fillColorNode = dp.FillColor
|
||||
fillPaint = parseFillPaint(dp.FillColor, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if obj.FillColor != nil {
|
||||
fillColor = parseFillColor(obj.FillColor)
|
||||
fillColorNode = obj.FillColor
|
||||
fillPaint = parseFillPaint(obj.FillColor, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if fillPaint == nil {
|
||||
fillPaint = fillColor
|
||||
}
|
||||
fontStyle := canvas.FontRegular
|
||||
weight := obj.Weight
|
||||
@@ -638,7 +647,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
if ff == nil {
|
||||
return
|
||||
}
|
||||
face := ff.Face(sizePt, fillColor, fontStyle, canvas.FontNormal)
|
||||
face := ff.Face(sizePt, fillPaint, fontStyle, canvas.FontNormal)
|
||||
glyphRunes := r.textObjectGlyphRunes(fontID, obj)
|
||||
codePos := 0
|
||||
for _, tc := range obj.TextCode {
|
||||
@@ -683,14 +692,22 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
tx, ty := ctm.Transform(cx, cy)
|
||||
canvasX, canvasY := tx+bx, pageH-(ty+by)
|
||||
textWidth := face.TextWidth(str)
|
||||
if fillColor != nil {
|
||||
ctx.SetFillColor(fillColor)
|
||||
glyphFillPaint := fillPaint
|
||||
if fillColorNode != nil && fillColorNode.AxialShd != nil {
|
||||
glyphFillPaint = parseFillPaint(fillColorNode, bx, by, pageH, canvasX, canvasY)
|
||||
}
|
||||
if glyphFillPaint != nil {
|
||||
ctx.SetFill(glyphFillPaint)
|
||||
if embeddedFont {
|
||||
path, width := face.ToPath(str)
|
||||
textWidth = width
|
||||
ctx.DrawPath(canvasX, canvasY, path)
|
||||
} else {
|
||||
text := canvas.NewTextLine(face, str, canvas.Left)
|
||||
textFace := face
|
||||
if fillColorNode != nil && fillColorNode.AxialShd != nil {
|
||||
textFace = ff.Face(sizePt, glyphFillPaint, fontStyle, canvas.FontNormal)
|
||||
}
|
||||
text := canvas.NewTextLine(textFace, str, canvas.Left)
|
||||
ctx.DrawText(canvasX, canvasY, text)
|
||||
}
|
||||
}
|
||||
@@ -738,7 +755,14 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
||||
}
|
||||
inv := make(map[uint16]rune)
|
||||
for k, v := range mapping {
|
||||
inv[v] = k
|
||||
if k == packedGlyphRune(v) {
|
||||
inv[v] = k
|
||||
}
|
||||
}
|
||||
for k, v := range mapping {
|
||||
if _, ok := inv[v]; !ok {
|
||||
inv[v] = k
|
||||
}
|
||||
}
|
||||
r.FontGIDMap[fontID] = inv
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"image/color"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/tdewolff/canvas"
|
||||
)
|
||||
|
||||
// parseColor 解析颜色字符串
|
||||
@@ -58,6 +59,22 @@ func parseFillColor(fillColor *FillColor) color.Color {
|
||||
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
||||
}
|
||||
|
||||
// parseFillPaint 解析填充画刷
|
||||
// 入参: fillColor 填充颜色节点, x X坐标, y Y坐标, pageH 页面高度, originX 原点X坐标, originY 原点Y坐标
|
||||
// 返回: any 填充画刷
|
||||
func parseFillPaint(fillColor *FillColor, x, y, pageH, originX, originY float64) any {
|
||||
if fillColor == nil {
|
||||
return nil
|
||||
}
|
||||
if strings.TrimSpace(fillColor.Value) != "" {
|
||||
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
|
||||
}
|
||||
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
||||
return gradient
|
||||
}
|
||||
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
||||
}
|
||||
|
||||
// parseStrokeColor 解析勾边颜色
|
||||
// 入参: strokeColor 勾边颜色节点
|
||||
// 返回: color.Color 颜色对象
|
||||
@@ -89,6 +106,48 @@ func parseAxialShdColor(axialShd *AxialShd, alpha *int) color.Color {
|
||||
return color.Black
|
||||
}
|
||||
|
||||
// parseAxialShdGradient 解析轴向渐变
|
||||
// 入参: axialShd 轴向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度, originX 原点X坐标, originY 原点Y坐标
|
||||
// 返回: canvas.Gradient 渐变对象
|
||||
func parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH, originX, originY float64) canvas.Gradient {
|
||||
if axialShd == nil {
|
||||
return nil
|
||||
}
|
||||
start := parseFloats(axialShd.StartPoint)
|
||||
end := parseFloats(axialShd.EndPoint)
|
||||
if len(start) < 2 || len(end) < 2 {
|
||||
return nil
|
||||
}
|
||||
grad := canvas.NewGradient()
|
||||
for _, segment := range axialShd.Segment {
|
||||
if strings.TrimSpace(segment.Color.Value) == "" {
|
||||
continue
|
||||
}
|
||||
segmentAlpha := alpha
|
||||
if segmentAlpha == nil {
|
||||
segmentAlpha = segment.Color.Alpha
|
||||
}
|
||||
grad.Add(segment.Position, colorToRGBA(parseColorWithAlpha(segment.Color.Value, segmentAlpha)))
|
||||
}
|
||||
if len(grad) == 0 {
|
||||
return nil
|
||||
}
|
||||
startPoint := canvas.Point{X: x + start[0] - originX, Y: pageH - (y + start[1]) - originY}
|
||||
endPoint := canvas.Point{X: x + end[0] - originX, Y: pageH - (y + end[1]) - originY}
|
||||
if startPoint.Equals(endPoint) {
|
||||
return nil
|
||||
}
|
||||
return grad.ToLinear(startPoint, endPoint)
|
||||
}
|
||||
|
||||
// colorToRGBA 转换颜色对象
|
||||
// 入参: c 颜色对象
|
||||
// 返回: color.RGBA RGBA颜色
|
||||
func colorToRGBA(c color.Color) color.RGBA {
|
||||
r, g, b, a := c.RGBA()
|
||||
return color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}
|
||||
}
|
||||
|
||||
// GetDeltaX 获取X轴偏移量数组
|
||||
// 返回: []float64 偏移量数组
|
||||
func (tc *TextCode) GetDeltaX() []float64 {
|
||||
|
||||
Reference in New Issue
Block a user