opentype

package module
v0.5.0 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, shaper 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, pull per-rune advances and 8-bit alpha coverage masks — and then goes further: CFF/CFF2 outlines, variable fonts, GPOS/GSUB shaping, kerning and TrueType/CFF hinting are all in scope.

Install

go get github.com/go-opentype/opentype

Quick start

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

func main() {
	ttf, err := os.ReadFile("MyFont.ttf") // or .otf (CFF/CFF2)
	if err != nil {
		panic(err)
	}

	f, err := opentype.Parse(ttf)
	if err != nil {
		panic(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
	fmt.Println(m, w, adv)

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

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

Features

  • sfnt container parsing (0x00010000 TrueType and OTTO CFF magics)
  • TrueType glyf outlines — simple and composite glyphs, implied on-curve midpoint synthesis, cycle/depth-guarded composites
  • CFF and CFF2 outlines — Type 2 charstrings, subroutines, seac accent composition, CFF2 variable-font blend operators
  • Variable fontsfvar axes and named instances, avar axis-value mapping, gvar/CFF2 glyph-outline interpolation, MVAR metric variation, via (*Face).SetVariation
  • GSUB/GPOS shaping — ligatures, contextual and positional-form substitution (isol/init/medi/fina), pair/single/cursive positioning, mark-to-base and mark-to-mark attachment
  • Kerning — GPOS pair positioning with legacy kern-table fallback through a single Kerner
  • Hinting — TrueType instruction interpreter and a CFF/Type 2 stem grid-fitter, toggled per Face with SetHinting; optional SetStemDarkening
  • Vertical metricsvhea/vmtx/VORG-aware vertical advances and origins for vertical writing modes
  • Anti-aliased rasterisation via 4×4 supersampling under the non-zero winding rule

A Font is immutable after Parse and safe for concurrent use. A Face caches rasterised glyphs and is not safe for concurrent use; build one Face per goroutine if needed.

API tour

Symbol Purpose
Parse(b []byte) (*Font, error) Decode an sfnt (TrueType or CFF/OTTO) font blob.
(*Font).NumGlyphs() int Glyph count (from maxp).
(*Font).GlyphIndex(r rune) (GlyphIndex, bool) Map a rune via the cmap.
(*Font).GlyphIndexVariation(r, vs rune) (GlyphIndex, bool) Map a rune + Unicode variation selector.
(*Font).Axes() []Axis Variable-font design axes (fvar).
(*Font).NamedInstances() []NamedInstance Named positions in the variation space.
(*Font).GPOS() *GPOS / (*Font).GSUB() *GSUB Parsed layout tables, or nil.
(*Font).HasVerticalMetrics() bool Whether vhea/vmtx are present.
(*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 / (*Face).Measure(s string) int Rune / string advance in pixels.
(*Face).Kern(prev, r rune) int / (*Face).MeasureKerned(s string) int GPOS-or-kern pair adjustment.
(*Face).GlyphMask(r, x, y) (image.Rectangle, *image.Alpha, image.Point, int, bool) Rasterised glyph + advance.
(*Face).Shape(text string, features ...string) []GlyphIndex GSUB-substituted glyph run.
(*Face).ShapePositioned(text string, features ...string) []PositionedGlyph GSUB + GPOS glyph run with pen offsets.
(*Face).SetVariation(coords map[string]float64) Instance a variable font at the given axis coordinates.
(*Face).SetHinting(on bool) / (*Face).SetStemDarkening(on bool) Toggle the TrueType/CFF hinter.
(*Face).VerticalAdvance(r rune) int / (*Face).VerticalOrigin(r rune) (int, bool) Vertical writing-mode metrics.

See example_test.go for runnable examples of each of these, and go doc github.com/go-opentype/opentype for the full reference.

Support matrix

  • sfnt container (0x00010000 and true TrueType magics, OTTO CFF magic)
  • head, maxp, hhea, hmtx (including the trailing-run shared advance)
  • cmap formats 4 (BMP) and 12 (full Unicode), preferring 12, plus format 14 (Unicode variation sequences)
  • loca (short and long), glyf simple and composite TrueType glyphs
  • CFF and CFF2 (OTTO) Type 2 charstring outlines, including seac
  • fvar/avar/gvar/MVAR and CFF2 blends for variable fonts
  • GSUB/GPOS/GDEF, the legacy kern table
  • the OpenType MATH table — math-typesetting metrics (constants, per-glyph italic correction / top-accent attachment / corner kerning, and stretchy-glyph size variants and assemblies), exposed pixel-scaled through a Face (HasMath, MathConstant, ItalicCorrection, TopAccentAttachment, MathKern, MathVariants); math layout is left to a higher-level engine
  • TrueType instruction hinting and a CFF stem grid-fitter/darkener
  • vhea/vmtx/VORG vertical metrics
  • anti-aliased rasterisation via 4×4 supersampling under the non-zero winding rule

Rasterisation uses uniform supersampling (not the delta-hinted rasterisation of a native TrueType/CFF hinter's drop-out control), so it favours correctness and portability over the very last drop of small-size sharpness.

Testing

Tests never depend on an external font to reach 100% coverage: they synthesise minimal-but-valid TrueType and CFF fonts in memory to deterministically exercise every parse, shape and raster branch, including the error paths. A real-world font, Adobe's Source Serif 4 (SIL Open Font License, bundled under testdata/), is used in example_test.go and in a handful of sanity-check tests, so the documented examples double as an end-to-end smoke test against production CFF charstrings and Private DICTs. 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.

Part of the go-opentype pure-Go text stack

go-opentype/opentype is the parsing/shaping/rasterising engine at the base of a dependency-free text stack:

  • opentype (this repo) — font parsing, GSUB/GPOS shaping, hinting and rasterisation.
  • bidi — a full Unicode Bidirectional Algorithm (UBA) implementation, for ordering mixed left-to-right/right-to-left text before it is shaped.
  • shape — a HarfBuzz-lite complex-script shaper (Arabic, Indic, Hangul, USE, Egyptian hieroglyphs, ...) built on this package's GSUB/GPOS engine.
  • fonts — 36 bundled OFL/BSD font families, per-family lazily go:embed-ed, ready to feed to Parse.

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.

The OpenType MATH table (math-typesetting metrics: constants, per-glyph italic correction and kerning, and stretchy-glyph size variants and assemblies) is decoded and exposed pixel-scaled through a Face; see Font.HasMath and math.go. Math layout itself (box building, the TeX math rules) is a higher-level engine that consumes these values.

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.

Embedding and subsetting

For a consumer that embeds a font (a PDF or EPUB writer), the package exposes the primitives such an embedder otherwise re-implements by re-parsing the sfnt: the PDF FontDescriptor scalars (Font.UnitsPerEm, Font.FontBBox, Font.CapHeight, Font.XHeight, Font.ItalicAngle, Font.StemV, Font.Flags, ...); raw table access (Font.TableTags, Font.Table); by-glyph-index advances that honour the current variation (Face.AdvanceIndex, Face.AdvanceIndexUnits, Face.VerticalAdvanceIndex); TrueType glyph subsetting (Font.SubsetTrueType) and CFF charstring subsetting (Font.SubsetCFF); and baking a variable font at a chosen axis position into a static instance (Font.Instance, Font.InstanceBytes).

Index

Examples

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. When a variation is set the HVAR delta (or the gvar phantom-point fallback) is included.

func (*Face) AdvanceIndex added in v0.5.0

func (fc *Face) AdvanceIndex(gid GlyphIndex) int

AdvanceIndex returns the horizontal advance of glyph gid in whole pixels at the face's size, honouring the face's current variation (the HVAR delta, or the gvar phantom-point fallback, is folded in exactly as Face.Advance does for a rune). With no variation set it is the base advance scaled to pixels. It is the by-glyph-index counterpart of Face.Advance, for a caller that already holds a glyph id rather than a rune. An out-of-range gid advances by zero.

func (*Face) AdvanceIndexUnits added in v0.5.0

func (fc *Face) AdvanceIndexUnits(gid GlyphIndex) float64

AdvanceIndexUnits returns the horizontal advance of glyph gid in font units at the face's current variation, as a float64 (the value AdvanceIndex rounds and scales to pixels). It is the exact width a PDF /W array wants — font-unit widths that track the instanced font — without the caller re-deriving the HVAR delta. An out-of-range gid advances by zero.

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.

Example

ExampleFace_GlyphMask rasterises a single glyph and reports the coverage mask placement, exactly the shape golang.org/x/image/font.Face.Glyph returns, so swapping this Face in for an x/image face is mechanical.

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

// loadTestFont reads the CFF/OpenType font bundled under testdata for the
// examples below (Adobe Source Serif 4, SIL Open Font License; see
// testdata/OFL.txt).
func loadTestFont() *opentype.Font {
	b, err := os.ReadFile("testdata/SourceSerif4-Regular.otf")
	if err != nil {
		panic(err)
	}
	f, err := opentype.Parse(b)
	if err != nil {
		panic(err)
	}
	return f
}

func main() {
	f := loadTestFont()
	face := f.NewFace(16)

	bounds, mask, maskp, advance, ok := face.GlyphMask('H', 0, 16)
	fmt.Println("ok =", ok)
	fmt.Println("bounds =", bounds)
	fmt.Println("mask != nil:", mask != nil)
	if mask != nil {
		fmt.Println("mask bounds =", mask.Bounds())
	}
	fmt.Println("maskp =", maskp)
	fmt.Println("advance =", advance)
}
Output:
ok = true
bounds = (0,5)-(12,16)
mask != nil: true
mask bounds = (0,0)-(12,11)
maskp = (0,0)
advance = 13

func (*Face) GlyphMaskIndex added in v0.3.3

func (fc *Face) GlyphMaskIndex(gid GlyphIndex, x, y int) (bounds image.Rectangle, mask *image.Alpha, maskp image.Point, advance int, ok bool)

GlyphMaskIndex rasterises the glyph with index gid directly — bypassing the cmap — and positions it with (x, y) as the pen origin on the baseline. It is the by-glyph-index counterpart of Face.GlyphMask: a complex-text shaper (see github.com/go-opentype/shape) emits glyph indices after applying GSUB substitutions and GPOS positioning, so the runes have already been resolved to glyphs and a caller must render each glyph by its index rather than by a rune. Callers add the shaper's per-glyph pixel offset to (x, y) before the call and advance the pen by the shaper's advance afterwards.

It returns the destination bounds, an *image.Alpha coverage mask, the offset into that mask corresponding to bounds.Min (always the origin), the glyph's own horizontal advance width in pixels, and ok.

ok is false when gid is out of range or its outline is corrupt; callers should render nothing in that case. A valid-but-empty glyph (for example a space) returns ok true with a nil mask, an empty bounds, and its advance.

func (*Face) ItalicCorrection added in v0.4.0

func (fc *Face) ItalicCorrection(gid GlyphIndex) int

ItalicCorrection returns glyph gid's math italic correction in pixels, or 0 when the glyph has none (or the font carries no MATH table). It is the space to insert after a slanted glyph before a following upright one, and the horizontal offset for a subscript that follows a superscript.

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.

Example

ExampleFace_Kern shows kerning-aware measurement: MeasureKerned folds in the pair adjustment that plain Measure does not.

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

// loadTestFont reads the CFF/OpenType font bundled under testdata for the
// examples below (Adobe Source Serif 4, SIL Open Font License; see
// testdata/OFL.txt).
func loadTestFont() *opentype.Font {
	b, err := os.ReadFile("testdata/SourceSerif4-Regular.otf")
	if err != nil {
		panic(err)
	}
	f, err := opentype.Parse(b)
	if err != nil {
		panic(err)
	}
	return f
}

func main() {
	f := loadTestFont()
	face := f.NewFace(16)

	fmt.Println("Kern('A','V') =", face.Kern('A', 'V'))
	fmt.Println("Measure(\"AV\") =", face.Measure("AV"))
	fmt.Println("MeasureKerned(\"AV\") =", face.MeasureKerned("AV"))
}
Output:
Kern('A','V') = -2
Measure("AV") = 22
MeasureKerned("AV") = 20

func (*Face) MathConstant added in v0.4.0

func (fc *Face) MathConstant(which MathConstant) int

MathConstant returns the MathConstants value which, pixel-scaled at the face's size — except the three percentage constants, which are pure ratios and returned verbatim. It returns 0 when the font has no MATH constants or which is out of range.

func (*Face) MathKern added in v0.4.0

func (fc *Face) MathKern(gid GlyphIndex, corner MathKernCorner, correctionHeight int) int

MathKern returns the math cut-in kern (in pixels) for glyph gid at the given corner and correction height, the height (in pixels above the baseline) at which the adjacent script attaches. It returns 0 when the glyph has no kern for that corner or the font carries no MATH table.

func (*Face) MathVariants added in v0.4.0

func (fc *Face) MathVariants(gid GlyphIndex, vertical bool) ([]MathVariant, *MathAssembly)

MathVariants returns the size variants and the stretchy-glyph assembly for glyph gid along the requested axis (vertical when vertical is true, otherwise horizontal), with every measurement pixel-scaled at the face's size. Variants are ordered smallest-first as stored in the font; the assembly is nil when the glyph has no assembly recipe. Both results are nil when the glyph has no construction on that axis (or the font carries no MATH table). A math layout engine picks the first variant whose advance meets the target size, or builds the assembly when even the largest variant is too small.

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. When a variation is set and the font carries an MVAR table, the global metric deltas for the ascender ("hasc"), descender ("hdsc") and line gap ("hlgp") are folded in.

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) SetStemDarkening added in v0.3.7

func (fc *Face) SetStemDarkening(on bool)

SetStemDarkening enables or disables stem darkening (weight compensation) for CFF/CFF2 glyphs. When both hinting and darkening are on, every grid-fitted stem is embiggened by a small ppem-dependent amount (strongest at small sizes, fading to none at larger sizes) so stems keep enough contrast on the anti-aliased rasteriser at small text sizes, as FreeType's CFF engine does by default. It has no effect while hinting is off or on glyf (TrueType) outlines. Darkening is off by default, so the default hinted output is byte-identical to the undarkened result. 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.

Example

ExampleFace_Shape runs the font's own GSUB/GPOS tables through Shape and ShapePositioned to turn a rune run into glyph indices and pen-relative positions, without any external shaping engine.

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

// loadTestFont reads the CFF/OpenType font bundled under testdata for the
// examples below (Adobe Source Serif 4, SIL Open Font License; see
// testdata/OFL.txt).
func loadTestFont() *opentype.Font {
	b, err := os.ReadFile("testdata/SourceSerif4-Regular.otf")
	if err != nil {
		panic(err)
	}
	f, err := opentype.Parse(b)
	if err != nil {
		panic(err)
	}
	return f
}

func main() {
	f := loadTestFont()
	face := f.NewFace(16)

	glyphs := face.Shape("Hi")
	fmt.Println("glyphs =", glyphs)

	for _, g := range face.ShapePositioned("Hi") {
		fmt.Printf("glyph=%d dx=%d dy=%d adv=%d\n", g.Glyph, g.XOffset, g.YOffset, g.XAdvance)
	}
}
Output:
glyphs = [9 36]
glyph=9 dx=0 dy=0 adv=13
glyph=36 dx=0 dy=0 adv=5

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) TopAccentAttachment added in v0.4.0

func (fc *Face) TopAccentAttachment(gid GlyphIndex) (int, bool)

TopAccentAttachment returns the x position (in pixels from the glyph origin) at which a top accent should be centred over glyph gid. ok is false when the glyph has no entry, in which case a layout engine centres on the glyph's advance instead.

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) VerticalAdvanceIndex added in v0.5.0

func (fc *Face) VerticalAdvanceIndex(gid GlyphIndex) int

VerticalAdvanceIndex returns the vertical advance (advance height) of glyph gid in whole pixels at the face's size, honouring the face's current variation (the VVAR delta is folded in). When the font has no vmtx table the advance is one em. It is the by-glyph-index counterpart of Face.VerticalAdvance.

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).

Example

ExampleParse parses a font blob and reports its glyph count.

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

// loadTestFont reads the CFF/OpenType font bundled under testdata for the
// examples below (Adobe Source Serif 4, SIL Open Font License; see
// testdata/OFL.txt).
func loadTestFont() *opentype.Font {
	b, err := os.ReadFile("testdata/SourceSerif4-Regular.otf")
	if err != nil {
		panic(err)
	}
	f, err := opentype.Parse(b)
	if err != nil {
		panic(err)
	}
	return f
}

func main() {
	f := loadTestFont()
	fmt.Println(f.NumGlyphs())
}
Output:
1464

func (*Font) Ascent added in v0.5.0

func (f *Font) Ascent() int

Ascent returns the typographic ascender in font units (hhea.ascender): the height above the baseline. It is the same value Face.Metrics scales to pixels.

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) CapHeight added in v0.5.0

func (f *Font) CapHeight() int

CapHeight returns the cap height in font units (OS/2.sCapHeight). It is zero when the font has no OS/2 table or an OS/2 version below 2, which do not carry it; a PDF consumer that needs a non-zero value substitutes the ascender.

func (*Font) Descent added in v0.5.0

func (f *Font) Descent() int

Descent returns the typographic descender in font units (hhea.descender). It is conventionally negative (below the baseline), as stored in hhea.

func (*Font) Flags added in v0.5.0

func (f *Font) Flags() int

Flags returns the PDF FontDescriptor /Flags value for the font: a bit set combining FixedPitch, Serif, Symbolic-or-Nonsymbolic and Italic, derived from the post, OS/2 and head tables. A caller embeds it verbatim as /Flags. Exactly one of the Symbolic and Nonsymbolic bits is always set.

func (*Font) FontBBox added in v0.5.0

func (f *Font) FontBBox() (xMin, yMin, xMax, yMax int)

FontBBox returns the font bounding box in font units (head.xMin/yMin/xMax/yMax): the union of every glyph's bounds. A PDF FontDescriptor's /FontBBox is this box scaled to glyph space.

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) HasMath added in v0.4.0

func (f *Font) HasMath() bool

HasMath reports whether the font carries an OpenType MATH table (the metrics a math-typesetting engine needs). When false, all Face math accessors return their zero result.

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) Instance added in v0.5.0

func (f *Font) Instance(coords map[string]float64) (*Font, error)

Instance bakes the font at the user-space axis coordinates coords into a new, static Font: a font with the requested variation permanently applied and no variation axes. Axes absent from coords take their default. It is the parsed counterpart of InstanceBytes.

It returns an error for a non-variable font, a CFF2 (variable CFF) font, or when coords names an axis the font does not have.

func (*Font) InstanceBytes added in v0.5.0

func (f *Font) InstanceBytes(coords map[string]float64) ([]byte, error)

InstanceBytes bakes the font at coords into the bytes of a new static TrueType sfnt (see Instance for the semantics). The returned font carries instanced glyf outlines and advance widths and no variation tables; Instance parses these bytes.

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) IsExtendedShapeGlyph added in v0.4.0

func (f *Font) IsExtendedShapeGlyph(gid GlyphIndex) bool

IsExtendedShapeGlyph reports whether gid is in the MATH table's extended-shape coverage: a tall glyph (such as a large integral) whose superscript is positioned as if the glyph were a stretched, rather than a fixed-size, shape.

func (*Font) IsFixedPitch added in v0.5.0

func (f *Font) IsFixedPitch() bool

IsFixedPitch reports whether the font is monospaced (post.isFixedPitch).

func (*Font) IsItalic added in v0.5.0

func (f *Font) IsItalic() bool

IsItalic reports whether the font is italic or oblique, from any of the three places a font may record it: a non-zero post italic angle, the head macStyle italic bit, or the OS/2 fsSelection italic bit.

func (*Font) IsSerif added in v0.5.0

func (f *Font) IsSerif() bool

IsSerif reports whether the font is a serif design, read from the high byte of OS/2.sFamilyClass (the IBM font-class id): classes 1-7 are serif families, class 8 is sans-serif, and the rest (script, symbolic, ...) are neither. It is false when the font has no OS/2 table.

func (*Font) ItalicAngle added in v0.5.0

func (f *Font) ItalicAngle() float64

ItalicAngle returns the italic (slant) angle in counter-clockwise degrees from vertical (post.italicAngle): zero for an upright font, negative for the usual forward slant. It is zero when the font has no post table.

func (*Font) LineGap added in v0.5.0

func (f *Font) LineGap() int

LineGap returns the typographic line gap in font units (hhea.lineGap): the recommended extra spacing between lines beyond ascent minus descent.

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.

Example

ExampleFont_NewFace builds a sized Face and measures a string, mirroring the narrow slice of golang.org/x/image/font/opentype this package replaces.

package main

import (
	"fmt"
	"os"

	"github.com/go-opentype/opentype"
)

// loadTestFont reads the CFF/OpenType font bundled under testdata for the
// examples below (Adobe Source Serif 4, SIL Open Font License; see
// testdata/OFL.txt).
func loadTestFont() *opentype.Font {
	b, err := os.ReadFile("testdata/SourceSerif4-Regular.otf")
	if err != nil {
		panic(err)
	}
	f, err := opentype.Parse(b)
	if err != nil {
		panic(err)
	}
	return f
}

func main() {
	f := loadTestFont()
	face := f.NewFace(16) // 16px per em

	m := face.Metrics()
	fmt.Println("ascent", m.Ascent, "descent", m.Descent, "height", m.Height)
	fmt.Println("Measure(Hello) =", face.Measure("Hello"))
	fmt.Println("Advance('H') =", face.Advance('H'))
}
Output:
ascent 17 descent 5 height 22
Measure(Hello) = 40
Advance('H') = 13

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).

func (*Font) StemV added in v0.5.0

func (f *Font) StemV() int

StemV returns an estimate, in font units, of the dominant vertical stem width a PDF FontDescriptor requires as /StemV. OpenType fonts do not store StemV, so it is approximated from the OS/2 weight class with the linear relation 50 + (weightClass-100)*11/100 (about 83 at the normal weight 400, ~116 at bold 700), clamped to a floor of 50. A font without an OS/2 table is treated as the normal weight. Callers that can do better should read WeightClass directly.

func (*Font) SubsetCFF added in v0.5.0

func (f *Font) SubsetCFF(gids []GlyphIndex) ([]byte, error)

SubsetCFF builds a subset 'CFF ' table (the raw font program, as a PDF /FontFile3 CIDFontType0C stream embeds it) that keeps the charstrings of the glyphs in gids plus glyph 0, replacing every other glyph's charstring with an empty one. Glyph numbering is preserved, so the font's charset, encoding, Private DICT and subroutines remain valid and a PDF embeds the result with an Identity CIDToGIDMap (no glyph-id remap is needed, unlike SubsetTrueType).

It returns an error for a font with no CFF table (a TrueType font — use SubsetTrueType), for a CFF2 (variable) font, for a CID-keyed CFF (one with a ROS, FDArray or FDSelect operator), or when a requested glyph id is out of range. See the file-level comment for the retained-subroutine and endchar-seac scope notes.

func (*Font) SubsetTrueType added in v0.5.0

func (f *Font) SubsetTrueType(gids []GlyphIndex) (data []byte, oldToNew map[GlyphIndex]GlyphIndex, err error)

SubsetTrueType builds a minimal, valid TrueType sfnt carrying only the glyphs in gids plus glyph 0 and every component a composite among them references. The kept glyphs are renumbered compactly (glyph 0 stays 0); the returned oldToNew map gives each retained original glyph id its id in the subset, which a PDF CIDFontType2 embedder turns into a /CIDToGIDMap (CID = original id -> subset id) and a content-stream writer uses to re-address glyphs.

The rebuilt font contains glyf, loca (long form), head, hhea, maxp, hmtx and a minimal cmap (enough to re-parse; a PDF addresses the subset by glyph id, not through the cmap), plus the TrueType instruction tables (cvt/fpgm/prep) when the original font carried them so hinted glyphs still render as designed. Composite component references are rewritten to the new glyph numbering.

It returns an error for a font with no glyf table (a CFF/OpenType font — use SubsetCFF) or when a requested glyph id is out of range.

func (*Font) Table added in v0.5.0

func (f *Font) Table(tag string) (data []byte, ok bool)

Table returns a copy of the raw bytes of the table named by its four-byte tag, and ok reporting whether the font carries that table. The tag must be the exact sfnt spelling including any trailing space (for example "cvt " or "OS/2"). The returned slice is a fresh copy the caller may retain or mutate; it never aliases the font's backing data.

func (*Font) TableTags added in v0.5.0

func (f *Font) TableTags() []string

TableTags returns the four-byte tags of every table in the font, sorted lexicographically. The returned slice is freshly allocated and owned by the caller. Tags retain their sfnt spelling, including any trailing space (for example "cvt " and "OS/2").

func (*Font) UnitsPerEm added in v0.5.0

func (f *Font) UnitsPerEm() int

UnitsPerEm returns the font's design units per em (from head), the scale in which every other font-unit metric this package reports is expressed. It is never zero (Parse rejects a font whose head declares zero).

func (*Font) WeightClass added in v0.5.0

func (f *Font) WeightClass() int

WeightClass returns the OS/2 usWeightClass (100 thin .. 400 normal .. 900 black), or 0 when the font carries no OS/2 table.

func (*Font) WidthClass added in v0.5.0

func (f *Font) WidthClass() int

WidthClass returns the OS/2 usWidthClass (1 ultra-condensed .. 5 normal .. 9 ultra-expanded), or 0 when the font carries no OS/2 table.

func (*Font) XHeight added in v0.5.0

func (f *Font) XHeight() int

XHeight returns the x height in font units (OS/2.sxHeight). It is zero when the font has no OS/2 table or an OS/2 version below 2 (which do not carry it).

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 MathAssembly added in v0.4.0

type MathAssembly struct {
	ItalicsCorrection   int
	MinConnectorOverlap int
	Parts               []MathAssemblyPart
}

MathAssembly is the recipe for building a stretchy glyph out of repeatable parts, with all measurements in pixels at the Face's size. A layout engine stacks the parts (vertically or horizontally), overlapping neighbours by at least MinConnectorOverlap and repeating the Extender parts until the assembly reaches the target size.

type MathAssemblyPart added in v0.4.0

type MathAssemblyPart struct {
	Glyph          GlyphIndex
	StartConnector int
	EndConnector   int
	FullAdvance    int
	Extender       bool
}

MathAssemblyPart is one component of a MathAssembly. StartConnector and EndConnector are the maximum overlap lengths at the part's leading and trailing ends; FullAdvance is the part's full extent along the stretch axis.

type MathConstant added in v0.4.0

type MathConstant int

MathConstant identifies one scalar in the MATH table's MathConstants record. The constants are listed in their on-disk order. Face.MathConstant returns each pixel-scaled, except the three percentage constants (ScriptPercentScaleDown, ScriptScriptPercentScaleDown and RadicalDegreeBottomRaisePercent), which are pure ratios and returned verbatim.

const (
	ScriptPercentScaleDown MathConstant = iota
	ScriptScriptPercentScaleDown
	DelimitedSubFormulaMinHeight
	DisplayOperatorMinHeight
	MathLeading
	AxisHeight
	AccentBaseHeight
	FlattenedAccentBaseHeight
	SubscriptShiftDown
	SubscriptTopMax
	SubscriptBaselineDropMin
	SuperscriptShiftUp
	SuperscriptShiftUpCramped
	SuperscriptBottomMin
	SuperscriptBaselineDropMax
	SubSuperscriptGapMin
	SuperscriptBottomMaxWithSubscript
	SpaceAfterScript
	UpperLimitGapMin
	UpperLimitBaselineRiseMin
	LowerLimitGapMin
	LowerLimitBaselineDropMin
	StackTopShiftUp
	StackTopDisplayStyleShiftUp
	StackBottomShiftDown
	StackBottomDisplayStyleShiftDown
	StackGapMin
	StackDisplayStyleGapMin
	StretchStackTopShiftUp
	StretchStackBottomShiftDown
	StretchStackGapAboveMin
	StretchStackGapBelowMin
	FractionNumeratorShiftUp
	FractionNumeratorDisplayStyleShiftUp
	FractionDenominatorShiftDown
	FractionDenominatorDisplayStyleShiftDown
	FractionNumeratorGapMin
	FractionNumDisplayStyleGapMin
	FractionRuleThickness
	FractionDenominatorGapMin
	FractionDenomDisplayStyleGapMin
	SkewedFractionHorizontalGap
	SkewedFractionVerticalGap
	OverbarVerticalGap
	OverbarRuleThickness
	OverbarExtraAscender
	UnderbarVerticalGap
	UnderbarRuleThickness
	UnderbarExtraDescender
	RadicalVerticalGap
	RadicalDisplayStyleVerticalGap
	RadicalRuleThickness
	RadicalExtraAscender
	RadicalKernBeforeDegree
	RadicalKernAfterDegree
	RadicalDegreeBottomRaisePercent
)

The MathConstants record fields, in table order. mathConstCount is the count sentinel (not a real constant).

type MathKernCorner added in v0.4.0

type MathKernCorner int

MathKernCorner selects one of a glyph's four math-kern corners, used for the staircase cut-in between a base glyph and an adjacent script.

const (
	MathKernTopRight MathKernCorner = iota
	MathKernTopLeft
	MathKernBottomRight
	MathKernBottomLeft
)

The four math-kern corners, in the on-disk order of a MathKernInfoRecord.

type MathVariant added in v0.4.0

type MathVariant struct {
	Glyph   GlyphIndex
	Advance int
}

MathVariant is one size variant of a stretchy glyph: the variant's glyph and its advance (height for a vertical variant, width for a horizontal one) in pixels at the Face's size.

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