-
Notifications
You must be signed in to change notification settings - Fork 2
/
draw.go
168 lines (145 loc) · 4.35 KB
/
draw.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
168
package main
import (
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"github.com/tdewolff/canvas"
"github.com/tdewolff/font"
"golang.org/x/image/draw"
"golang.org/x/image/tiff"
"golang.org/x/image/vector"
)
type Show struct {
Index int `short:"i" desc:"Font index for font collections"`
GlyphID uint16 `short:"g" name:"glyph" desc:"Glyph ID"`
Char string `short:"c" desc:"Literal character"`
Unicode string `short:"u" desc:"Unicode codepoint"`
Width int `desc:"Image width"`
PPEM uint16 `default:"40" desc:"Pixels per em-square"`
Scale int `default:"4" desc:"Image scale"`
Ratio float64 `desc:"Image width/height ratio"`
Output string `short:"o" desc:"Output filename"`
Input string `index:"0" desc:"Input file"`
}
func (cmd *Show) Run() error {
terminal := cmd.Output == "" || cmd.Output == "-"
b, err := ioutil.ReadFile(cmd.Input)
if err != nil {
return err
}
sfnt, err := font.ParseSFNT(b, cmd.Index)
if err != nil {
return err
}
if cmd.Char != "" {
rs := []rune(cmd.Char)
if len(rs) != 1 {
return fmt.Errorf("char must be one character")
}
cmd.GlyphID = sfnt.GlyphIndex(rs[0])
} else if cmd.Unicode != "" {
codepoint, err := strconv.ParseInt(cmd.Unicode, 16, 32)
if err != nil {
return fmt.Errorf("invalid unicode codepoint: %v", err)
} else if codepoint < 0 {
return fmt.Errorf("invalid unicode codepoint: U+%4X\n", codepoint)
}
cmd.GlyphID = sfnt.GlyphIndex(rune(codepoint))
if cmd.GlyphID == 0 {
return fmt.Errorf("glyph not found for U+%4X\n", codepoint)
}
}
fmt.Println("GlyphID:", cmd.GlyphID)
fmt.Printf("Char: %v (%v)\n", printableRune(sfnt.Cmap.ToUnicode(cmd.GlyphID)), sfnt.Cmap.ToUnicode(cmd.GlyphID))
if name := sfnt.GlyphName(cmd.GlyphID); name != "" {
fmt.Println("Name:", name)
}
if cmd.Width != 0 {
if cmd.Width < 0 {
return fmt.Errorf("width must be positive")
}
cmd.PPEM = uint16(float64(cmd.Width) * float64(sfnt.Head.UnitsPerEm) / float64(sfnt.GlyphAdvance(cmd.GlyphID)))
}
ascent := sfnt.Hhea.Ascender
descent := -sfnt.Hhea.Descender
width := int(float64(cmd.PPEM)*float64(sfnt.GlyphAdvance(cmd.GlyphID))/float64(sfnt.Head.UnitsPerEm) + 0.5)
height := int(float64(cmd.PPEM)*float64(ascent+descent)/float64(sfnt.Head.UnitsPerEm) + 0.5)
//baseline := int(float64(ppem)*float64(ascent)/float64(sfnt.Head.UnitsPerEm) + 0.5)
xpadding := int(float64(width)*0.2 + 0.5)
ypadding := xpadding
if terminal {
ypadding = 0
}
if 2048 < width {
return fmt.Errorf("width cannot exceed 2048")
}
f := float64(cmd.PPEM) / float64(sfnt.Head.UnitsPerEm)
p := &canvas.Path{}
err = sfnt.GlyphPath(p, cmd.GlyphID, cmd.PPEM, 0.0, float64(descent), 1.0, font.NoHinting)
if err != nil {
return err
}
rect := image.Rect(0, 0, width+2*xpadding, height+2*ypadding)
glyphRect := image.Rect(xpadding, ypadding, width+xpadding, height+ypadding)
img := image.NewRGBA(rect)
draw.Draw(img, rect, image.NewUniform(canvas.White), image.ZP, draw.Over)
ras := vector.NewRasterizer(width, height)
p.ToRasterizer(ras, canvas.DPMM(f))
ras.Draw(img, glyphRect, image.NewUniform(canvas.Black), image.ZP)
if cmd.Ratio == 0.0 {
if terminal {
cmd.Ratio = 2.0
} else {
cmd.Ratio = 1.0
}
}
if cmd.Ratio != 1.0 {
origImg := img
origRect := rect
rect := image.Rect(0, 0, int(float64(origRect.Max.X)*cmd.Ratio+0.5), origRect.Max.Y)
img = image.NewRGBA(rect)
draw.ApproxBiLinear.Scale(img, rect, origImg, origRect, draw.Over, nil)
}
if terminal {
if 80 < width {
return fmt.Errorf("width cannot exceed 80 for terminal output")
}
printASCII(img)
return nil
}
if cmd.Scale != 1 {
origImg := img
origRect := rect
rect := image.Rect(0, 0, (origRect.Max.X)*cmd.Scale, (origRect.Max.Y)*cmd.Scale)
img = image.NewRGBA(rect)
draw.NearestNeighbor.Scale(img, rect, origImg, origRect, draw.Over, nil)
}
ext := filepath.Ext(cmd.Output)
if ext != ".png" && ext != ".jpg" && ext != ".jpeg" && ext != ".gif" && ext != ".tiff" {
return fmt.Errorf("output extension must be PNG, JPG, GIF, or TIFF")
}
w, err := os.Create(cmd.Output)
if err != nil {
return err
}
switch ext {
case ".png":
err = png.Encode(w, img)
case ".jpg", ".jpeg":
err = jpeg.Encode(w, img, nil)
case ".gif":
err = gif.Encode(w, img, nil)
case ".tiff":
err = tiff.Encode(w, img, nil)
}
if err != nil {
return err
}
return nil
}