package main import ( "bufio" "image" "image/color" _ "image/jpeg" "image/png" "io/ioutil" "log" "math/rand" "os" "path" "strings" "time" "github.com/golang/freetype/truetype" "github.com/llgcode/draw2d" "github.com/llgcode/draw2d/draw2dimg" "golang.org/x/image/draw" "golang.org/x/image/math/fixed") var ( fontname = "gooddog-plain.regular" comicfontdata = draw2d.FontData{ Name: fontname, Family: draw2d.FontFamilySans, Style: draw2d.FontStyleNormal, } comicfont *truetype.Font // username -> facename userface = map[string]string{} // facename -> image faces = map[string]image.Image{} // slice of facenames facekeys []string) func init() { // seed random rand.Seed(time.Now().Unix()) // load font fnt, err := ioutil.ReadFile(fontname + ".ttf") if err != nil { panic(err) } comicfont, err = truetype.Parse(fnt) if err != nil { panic(err) } draw2d.RegisterFont(comicfontdata, comicfont) // load faces if fis, err := ioutil.ReadDir("faces/"); err != nil { panic(err) } else { for _, fi := range fis { if i := loadFromPngFile("faces/" + fi.Name()); i != nil { base := strings.TrimSuffix(fi.Name(), path.Ext(fi.Name())) log.Printf("loaded face %s", base) faces[base] = i facekeys = append(facekeys, base) } } }} func saveToPngFile(filePath string, m image.Image) { f, err := os.Create(filePath) if err != nil { log.Println(err) return } defer f.Close() b := bufio.NewWriter(f) err = png.Encode(b, m) if err != nil { log.Println(err) return } err = b.Flush() if err != nil { log.Println(err) return } log.Printf("Wrote %s OK.\n", filePath)} func loadFromPngFile(filePath string) image.Image { f, err := os.OpenFile(filePath, 0, 0) if f == nil { log.Printf("can't open file; err=%s\n", err) return nil } defer f.Close() b := bufio.NewReader(f) i, _, err := image.Decode(b) if err != nil { log.Println(err) return nil } //log.Printf("Read %s OK.\n", filePath) return i} func makeusercomic(who, text string) image.Image { var facename string if fname, ok := userface[who]; !ok { // assign random face userface[who] = facekeys[rand.Intn(len(facekeys))] facename = userface[who] } else { facename = fname } i, _ := makecomic(text, facename) return i} func GetFontHeight(fd draw2d.FontData, size float64) (height float64) { font := draw2d.GetFont(fd) fupe := font.FUnitsPerEm() bounds := font.Bounds(fixed.Int26_6(fupe)) height = float64(bounds.Max.Y-bounds.Min.Y) * size / float64(fupe) return}func RenderString(text string, fd draw2d.FontData, size float64, fill color.Color) (buffer image.Image) { const stretchFactor = 1.2 height := GetFontHeight(fd, size) * stretchFactor widthMax := float64(len(text)) * size buf := image.NewRGBA(image.Rectangle{ Min: image.Point{0, 0}, Max: image.Point{int(widthMax + 1), int(height + 1)}, }) gc := draw2dimg.NewGraphicContext(buf) gc.Translate(0, height/stretchFactor) gc.SetFontData(fd) gc.SetFontSize(size) gc.SetStrokeColor(color.Black) gc.SetFillColor(fill) width := gc.FillStringAt(text, 1, 1) gc.StrokeStringAt(text, 1, 1) buffer = buf.SubImage(image.Rectangle{ Min: image.Point{0, 0}, Max: image.Point{int(width + 1), int(height + 1)}, }) return} // turn a string into a slice of strings based on a width limit// TODO: make utf8-safefunc wrap(str string, width int) []string { var out []string lines := strings.Split(str, "\n") for _, l := range lines { words := strings.Split(l, " ") if len(words) == 0 { return out } // current line we are making current := words[0] // # spaces left before the end remaining := width - len(current) for _, word := range words[1:] { if len(word)+1 > remaining { out = append(out, current) current = word remaining = width - len(word) } else { current += " " + word remaining -= 1 + len(word) } } out = append(out, current) } return out} var iRect = image.Rect(0, 0, 300, 300) func makecomic(text, face string) (image.Image, error) { facei := faces[face] dest := image.NewRGBA(iRect) b := iRect draw2dimg.DrawImage(facei, dest, draw2d.NewIdentityMatrix(), draw.Over, draw2dimg.BilinearFilter) size := 24.0 lines := wrap(text, 25) height := GetFontHeight(comicfontdata, size) var ilines []image.Image for _, l := range lines { ilines = append(ilines, RenderString(l, comicfontdata, size, color.White)) } tr := draw2d.NewIdentityMatrix() tr.Translate(25.0, float64(b.Dy())-10.0) tr.Translate(0, -float64(len(ilines))*height) for _, i := range ilines { draw2dimg.DrawImage(i, dest, tr, draw.Over, draw2dimg.BilinearFilter) tr.Translate(0, height) } // gc.StrokeStringAt(text, float64(20), float64((b.Dy())-20)) // gc.FillStringAt(text, float64(20), float64((b.Dy())-20)) return dest, nil}
package main
import (
"bufio"
"image"
"image/color"
_ "image/jpeg"
"image/png"
"io/ioutil"
"log"
"math/rand"
"os"
"path"
"strings"
"time"
"github.com/golang/freetype/truetype"
"github.com/llgcode/draw2d"
"github.com/llgcode/draw2d/draw2dimg"
"golang.org/x/image/draw"
"golang.org/x/image/math/fixed"
)
var (
fontname = "gooddog-plain.regular"
comicfontdata = draw2d.FontData{
Name: fontname,
Family: draw2d.FontFamilySans,
Style: draw2d.FontStyleNormal,
}
comicfont *truetype.Font
// username -> facename
userface = map[string]string{}
// facename -> image
faces = map[string]image.Image{}
// slice of facenames
facekeys []string
)
func init() {
// seed random
rand.Seed(time.Now().Unix())
// load font
fnt, err := ioutil.ReadFile(fontname + ".ttf")
if err != nil {
panic(err)
}
comicfont, err = truetype.Parse(fnt)
if err != nil {
panic(err)
}
draw2d.RegisterFont(comicfontdata, comicfont)
// load faces
if fis, err := ioutil.ReadDir("faces/"); err != nil {
panic(err)
} else {
for _, fi := range fis {
if i := loadFromPngFile("faces/" + fi.Name()); i != nil {
base := strings.TrimSuffix(fi.Name(), path.Ext(fi.Name()))
log.Printf("loaded face %s", base)
faces[base] = i
facekeys = append(facekeys, base)
}
}
}
}
func saveToPngFile(filePath string, m image.Image) {
f, err := os.Create(filePath)
if err != nil {
log.Println(err)
return
}
defer f.Close()
b := bufio.NewWriter(f)
err = png.Encode(b, m)
if err != nil {
log.Println(err)
return
}
err = b.Flush()
if err != nil {
log.Println(err)
return
}
log.Printf("Wrote %s OK.\n", filePath)
}
func loadFromPngFile(filePath string) image.Image {
f, err := os.OpenFile(filePath, 0, 0)
if f == nil {
log.Printf("can't open file; err=%s\n", err)
return nil
}
defer f.Close()
b := bufio.NewReader(f)
i, _, err := image.Decode(b)
if err != nil {
log.Println(err)
return nil
}
//log.Printf("Read %s OK.\n", filePath)
return i
}
func makeusercomic(who, text string) image.Image {
var facename string
if fname, ok := userface[who]; !ok {
// assign random face
userface[who] = facekeys[rand.Intn(len(facekeys))]
facename = userface[who]
} else {
facename = fname
}
i, _ := makecomic(text, facename)
return i
}
func GetFontHeight(fd draw2d.FontData, size float64) (height float64) {
font := draw2d.GetFont(fd)
fupe := font.FUnitsPerEm()
bounds := font.Bounds(fixed.Int26_6(fupe))
height = float64(bounds.Max.Y-bounds.Min.Y) * size / float64(fupe)
return
}
func RenderString(text string, fd draw2d.FontData, size float64, fill color.Color) (buffer image.Image) {
const stretchFactor = 1.2
height := GetFontHeight(fd, size) * stretchFactor
widthMax := float64(len(text)) * size
buf := image.NewRGBA(image.Rectangle{
Min: image.Point{0, 0},
Max: image.Point{int(widthMax + 1), int(height + 1)},
})
gc := draw2dimg.NewGraphicContext(buf)
gc.Translate(0, height/stretchFactor)
gc.SetFontData(fd)
gc.SetFontSize(size)
gc.SetStrokeColor(color.Black)
gc.SetFillColor(fill)
width := gc.FillStringAt(text, 1, 1)
gc.StrokeStringAt(text, 1, 1)
buffer = buf.SubImage(image.Rectangle{
Min: image.Point{0, 0},
Max: image.Point{int(width + 1), int(height + 1)},
})
return
}
// turn a string into a slice of strings based on a width limit
// TODO: make utf8-safe
func wrap(str string, width int) []string {
var out []string
lines := strings.Split(str, "\n")
for _, l := range lines {
words := strings.Split(l, " ")
if len(words) == 0 {
return out
}
// current line we are making
current := words[0]
// # spaces left before the end
remaining := width - len(current)
for _, word := range words[1:] {
if len(word)+1 > remaining {
out = append(out, current)
current = word
remaining = width - len(word)
} else {
current += " " + word
remaining -= 1 + len(word)
}
}
out = append(out, current)
}
return out
}
var iRect = image.Rect(0, 0, 300, 300)
func makecomic(text, face string) (image.Image, error) {
facei := faces[face]
dest := image.NewRGBA(iRect)
b := iRect
draw2dimg.DrawImage(facei, dest, draw2d.NewIdentityMatrix(), draw.Over, draw2dimg.BilinearFilter)
size := 24.0
lines := wrap(text, 25)
height := GetFontHeight(comicfontdata, size)
var ilines []image.Image
for _, l := range lines {
ilines = append(ilines, RenderString(l, comicfontdata, size, color.White))
}
tr := draw2d.NewIdentityMatrix()
tr.Translate(25.0, float64(b.Dy())-10.0)
tr.Translate(0, -float64(len(ilines))*height)
for _, i := range ilines {
draw2dimg.DrawImage(i, dest, tr, draw.Over, draw2dimg.BilinearFilter)
tr.Translate(0, height)
}
// gc.StrokeStringAt(text, float64(20), float64((b.Dy())-20))
// gc.FillStringAt(text, float64(20), float64((b.Dy())-20))
return dest, nil
}