Files
opencloud/thumbnails/pkg/preprocessor/preprocessor.go
2021-04-29 13:48:01 +02:00

107 lines
2.4 KiB
Go

package preprocessor
import (
"bufio"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"github.com/pkg/errors"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/goregular"
"image"
"image/draw"
"io"
"mime"
"strings"
)
const (
fontSize = 12
spacing float64 = 1.5
)
type FileConverter interface {
Convert(r io.Reader) (image.Image, error)
}
type ImageDecoder struct{}
func (i ImageDecoder) Convert(r io.Reader) (image.Image, error) {
img, _, err := image.Decode(r)
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
return img, nil
}
type TxtToImageConverter struct{}
func (t TxtToImageConverter) Convert(r io.Reader) (image.Image, error) {
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
draw.Draw(img, img.Bounds(), image.White, image.Point{}, draw.Src)
c := freetype.NewContext()
// Ignoring the error since we are using the embedded Golang font.
// This shouldn't return an error.
f, _ := truetype.Parse(goregular.TTF)
c.SetFont(f)
c.SetFontSize(fontSize)
c.SetClip(img.Bounds())
c.SetDst(img)
c.SetSrc(image.Black)
c.SetHinting(font.HintingFull)
pt := freetype.Pt(10, 10+int(c.PointToFixed(fontSize)>>6))
scanner := bufio.NewScanner(r)
for scanner.Scan() {
txt := scanner.Text()
cs := chunks(txt, 80)
for _, s := range cs {
_, err := c.DrawString(strings.TrimSpace(s), pt)
if err != nil {
return nil, err
}
pt.Y += c.PointToFixed(fontSize * spacing)
if pt.Y.Round() >= img.Bounds().Dy() {
return img, scanner.Err()
}
}
}
return img, scanner.Err()
}
// Code from https://stackoverflow.com/a/61469854
// Written By Igor Mikushkin
func chunks(s string, chunkSize int) []string {
if chunkSize >= len(s) {
return []string{s}
}
var chunks []string
chunk := make([]rune, chunkSize)
length := 0
for _, r := range s {
chunk[length] = r
length++
if length == chunkSize {
chunks = append(chunks, string(chunk))
length = 0
}
}
if length > 0 {
chunks = append(chunks, string(chunk[:length]))
}
return chunks
}
func ForType(mimeType string) FileConverter {
// We can ignore the error here because we parse it in IsMimeTypeSupported before and if it fails
// return the service call. So we should only get here when the mimeType parses fine.
mimeType, _, _ = mime.ParseMediaType(mimeType)
switch mimeType {
case "text/plain":
return TxtToImageConverter{}
default:
return ImageDecoder{}
}
}