~mendelmaleh/dummy

494d45fc9e9da252c50728de24cd2fadf27384f8 — Mendel E 1 year, 5 months ago 67121cb
Add text rendering
5 files changed, 133 insertions(+), 9 deletions(-)

M README.md
M api.md
M image.go
M main.go
M options.go
M README.md => README.md +5 -1
@@ 8,13 8,17 @@ go get git.sr.ht/~mendelmaleh/dummy
```

## config
The config uses the TOML format. You can define the `address:port` it runs on,
The config uses the TOML format. You should define the `address:port` it runs on,
and its pattern root endpoint.

Additionally, a directory with fonts is required, with minimum one font, set as the default. Ensure the `fonts` value has a trailing slash.

```
[dummy]
addr = "localhost:port"
pattern = "/"
fonts = "fonts/"
font = "Open Sans"
```

## usage

M api.md => api.md +12 -4
@@ 71,7 71,7 @@ _This option is only available on the `/web/` endpoint._
Set the `og:description` content.


## Text options (WIP)
## Text options

```
text	string	""


@@ 84,14 84,22 @@ last passed `color` param.
font	string	""
```

Set the font family for the text.
Set the font family for the text. There will be a default, set in the config.

```
size	int
size	float64		80.0
```

Set the font size for the text.
TODO: default value, dpi?

```
dpi		float64		72.0
```

Set the dpi for font rendering.


## Text options (WIP)

```
center	string	"height"

M image.go => image.go +29 -0
@@ 4,6 4,10 @@ import (
	"image"
	"image/color"
	"image/draw"

	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

type img struct {


@@ 13,6 17,7 @@ type img struct {
func Image(opt Options) image.Image {
	im := img{image.NewRGBA(image.Rectangle{image.Point{}, image.Point{opt.Width, opt.Height}})}

	// image
	switch opt.Pattern {
	case "plain":
		draw.Draw(im, im.Bounds(), image.NewUniform(opt.Colors[0]), image.Point{}, draw.Src)


@@ 23,6 28,30 @@ func Image(opt Options) image.Image {
		draw.DrawMask(im, im.Bounds(), image.NewUniform(opt.Colors[1]), image.Point{}, &diagonal{im, h, w}, image.Point{}, draw.Over)
	}

	// text
	face := truetype.NewFace(opt.Font, &truetype.Options{
		Size:    opt.Size,
		DPI:     opt.DPI,
		Hinting: font.HintingNone,
	})

	// TODO: font color? colors[-1]?
	drawer := &font.Drawer{
		Dst:  im,
		Src:  image.NewUniform(opt.Colors[1]),
		Face: face,
	}

	b, x := font.BoundString(face, opt.Text)
	y := b.Max.Y - b.Min.Y

	drawer.Dot = fixed.Point26_6{
		X: (fixed.I(opt.Width) - x) / 2,
		Y: (fixed.I(opt.Height) + y) / 2,
	}

	drawer.DrawString(opt.Text)

	return im
}


M main.go => main.go +44 -2
@@ 8,18 8,22 @@ import (
	"log"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"github.com/pelletier/go-toml"
)

type Config struct {
	Dummy struct {
		Addr    string
		Pattern string
		Addr, Pattern, Fonts, Font string
	}
}

var fonts = make(map[string]*truetype.Font)

func main() {
	doc, err := ioutil.ReadFile("config.toml")
	if err != nil {


@@ 32,6 36,44 @@ func main() {
		log.Fatal(err)
	}

	fontfiles, err := ioutil.ReadDir(config.Dummy.Fonts)
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range fontfiles {
		if filepath.Ext(file.Name()) != ".ttf" {
			continue
		}

		filebytes, err := ioutil.ReadFile(config.Dummy.Fonts + file.Name())
		if err != nil {
			log.Println(err) // don't exit if one font doesn't work
			continue
		}

		font, err := freetype.ParseFont(filebytes)
		if err != nil {
			log.Println(err)
			continue
		}

		// fmt.Println(font.Name(truetype.NameIDFontFamily))
		fonts[font.Name(truetype.NameIDFontFamily)] = font
	}

	if len(fonts) < 1 {
		log.Fatal("No fonts loaded")
	}

	// set default font after it's loaded
	font, ok := fonts[config.Dummy.Font]
	if !ok {
		log.Fatal("Default font not found")
	}

	dopt.Font = font

	tmpl := template.New("web").Funcs(template.FuncMap{"title": strings.Title})
	tmpl = template.Must(tmpl.Parse(`<!doctype html>
<html prefix="og: http://ogp.me/ns#">

M options.go => options.go +43 -2
@@ 4,6 4,8 @@ import (
	"image/color"
	"net/url"
	"strconv"

	"github.com/golang/freetype/truetype"
)

type Options struct {


@@ 12,6 14,11 @@ type Options struct {
	Pattern       string
	Colors        []color.RGBA

	// text
	Text      string
	Font      *truetype.Font
	Size, DPI float64

	// web
	Desc string
}


@@ 23,10 30,14 @@ var dopt = &Options{
	Height:  200,
	Pattern: "plain",
	Colors: []color.RGBA{
		color.RGBA{0x24, 0x29, 0x33, 0xff},
		color.RGBA{0xec, 0xef, 0xf4, 0xff},
		color.RGBA{0, 0, 0, 0xff},
		color.RGBA{0xff, 0xff, 0xff, 0xff},
	},

	// text // default font is set in main after its loaded.
	Size: 80,
	DPI:  72,

	// web
	Desc: "Dummy image generator",
}


@@ 68,6 79,36 @@ func (opt *Options) FromForm(form url.Values) error {
		}
	}

	// text
	if t, ok := form["text"]; ok {
		opt.Text = t[0]
	}

	if v, ok := form["font"]; ok {
		// TODO: less strict font matching
		if f, ok := fonts[v[0]]; ok {
			opt.Font = f
		}
	}

	if s, ok := form["size"]; ok {
		f, err := strconv.ParseFloat(s[0], 64)
		if err != nil {
			return err
		}

		opt.Size = f
	}

	if d, ok := form["dpi"]; ok {
		f, err := strconv.ParseFloat(d[0], 64)
		if err != nil {
			return err
		}

		opt.DPI = f
	}

	// web
	if d, ok := form["desc"]; ok {
		opt.Desc = d[0]