paint

package
v0.8.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: BSD-3-Clause Imports: 21 Imported by: 1

Documentation

Overview

Package paint rasterises a laid-out box tree onto an *image.RGBA using go-opentype for anti-aliased text, go-widgets/painter for backgrounds and go-gfx-decoded bitmaps for <img>. It also provides the Measurer the layout package needs (advances + vertical metrics from real font faces).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FontFamilyOf added in v0.4.0

func FontFamilyOf(g css.Generic) css.FontFamily

FontFamilyOf is the bare generic bucket as a FontFamily — the key the bundled families are registered under.

func Paint

func Paint(dst *image.RGBA, box *layout.Box, f *Fonts, imgs map[*dom.Node]image.Image)

Paint draws the box tree onto dst. imgs supplies decoded <img> bitmaps keyed by node (already scaled to the layout size); a missing entry is skipped. The caller owns clearing dst to its base (e.g. white) beforehand. It is equivalent to PaintFull with no background-image bitmaps.

func PaintFull

func PaintFull(dst *image.RGBA, box *layout.Box, f *Fonts, imgs map[*dom.Node]image.Image, bgImgs map[string]image.Image)

PaintFull draws the box tree onto dst, additionally painting CSS background-image layers (gradients and url() bitmaps). bgImgs supplies decoded background bitmaps keyed by their raw url() token; gradients need no bitmap.

Types

type Fonts

type Fonts struct {
	// contains filtered or unexported fields
}

Fonts is a registry of parsed font families (per family × bold × italic) with a per-size Face cache. It is not safe for concurrent use (Faces cache glyphs); build one per render.

func NewFonts

func NewFonts() *Fonts

NewFonts parses the bundled families in all four styles: sans = Inter, serif = Lora (both with real Bold/Italic/BoldItalic), mono = Go Mono (Regular only — bold/italic fall back to it, as the family ships no other styles). It panics only if a bundled font fails to parse, which would be a build-time defect in the fonts module, not a runtime condition.

The bundled files are parsed once per process and the parsed fonts shared by every Fonts: an *opentype.Font is read-only once parsed (a Face, which caches glyphs, is what is per-registry). Parsing them per registry — once per render — was most of the time and allocation of rendering a small page.

func (*Fonts) Measure

func (f *Fonts) Measure(text string, fam css.FontFamily, sizePx float64, weight int, italic bool) float64

Measure returns the advance width of text in CSS px at the exact size asked: the shaped glyphs' advances in font units scaled to sizePx, unrounded, for the family's face and for the fallback face on the runs that need it (see Runs). The raster painter still draws with a face at the nearest whole-pixel size and whole-pixel advances, so its glyphs sit on the pixel grid; layout, though, measures the truth — a consumer that draws at the true size (a PDF) then finds the words where the layout put them. Before this, a 14.4 px bold word was measured with a 14 px face, 2.8 % short, and printed at 14.4 px it ran into the space after it.

Shaping (see requiredLigatureFeature) runs per Run, after Runs has already split the text by cmap coverage: a ligature never spans a fallback-font boundary, matching how a real text shaper only ever merges glyphs within one already-selected face.

func (*Fonts) Metrics

func (f *Fonts) Metrics(fam css.FontFamily, sizePx float64, weight int, italic bool) (ascent, lineHeight float64)

Metrics implements layout.Measurer: ascent and line height.

func (*Fonts) Register added in v0.4.0

func (f *Fonts) Register(family string, weight int, italic bool, sfnt []byte) error

Register adds a face a document's own @font-face rule brought, parsed from SFNT bytes, under family (matched case-insensitively against the names a `font-family` declaration gives) at the weight and slant the rule declared.

The slot it occupies is the one the layout will ask for: bold is weight >= 600, the same threshold styleFace applies to a CSS weight, so a 600 and a 700 face compete for one slot and the one nearer 700 keeps it. Registering invalidates the face cache, since a cached Face was built from whichever font answered before.

func (*Fonts) RegisterFont added in v0.8.0

func (f *Fonts) RegisterFont(family string, weight int, italic bool, ft *opentype.Font) error

RegisterFont is Register for a font already parsed, which a caller that parsed it to validate it, or keeps parsed fonts across renders, passes instead of the bytes. The font is only read, never modified, so one *opentype.Font may be registered with any number of registries, concurrently.

func (*Fonts) Registered added in v0.4.0

func (f *Fonts) Registered(family string) bool

Registered reports whether any face has been registered for a family.

func (*Fonts) Runs added in v0.4.0

func (f *Fonts) Runs(s string, fam css.FontFamily, weight int, italic bool) []Run

Runs splits s into the runs the family's face and the fallback face set between them: a character goes to the fallback exactly when the family has no glyph for it and the fallback has; one neither covers stays with the family. A space or other whitespace joins whichever run it is in, so "① ②" is one fallback run rather than three. Measure and the painter walk these runs, and a consumer that sets the same text elsewhere (a PDF exporter embedding both fonts) must walk them too, so its glyphs come from the faces the layout measured with.

type Run added in v0.4.0

type Run struct {
	Text     string
	Fallback bool
}

Run is a maximal stretch of a text set in one face: the family's own, or — Fallback — the last-resort family's, because the family has no glyph for those characters.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL