-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathtruetype.go
167 lines (145 loc) · 4.41 KB
/
truetype.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2012 The go-gl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gltext
import (
"errors"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/math/fixed"
"image"
"image/draw"
"io"
"io/ioutil"
"sort"
)
// RuneRanges specify the rune ranges for ordered disjoint subsets of the ttf
// EG 32 - 127, 5000 - 6000 will created a more compact bitmap that holds the
// specified ranges of runes.
type RuneRange struct {
Low, High rune
}
type RuneRanges []RuneRange
func (rr RuneRanges) Len() int { return len(rr) }
func (rr RuneRanges) Swap(i, j int) { rr[i], rr[j] = rr[j], rr[i] }
func (rr RuneRanges) Less(i, j int) bool { return rr[i].Low < rr[j].Low }
func (rr RuneRanges) Validate() bool {
sort.Sort(rr)
previousMax := rune(0)
for _, r := range rr {
if r.Low <= previousMax {
return false
}
if r.Low > r.High {
return false
}
previousMax = r.High
}
return true
}
// GetGlyphIndex returns the location of the glyph data within
// the compressed rune ranges covered by the font
// EG if runes 0-25, 100-110 are supported by the font then
// the actual location of 100 will be in position 26 in the png image
func (rr RuneRanges) GetGlyphIndex(char rune) rune {
var index, offset rune
index = -1
for _, runes := range rr {
if char >= runes.Low && char <= runes.High {
index = char - runes.Low + offset
}
offset += runes.High - runes.Low + 1
}
return index
}
// http://www.freetype.org/freetype2/docs/tutorial/step2.html
// LoadTruetype loads a truetype font from the given stream and
// applies the given font scale in points.
//
// The low and high values determine the lower and upper rune limits
// we should load for this font. For standard ASCII this would be: 32, 127.
func NewTruetypeFontConfig(r io.Reader, scale fixed.Int26_6, runeRanges RuneRanges, runesPerRow, adjustHeight fixed.Int26_6) (*FontConfig, error) {
if !runeRanges.Validate() {
return nil, errors.New("Invalid rune ranges supplied.")
}
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
// Read the truetype font.
ttf, err := truetype.Parse(data)
if err != nil {
return nil, err
}
// Create our FontConfig type.
fc := &FontConfig{}
length := rune(0)
for _, r := range runeRanges {
length += r.High - r.Low + 1
}
fc.RuneRanges = runeRanges
fc.Glyphs = make(Charset, int(length))
// Create an image, large enough to store all requested glyphs.
// The resulting image is set to power of 2 dimensions so it might be wise to adjust the runesPerRow
// parameter to ensure that unnecessary space isn't created based on the character set being used
gc := fixed.Int26_6(len(fc.Glyphs))
runesPerCol := (gc / runesPerRow) + 1
gb := ttf.Bounds(scale)
gw := (gb.Max.X - gb.Min.X)
gh := (gb.Max.Y - gb.Min.Y) + adjustHeight
iw := Pow2(uint32(gw * runesPerRow))
ih := Pow2(uint32(gh * runesPerCol))
if iw > ih {
ih = iw
} else {
iw = ih
}
fg, bg := image.White, image.Transparent
rect := image.Rect(0, 0, int(iw), int(ih))
fc.Image = image.NewNRGBA(rect)
draw.Draw(fc.Image, fc.Image.Bounds(), bg, image.ZP, draw.Src)
// Use a freetype context to do the drawing.
c := freetype.NewContext()
c.SetDPI(72) // Do not change this. It is required in order to have a properly aligned bounding box!!!
c.SetFont(ttf)
c.SetFontSize(float64(scale))
c.SetClip(fc.Image.Bounds())
c.SetDst(fc.Image)
c.SetSrc(fg)
// Iterate over all relevant glyphs in the truetype font and draw them all to the image buffer
// Add Glyph objects to track various glyph values
var gi fixed.Int26_6
var gx, gy fixed.Int26_6
for _, runeRange := range fc.RuneRanges {
for ch := runeRange.Low; ch <= runeRange.High; ch++ {
index := ttf.Index(ch)
metric := ttf.HMetric(scale, index)
if gi%runesPerRow == 0 {
gx = 0
if gi > 0 {
gy += gh
}
} else {
gx += gw
}
fc.Glyphs[gi].Advance = int(metric.AdvanceWidth)
fc.Glyphs[gi].X = int(gx)
fc.Glyphs[gi].Y = int(gy)
fc.Glyphs[gi].Width = int(gw)
fc.Glyphs[gi].Height = int(gh)
pt := freetype.Pt(int(gx), int(gy)+int(c.PointToFixed(float64(scale))>>6))
c.DrawString(string(ch), pt)
gi++
}
}
return fc, nil
}
func LoadTruetypeFontConfig(rootPath, name string) (*FontConfig, error) {
fc := &FontConfig{}
fc.Name = name
err := fc.Load(rootPath)
if err != nil {
return nil, err
}
return fc, nil
}