Merge pull request #3 from essentialkaos/develop

Version 1.1.0
main
ᴀɴᴛᴏɴ ɴᴏᴠᴏᴊɪʟᴏᴠ 3 years ago committed by GitHub
commit 347b808a7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,13 +3,14 @@
<p align="center"> <p align="center">
<a href="https://kaos.sh/g/go-badge.v1"><img src="https://gh.kaos.st/godoc.svg" alt="PkgGoDev" /></a> <a href="https://kaos.sh/g/go-badge.v1"><img src="https://gh.kaos.st/godoc.svg" alt="PkgGoDev" /></a>
<a href="https://kaos.sh/w/go-badge/ci"><img src="https://kaos.sh/w/go-badge/ci.svg" alt="GitHub Actions CI Status" /></a> <a href="https://kaos.sh/w/go-badge/ci"><img src="https://kaos.sh/w/go-badge/ci.svg" alt="GitHub Actions CI Status" /></a>
<a href="https://kaos.sh/r/go-badge"><img src="https://kaos.sh/r/go-badge.svg" alt="GoReportCard" /></a>
<a href="https://kaos.sh/c/go-badge"><img src="https://kaos.sh/c/go-badge.svg" alt="Coverage Status" /></a> <a href="https://kaos.sh/c/go-badge"><img src="https://kaos.sh/c/go-badge.svg" alt="Coverage Status" /></a>
<a href="https://kaos.sh/b/go-badge"><img src="https://kaos.sh/b/0cbded00-9dfb-458b-bdf8-27b2c70ede9b.svg" alt="Codebeat badge" /></a> <a href="https://kaos.sh/b/go-badge"><img src="https://kaos.sh/b/0cbded00-9dfb-458b-bdf8-27b2c70ede9b.svg" alt="Codebeat badge" /></a>
<a href="https://kaos.sh/w/go-badge/codeql"><img src="https://kaos.sh/w/go-badge/codeql.svg" alt="GitHub Actions CodeQL Status" /></a> <a href="https://kaos.sh/w/go-badge/codeql"><img src="https://kaos.sh/w/go-badge/codeql.svg" alt="GitHub Actions CodeQL Status" /></a>
<a href="#license"><img src="https://gh.kaos.st/apache2.svg"></a> <a href="#license"><img src="https://gh.kaos.st/apache2.svg"></a>
</p> </p>
<p align="center"><a href="#installation">Installation</a><a href="#usage-example">Usage example</a><a href="#build-status">Build Status</a><a href="#contributing">Contributing</a><a href="#license">License</a></p> <p align="center"><a href="#installation">Installation</a><a href="#usage-example">Usage example</a><a href="#build-status">Build Status</a><a href="#contributing">Contributing</a><a href="#thanks">Thanks</a><a href="#license">License</a></p>
<br/> <br/>
@ -44,13 +45,13 @@ import (
// ////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////// //
func main() { func main() {
g, err := badge.NewGenerator("Verdana.ttf") g, err := badge.NewGenerator("Verdana.ttf", 11)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Println(g.GeneratePlastic("status", "ok", "#97ca00")) fmt.Println(string(g.GeneratePlastic("status", "ok", "#97ca00")))
} }
``` ```
@ -65,6 +66,14 @@ func main() {
Before contributing to this project please read our [Contributing Guidelines](https://github.com/essentialkaos/contributing-guidelines#contributing-guidelines). Before contributing to this project please read our [Contributing Guidelines](https://github.com/essentialkaos/contributing-guidelines#contributing-guidelines).
### Thanks
We would like to thank:
* All authors and [contributors](https://github.com/badges/shields/graphs/contributors) of [shields.io](https://shields.io) service;
* All authors of [`freetype`](https://github.com/golang/freetype/blob/master/AUTHORS) package;
* [@narqo](https://github.com/narqo) for [`go-badge`](https://github.com/narqo/go-badge) package.
### License ### License
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

@ -20,23 +20,53 @@ import (
// ////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////// //
const _TEMPLATE_PLASTIC = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="18" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1" stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="{WIDTH}" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="{LABEL_WIDTH}" height="18" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="18" fill="{COLOR}"/><rect width="{WIDTH}" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="{LABEL_X}" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{LABEL_X}" y="130" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text aria-hidden="true" x="{MESSAGE_X}" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text><text x="{MESSAGE_X}" y="130" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>` const (
COLOR_BLUE = "#007ec6"
COLOR_BRIGHTGREEN = "#4c1"
COLOR_GREEN = "#97ca00"
COLOR_GREY = "#555"
COLOR_LIGHTGREY = "#9f9f9f"
COLOR_ORANGE = "#fe7d37"
COLOR_RED = "#e05d44"
COLOR_YELLOW = "#dfb317"
COLOR_YELLOWGREEN = "#a4a61d"
COLOR_SUCCESS = "#4c1"
COLOR_IMPORTANT = "#fe7d37"
COLOR_CRITICAL = "#e05d44"
COLOR_INFORMATIONAL = "#007ec6"
COLOR_INACTIVE = "#9f9f9f"
)
const (
DEFAULT_OFFSET = 9 // default font offset
DEFAULT_SPACING = 0 // default letter spacing
)
// ////////////////////////////////////////////////////////////////////////////////// //
const _TEMPLATE_PLASTIC = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="18" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1" stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="{WIDTH}" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="{LABEL_WIDTH}" height="18" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="18" fill="{COLOR}"/><rect width="{WIDTH}" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="{FONT},Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="{FONT_SIZE}"><text aria-hidden="true" x="{LABEL_X}" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{LABEL_X}" y="130" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text aria-hidden="true" x="{MESSAGE_X}" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text><text x="{MESSAGE_X}" y="130" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>`
const _TEMPLATE_FLAT = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="20" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="{WIDTH}" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="{LABEL_WIDTH}" height="20" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="20" fill="{COLOR}"/><rect width="{WIDTH}" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="{LABEL_X}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{LABEL_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text aria-hidden="true" x="{MESSAGE_X}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text><text x="{MESSAGE_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>` const _TEMPLATE_FLAT = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="20" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="{WIDTH}" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="{LABEL_WIDTH}" height="20" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="20" fill="{COLOR}"/><rect width="{WIDTH}" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="{FONT},Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="{FONT_SIZE}"><text aria-hidden="true" x="{LABEL_X}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{LABEL_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text aria-hidden="true" x="{MESSAGE_X}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text><text x="{MESSAGE_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>`
const _TEMPLATE_FLAT_SQUARE = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="20" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><g shape-rendering="crispEdges"><rect width="{LABEL_WIDTH}" height="20" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="20" fill="{COLOR}"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="{LABEL_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{MESSAGE_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>` const _TEMPLATE_FLAT_SQUARE = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{WIDTH}" height="20" role="img" aria-label="{LABEL}: {MESSAGE}"><title>{LABEL}: {MESSAGE}</title><g shape-rendering="crispEdges"><rect width="{LABEL_WIDTH}" height="20" fill="#555"/><rect x="{LABEL_WIDTH}" width="{MESSAGE_WIDTH}" height="20" fill="{COLOR}"/></g><g fill="#fff" text-anchor="middle" font-family="{FONT},Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="{FONT_SIZE}"><text x="{LABEL_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{LABEL_LENGTH}">{LABEL}</text><text x="{MESSAGE_X}" y="140" transform="scale(.1)" fill="#fff" textLength="{MESSAGE_LENGTH}">{MESSAGE}</text></g></svg>`
// ////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////// //
// Generator is badge generator
type Generator struct { type Generator struct {
Offset int Offset int // Text offset
Spacing float64 // Letter spacing
drawer *font.Drawer fontSize int
fontName string
drawer *font.Drawer
} }
// ////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////// //
func NewGenerator(fontFile string) (*Generator, error) { // NewGenerator creates new badge generator with given font
func NewGenerator(fontFile string, fontSize int) (*Generator, error) {
fontData, err := ioutil.ReadFile(fontFile) fontData, err := ioutil.ReadFile(fontFile)
if err != nil { if err != nil {
@ -50,10 +80,15 @@ func NewGenerator(fontFile string) (*Generator, error) {
} }
return &Generator{ return &Generator{
Offset: 9, Offset: DEFAULT_OFFSET,
Spacing: DEFAULT_SPACING,
fontSize: fontSize,
fontName: fontTTF.Name(truetype.NameIDFontFullName),
drawer: &font.Drawer{ drawer: &font.Drawer{
Face: truetype.NewFace(fontTTF, &truetype.Options{ Face: truetype.NewFace(fontTTF, &truetype.Options{
Size: 11, Size: float64(fontSize),
DPI: 72, DPI: 72,
Hinting: font.HintingFull, Hinting: font.HintingFull,
}), }),
@ -64,24 +99,24 @@ func NewGenerator(fontFile string) (*Generator, error) {
// ////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////// //
// GeneratePlastic generates SVG badge in plastic style // GeneratePlastic generates SVG badge in plastic style
func (g *Generator) GeneratePlastic(label, message, color string) string { func (g *Generator) GeneratePlastic(label, message, color string) []byte {
return g.generateBadge(_TEMPLATE_PLASTIC, label, message, color) return g.generateBadge(_TEMPLATE_PLASTIC, label, message, color)
} }
// GenerateFlat generates SVG badge in flat style // GenerateFlat generates SVG badge in flat style
func (g *Generator) GenerateFlat(label, message, color string) string { func (g *Generator) GenerateFlat(label, message, color string) []byte {
return g.generateBadge(_TEMPLATE_FLAT, label, message, color) return g.generateBadge(_TEMPLATE_FLAT, label, message, color)
} }
// GenerateFlatSquare generates SVG badge in flat-square style // GenerateFlatSquare generates SVG badge in flat-square style
func (g *Generator) GenerateFlatSquare(label, message, color string) string { func (g *Generator) GenerateFlatSquare(label, message, color string) []byte {
return g.generateBadge(_TEMPLATE_FLAT_SQUARE, label, message, color) return g.generateBadge(_TEMPLATE_FLAT_SQUARE, label, message, color)
} }
// ////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////// //
// generateBadge generates badge with given template // generateBadge generates badge with given template
func (g *Generator) generateBadge(template, label, message, color string) string { func (g *Generator) generateBadge(template, label, message, color string) []byte {
if !strings.HasPrefix(color, "#") { if !strings.HasPrefix(color, "#") {
color = "#" + color color = "#" + color
} }
@ -91,8 +126,9 @@ func (g *Generator) generateBadge(template, label, message, color string) string
fW := lW + mW fW := lW + mW
lX := ((lW/2 + 1) * 10) + 5 lX := ((lW/2 + 1) * 10) + 5
mX := ((lW + (mW / 2) - 1) * 10) + 5 mX := ((lW + (mW / 2) - 1) * 10) + 5
lL := (lW - 10) * 10 lL := int(float64(lW-10) * (10.0 + g.Spacing))
mL := (mW - 10) * 10 mL := int(float64(mW-10) * (10.0 + g.Spacing))
fS := g.fontSize * 10
badge := strings.ReplaceAll(template, "{LABEL}", label) badge := strings.ReplaceAll(template, "{LABEL}", label)
badge = strings.ReplaceAll(badge, "{MESSAGE}", message) badge = strings.ReplaceAll(badge, "{MESSAGE}", message)
@ -104,6 +140,8 @@ func (g *Generator) generateBadge(template, label, message, color string) string
badge = strings.ReplaceAll(badge, "{MESSAGE_X}", strconv.Itoa(mX)) badge = strings.ReplaceAll(badge, "{MESSAGE_X}", strconv.Itoa(mX))
badge = strings.ReplaceAll(badge, "{LABEL_LENGTH}", strconv.Itoa(lL)) badge = strings.ReplaceAll(badge, "{LABEL_LENGTH}", strconv.Itoa(lL))
badge = strings.ReplaceAll(badge, "{MESSAGE_LENGTH}", strconv.Itoa(mL)) badge = strings.ReplaceAll(badge, "{MESSAGE_LENGTH}", strconv.Itoa(mL))
badge = strings.ReplaceAll(badge, "{FONT}", g.fontName)
badge = strings.ReplaceAll(badge, "{FONT_SIZE}", strconv.Itoa(fS))
return badge return []byte(badge)
} }

@ -30,7 +30,7 @@ var _ = Suite(&BadgeSuite{})
func (s *BadgeSuite) SetUpTest(c *C) { func (s *BadgeSuite) SetUpTest(c *C) {
var err error var err error
s.generator, err = NewGenerator("Verdana.ttf") s.generator, err = NewGenerator("Verdana.ttf", 11)
if err != nil { if err != nil {
c.Fatal(err.Error()) c.Fatal(err.Error())
@ -38,11 +38,11 @@ func (s *BadgeSuite) SetUpTest(c *C) {
} }
func (s *BadgeSuite) TestErrors(c *C) { func (s *BadgeSuite) TestErrors(c *C) {
_, err := NewGenerator("unknown.ttf") _, err := NewGenerator("unknown.ttf", 0)
c.Assert(err, NotNil) c.Assert(err, NotNil)
_, err = NewGenerator("badge.go") _, err = NewGenerator("badge.go", 0)
c.Assert(err, NotNil) c.Assert(err, NotNil)
} }
@ -56,7 +56,7 @@ func (s *BadgeSuite) TestPlastic(c *C) {
ourBadge := s.generator.GeneratePlastic("test1", "good", "ff69b4") ourBadge := s.generator.GeneratePlastic("test1", "good", "ff69b4")
c.Assert(ourBadge, Equals, string(srcBadge)) c.Assert(ourBadge, DeepEquals, srcBadge)
} }
func (s *BadgeSuite) TestFlat(c *C) { func (s *BadgeSuite) TestFlat(c *C) {
@ -68,7 +68,7 @@ func (s *BadgeSuite) TestFlat(c *C) {
ourBadge := s.generator.GenerateFlat("test1", "good", "ff69b4") ourBadge := s.generator.GenerateFlat("test1", "good", "ff69b4")
c.Assert(ourBadge, Equals, string(srcBadge)) c.Assert(ourBadge, DeepEquals, srcBadge)
} }
func (s *BadgeSuite) TestFlatSquare(c *C) { func (s *BadgeSuite) TestFlatSquare(c *C) {
@ -80,5 +80,5 @@ func (s *BadgeSuite) TestFlatSquare(c *C) {
ourBadge := s.generator.GenerateFlatSquare("test1", "good", "ff69b4") ourBadge := s.generator.GenerateFlatSquare("test1", "good", "ff69b4")
c.Assert(ourBadge, Equals, string(srcBadge)) c.Assert(ourBadge, DeepEquals, srcBadge)
} }

Loading…
Cancel
Save