-
Notifications
You must be signed in to change notification settings - Fork 26
/
badge.go
111 lines (94 loc) · 2.43 KB
/
badge.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
package badge
import (
"bytes"
"html/template"
"io"
"sync"
"github.com/golang/freetype/truetype"
"github.com/narqo/go-badge/fonts"
"golang.org/x/image/font"
)
type badge struct {
Subject string
Status string
Color Color
Bounds bounds
}
type bounds struct {
// SubjectDx is the width of subject string of the badge.
SubjectDx float64
SubjectX float64
// StatusDx is the width of status string of the badge.
StatusDx float64
StatusX float64
}
func (b bounds) Dx() float64 {
return b.SubjectDx + b.StatusDx
}
type badgeDrawer struct {
fd *font.Drawer
tmpl *template.Template
mutex *sync.Mutex
}
func (d *badgeDrawer) Render(subject, status string, color Color, w io.Writer) error {
d.mutex.Lock()
subjectDx := d.measureString(subject)
statusDx := d.measureString(status)
d.mutex.Unlock()
bdg := badge{
Subject: subject,
Status: status,
Color: color,
Bounds: bounds{
SubjectDx: subjectDx,
SubjectX: subjectDx/2.0 + 1,
StatusDx: statusDx,
StatusX: subjectDx + statusDx/2.0 - 1,
},
}
return d.tmpl.Execute(w, bdg)
}
func (d *badgeDrawer) RenderBytes(subject, status string, color Color) ([]byte, error) {
buf := &bytes.Buffer{}
err := d.Render(subject, status, color, buf)
return buf.Bytes(), err
}
// shield.io uses Verdana.ttf to measure text width with an extra 10px.
// As we use Vera.ttf, we have to tune this value a little.
const extraDx = 13
func (d *badgeDrawer) measureString(s string) float64 {
return float64(d.fd.MeasureString(s)>>6) + extraDx
}
// Render renders a badge of the given color, with given subject and status to w.
func Render(subject, status string, color Color, w io.Writer) error {
return drawer.Render(subject, status, color, w)
}
// RenderBytes renders a badge of the given color, with given subject and status to bytes.
func RenderBytes(subject, status string, color Color) ([]byte, error) {
return drawer.RenderBytes(subject, status, color)
}
const (
dpi = 72
fontsize = 11
)
var drawer *badgeDrawer
func init() {
drawer = &badgeDrawer{
fd: mustNewFontDrawer(fontsize, dpi),
tmpl: template.Must(template.New("flat-template").Parse(flatTemplate)),
mutex: &sync.Mutex{},
}
}
func mustNewFontDrawer(size, dpi float64) *font.Drawer {
ttf, err := truetype.Parse(fonts.VeraSans)
if err != nil {
panic(err)
}
return &font.Drawer{
Face: truetype.NewFace(ttf, &truetype.Options{
Size: size,
DPI: dpi,
Hinting: font.HintingFull,
}),
}
}