diff --git a/go.mod b/go.mod index b7756e0..2bed8df 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/dgraph-io/badger/v4 v4.2.0 github.com/fiatjaf/eventstore v0.2.6 github.com/fiatjaf/khatru v0.0.6 + github.com/fogleman/gg v1.3.0 github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a github.com/kelseyhightower/envconfig v1.4.0 github.com/lukevers/freetype-go v0.0.0-20150513150840-77e276735410 @@ -18,6 +19,7 @@ require ( github.com/rs/cors v1.10.0 github.com/rs/zerolog v1.29.1 github.com/stretchr/testify v1.8.4 + github.com/texttheater/golang-levenshtein v1.0.1 github.com/tylermmorton/tmpl v0.0.0-20231025031313-5552ee818c6d golang.org/x/exp v0.0.0-20231006140011-7918f672742d mvdan.cc/xurls/v2 v2.5.0 @@ -41,6 +43,7 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.3.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -56,13 +59,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect - github.com/texttheater/golang-levenshtein v1.0.1 // indirect github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect go.opencensus.io v0.24.0 // indirect + golang.org/x/image v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index 4bf2746..9c92535 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,8 @@ github.com/fiatjaf/eventstore v0.2.6 h1:ulKmQhYnJXOEBDJQzbq6p9lJY4HhbIx5OCmGRJ5u github.com/fiatjaf/eventstore v0.2.6/go.mod h1:Zx1XqwICh7RxxKLkgc0aXlVo298ABs4W5awP/1/bYYs= github.com/fiatjaf/khatru v0.0.6 h1:6d5gnKdgsaHD+NZFzsVp22nqxnGxtlLLR8gYVea6KBU= github.com/fiatjaf/khatru v0.0.6/go.mod h1:gvfXhDel30t84mkWk5aDwBRD1N8py4RixIwGD0i+LMY= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -83,6 +85,8 @@ github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/K github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -215,6 +219,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= +golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/render_image.go b/render_image.go index 384f872..80cdb8c 100644 --- a/render_image.go +++ b/render_image.go @@ -5,7 +5,7 @@ import ( "embed" "fmt" "image" - "image/draw" + "image/color" "image/png" "net/http" "strings" @@ -13,8 +13,9 @@ import ( "unicode" "github.com/apatters/go-wordwrap" - "github.com/lukevers/freetype-go/freetype" - "github.com/lukevers/freetype-go/freetype/truetype" + "github.com/fogleman/gg" + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" ) const ( @@ -151,7 +152,7 @@ func normalizeText(input []string, breakWords bool) []string { return lines } -func drawImage(lines []string, font *truetype.Font, style Style) (image.Image, error) { +func drawImage(lines []string, ttf *truetype.Font, style Style) (image.Image, error) { width := 700 height := 525 paddingLeft := 0 @@ -162,43 +163,24 @@ func drawImage(lines []string, font *truetype.Font, style Style) (image.Image, e height = width * 268 / 512 } - // get the physical image ready with colors/size - fg, bg := image.Black, image.White - img := image.NewRGBA(image.Rect(0, 0, width, height)) + img := gg.NewContext(width, height) + img.SetColor(color.Black) + img.SetFontFace(truetype.NewFace(ttf, &truetype.Options{ + Size: FONT_SIZE, + DPI: 300, + Hinting: font.HintingFull, + })) - // draw the empty image - draw.Draw(img, img.Bounds(), bg, image.Point{}, draw.Src) - - // create new freetype context to get ready for adding text. - c := freetype.NewContext() - c.SetDPI(300) - c.SetFont(font) - c.SetFontSize(FONT_SIZE) - c.SetClip(img.Bounds()) - c.SetDst(img) - c.SetSrc(fg) - c.SetHinting(freetype.NoHinting) - - // draw each line separately - var count float64 = 1 - for _, line := range lines { - if err := drawText(c, line, count, paddingLeft); err != nil { - return nil, err - } - count++ + for i, line := range lines { + img.DrawStringWrapped(line, + float64(10+paddingLeft), + float64(10+(i*FONT_SIZE*300*256.0/72.0)>>8), + 0, 0, + float64(width-10-paddingLeft), + float64(height-10), gg.AlignLeft, + ) } - - return img, nil -} - -func drawText(c *freetype.Context, text string, line float64, paddingLeft int) error { - // We need an offset because we need to know where exactly on the - // image to place the text. The `line` is how much of an offset - // that we need to provide (which line the text is going on). - offsetY := 10 + int(c.PointToFix32(FONT_SIZE*line)>>8) - - _, err := c.DrawString(text, freetype.Pt(10+paddingLeft, offsetY)) - return err + return img.Image(), nil } // replace nevent and note with their text, as an extra line prefixed by BLOCK @@ -325,7 +307,7 @@ gotLang: return nil, false, err } - font, err := freetype.ParseFont(fontData) + font, err := truetype.Parse(fontData) if err != nil { return nil, false, err }