font

package module
v0.0.0-...-9f995da Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2026 License: MIT Imports: 28 Imported by: 29

README

Font API reference

Parsers for SFNT-based fonts (TTF, OTF, WOFF, WOFF2, EOT) that can extract glyph paths, advancement and kerning data, Unicode mapping and glyph lookups. It also supports merging two fonts as well as subsetting fonts using a list of glyphs. It can write out TTF/OTF as well as WOFF2 fonts. The WOFF and WOFF2 converters have been testing using the validation tests from the W3C (https://github.com/w3c/woff/tree/master/woff1/tests and https://github.com/w3c/woff2-tests).

fontcmd: font toolkit that can select a subset of glyphs from a font, merge fonts, or show font information and display glyphs in the command line or as a raster image.

Usage

Parse font
import "github.com/tdewolff/font"

// Can be any of TTF, OTF, TTC, WOFF, WOFF2, or EOT
fontBytes, err := os.ReadFile("DejaVuSerif.otf")
if err != nil {
    panic(err)
}

sfnt, err := font.ParseSFNT(fontBytes, 0) // 0 is the first index for TTC fonts
if err != nil {
    panic(err)
}

// font information
sfnt.NumGlyphs() uint16
sfnt.UnitsPerEm() uint16
sfnt.VerticalMetrics() (uint16, uint16, uint16)

// glyph mappings
sfnt.GlyphIndex(r rune) uint16
sfnt.GlyphToUnicode(glyphID uint16) []rune
sfnt.GlyphName(glyphID uint16) string
sfnt.FindGlyphName(name string) uint16

// glyph shapes
sfnt.GlyphPath(p Pather, glyphID, ppem uint16, x, y, scale float64, hinting Hinting) error
sfnt.GlyphBounds(glyphID uint16) (int16, int16, int16, int16)
sfnt.GlyphAdvance(glyphID uint16) uint16
sfnt.GlyphVerticalAdvance(glyphID uint16) uint16
sfnt.Kerning(left, right uint16) int16

// editting
sfnt.SetGlyphNames(names []string) error
sfnt.Merge(sfnt *SFNT, options MergeOptions) error
sfnt.Subset(glyphIDs []uint16, options SubsetOptions) (*SFNT, error)
sfnt.Write() []byte
sfnt.WriteWOFF2() ([]byte, error)
Extract glyph shape
import (
    "github.com/tdewolff/canvas"
    "github.com/tdewolff/font"
)

const mmPerPt = 25.4 / 72.0

func main () {
    // Can be any of TTF, OTF, TTC, WOFF, WOFF2, or EOT
    fontBytes, err := os.ReadFile("DejaVuSerif.otf")
    if err != nil {
        panic(err)
    }

    sfnt, err := font.ParseSFNT(fontBytes, 0) // 0 is the first index for TTC fonts
    if err != nil {
        panic(err)
    }

    dpmm := 2.0 // pixels per mm, rasterisation resolution
    fontSize := 12.0 // font size in points

    scale := fontSize * mmPerPt / float64(sfnt.UnitsPerEm()) // mm per units-per-em
    ppem := uint16(dpmm * fontSize * mmPerPt + 0.5) // size of em in pixels
    ascender, descender, _ := sfnt.VerticalMetrics()

    var x, y int16
    p := &canvas.Path{}

    indexA := sfnt.GlyphIndex('A') // can be 0 if not present
    indexV := sfnt.GlyphIndex('V') // can be 0 if not present
    if err := sfnt.GlyphPath(p, indexA, ppem, scale*float64(x), scale*float64(y), scale, font.NoHinting); err != nil {
        panic(err)
    }
    x += int16(sfnt.GlyphAdvance(indexA))
    x += sfnt.Kerning(indexA, indexV)

    if err := sfnt.GlyphPath(p, indexV, ppem, scale*float64(x), scale*float64(y), scale, font.NoHinting); err != nil {
        panic(err)
    }
    x += int16(sfnt.GlyphAdvance(indexV))

    width := scale*float64(scale)
    height := scale*float64(ascender+descender)

    c := canvas.New(width, height)
    ctx := canvas.NewContext(c)
    ctx.DrawPath(0.0, 0.0, p)
    if err := renderers.Write("AV.png", c, canvas.Resolution(dpmm)); err != nil {
        panic(err)
    }
}

Output of AV text

Merge fonts
import "github.com/tdewolff/font"

// Load two fonts
fontBytes, err := os.ReadFile("DejaVuSerif.otf")
if err != nil {
    panic(err)
}
font2Bytes, err := os.ReadFile("DejaVuSerif-Extended.otf")
if err != nil {
    panic(err)
}

sfnt, err := font.ParseSFNT(fontBytes, 0)
if err != nil {
    panic(err)
}
sfnt2, err := font.ParseSFNT(font2Bytes, 0)
if err != nil {
    panic(err)
}

// Add all glyphs of the second font to the first
options := font.MergeOptions{
    RearrangeCmap: false, // don't rewrite unicode mapping to sequential order
}
if err := sfnt.Merge(sfnt2, options); err != nil {
    panic(err)
}

if err := os.WriteFile("DejaVuSerif-Merged.otf", sfnt.Write(), 0644); err != nil{
    panic(err)
}
Subset font
import "github.com/tdewolff/font"

fontBytes, err := os.ReadFile("DejaVuSerif.otf")
if err != nil {
    panic(err)
}

sfnt, err := font.ParseSFNT(fontBytes, 0)
if err != nil {
    panic(err)
}

var glyphIDs []uint16
for c := 'A'; c <= 'Z'; c++ {
    if glyphID := sfnt.GlyphIndex(c); glyphID != 0 {
        glyphIDs = append(glyphIDs, glyphID)
    }
}

// Create subset
options := font.SubsetOptions{
    Tables: font.KeepMinTables, // keep only required tables
}
sfntSubset, err := sfnt.Subset(glyphIDs, options)
if err != nil {
    panic(err)
}

if err := os.WriteFile("DejaVuSerif-Subset.otf", sfntSubset.Write(), 0644); err != nil{
    panic(err)
}

380132 bytes => 6700 bytes (1.7%)

Convert to TTF/OTF
import "github.com/tdewolff/font"

// Can be any of TTF, OTF, TTC, WOFF, WOFF2, or EOT
fontBytes, err := os.ReadFile("DejaVuSerif.woff")
if err != nil {
    panic(err)
}

// []byte to []byte
sfnt, err := font.ToSFNT(fontBytes)
if err != nil {
    panic(err)
}

ext := font.Extension(sfnt) // .ttf or .otf
if err := os.WriteFile("DejaVuSerif"+ext, sfnt, 0644); err != nil{
    panic(err)
}

License

Released under the MIT license.

Documentation

Index

Constants

View Source
const (
	PlatformUnicode   = PlatformID(0)
	PlatformMacintosh = PlatformID(1)
	PlatformWindows   = PlatformID(3)
	PlatformCustom    = PlatformID(4)
)

see PlatformID

View Source
const (
	EncodingUnicode2BMP                 = EncodingID(3)
	EncodingUnicode2FullRepertoir       = EncodingID(4)
	EncodingUnicodeVariationSequences   = EncodingID(5)
	EncodingUnicodeFullRepertoire       = EncodingID(6)
	EncodingMacintoshRoman              = EncodingID(0)
	EncodingMacintoshJapanese           = EncodingID(1)
	EncodingMacintoshChineseTraditional = EncodingID(2)
	EncodingMacintoshKorean             = EncodingID(3)
	EncodingMacintoshArabic             = EncodingID(4)
	EncodingMacintoshHebrew             = EncodingID(5)
	EncodingMacintoshGreek              = EncodingID(6)
	EncodingMacintoshRussian            = EncodingID(7)
	EncodingMacintoshRSymbol            = EncodingID(8)
	EncodingMacintoshDevanagari         = EncodingID(9)
	EncodingMacintoshGurmukhi           = EncodingID(10)
	EncodingMacintoshGujarati           = EncodingID(11)
	EncodingMacintoshOriya              = EncodingID(12)
	EncodingMacintoshBengali            = EncodingID(13)
	EncodingMacintoshTamil              = EncodingID(14)
	EncodingMacintoshTelugu             = EncodingID(15)
	EncodingMacintoshKannada            = EncodingID(16)
	EncodingMacintoshMalayalam          = EncodingID(17)
	EncodingMacintoshSinhalese          = EncodingID(18)
	EncodingMacintoshBurmese            = EncodingID(19)
	EncodingMacintoshKhmer              = EncodingID(20)
	EncodingMacintoshThai               = EncodingID(21)
	EncodingMacintoshLaotian            = EncodingID(22)
	EncodingMacintoshGeorgian           = EncodingID(23)
	EncodingMacintoshArmenian           = EncodingID(24)
	EncodingMacintoshChineseSimplified  = EncodingID(25)
	EncodingMacintoshTibetan            = EncodingID(26)
	EncodingMacintoshMongolian          = EncodingID(27)
	EncodingMacintoshGeez               = EncodingID(28)
	EncodingMacintoshSlavic             = EncodingID(29)
	EncodingMacintoshVietnamese         = EncodingID(30)
	EncodingMacintoshSindhi             = EncodingID(31)
	EncodingMacintoshUninterpreted      = EncodingID(32)
	EncodingWindowsSymbol               = EncodingID(0)
	EncodingWindowsUnicodeBMP           = EncodingID(1)
	EncodingWindowsShiftJIS             = EncodingID(2)
	EncodingWindowsPRC                  = EncodingID(3)
	EncodingWindowsBig5                 = EncodingID(4)
	EncodingWindowsWansung              = EncodingID(5)
	EncodingWindowsJohab                = EncodingID(6)
	EncodingWindowsUnicodeFullRepertoir = EncodingID(10)
)

see EncodingID

View Source
const (
	NameCopyrightNotice            = NameID(0)
	NameFontFamily                 = NameID(1)
	NameFontSubfamily              = NameID(2)
	NameUniqueIdentifier           = NameID(3)
	NameFull                       = NameID(4)
	NameVersion                    = NameID(5)
	NamePostScript                 = NameID(6)
	NameTrademark                  = NameID(7)
	NameManufacturer               = NameID(8)
	NameDesigner                   = NameID(9)
	NameDescription                = NameID(10)
	NameVendorURL                  = NameID(11)
	NameDesignerURL                = NameID(12)
	NameLicense                    = NameID(13)
	NameLicenseURL                 = NameID(14)
	NamePreferredFamily            = NameID(16)
	NamePreferredSubfamily         = NameID(17)
	NameCompatibleFull             = NameID(18)
	NameSampleText                 = NameID(19)
	NamePostScriptCID              = NameID(20)
	NameWWSFamily                  = NameID(21)
	NameWWSSubfamily               = NameID(22)
	NameLightBackgroundPalette     = NameID(23)
	NameDarkBackgroundPalette      = NameID(24)
	NameVariationsPostScriptPrefix = NameID(25)
)

see NameID

View Source
const (
	UnknownScript = ScriptTag("")
	DefaultScript = ScriptTag("DFLT")
)
View Source
const (
	UnknownLanguage = LanguageTag("")
	DefaultLanguage = LanguageTag("DFLT") // permanently reserved and not used in font

)
View Source
const MaxCmapSegments = 20000

MaxCmapSegments is the maximum number of cmap segments that will be accepted.

View Source
const (
	UnknownFeature = FeatureTag("")
)

Variables

View Source
var (
	KeepAllTables = []string{"all"}
	KeepMinTables = []string{"min"}
	KeepPDFTables = []string{"pdf"}
)
View Source
var ErrBadNumOperands = fmt.Errorf("bad number of operands for operator")
View Source
var ErrExceedsMemory = fmt.Errorf("memory limit exceded")

ErrExceedsMemory is returned if the font is malformed.

View Source
var ErrInvalidFontData = fmt.Errorf("invalid font data")

ErrInvalidFontData is returned if the font is malformed.

View Source
var MaxMemory uint32 = 30 * 1024 * 1024

MaxMemory is the maximum memory that can be allocated by a font.

Functions

func DefaultFontDirs

func DefaultFontDirs() []string

func DefaultGenericFonts

func DefaultGenericFonts() map[string][]string

func Extension

func Extension(b []byte) string

Extension returns the file extension for a given font. An empty string is returned when the font is not recognized.

func FromGoFreetype

func FromGoFreetype(font *truetype.Font) []byte

FromGoFreetype parses a structure from truetype.Font to a valid SFNT byte slice.

func FromGoSFNT

func FromGoSFNT(font *sfnt.Font) []byte

FromGoSFNT parses a structure from sfnt.Font to a valid SFNT byte slice.

func MediaType

func MediaType(b []byte) (string, error)

MediaType returns the media type (MIME) for a given font.

func NewSFNTReader

func NewSFNTReader(r io.Reader) (*bytes.Reader, error)

NewSFNTReader takes an io.Reader and transforms it into an SFNT reader. That is, given TTF/OTF/WOFF/WOFF2/EOT input, it will return TTF/OTF output.

func ParseEOT

func ParseEOT(b []byte) ([]byte, error)

ParseEOT parses the EOT font format and returns its contained SFNT font format (TTF or OTF). See https://www.w3.org/Submission/EOT/

func ParseWOFF

func ParseWOFF(b []byte) ([]byte, error)

ParseWOFF parses the WOFF font format and returns its contained SFNT font format (TTF or OTF). See https://www.w3.org/TR/WOFF/

func ParseWOFF2

func ParseWOFF2(b []byte) ([]byte, error)

ParseWOFF2 parses the WOFF2 font format and returns its contained SFNT font format (TTF or OTF). See https://www.w3.org/TR/WOFF2/

func ToSFNT

func ToSFNT(b []byte) ([]byte, error)

ToSFNT takes a byte slice and transforms it into an SFNT byte slice. That is, given TTF/OTF/WOFF/WOFF2/EOT input, it will return TTF/OTF output.

func Uint8ToFlags

func Uint8ToFlags(v uint8) (flags [8]bool)

Uint8ToFlags converts a uint8 in 8 booleans from least to most significant.

func Uint16ToFlags

func Uint16ToFlags(v uint16) (flags [16]bool)

Uint16ToFlags converts a uint16 in 16 booleans from least to most significant.

Types

type AFM

type AFM struct {
	FontName   string
	FullName   string
	FamilyName string
	Weight     string
	FontBBox   [4]int16

	CapHeight          uint16
	XHeight            uint16
	Ascender           uint16
	Descender          uint16
	UnderlinePosition  uint16
	UnderlineThickness uint16
	ItalicAngle        float64
	CharWidth          [2]uint16
	IsFixedPitch       bool

	CharMetrics []AFMCharMetrics
	Ligatures   map[[2]uint16]uint16
	KernPairs   map[[2]uint16]int16
	// contains filtered or unexported fields
}

func ParseAFM

func ParseAFM(b []byte) (*AFM, error)

func (*AFM) FindGlyphName

func (afm *AFM) FindGlyphName(name string) uint16

FindGlyphName returns the glyphID for a given glyph name. When the name is not defined it returns 0.

func (*AFM) GlyphAdvance

func (afm *AFM) GlyphAdvance(glyphID uint16) uint16

GlyphAdvance returns the (horizontal) advance width of the glyph.

func (*AFM) GlyphBounds

func (afm *AFM) GlyphBounds(glyphID uint16) (int16, int16, int16, int16)

GlyphBounds returns the bounding rectangle (xmin,ymin,xmax,ymax) of the glyph.

func (*AFM) GlyphIndex

func (afm *AFM) GlyphIndex(r rune) uint16

GlyphIndex returns the glyphID for a given rune. When the rune is not defined it returns 0.

func (*AFM) GlyphName

func (afm *AFM) GlyphName(glyphID uint16) string

GlyphName returns the name of the glyph. It returns an empty string when no name exists.

func (*AFM) Kerning

func (afm *AFM) Kerning(left, right uint16) int16

Kerning returns the kerning between two glyphs, i.e. the advance correction for glyph pairs.

func (*AFM) NumGlyphs

func (afm *AFM) NumGlyphs() uint16

NumGlyphs returns the number of glyphs the font contains.

type AFMCharMetrics

type AFMCharMetrics struct {
	CharacterCode int
	Width         uint16
	BBox          [4]int16
	Name          string
}

type EncodingID

type EncodingID uint16

EncodingID is the encoding identifier for the name table

type FeatureTag

type FeatureTag string

type FontMetadata

type FontMetadata struct {
	Filename string
	Family   string
	Style
}

func (FontMetadata) String

func (metadata FontMetadata) String() string

type Hinting

type Hinting int

Hinting specifies the type of hinting to use (none supported yes).

const (
	NoHinting Hinting = iota
	VerticalHinting
)

see Hinting

type LanguageTag

type LanguageTag string

type MergeOptions

type MergeOptions struct {
	RearrangeCmap bool // Rearrange glyph unicode mapping, assigning a sequential codepoint for each glyph in order starting at 33 (exclamation). May be useful when embedding into a PDF with a custom unicode mapping.
}

type NameID

type NameID uint16

NameID is the name identifier for the name table

type Pather

type Pather interface {
	MoveTo(float64, float64)
	LineTo(float64, float64)
	QuadTo(float64, float64, float64, float64)
	CubeTo(float64, float64, float64, float64, float64, float64)
	Close()
}

Pather is an interface to append a glyph's path to canvas.Path.

type PlatformID

type PlatformID uint16

PlatformID is the platform identifier for the name table

type SFNT

type SFNT struct {
	Length            uint32
	Version           string
	IsCFF, IsTrueType bool // only one can be true
	Tables            map[string][]byte

	// required
	Cmap *cmapTable
	Head *headTable
	Hhea *hheaTable
	Hmtx *hmtxTable
	Maxp *maxpTable
	Name *nameTable
	OS2  *os2Table
	Post *postTable

	// TrueType
	Glyf *glyfTable
	Loca *locaTable

	// CFF
	CFF *cffTable

	// optional
	Kern *kernTable
	Vhea *vheaTable
	Vmtx *vmtxTable

	// TODO: SFNT tables
	//Hdmx *hdmxTable
	Gpos *gposgsubTable
	Gsub *gposgsubTable
	Jsft *jsftTable
}

SFNT is a parsed OpenType font.

func ParseEmbeddedSFNT

func ParseEmbeddedSFNT(b []byte, index int) (*SFNT, error)

ParseEmbeddedSFNT is like ParseSFNT but for embedded font files in PDFs. It allows font files with fewer required tables.

func ParseFont

func ParseFont(b []byte, index int) (*SFNT, error)

ParseFont parses a byte slice and of a TTF, OTF, WOFF, WOFF2, or EOT font format. It will return the parsed font and its mimetype. DEPRECATED: use ParseSFNT

func ParseSFNT

func ParseSFNT(b []byte, index int) (*SFNT, error)

ParseSFNT parses an OpenType file format (TTF, OTF, TTC). The index is used for font collections to select a single font.

func (*SFNT) FindGlyphName

func (sfnt *SFNT) FindGlyphName(name string) uint16

FindGlyphName returns the glyphID for a given glyph name. When the name is not defined it returns 0.

func (*SFNT) GlyphAdvance

func (sfnt *SFNT) GlyphAdvance(glyphID uint16) uint16

GlyphAdvance returns the (horizontal) advance width of the glyph.

func (*SFNT) GlyphBounds

func (sfnt *SFNT) GlyphBounds(glyphID uint16) (int16, int16, int16, int16)

GlyphBounds returns the bounding rectangle (xmin,ymin,xmax,ymax) of the glyph.

func (*SFNT) GlyphIndex

func (sfnt *SFNT) GlyphIndex(r rune) uint16

GlyphIndex returns the glyph ID for the corresponding rune. It looks for each character map subtable in the order in which they appear and returns the first match, or 0 when no match is found.

func (*SFNT) GlyphName

func (sfnt *SFNT) GlyphName(glyphID uint16) string

GlyphName returns the name of the glyph. It returns an empty string when no name exists.

func (*SFNT) GlyphPath

func (sfnt *SFNT) GlyphPath(p Pather, glyphID, ppem uint16, x, y, scale float64, hinting Hinting) error

GlyphPath draws the glyph's contour as a path to the pather interface. It will use the specified ppem (pixels-per-EM) for hinting purposes. The path is draws to the (x,y) coordinate and scaled using the given scale factor.

func (*SFNT) GlyphToUnicode

func (sfnt *SFNT) GlyphToUnicode(glyphID uint16) []rune

GlyphToUnicode returns the runes for the corresponding glyph ID. It looks for each character map subtable in the order in which they appear and returns the runes which reference the glyph ID, or nil when no match is found. This is the inverse mapping of the character map table and is cached after the first call. Each glyph may be mapped from multiple runes.

func (*SFNT) GlyphVerticalAdvance

func (sfnt *SFNT) GlyphVerticalAdvance(glyphID uint16) uint16

GlyphVerticalAdvance returns the vertical advance width of the glyph.

func (*SFNT) Kerning

func (sfnt *SFNT) Kerning(left, right uint16) int16

Kerning returns the kerning between two glyphs, i.e. the advance correction for glyph pairs.

func (*SFNT) Merge

func (sfnt *SFNT) Merge(sfnt2 *SFNT, options MergeOptions) error

Merge merges the glyphs of another font into the current one in-place by merging the glyf, loca, kern tables (or CFF table for CFF fonts), as well as the hmtx, cmap, and post tables. Also updates the maxp, head, and OS/2 tables. The other font remains untouched.

func (*SFNT) NumGlyphs

func (sfnt *SFNT) NumGlyphs() uint16

NumGlyphs returns the number of glyphs the font contains.

func (*SFNT) SetGlyphNames

func (sfnt *SFNT) SetGlyphNames(names []string) error

func (*SFNT) Subset

func (sfnt *SFNT) Subset(glyphIDs []uint16, options SubsetOptions) (*SFNT, error)

Subset trims an SFNT font to contain only the passed glyphIDs, thereby resulting in a significant size reduction. The glyphIDs will apear in the specified order in the file and their dependencies are added to the end.

func (*SFNT) UnitsPerEm

func (sfnt *SFNT) UnitsPerEm() uint16

UnitsPerEm returns the number of units per em

func (*SFNT) VerticalMetrics

func (sfnt *SFNT) VerticalMetrics() (uint16, uint16, uint16)

VerticalMetrics returns the ascender, descender, and line gap values. It returns the "win" values, or the "typo" values if OS/2.FsSelection.USE_TYPO_METRICS is set. If those are zero or not set, default to the "hhea" values.

func (*SFNT) Write

func (sfnt *SFNT) Write() []byte

Write writes out the SFNT file.

func (*SFNT) WriteWOFF2

func (sfnt *SFNT) WriteWOFF2() ([]byte, error)

type ScriptTag

type ScriptTag string

type Style

type Style int

Style defines the font style to be used for the font. It specifies a boldness with optionally italic, e.g. Black | Italic will specify a black boldness (a font-weight of 800 in CSS) and italic.

const (
	UnknownStyle Style = -1
	Thin         Style = iota
	ExtraLight
	Light
	Regular
	Medium
	SemiBold
	Bold
	ExtraBold
	Black
	Italic Style = 1 << 8
)

see Style

func ParseStyle

func ParseStyle(s string) Style

func ParseStyleCSS

func ParseStyleCSS(weight int, italic bool) Style

func (Style) Italic

func (style Style) Italic() bool

Italic returns true if italic.

func (Style) String

func (style Style) String() string

func (Style) Weight

func (style Style) Weight() Style

Weight returns the font weight (Regular, Bold, ...)

type SubsetOptions

type SubsetOptions struct {
	Tables []string // names of tables to include, special values are KeepAllTables, KeepMinTables, or KeepPDFTables
}

type SystemFonts

type SystemFonts struct {
	Generics map[string][]string
	Fonts    map[string]map[Style]FontMetadata
}

func FindSystemFonts

func FindSystemFonts(dirs []string) (*SystemFonts, error)

func LoadSystemFonts

func LoadSystemFonts(filename string) (*SystemFonts, error)

func (*SystemFonts) Add

func (s *SystemFonts) Add(metadata FontMetadata)

func (*SystemFonts) Match

func (s *SystemFonts) Match(name string, style Style) (FontMetadata, bool)

func (*SystemFonts) Save

func (s *SystemFonts) Save(filename string) error

func (*SystemFonts) String

func (s *SystemFonts) String() string

type ValueRecord

type ValueRecord struct {
	XPlacement       int16
	YPlacement       int16
	XAdvance         int16
	YAdvance         int16
	XPlaDeviceOffset uint16
	YPlaDeviceOffset uint16
	XAdvDeviceOffset uint16
	YAdvDeviceOffset uint16
}

Directories

Path Synopsis
cmd
fontcmd module
examples
glyphs command
merge command
subset command

Jump to

Keyboard shortcuts

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