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 {
|
if gid == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||||
if r, ok := adobeGB1CIDToUnicode(cid); ok {
|
if r, ok := adobeGB1CIDToUnicode(cid); ok {
|
||||||
mapping[r] = uint16(gid)
|
mapping[r] = uint16(gid)
|
||||||
} else {
|
|
||||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mapping
|
return mapping
|
||||||
@@ -816,7 +815,7 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
|||||||
if gid == 0 {
|
if gid == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||||
}
|
}
|
||||||
return mapping
|
return mapping
|
||||||
}
|
}
|
||||||
@@ -838,8 +837,9 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
|||||||
r = getUnicodeFromName(name)
|
r = getUnicodeFromName(name)
|
||||||
}
|
}
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
r = 0xE000 + rune(gid)
|
r = packedGlyphRune(uint16(gid))
|
||||||
}
|
}
|
||||||
|
mapping[packedGlyphRune(uint16(gid))] = uint16(gid)
|
||||||
mapping[r] = uint16(gid)
|
mapping[r] = uint16(gid)
|
||||||
}
|
}
|
||||||
return mapping
|
return mapping
|
||||||
@@ -867,7 +867,14 @@ func getCFFCIDRuneMap(data []byte) map[uint16]rune {
|
|||||||
}
|
}
|
||||||
gidRunes := make(map[uint16]rune)
|
gidRunes := make(map[uint16]rune)
|
||||||
for run, gid := range mapping {
|
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)
|
result := make(map[uint16]rune)
|
||||||
for gid, cid := range sids {
|
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 {
|
if len(mapping) == 0 {
|
||||||
mapping = make(map[rune]uint16)
|
mapping = make(map[rune]uint16)
|
||||||
for i := uint16(0); i < numGlyphs; i++ {
|
for i := uint16(0); i < numGlyphs; i++ {
|
||||||
mapping[rune(i)] = i
|
mapping[packedGlyphRune(i)] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newTables["cmap"] = buildCmapTable(numGlyphs, mapping)
|
newTables["cmap"] = buildCmapTable(numGlyphs, mapping)
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ func align4(n uint32) uint32 {
|
|||||||
return (n + 3) & ^uint32(3)
|
return (n + 3) & ^uint32(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packedGlyphRune 获取包装字体字符
|
||||||
|
// 入参: gid 字形ID
|
||||||
|
// 返回: rune 包装字体字符
|
||||||
|
func packedGlyphRune(gid uint16) rune {
|
||||||
|
return 0xF0000 + rune(gid)
|
||||||
|
}
|
||||||
|
|
||||||
// calcTableChecksum 计算字体表校验和
|
// calcTableChecksum 计算字体表校验和
|
||||||
// 入参: data 表数据
|
// 入参: data 表数据
|
||||||
// 返回: uint32 校验和
|
// 返回: uint32 校验和
|
||||||
|
|||||||
+29
-5
@@ -602,11 +602,20 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
if fillColor == nil {
|
if fillColor == nil {
|
||||||
fillColor = canvas.Black
|
fillColor = canvas.Black
|
||||||
}
|
}
|
||||||
|
var fillPaint any = fillColor
|
||||||
|
var fillColorNode *FillColor
|
||||||
if dp != nil && dp.FillColor != nil {
|
if dp != nil && dp.FillColor != nil {
|
||||||
fillColor = parseFillColor(dp.FillColor)
|
fillColor = parseFillColor(dp.FillColor)
|
||||||
|
fillColorNode = dp.FillColor
|
||||||
|
fillPaint = parseFillPaint(dp.FillColor, bx, by, pageH, 0, 0)
|
||||||
}
|
}
|
||||||
if obj.FillColor != nil {
|
if obj.FillColor != nil {
|
||||||
fillColor = parseFillColor(obj.FillColor)
|
fillColor = parseFillColor(obj.FillColor)
|
||||||
|
fillColorNode = obj.FillColor
|
||||||
|
fillPaint = parseFillPaint(obj.FillColor, bx, by, pageH, 0, 0)
|
||||||
|
}
|
||||||
|
if fillPaint == nil {
|
||||||
|
fillPaint = fillColor
|
||||||
}
|
}
|
||||||
fontStyle := canvas.FontRegular
|
fontStyle := canvas.FontRegular
|
||||||
weight := obj.Weight
|
weight := obj.Weight
|
||||||
@@ -638,7 +647,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
if ff == nil {
|
if ff == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
face := ff.Face(sizePt, fillColor, fontStyle, canvas.FontNormal)
|
face := ff.Face(sizePt, fillPaint, fontStyle, canvas.FontNormal)
|
||||||
glyphRunes := r.textObjectGlyphRunes(fontID, obj)
|
glyphRunes := r.textObjectGlyphRunes(fontID, obj)
|
||||||
codePos := 0
|
codePos := 0
|
||||||
for _, tc := range obj.TextCode {
|
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)
|
tx, ty := ctm.Transform(cx, cy)
|
||||||
canvasX, canvasY := tx+bx, pageH-(ty+by)
|
canvasX, canvasY := tx+bx, pageH-(ty+by)
|
||||||
textWidth := face.TextWidth(str)
|
textWidth := face.TextWidth(str)
|
||||||
if fillColor != nil {
|
glyphFillPaint := fillPaint
|
||||||
ctx.SetFillColor(fillColor)
|
if fillColorNode != nil && fillColorNode.AxialShd != nil {
|
||||||
|
glyphFillPaint = parseFillPaint(fillColorNode, bx, by, pageH, canvasX, canvasY)
|
||||||
|
}
|
||||||
|
if glyphFillPaint != nil {
|
||||||
|
ctx.SetFill(glyphFillPaint)
|
||||||
if embeddedFont {
|
if embeddedFont {
|
||||||
path, width := face.ToPath(str)
|
path, width := face.ToPath(str)
|
||||||
textWidth = width
|
textWidth = width
|
||||||
ctx.DrawPath(canvasX, canvasY, path)
|
ctx.DrawPath(canvasX, canvasY, path)
|
||||||
} else {
|
} 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)
|
ctx.DrawText(canvasX, canvasY, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,7 +755,14 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
|||||||
}
|
}
|
||||||
inv := make(map[uint16]rune)
|
inv := make(map[uint16]rune)
|
||||||
for k, v := range mapping {
|
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
|
r.FontGIDMap[fontID] = inv
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"github.com/tdewolff/canvas"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseColor 解析颜色字符串
|
// parseColor 解析颜色字符串
|
||||||
@@ -58,6 +59,22 @@ func parseFillColor(fillColor *FillColor) color.Color {
|
|||||||
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
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 解析勾边颜色
|
// parseStrokeColor 解析勾边颜色
|
||||||
// 入参: strokeColor 勾边颜色节点
|
// 入参: strokeColor 勾边颜色节点
|
||||||
// 返回: color.Color 颜色对象
|
// 返回: color.Color 颜色对象
|
||||||
@@ -89,6 +106,48 @@ func parseAxialShdColor(axialShd *AxialShd, alpha *int) color.Color {
|
|||||||
return color.Black
|
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轴偏移量数组
|
// GetDeltaX 获取X轴偏移量数组
|
||||||
// 返回: []float64 偏移量数组
|
// 返回: []float64 偏移量数组
|
||||||
func (tc *TextCode) GetDeltaX() []float64 {
|
func (tc *TextCode) GetDeltaX() []float64 {
|
||||||
|
|||||||
Reference in New Issue
Block a user