github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/README.md (about) 1 # etxt 2 [![Go Reference](https://pkg.go.dev/badge/github.com/Kintar/etxt.svg)](https://pkg.go.dev/github.com/Kintar/etxt) 3 4 **etxt** is a package for font management and text rendering in Golang designed to be used with the [**Ebitengine**](https://github.com/hajimehoshi/ebiten) game engine. 5 6 While Ebitengine already provides the [**ebiten/text**](https://pkg.go.dev/github.com/hajimehoshi/ebiten/v2/text) package that makes *getting some text drawn on screen* easy enough, **etxt** aims to help you actually understand what you are doing, doing it in a structured way and giving you much more control over it. 7 8 As a quick summary of what this package provides: 9 - Structured font management and usage through the `FontLibrary` and `Renderer` types; because having to create and manage new `font.Face`s just to change text size is *not* ok. 10 - Full control over glyph mask caching and rasterization (or just stay with the defaults!). 11 - A few custom rasterizers that allow you to draw faux-bold, oblique, ~~blurred and hollow text~~ (WIP). Not really "main features", though, only examples of what you can do with **etxt**. 12 - Lots of [examples](https://github.com/Kintar/etxt/tree/main/examples) and thorough documentation. 13 14 ## Code example 15 Less talk and more code! 16 ```Golang 17 package main 18 19 import ( "log" ; "time" ; "image/color" ) 20 import "github.com/hajimehoshi/ebiten/v2" 21 import "github.com/Kintar/etxt" 22 23 type Game struct { txtRenderer *etxt.Renderer } 24 func (self *Game) Layout(int, int) (int, int) { return 400, 400 } 25 func (self *Game) Update() error { return nil } 26 func (self *Game) Draw(screen *ebiten.Image) { 27 // hacky color computation 28 millis := time.Now().UnixMilli() 29 blue := (millis/16) % 512 30 if blue >= 256 { blue = 511 - blue } 31 changingColor := color.RGBA{ 0, 255, uint8(blue), 255 } 32 33 // set relevant text renderer properties and draw 34 self.txtRenderer.SetTarget(screen) 35 self.txtRenderer.SetColor(changingColor) 36 self.txtRenderer.Draw("Hello World!", 200, 200) 37 } 38 39 func main() { 40 // load font library 41 fontLib := etxt.NewFontLibrary() 42 _, _, err := fontLib.ParseDirFonts("game_dir/assets/fonts") // !! 43 if err != nil { 44 log.Fatalf("Error while loading fonts: %s", err.Error()) 45 } 46 47 // check that we have the fonts we want 48 // (shown for completeness, you don't need this in most cases) 49 expectedFonts := []string{ "Roboto Bold", "Carter One" } // !! 50 for _, fontName := range expectedFonts { 51 if !fontLib.HasFont(fontName) { 52 log.Fatal("missing font: " + fontName) 53 } 54 } 55 56 // check that the fonts have the characters we want 57 // (shown for completeness, you don't need this in most cases) 58 err = fontLib.EachFont(checkMissingRunes) 59 if err != nil { log.Fatal(err) } 60 61 // create a new text renderer and configure it 62 txtRenderer := etxt.NewStdRenderer() 63 glyphsCache := etxt.NewDefaultCache(10*1024*1024) // 10MB 64 txtRenderer.SetCacheHandler(glyphsCache.NewHandler()) 65 txtRenderer.SetFont(fontLib.GetFont(expectedFonts[0])) 66 txtRenderer.SetAlign(etxt.YCenter, etxt.XCenter) 67 txtRenderer.SetSizePx(64) 68 69 // run the "game" 70 ebiten.SetWindowSize(400, 400) 71 err = ebiten.RunGame(&Game{ txtRenderer }) 72 if err != nil { log.Fatal(err) } 73 } 74 75 // helper function used with FontLibrary.EachFont to make sure 76 // all loaded fonts contain the characters or alphabet we want 77 func checkMissingRunes(name string, font *etxt.Font) error { 78 const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 79 const symbols = "0123456789 .,;:!?-()[]{}_&#@" 80 81 missing, err := etxt.GetMissingRunes(font, letters + symbols) 82 if err != nil { return err } 83 if len(missing) > 0 { 84 log.Fatalf("Font '%s' missing runes: %s", name, string(missing)) 85 } 86 return nil 87 } 88 ``` 89 90 This example focuses on the mundane usage of the main **etxt** `FontLibrary` and `Renderer` types, with abundant checks to fail fast if anything seems out of place. 91 92 If you want flashier examples you will find [many more](https://github.com/Kintar/etxt/tree/main/examples) in the project, make sure to check them out! 93 94 ## Can I use this package without Ebitengine? 95 Yeah, you can compile it with `-tags gtxt`. Notice that `gtxt` will make text drawing happen on the CPU, so don't try to use it for real-time stuff. In particular, be careful to not accidentally use `gtxt` with Ebitengine (they are compatible in many cases, but performance will die). 96 97 ## Should I bother learning to use etxt? 98 If you are only dealing with text rendering incidentally and **ebiten/text** does the job well enough for you, feel free to stay with that. 99 100 The main consideration when using **etxt** is that you need to be minimally acquainted with how fonts work. [FreeType glyph conventions](https://freetype.org/freetype2/docs/glyphs/index.html) is the go to reference that you *really should be reading* (up to section IV or V). 101 102 ## Any future plans? 103 This package is already quite solid, there are only a few points left to improve: 104 - Adding a few more effects (hollow text, shaders, etc). 105 - Missing a couple important examples (crisp UI and shaders). 106 107 If I get really bored, I'd also like to look into: 108 - Contributing to Golang's **sfnt** to [expose more tables](https://github.com/golang/go/issues/45325) and allow the creation of minimal packages to do basic [text shaping](https://github.com/Kintar/etxt/blob/main/docs/shaping.md) in arabic or other complex scripts. 109 - Add outline expansion. Freetype and libASS do this, and it would be quite nice to get high quality outlines and better faux-bolds... but it's also *hard*; I don't really know if I want to go there. 110 - Triangulation and GPU rendering of Bézier curves are also interesting for Ebitengine (although they probably don't belong in this package).