Skip to content

Fonts and faces#

A face represents a font file and a font is an instance of a face. The font itself is not part of the pdf backend.

func LoadFace(pw *PDF, 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
}

which can then be used to instantiate a font (see the fonts chapter in the backend). The font itself is not relevant in the (low level) PDF file, it is only referenced by the font size and the internal name of the face.

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 “fi” ligature. Here the input string consists of two runes, but map in most fonts to a single code point.

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

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

where f is a font object. found is true if there is a mapping from this rune to a glyph id.

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.