From 32612670a3b8430e2f35bfac05080b49434e3678 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Fri, 19 Feb 2016 16:35:43 +0000 Subject: [PATCH] mods: Add all the meme generation code --- Makefile | 1 + mods/main.go | 9 +++ mods/memegen.go | 163 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 170 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e671976..f5cd0da 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ all: clessy-broker clessy-mods clessy-stats deps: go get github.com/boltdb/bolt/... go get github.com/golang/freetype + go get github.com/llgcode/draw2d install-tg: go install github.com/hamcha/clessy/tg diff --git a/mods/main.go b/mods/main.go index f5589d6..3cf7a5c 100644 --- a/mods/main.go +++ b/mods/main.go @@ -9,6 +9,7 @@ import ( func initmods() { initviaggi() + initmeme() } func dispatch(broker *tg.Broker, update tg.APIMessage) { @@ -26,10 +27,12 @@ func isCommand(update tg.APIMessage, cmdname string) bool { } var botname *string +var impact *string func main() { brokerAddr := flag.String("broker", "localhost:7314", "Broker address:port") botname = flag.String("botname", "maudbot", "Bot name for /targetet@commands") + impact = flag.String("impact", "impact.ttf", "Path to impact.ttf (Impact font)") flag.Parse() initmods() @@ -39,3 +42,9 @@ func main() { panic(err) } } + +func assert(err error) { + if err != nil { + panic(err) + } +} diff --git a/mods/memegen.go b/mods/memegen.go index ea70c6c..487d269 100644 --- a/mods/memegen.go +++ b/mods/memegen.go @@ -1,15 +1,51 @@ package main import ( - "fmt" + "bytes" + "image" + _ "image/gif" + "image/jpeg" + _ "image/png" + "io/ioutil" + "os" "strings" + "github.com/golang/freetype" "github.com/hamcha/clessy/tg" + "github.com/llgcode/draw2d" + "github.com/llgcode/draw2d/draw2dimg" ) +var memeFontData draw2d.FontData + +func initmeme() { + fontfile, err := os.Open(*impact) + assert(err) + defer fontfile.Close() + + bytes, err := ioutil.ReadAll(fontfile) + assert(err) + + font, err := freetype.ParseFont(bytes) + assert(err) + + memeFontData := draw2d.FontData{"impact", draw2d.FontFamilySans, 0} + draw2d.RegisterFont(memeFontData, font) +} + func memegen(broker *tg.Broker, update tg.APIMessage) { if update.Photo != nil && update.Caption != nil { - if strings.HasPrefix(*(update.Caption), "/meme") { + caption := *(update.Caption) + if strings.HasPrefix(caption, "/meme ") && len(caption) > 6 { + idx := strings.Index(caption, ";") + if idx < 0 { + broker.SendTextMessage(update.Chat, "Formato: /meme TESTO IN ALTO;TESTO IN BASSO", &(update.MessageID)) + return + } + + txtup := caption[6:idx] + txtdw := caption[idx+1:] + maxsz := 0 photo := tg.APIPhotoSize{} for _, curphoto := range update.Photo { @@ -18,7 +54,128 @@ func memegen(broker *tg.Broker, update tg.APIMessage) { photo = curphoto } } - fmt.Println(photo) + broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) { + img, _, err := image.Decode(bytes.NewReader(data.Bytes)) + if err != nil { + broker.SendTextMessage(update.Chat, "ERROR: Non riesco a leggere l'immagine", &(update.MessageID)) + return + } + + //TODO Clean up this mess + + // Create target image + bounds := img.Bounds() + iwidth := float64(bounds.Size().X) + iheight := float64(bounds.Size().Y) + + timg := image.NewRGBA(bounds) + gc := draw2dimg.NewGraphicContext(timg) + gc.SetStrokeColor(image.Black) + gc.SetFillColor(image.White) + gc.SetFontData(memeFontData) + gc.DrawImage(img) + + write := func(text string, istop bool) { + text = strings.ToUpper(strings.TrimSpace(text)) + gc.Restore() + gc.Save() + + // Detect appropriate font size + scale := iwidth / iheight * 200 + gc.SetFontSize(scale) + gc.SetLineWidth(scale / 15) + + // Get NEW bounds + left, top, right, bottom := gc.GetStringBounds(text) + + width := right - left + texts := []string{text} + if width > iwidth { + // Split text + texts = splitCenter(text) + + // Get longest line + longer := float64(0) + longid := 0 + widths := make([]float64, len(texts)) + for id := range texts { + tleft, _, tright, _ := gc.GetStringBounds(texts[id]) + widths[id] = tright - tleft + if width > longer { + longer = widths[id] + longid = id + } + } + + // Still too big? Decrease font size again + iter := 0 + for width > iwidth && iter < 10 { + left, _, right, _ = gc.GetStringBounds(texts[longid]) + gc.SetFontSize(scale * 0.8) + width = right - left + iter++ + } + } + + height := bottom - top + margin := float64(10) + lines := float64(len(texts) - 1) + + gc.Save() + for id, txt := range texts { + gc.Save() + left, _, right, _ = gc.GetStringBounds(txt) + width = right - left + + y := float64(0) + if istop { + y = (height-margin)*float64(id+1) + margin*5 + } else { + y = iheight - (height * lines) + (height * float64(id)) - margin*5 + } + + gc.Translate((iwidth-width)/2, y) + gc.StrokeString(txt) + gc.FillString(txt) + gc.Restore() + } + } + write(txtup, true) + write(txtdw, false) + + buf := new(bytes.Buffer) + err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80})) + }) } } } + +func splitCenter(text string) []string { + cindx := int(len(text) / 2) + bs := 0 + md := 9999 + id := -1 + for i := 0; ; i++ { + idx := strings.Index(text[bs:], " ") + if idx < 0 { + break + } + bs += idx + 1 + diff := abs(cindx - bs) + if diff < md { + md = diff + id = bs - 1 + } + } + if id >= 0 { + return []string{text[0:id], text[id+1:]} + } + return []string{text} +} + +func abs(i int) int { + if i < 0 { + return -i + } + return i +}