~eliasnaur/gio

ccf24c0bd2c1e5575f54bd53623be993322ba6a3 — Chris Waldon 1 year, 7 months ago a7c9ca9
font/opentype: make reusing font.Face efficient and safe

This commit updates the internal representation of a font to separate the
threadsafe and non-threadsafe operations in a way that enures font.Faces can
be shared by all text shapers in an application. This should ensure that applications
only need to parse fonts a single time, saving a great deal of memory for
applications that open many windows (which each need a different text shaper).

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
1 files changed, 12 insertions(+), 7 deletions(-)

M font/opentype/opentype.go
M font/opentype/opentype.go => font/opentype/opentype.go +12 -7
@@ 22,9 22,11 @@ import (
	"github.com/go-text/typesetting/opentype/loader"
)

// Face is a shapeable representation of a font.
// Face is a thread-safe representation of a loaded font. For efficiency, applications
// should construct a face for any given font file once, reusing it across different
// text shapers.
type Face struct {
	face    font.Face
	face    font.Font
	aspect  metadata.Aspect
	family  string
	variant string


@@ 36,12 38,12 @@ func Parse(src []byte) (Face, error) {
	if err != nil {
		return Face{}, err
	}
	face, aspect, family, variant, err := parseLoader(ld)
	font, aspect, family, variant, err := parseLoader(ld)
	if err != nil {
		return Face{}, fmt.Errorf("failed parsing truetype font: %w", err)
	}
	return Face{
		face:    face,
		face:    font,
		aspect:  aspect,
		family:  family,
		variant: variant,


@@ 81,7 83,7 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) {
}

// parseLoader parses the contents of the loader into a face and its metadata.
func parseLoader(ld *loader.Loader) (_ font.Face, _ metadata.Aspect, family, variant string, _ error) {
func parseLoader(ld *loader.Loader) (_ font.Font, _ metadata.Aspect, family, variant string, _ error) {
	ft, err := fontapi.NewFont(ld)
	if err != nil {
		return nil, metadata.Aspect{}, "", "", err


@@ 90,11 92,14 @@ func parseLoader(ld *loader.Loader) (_ font.Face, _ metadata.Aspect, family, var
	if data.IsMonospace {
		variant = "Mono"
	}
	return &fontapi.Face{Font: ft}, data.Aspect, data.Family, variant, nil
	return ft, data.Aspect, data.Family, variant, nil
}

// Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper.
// Face many be invoked any number of times and is safe so long as each return value is
// only used by one goroutine.
func (f Face) Face() font.Face {
	return f.face
	return &fontapi.Face{Font: f.face}
}

// FontFace returns a text.Font with populated font metadata for the