package main import ( "bytes" "encoding/base64" "image" _ "image/gif" "image/jpeg" _ "image/png" "io/ioutil" "log" "os" "strings" "git.fromouter.space/crunchy-rocks/draw2d" "git.fromouter.space/crunchy-rocks/draw2d/draw2dimg" "git.fromouter.space/crunchy-rocks/freetype" "git.fromouter.space/hamcha/tg" ) var memeFontData draw2d.FontData func meme_init() { 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) log.Println("[meme] Loaded!") } func meme_message(broker *tg.Broker, update tg.APIMessage) { // Make replies work if update.ReplyTo != nil && update.Text != nil && update.ReplyTo.Photo != nil { update.Photo = update.ReplyTo.Photo update.Caption = update.Text } if update.Photo != nil && update.Caption != nil { 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 { if curphoto.Width > maxsz { maxsz = curphoto.Width photo = curphoto } } broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) { if data.Type == tg.BError { log.Printf("[memegen] Received error from broker: %s\n", *data.Error) broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } pbytes, err := base64.StdEncoding.DecodeString(*data.Bytes) if err != nil { log.Printf("[memegen] Base64 decode error: %s\n", err.Error()) broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } img, _, err := image.Decode(bytes.NewReader(pbytes)) if err != nil { log.Printf("[memegen] Image decode error: %s\n", err.Error()) broker.SendTextMessage(update.Chat, "ERROR: Non riesco a leggere l'immagine", &update.MessageID) return } broker.SendChatAction(update.Chat, tg.ActionUploadingPhoto) //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.Emojis = emojis 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 := iheight / iwidth * (iwidth / 10) 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 { log.Println("Warning, resizing!") gc.SetFontSize(scale * (0.8 - 0.1*float64(iter))) left, top, right, bottom = gc.GetStringBounds(texts[longid]) width = right - left iter++ } } height := bottom - top margin := float64(height / 50) 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})) if err != nil { log.Printf("[memegen] Image encode error: %s\n", err.Error()) broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } broker.SendPhoto(update.Chat, buf.Bytes(), "meme.jpg", "", &update.MessageID) }) } } } 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 }