Skip to content

Fonts and faces#

A face represents a font file.

func (pw *PDF) LoadFace(filename string, idx int) (*Face, error)

The Face is the following structure:

type Face struct {
    FaceID         int
    HarfbuzzFont   *harfbuzz.Font
    UnitsPerEM     int32
    Cmap           fonts.Cmap
    Filename       string
    PostscriptName string
}

You need to register the glyphs that you put into the PDF file, since the font will be subsetted:

func (face *Face) RegisterChar(codepoint int)

or

func (face *Face) RegisterChars(codepoints []int)

can be used.

Glyphs and code points#

There is no direct mapping between runes and glyph ids (code points) from the font. Take for example the “f​i” ligature. Here the input string consists of two runes, however in most fonts this is a single code point.

To get the code point of a rune, use the Cmap from the face:

fonts.GID, found := face.Cmap.Lookup('r')

where face is a face instance. found is true if there is a mapping from this rune to a glyph id. Similarly you can use

func (face *Face) Codepoint(r rune) int

and

func (face *Face) Codepoints(runes []rune) []int

to get the code points of a rune (list).

Shaping#

In most cases you don't need to lookup the code point for a rune, as the built-in harfbuzz library takes care of the mapping from input strings to code points. This process is called shaping and can be called like this:

text := "Hello, world!"
buf := harfbuzz.NewBuffer()
buf.AddRunes([]rune(text), 0, -1)
buf.GuessSegmentProperties()
buf.Shape(f.Face.HarfbuzzFont, features)

here f is font object and features is a slice []harfbuzz.Feature. After calling buf.Shape(), buf.Info is a slice in this format:

[]harfbuzz.GlyphInfo{
    {Cluster:0, Glyph:0x3f, Mask:0x80000000},
    {Cluster:1, Glyph:0x33, Mask:0x80000000},
    {Cluster:2, Glyph:0x49, Mask:0x80000000},
    {Cluster:3, Glyph:0x49, Mask:0x80000000},
    {Cluster:4, Glyph:0x52, Mask:0x80000000},
    {Cluster:5, Glyph:0x2e, Mask:0x80000000},
    {Cluster:6, Glyph:0x68, Mask:0x80000000},
    {Cluster:7, Glyph:0x73, Mask:0x80000000},
    {Cluster:8, Glyph:0x52, Mask:0x80000001},
    {Cluster:9, Glyph:0x61, Mask:0x80000000},
    {Cluster:10, Glyph:0x49, Mask:0x80000000},
    {Cluster:11, Glyph:0x30, Mask:0x80000000},
    {Cluster:12, Glyph:0x36, Mask:0x80000000},
}

You can use these Glyph entries for construction of the page content stream. To get positioning information, look at buf.Pos:

[]harfbuzz.GlyphPosition{
    {XAdvance:722, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:556, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:222, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:222, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:556, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:278, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:278, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:712, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:556, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:333, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:222, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:556, XOffset:0, YAdvance:0, YOffset:0},
    {XAdvance:278, XOffset:0, YAdvance:0, YOffset:0},
}

This should be enough to position the glyphs in the PDF.

You can see an example at github.com/boxesandglue/boxesandglue-examples/tree/main/baseline/font.