mirror of
https://github.com/Fluffy-Bean/Lynxie.git
synced 2025-06-28 21:06:16 +00:00
Fix caption command, replace font with Roboto
This commit is contained in:
parent
7c09627774
commit
3fafa834ed
4 changed files with 86 additions and 49 deletions
BIN
_resources/fonts/Roboto.ttf
Normal file
BIN
_resources/fonts/Roboto.ttf
Normal file
Binary file not shown.
8
_resources/resources.go
Normal file
8
_resources/resources.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package _resources
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed fonts/Roboto.ttf
|
||||||
|
var FontRoboto []byte
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~sbinet/gg"
|
"git.sr.ht/~sbinet/gg"
|
||||||
|
"github.com/Fluffy-Bean/lynxie/_resources"
|
||||||
"github.com/Fluffy-Bean/lynxie/app"
|
"github.com/Fluffy-Bean/lynxie/app"
|
||||||
"github.com/Fluffy-Bean/lynxie/internal/color"
|
"github.com/Fluffy-Bean/lynxie/internal/color"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
@ -25,9 +27,6 @@ var client = http.Client{
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed resources/Impact.ttf
|
|
||||||
var resourceImpactFont []byte
|
|
||||||
|
|
||||||
func RegisterImgCommands(a *app.App) {
|
func RegisterImgCommands(a *app.App) {
|
||||||
a.RegisterCommand("saveable", registerSaveable(a))
|
a.RegisterCommand("saveable", registerSaveable(a))
|
||||||
a.RegisterCommandAlias("gif", "saveable")
|
a.RegisterCommandAlias("gif", "saveable")
|
||||||
|
@ -38,7 +37,7 @@ func RegisterImgCommands(a *app.App) {
|
||||||
|
|
||||||
func registerSaveable(a *app.App) app.Callback {
|
func registerSaveable(a *app.App) app.Callback {
|
||||||
return func(h *app.Handler, args []string) app.Error {
|
return func(h *app.Handler, args []string) app.Error {
|
||||||
fileEndpoint, err := getClosestImage(h)
|
fileEndpoint, err := findClosestImage(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.Error{
|
return app.Error{
|
||||||
Msg: "Could not get image",
|
Msg: "Could not get image",
|
||||||
|
@ -77,7 +76,7 @@ func registerSaveable(a *app.App) app.Callback {
|
||||||
Image: &discordgo.MessageEmbedImage{
|
Image: &discordgo.MessageEmbedImage{
|
||||||
URL: "attachment://saveable.gif",
|
URL: "attachment://saveable.gif",
|
||||||
},
|
},
|
||||||
Color: color.RGBToDiscord(255, 255, 255),
|
Color: color.RGBToDiscord(1, 1, 1),
|
||||||
},
|
},
|
||||||
Files: []*discordgo.File{
|
Files: []*discordgo.File{
|
||||||
{
|
{
|
||||||
|
@ -95,7 +94,7 @@ func registerSaveable(a *app.App) app.Callback {
|
||||||
|
|
||||||
func registerCaption(a *app.App) app.Callback {
|
func registerCaption(a *app.App) app.Callback {
|
||||||
return func(h *app.Handler, args []string) app.Error {
|
return func(h *app.Handler, args []string) app.Error {
|
||||||
fileEndpoint, err := getClosestImage(h)
|
fileEndpoint, err := findClosestImage(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.Error{
|
return app.Error{
|
||||||
Msg: "Could not get image",
|
Msg: "Could not get image",
|
||||||
|
@ -135,42 +134,32 @@ func registerCaption(a *app.App) app.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var img image.Image
|
img, err := loadImageFromBytes(buff)
|
||||||
switch http.DetectContentType(buff) {
|
if err != nil {
|
||||||
case "image/png":
|
|
||||||
img, err = png.Decode(bytes.NewReader(buff))
|
|
||||||
if err != nil {
|
|
||||||
return app.Error{
|
|
||||||
Msg: "Failed to decode PNG",
|
|
||||||
Err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "image/jpeg":
|
|
||||||
img, err = jpeg.Decode(bytes.NewReader(buff))
|
|
||||||
if err != nil {
|
|
||||||
return app.Error{
|
|
||||||
Msg: "Failed to decode JPEG",
|
|
||||||
Err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return app.Error{
|
return app.Error{
|
||||||
Msg: "Unknown or unsupported image format",
|
Msg: "Failed to load image",
|
||||||
Err: errors.New("Unknown or unsupported image format " + http.DetectContentType(buff)),
|
Err: errors.New("Failed to load image " + err.Error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
imgWidth, imgHeight := img.Bounds().Dx(), img.Bounds().Dy()
|
||||||
|
|
||||||
fontSize := float64(img.Bounds().Dx() / 25)
|
captionSize := float64(imgWidth / 15)
|
||||||
if fontSize < 16 {
|
if captionSize < 16 {
|
||||||
fontSize = 16
|
captionSize = 16
|
||||||
} else if fontSize > 50 {
|
} else if captionSize > 50 {
|
||||||
fontSize = 50
|
captionSize = 50
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas := gg.NewContext(img.Bounds().Dx(), img.Bounds().Dy()+200)
|
// 8px padding all around
|
||||||
err = canvas.LoadFontFaceFromBytes(resourceImpactFont, fontSize)
|
_, captionHeight := measureText(_resources.FontRoboto, strings.Join(args, " "), captionSize, imgWidth-16)
|
||||||
|
captionHeight += 16
|
||||||
|
|
||||||
|
if captionHeight < 128 {
|
||||||
|
captionHeight = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas := gg.NewContext(imgWidth, imgHeight+captionHeight)
|
||||||
|
err = canvas.LoadFontFaceFromBytes(_resources.FontRoboto, captionSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.Error{
|
return app.Error{
|
||||||
Msg: "Failed to load font",
|
Msg: "Failed to load font",
|
||||||
|
@ -178,27 +167,25 @@ func registerCaption(a *app.App) app.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.SetRGBA(255, 255, 255, 255)
|
canvas.SetRGBA(1, 1, 1, 1)
|
||||||
canvas.Clear()
|
canvas.Clear()
|
||||||
|
|
||||||
canvas.SetRGBA(0, 0, 0, 255)
|
canvas.SetRGBA(0, 0, 0, 1)
|
||||||
canvas.DrawStringWrapped(
|
canvas.DrawStringWrapped(
|
||||||
strings.Join(args, " "),
|
strings.Join(args, " "),
|
||||||
float64(img.Bounds().Dx()/2),
|
float64(imgWidth/2), float64(captionHeight/2),
|
||||||
100,
|
0.5, 0.5, float64(imgWidth),
|
||||||
0.5,
|
|
||||||
0.5,
|
|
||||||
float64(img.Bounds().Dx()),
|
|
||||||
1.5,
|
1.5,
|
||||||
gg.AlignCenter,
|
gg.AlignCenter,
|
||||||
)
|
)
|
||||||
|
|
||||||
canvas.DrawImage(img, 0, 200)
|
canvas.DrawImage(img, 0, captionHeight)
|
||||||
|
|
||||||
var export bytes.Buffer
|
var export bytes.Buffer
|
||||||
err = canvas.EncodeJPG(bufio.NewWriter(&export), &jpeg.Options{
|
err = canvas.EncodeJPG(
|
||||||
Quality: 100,
|
bufio.NewWriter(&export),
|
||||||
})
|
&jpeg.Options{Quality: 100},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.Error{
|
return app.Error{
|
||||||
Msg: "Failed to encode JPEG",
|
Msg: "Failed to encode JPEG",
|
||||||
|
@ -212,7 +199,7 @@ func registerCaption(a *app.App) app.Callback {
|
||||||
Image: &discordgo.MessageEmbedImage{
|
Image: &discordgo.MessageEmbedImage{
|
||||||
URL: "attachment://caption.jpeg",
|
URL: "attachment://caption.jpeg",
|
||||||
},
|
},
|
||||||
Color: color.RGBToDiscord(255, 255, 255),
|
Color: color.RGBToDiscord(1, 1, 1),
|
||||||
},
|
},
|
||||||
Files: []*discordgo.File{
|
Files: []*discordgo.File{
|
||||||
{
|
{
|
||||||
|
@ -228,7 +215,35 @@ func registerCaption(a *app.App) app.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClosestImage(h *app.Handler) (string, error) {
|
func loadImageFromBytes(buff []byte) (image.Image, error) {
|
||||||
|
var (
|
||||||
|
img image.Image
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
contentType := http.DetectContentType(buff)
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
case "image/png":
|
||||||
|
img, err = png.Decode(bytes.NewReader(buff))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode png: %s", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "image/jpeg":
|
||||||
|
img, err = jpeg.Decode(bytes.NewReader(buff))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode jpeg: %s", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown or unsupported format: %s", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findClosestImage(h *app.Handler) (string, error) {
|
||||||
// Get message attachments
|
// Get message attachments
|
||||||
if len(h.Message.Attachments) >= 1 {
|
if len(h.Message.Attachments) >= 1 {
|
||||||
if h.Message.Attachments[0].Size > maxFileSize {
|
if h.Message.Attachments[0].Size > maxFileSize {
|
||||||
|
@ -257,3 +272,17 @@ func getClosestImage(h *app.Handler) (string, error) {
|
||||||
|
|
||||||
return "", errors.New("no files exists")
|
return "", errors.New("no files exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func measureText(font []byte, text string, size float64, width int) (int, int) {
|
||||||
|
canvas := gg.NewContext(width, width)
|
||||||
|
err := canvas.LoadFontFaceFromBytes(font, size)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedText := strings.Join(canvas.WordWrap(text, float64(width)), "\n")
|
||||||
|
|
||||||
|
lineWidth, lineHeight := canvas.MeasureMultilineString(wrappedText, 1.5)
|
||||||
|
|
||||||
|
return int(lineWidth), int(lineHeight)
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue