Skip to content

Commit

Permalink
utf8 and gradient support added
Browse files Browse the repository at this point in the history
  • Loading branch information
Stepan Pesternikov committed Nov 20, 2018
1 parent 0f7b16e commit f6bf89b
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 50 deletions.
65 changes: 54 additions & 11 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 40 additions & 5 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
[[dependencies]]
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true


[[constraint]]
branch = "master"
name = "github.com/golang/freetype"

[[dependencies]]
[[constraint]]
name = "github.com/lucasb-eyer/go-colorful"
version = "1.0.0"

[[constraint]]
branch = "master"
name = "github.com/nfnt/resize"

[[dependencies]]
[[constraint]]
name = "github.com/stretchr/testify"
version = "v1.1.4"
version = "1.2.2"

[[dependencies]]
[[constraint]]
branch = "master"
name = "golang.org/x/image"

[prune]
go-tests = true
unused-packages = true
2 changes: 1 addition & 1 deletion avatar.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (
defaultNInitials = 1
)

func NewAvatarFromInitials(text []byte, options *InitialsOptions) (*Initials, error) {
func NewAvatarFromInitials(text string, options *InitialsOptions) (*Initials, error) {
if options == nil {
options = &InitialsOptions{}
}
Expand Down
57 changes: 57 additions & 0 deletions avatar_gradient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package avatar

import (
"github.com/lucasb-eyer/go-colorful"
"image"
"image/draw"
)

type Color struct {
colorful.Color
}

// This table contains the "keypoints" of the colorgradient you want to generate.
// The position of each keypoint has to live in the range [0,1]
type GradientTable []struct {
Col Color
Pos float64
}

// This is the meat of the gradient computation. It returns a HCL-blend between
// the two colors around `t`.
// Note: It relies heavily on the fact that the gradient keypoints are sorted.
func (gt GradientTable) getInterpolatedColorFor(t float64) Color {
for i := 0; i < len(gt)-1; i++ {
c1 := gt[i]
c2 := gt[i+1]
if c1.Pos <= t && t <= c2.Pos {
// We are in between c1 and c2. Go blend them!
t := (t - c1.Pos) / (c2.Pos - c1.Pos)
return Color{Color: c1.Col.BlendHcl(c2.Col.Color, t).Clamped()}
}
}

// Nothing found? Means we're at (or past) the last gradient keypoint.
return gt[len(gt)-1].Col
}

// This is a very nice thing Golang forces you to do!
// It is necessary so that we can write out the literal of the colortable below.
func MustParseHex(s string) Color {
c, err := colorful.Hex(s)
if err != nil {
panic("MustParseHex: " + err.Error())
}
return Color{Color: c}
}

func newGradientImage(height, width int, gradientTable GradientTable) *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, width, height))

for y := height - 1; y >= 0; y-- {
c := gradientTable.getInterpolatedColorFor(float64(y) / float64(height))
draw.Draw(img, image.Rect(0, y, width, y+1), &image.Uniform{C: c}, image.ZP, draw.Src)
}

return img
}
69 changes: 38 additions & 31 deletions avatar_initials.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,59 @@ import (
)

type InitialsOptions struct {
BgColor color.Color
Size int
FontPath string
TextColor color.Color
NInitials int
BgColor color.Color
Size int
FontPath string
TextColor color.Color
NInitials int
GradientTable GradientTable
}

type Initials struct {
source []byte
source string
options *InitialsOptions
originalImage image.Image
squareImage image.Image
circleImage image.Image
}

func (i Initials) Source() []byte {
return i.source
return []byte(i.source)
}

func (i Initials) loadOriginalImage() (image.Image, error) {
text := i.source

nInitials := i.options.nInitials()

if nInitials > 0 {
text = getInitials(text, nInitials)
}

size := i.options.size() * 3 // 3 times bigger for better quality

// Draw background img
imgRect := image.Rect(0, 0, size, size)
dst := image.NewRGBA(imgRect)
draw.Draw(
dst,
dst.Bounds(),
image.NewUniform(i.options.bgColor()),
image.ZP,
draw.Src)
var dst *image.RGBA
if len(i.options.GradientTable) != 0 {
dst = newGradientImage(size, size, i.options.GradientTable)
} else {
imgRect := image.Rect(0, 0, size, size)
dst = image.NewRGBA(imgRect)
draw.Draw(
dst,
dst.Bounds(),
image.NewUniform(i.options.bgColor()),
image.ZP,
draw.Src)
}

ftFont, err := i.options.font()
if err != nil {
return nil, err
}

fontFace := truetype.NewFace(ftFont, &truetype.Options{
Size: getFontSizeThatFits(text, float64(size), ftFont),
Size: getFontSizeThatFits([]byte(text), float64(size), ftFont),
})

fd := font.Drawer{
Expand All @@ -72,10 +79,10 @@ func (i Initials) loadOriginalImage() (image.Image, error) {
}

// Figure out baseline and adv for string in img
txtWidth := fd.MeasureBytes(text)
txtWidth := fd.MeasureBytes([]byte(text))
txtWidthInt := int(txtWidth >> 6)

bounds, _ := fd.BoundBytes(text)
bounds, _ := fd.BoundBytes([]byte(text))
txtHeight := bounds.Max.Y - bounds.Min.Y
txtHeightInt := int(txtHeight >> 6)

Expand All @@ -84,7 +91,7 @@ func (i Initials) loadOriginalImage() (image.Image, error) {

fd.Dot = fixed.Point26_6{X: fixed.Int26_6(advLine << 6), Y: fixed.Int26_6(baseline << 6)}

fd.DrawBytes(text)
fd.DrawBytes([]byte(text))

return dst, nil
}
Expand Down Expand Up @@ -156,42 +163,42 @@ func getFontSizeThatFits(text []byte, imgWidth float64, ftFont *truetype.Font) f
return ratio * (imgWidth - (40./100)*imgWidth)
}

func getInitials(text []byte, nChars int) []byte {
func getInitials(text string, nChars int) string {
if len(text) == 0 {
return []byte("")
return ""
}

var initials = []byte{}
var previous = byte(' ')
var initials []rune
var previous = ' '

regEmail := regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
skipFromAt := regEmail.Match(text)
skipFromAt := regEmail.MatchString(text)

for _, ch := range text[:] {
if skipFromAt == true && rune(ch) == '@' {
for _, ch := range []rune(text) {
if skipFromAt == true && ch == '@' {
break
}

if isSymbol(rune(ch)) {
if isSymbol(ch) {
previous = ch
continue
}

if ((unicode.IsUpper(rune(ch)) && unicode.IsLower(rune(previous))) || (unicode.IsLower(rune(ch)) && len(initials) == 0)) || isSymbol(rune(previous)) {
if ((unicode.IsUpper(ch) && unicode.IsLower(previous)) || (unicode.IsLower(ch) && len(initials) == 0)) || isSymbol(previous) {
initials = append(initials, ch)
previous = ch
}
}

for i := len(initials); i < nChars && len(text) > i; i++ {
for i := len(initials); i < nChars && len([]rune(text)) > i; i++ {
if isSymbol(rune(text[i])) {
continue
}

initials = append(initials, text[i])
initials = append(initials, []rune(text)[i])
}

return initials
return string(initials)
}

func isSymbol(ch rune) bool {
Expand Down
Loading

0 comments on commit f6bf89b

Please sign in to comment.