opentype

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: BSD-3-Clause Imports: 8 Imported by: 0

README

go-opentype/opentype

CI pkg.go.dev coverage go license

Pure-Go, CGO=0, standard-library-only parser and anti-aliased rasteriser for TrueType/OpenType fonts. No golang.org/x/*, no third-party modules — it imports only image, encoding/binary, math, errors and friends, so it builds for every Go target including GOOS=js GOARCH=wasm.

It exists to functionally replace the narrow slice of golang.org/x/image/font/opentype that a glyph-blitting UI needs: parse a font blob, build a face at a pixel size, then pull per-rune advances and 8-bit alpha coverage masks.

Usage

f, err := opentype.Parse(ttf) // ttf is a []byte TrueType/OpenType blob
if err != nil {
    log.Fatal(err)
}
face := f.NewFace(16) // 16px per em

m := face.Metrics()             // Metrics{Ascent, Descent, Height} in pixels
w := face.Measure("Hello")      // total advance width in pixels
adv := face.Advance('H')        // one rune's advance in pixels

// Rasterise a glyph with (penX, baselineY) as the pen origin on the baseline.
bounds, mask, maskp, advance, ok := face.GlyphMask('H', penX, baselineY)
if ok && mask != nil {
    // Composite mask (an *image.Alpha coverage mask) onto your destination:
    // pixel (maskp.X+i, maskp.Y+j) covers destination (bounds.Min.X+i, bounds.Min.Y+j).
}

The GlyphMask shape deliberately mirrors x/image's font.Face.Glyph, so swapping this in for the x/image face is mechanical.

API

Symbol Purpose
Parse(b []byte) (*Font, error) Decode an sfnt font blob.
(*Font).NumGlyphs() int Glyph count (from maxp).
(*Font).GlyphIndex(r rune) (GlyphIndex, bool) Map a rune via the cmap.
(*Font).NewFace(sizePx int) *Face Build a sized face (scale = sizePx / unitsPerEm).
(*Face).Metrics() Metrics Ascent, Descent, Height in pixels.
(*Face).Advance(r rune) int Rune advance in pixels.
(*Face).Measure(s string) int Sum of advances in pixels.
(*Face).GlyphMask(r, x, y) (image.Rectangle, *image.Alpha, image.Point, int, bool) Rasterised glyph + advance.

A Font is immutable after Parse and safe for concurrent use. A Face caches rasterised glyphs and is not safe for concurrent use.

Support matrix

Supported (phase 1 — TrueType glyf outlines):

  • sfnt container (0x00010000 and true magic), table directory
  • head, maxp, hhea, hmtx (including the trailing-run shared advance)
  • cmap formats 4 (BMP) and 12 (full Unicode), preferring 12
  • loca (short and long), glyf simple glyphs (repeat flags, short/long deltas) and composite glyphs (ARGS_ARE_XY_VALUES, scale / x&y-scale / 2×2, MORE_COMPONENTS, with cycle and depth guards)
  • implied on-curve midpoint synthesis for quadratic contours
  • anti-aliased rasterisation via 4×4 supersampling under the non-zero winding rule

Not yet implemented (deferred to later phases):

  • CFF / OpenType (OTTO) outlinesParse returns a clear error
  • GPOS / GSUB shaping, ligatures, contextual substitution
  • kerning (kern / GPOS pairs)
  • hinting (TrueType instructions are skipped)
  • cmap formats other than 4 and 12; vertical metrics (vhea/vmtx)

Rasterisation is unhinted and uses uniform supersampling, so it favours correctness and portability over the last drop of small-size sharpness.

Testing

Tests never depend on an external font: they synthesise minimal-but-valid TrueType fonts in memory (table directory + head/maxp/hhea/hmtx/cmap/ loca/glyf) to deterministically exercise every parse and raster branch, including the error paths. CI enforces 100.0% statement coverage, go vet, and a cross-compile smoke over linux/{amd64,arm64,riscv64,loong64,ppc64le, s390x}, js/wasm, darwin/arm64 and windows/amd64.

License

BSD-3-Clause. See LICENSE.

Documentation

Overview

Package opentype is a pure-Go, CGO=0, standard-library-only parser and anti-aliased rasteriser for TrueType/OpenType fonts.

It is a functional replacement for the narrow slice of golang.org/x/image/font/opentype needed by a glyph-blitting UI: parse a font blob, build a Face at a pixel size, then obtain per-rune advances and 8-bit alpha coverage masks.

Scope

Phase 1 covers TrueType 'glyf' outlines: the sfnt container, the head, maxp, hhea, hmtx, cmap (formats 4 and 12), loca and glyf tables, simple and composite glyphs, implied on-curve points, and a non-zero-winding supersampling rasteriser. OpenType/CFF ('OTTO') outlines, GPOS/GSUB shaping, kerning and hinting are not implemented; see the README for the full support matrix.

Usage

f, err := opentype.Parse(ttf)
if err != nil { /* handle */ }
face := f.NewFace(16)
adv := face.Measure("Hello")
bounds, mask, maskp, advance, ok := face.GlyphMask('H', penX, baselineY)

A Font is immutable after Parse and safe for concurrent use; a Face caches rasterised glyphs and is not safe for concurrent use.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Axis added in v0.2.0

type Axis struct {
	Tag     string  // four-character axis tag, e.g. "wght", "wdth", "slnt"
	Min     float64 // minimum user coordinate
	Default float64 // default user coordinate (the font's default master)
	Max     float64 // maximum user coordinate
	Flags   uint16  // axis flags (bit 0: hidden axis)
	NameID  uint16  // 'name' table entry describing the axis
}

Axis is one design-variation axis of a variable font, decoded from the 'fvar' table. Min, Default and Max are user-space coordinates (for example 100, 400 and 900 for a typical weight axis).

type Face

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

Face renders a Font at a fixed pixel size. It caches rasterised glyphs and is not safe for concurrent use; build one Face per goroutine if needed.

func (*Face) Advance

func (fc *Face) Advance(r rune) int

Advance returns the horizontal advance of r in pixels, or 0 if the rune is not mapped by the font's cmap.

func (*Face) Font added in v0.3.1

func (fc *Face) Font() *Font

Font returns the Font this Face renders. It lets a shaper reach the font's layout tables (Font.GSUB, Font.GPOS) and per-glyph metrics (Font.GlyphAdvance) while keeping the Face for pixel sizing.

func (*Face) GlyphMask

func (fc *Face) GlyphMask(r rune, x, y int) (bounds image.Rectangle, mask *image.Alpha, maskp image.Point, advance int, ok bool)

GlyphMask rasterises r and positions it with (x, y) as the pen origin on the baseline. It returns the destination bounds, an *image.Alpha coverage mask, the offset into that mask corresponding to bounds.Min (always the origin), the advance width in pixels, and ok.

ok is false when the rune is not mapped by the cmap or its glyph outline is corrupt; callers should render nothing in that case. A mapped-but-empty glyph (for example a space) returns ok true with a nil mask, an empty bounds, and its advance.

func (*Face) Kern added in v0.2.0

func (fc *Face) Kern(prev, r rune) int

Kern returns the horizontal kerning adjustment between the consecutive runes prev and r, in whole pixels at the face's size. GPOS pair positioning is preferred; the legacy kern table is used as a fallback. It is zero when either rune is unmapped or the font carries no kerning for the pair.

func (*Face) Measure

func (fc *Face) Measure(s string) int

Measure returns the total advance width of s in pixels (the sum of each rune's Advance).

func (*Face) MeasureKerned added in v0.2.0

func (fc *Face) MeasureKerned(s string) int

MeasureKerned returns the total advance width of s in pixels like Measure, but additionally applies the font's kerning between each pair of consecutive runes. For a font with no kerning it equals Measure.

func (*Face) Metrics

func (fc *Face) Metrics() Metrics

Metrics returns the face's vertical metrics in pixels.

func (*Face) Scale added in v0.3.1

func (fc *Face) Scale() float64

Scale returns the face's font-unit-to-pixel scale factor (the render size in pixels divided by the font's unitsPerEm). A shaper multiplies a font-unit advance or offset by it to obtain whole pixels at the face's size.

func (*Face) SetHinting added in v0.2.0

func (fc *Face) SetHinting(on bool)

SetHinting enables or disables the TrueType instruction interpreter. When enabled, a glyf glyph that carries instructions is grid-fitted at the face's pixel size before rasterising; glyphs without instructions, composite glyphs and CFF fonts are unaffected. Hinting is off by default (the default output is unhinted anti-aliased coverage). The glyph cache is invalidated.

func (*Face) SetVariation added in v0.2.0

func (fc *Face) SetVariation(coords map[string]float64)

SetVariation instances the font at the user-space axis coordinates coords (keyed by axis tag, e.g. {"wght": 700}); axes absent from coords take their default. Subsequent GlyphMask calls reflect the instanced outlines, for both glyf (via gvar) and CFF2 (via its blend/vsindex operators) variable fonts. Pass nil to return to the default master. A non-variable font (or a CFF1 font, which has no variation) is unaffected. The glyph cache is invalidated so the change takes effect at once.

func (*Face) Shape added in v0.2.0

func (fc *Face) Shape(text string, features ...string) []GlyphIndex

Shape maps text to its glyph run and applies the GSUB lookups activated by the given feature tags (for example "liga" for standard ligatures, "smcp" for small caps). Each rune maps through the font's cmap; an unmapped rune becomes glyph 0 (.notdef). With no GSUB table, no features, or features that match no lookups, the run is the plain cmap mapping.

func (*Face) ShapePositioned added in v0.3.0

func (fc *Face) ShapePositioned(text string, features ...string) []PositionedGlyph

ShapePositioned maps text to a fully positioned glyph run: each rune maps through the cmap (an unmapped rune becomes glyph 0), the requested GSUB features are applied (ligatures, contextual substitution, ...), then GPOS positions the resulting glyphs — pair kerning, single/cursive adjustment and mark attachment (types 4/5/6 pull diacritics onto their base). The positioning features are the caller's features plus the always-on kern/mark/mkmk set.

The result has one PositionedGlyph per output glyph, with placement and advance in whole pixels at the face's size. A font lacking GSUB or GPOS simply skips that stage; with neither the run is the plain cmap mapping at its unadjusted advances.

func (*Face) VerticalAdvance added in v0.3.0

func (fc *Face) VerticalAdvance(r rune) int

VerticalAdvance returns the vertical advance (advance height) of r in pixels, or 0 if the rune is not mapped by the font's cmap. When the font has no vmtx table the advance is one em, scaled to the face's size.

This is the top-to-bottom analogue of Advance. Choosing the upright vertical glyph form for r (the 'vert'/'vrt2' features) is a separate shaping step done through GSUB; VerticalAdvance measures whichever glyph the cmap maps.

func (*Face) VerticalMetrics added in v0.3.0

func (fc *Face) VerticalMetrics() (ascent, descent, lineGap int)

VerticalMetrics returns the face's vertical-layout line metrics in pixels: the vhea vertTypoAscender, vertTypoDescender and vertTypoLineGap scaled to the face's size. All three are zero when the font has no vhea table.

func (*Face) VerticalOrigin added in v0.3.0

func (fc *Face) VerticalOrigin(r rune) (int, bool)

VerticalOrigin returns the y coordinate of r's vertical origin in pixels and whether the font supplies one (a VORG table). The origin locates the glyph relative to the pen when advancing top-to-bottom; ok is false when r is unmapped or the font has no VORG table.

type FeatureApp added in v0.3.1

type FeatureApp struct {
	Tag       string
	Positions []bool
}

FeatureApp requests one GSUB feature applied over a subset of a glyph run. Tag is the 4-byte feature tag (for example "init" or "liga"). Positions selects, by run index, the glyphs the feature's lookups may start at: a lookup is attempted at glyph i only when Positions[i] is true. A nil Positions applies the feature over the whole run (identical to Apply); a non-nil Positions restricts it, with indices at or past its end treated as false.

Masking is intended for the length-preserving single substitutions OpenType files the Arabic positional-form features (isol/init/medi/fina) as, so the mask indices stay aligned to the run as it is rewritten.

type Font

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

Font is a parsed TrueType/OpenType font. It is immutable after Parse and safe for concurrent use by multiple goroutines. Build a Face from it with NewFace to obtain sized metrics and rasterised glyphs.

func Parse

func Parse(b []byte) (*Font, error)

Parse decodes a TrueType/OpenType font from b and returns a Font. The byte slice is retained (not copied) and must not be mutated by the caller.

It fails on a corrupt or unsupported container: a bad sfnt magic, truncated data, or a missing required table. All three outline flavours are supported: TrueType ('glyf'/'loca'), CFF/OpenType (a "OTTO" sfnt, or any sfnt carrying a 'CFF ' table, decoded via cff.go) and variable CFF2 (a sfnt carrying a 'CFF2' table, decoded via cff2.go).

func (*Font) Axes added in v0.2.0

func (f *Font) Axes() []Axis

Axes returns the font's variation axes, or nil for a non-variable font.

func (*Font) GPOS added in v0.3.1

func (f *Font) GPOS() *GPOS

GPOS returns the font's parsed GPOS table, or nil when the font carries no GPOS table (in which case no positioning is available).

func (*Font) GSUB added in v0.3.1

func (f *Font) GSUB() *GSUB

GSUB returns the font's parsed GSUB table, or nil when the font carries no GSUB table (in which case no substitution is available).

func (*Font) GlyphAdvance added in v0.3.1

func (f *Font) GlyphAdvance(g GlyphIndex) int

GlyphAdvance returns the horizontal advance of glyph g in font units, or 0 when g is out of range. A shaper multiplies it by Face.Scale to obtain pixels, and feeds the per-glyph advances to [GPOS.Position] so mark attachment can pull a diacritic back onto its base.

func (*Font) GlyphIndex

func (f *Font) GlyphIndex(r rune) (GlyphIndex, bool)

GlyphIndex maps a rune to its glyph index via the selected cmap subtable. ok is false when the rune has no glyph in that subtable.

func (*Font) GlyphIndexVariation added in v0.2.0

func (f *Font) GlyphIndexVariation(r, vs rune) (GlyphIndex, bool)

GlyphIndexVariation resolves a Unicode variation sequence (a base rune followed by a variation selector) to a glyph index, using the font's format-14 cmap subtable.

ok is false when the font has no format-14 subtable, the variation selector is not registered in it, or the sequence is registered as a "default" mapping whose base rune has no glyph in the ordinary cmap.

func (*Font) HasVerticalMetrics added in v0.3.0

func (f *Font) HasVerticalMetrics() bool

HasVerticalMetrics reports whether the font carries the vertical header and per-glyph vertical advances (vhea and vmtx) required to lay text out top-to-bottom. When it returns false, VerticalAdvance falls back to the em square and VerticalMetrics returns zeroes.

func (*Font) InstancePoints added in v0.2.0

func (f *Font) InstancePoints(gid int, coords map[string]float64) ([]contour, error)

InstancePoints returns glyph gid's outline, as contours in font units, instanced at the user-space axis coordinates coords (keyed by axis tag). Axes absent from coords take their default. For a non-variable font, or a glyph with no variation data, it returns the default outline. Both simple and composite glyphs are varied: a composite's component offsets get their deltas and each component is instanced recursively at the same coordinate.

func (*Font) NamedInstances added in v0.2.0

func (f *Font) NamedInstances() []NamedInstance

NamedInstances returns the font's named instances, or nil if the font is not variable or declares none.

func (*Font) NewFace

func (f *Font) NewFace(sizePx int) *Face

NewFace returns a Face that renders f at sizePx pixels per em. The scale factor is sizePx / unitsPerEm; sizePx should be positive.

func (*Font) NormalizeCoords added in v0.2.0

func (f *Font) NormalizeCoords(user map[string]float64) []int16

NormalizeCoords converts a set of user-space axis coordinates (keyed by axis tag) into per-axis normalized F2Dot14 values in the range [-1, 1], the form gvar and avar operate in. Axes not present in user take their default (which normalizes to 0). Values outside an axis's [min, max] are clamped. If the font declares an 'avar' table its segment maps are applied. The result has one entry per fvar axis, in axis order; it is nil for a non-variable font.

func (*Font) NumGlyphs

func (f *Font) NumGlyphs() int

NumGlyphs returns the number of glyphs in the font (from the maxp table).

type GPOS added in v0.3.1

type GPOS = gpos

GPOS is the exported handle to a font's parsed GPOS (glyph positioning) table, obtained via Font.GPOS. Its [GPOS.Position] method runs positioning lookups (pair kerning, mark attachment, cursive attachment, ...) over a glyph run a caller has already substituted.

type GSUB added in v0.3.1

type GSUB = gsub

GSUB is the exported handle to a font's parsed GSUB (glyph substitution) table, obtained via Font.GSUB. It exposes [GSUB.Apply] (whole-run) and [GSUB.ApplyMasked] (positional) so a shaper outside this package can drive substitution lookups directly.

type GlyphIndex

type GlyphIndex uint16

GlyphIndex is a glyph identifier within a font: an index into the font's glyph store, as produced by the cmap. Zero is the ".notdef" glyph.

type GlyphPosition added in v0.3.0

type GlyphPosition struct {
	XOffset  int
	YOffset  int
	XAdvance int
	YAdvance int
}

GlyphPosition is the positioning adjustment GPOS computes for one glyph in a run: pen placement offsets (XOffset, YOffset) and advance adjustments (XAdvance, YAdvance), all in font units.

type Kerner added in v0.2.0

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

Kerner combines GPOS and the legacy kern table: GPOS is preferred and the kern table is a fallback used when GPOS yields no adjustment. Either source may be nil.

func (*Kerner) Kerning added in v0.2.0

func (kn *Kerner) Kerning(left, right GlyphIndex) int

Kerning returns the X-advance adjustment between left and right, preferring a non-zero GPOS value and otherwise falling back to the legacy kern table.

type Metrics

type Metrics struct {
	Ascent  int // baseline-to-top distance (a positive height above the baseline)
	Descent int // baseline-to-bottom distance (a positive depth below the baseline)
	Height  int // line height: ascent + descent + line gap
}

Metrics holds a Face's vertical metrics in whole pixels.

type NamedInstance added in v0.2.0

type NamedInstance struct {
	SubfamilyNameID  uint16             // 'name' entry for the instance subfamily
	Flags            uint16             // instance flags (reserved)
	Coordinates      map[string]float64 // axis tag -> user coordinate
	PostScriptNameID uint16             // 'name' entry for the PostScript name, 0 if absent
}

NamedInstance is a named position in the variation space (for example "Bold" or "Condensed"), decoded from the 'fvar' table.

type PositionedGlyph added in v0.3.0

type PositionedGlyph struct {
	Glyph    GlyphIndex
	XOffset  int
	YOffset  int
	XAdvance int
}

PositionedGlyph is one glyph of a positioned run produced by ShapePositioned: the glyph to draw, the pen-relative placement offsets (XOffset, YOffset) GPOS assigned it (a non-zero YOffset lifts an attached diacritic onto its base), and the advance to move the pen by afterwards, all in whole pixels at the face's size.

Jump to

Keyboard shortcuts

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