diff --git a/font/font.go b/font/font.go index 9d00f35b..17628dfc 100644 --- a/font/font.go +++ b/font/font.go @@ -20,10 +20,11 @@ type Weight int // Font specify a particular typeface variant, style and weight. type Font struct { + // Typeface specifies the name of the family of faces. Typeface Typeface - Variant Variant - Style Style - // Weight is the text weight. If zero, Normal is used instead. + // Style specifies the kind of text style. + Style Style + // Weight is the text weight. Weight Weight } @@ -37,9 +38,6 @@ type Face interface { // string denotes the default typeface. type Typeface string -// Variant denotes a typeface variant such as "Mono" or "Smallcaps". -type Variant string - const ( Regular Style = iota Italic diff --git a/font/gofont/gofont.go b/font/gofont/gofont.go index 1e2818ad..835c0265 100644 --- a/font/gofont/gofont.go +++ b/font/gofont/gofont.go @@ -37,11 +37,11 @@ var ( func loadRegular() { regOnce.Do(func() { - face, err := opentype.Parse(goregular.TTF) + faces, err := opentype.ParseCollection(goregular.TTF) if err != nil { panic(fmt.Errorf("failed to parse font: %v", err)) } - reg = []font.FontFace{{Font: font.Font{Typeface: "Go"}, Face: face}} + reg = faces collection = append(collection, reg[0]) }) } @@ -56,17 +56,17 @@ func Regular() []font.FontFace { func Collection() []font.FontFace { loadRegular() once.Do(func() { - register(font.Font{Style: font.Italic}, goitalic.TTF) - register(font.Font{Weight: font.Bold}, gobold.TTF) - register(font.Font{Style: font.Italic, Weight: font.Bold}, gobolditalic.TTF) - register(font.Font{Weight: font.Medium}, gomedium.TTF) - register(font.Font{Weight: font.Medium, Style: font.Italic}, gomediumitalic.TTF) - register(font.Font{Variant: "Mono"}, gomono.TTF) - register(font.Font{Variant: "Mono", Weight: font.Bold}, gomonobold.TTF) - register(font.Font{Variant: "Mono", Weight: font.Bold, Style: font.Italic}, gomonobolditalic.TTF) - register(font.Font{Variant: "Mono", Style: font.Italic}, gomonoitalic.TTF) - register(font.Font{Variant: "Smallcaps"}, gosmallcaps.TTF) - register(font.Font{Variant: "Smallcaps", Style: font.Italic}, gosmallcapsitalic.TTF) + register(goitalic.TTF) + register(gobold.TTF) + register(gobolditalic.TTF) + register(gomedium.TTF) + register(gomediumitalic.TTF) + register(gomono.TTF) + register(gomonobold.TTF) + register(gomonobolditalic.TTF) + register(gomonoitalic.TTF) + register(gosmallcaps.TTF) + register(gosmallcapsitalic.TTF) // Ensure that any outside appends will not reuse the backing store. n := len(collection) collection = collection[:n:n] @@ -74,11 +74,10 @@ func Collection() []font.FontFace { return collection } -func register(fnt font.Font, ttf []byte) { - face, err := opentype.Parse(ttf) +func register(ttf []byte) { + faces, err := opentype.ParseCollection(ttf) if err != nil { panic(fmt.Errorf("failed to parse font: %v", err)) } - fnt.Typeface = "Go" - collection = append(collection, font.FontFace{Font: fnt, Face: face}) + collection = append(collection, faces[0]) } diff --git a/font/opentype/opentype.go b/font/opentype/opentype.go index e5404c7f..0e51ee3d 100644 --- a/font/opentype/opentype.go +++ b/font/opentype/opentype.go @@ -26,10 +26,8 @@ import ( // should construct a face for any given font file once, reusing it across different // text shapers. type Face struct { - face font.Font - aspect metadata.Aspect - family string - variant string + face font.Font + font giofont.Font } // Parse constructs a Face from source bytes. @@ -38,15 +36,13 @@ func Parse(src []byte) (Face, error) { if err != nil { return Face{}, err } - font, aspect, family, variant, err := parseLoader(ld) + font, md, err := parseLoader(ld) if err != nil { return Face{}, fmt.Errorf("failed parsing truetype font: %w", err) } return Face{ - face: font, - aspect: aspect, - family: family, - variant: variant, + face: font, + font: md, }, nil } @@ -63,15 +59,13 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) { } out := make([]giofont.FontFace, len(lds)) for i, ld := range lds { - face, aspect, family, variant, err := parseLoader(ld) + face, md, err := parseLoader(ld) if err != nil { return nil, fmt.Errorf("reading font %d of collection: %s", i, err) } ff := Face{ - face: face, - aspect: aspect, - family: family, - variant: variant, + face: face, + font: md, } out[i] = giofont.FontFace{ Face: ff, @@ -82,17 +76,32 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) { return out, nil } +func DescriptionToFont(md metadata.Description) giofont.Font { + return giofont.Font{ + Typeface: giofont.Typeface(md.Family), + Style: gioStyle(md.Aspect.Style), + Weight: gioWeight(md.Aspect.Weight), + } +} + +func FontToDescription(font giofont.Font) metadata.Description { + return metadata.Description{ + Family: string(font.Typeface), + Aspect: metadata.Aspect{ + Style: mdStyle(font.Style), + Weight: mdWeight(font.Weight), + }, + } +} + // parseLoader parses the contents of the loader into a face and its metadata. -func parseLoader(ld *loader.Loader) (_ font.Font, _ metadata.Aspect, family, variant string, _ error) { +func parseLoader(ld *loader.Loader) (font.Font, giofont.Font, error) { ft, err := fontapi.NewFont(ld) if err != nil { - return nil, metadata.Aspect{}, "", "", err + return nil, giofont.Font{}, err } - data := metadata.Metadata(ld) - if data.IsMonospace { - variant = "Mono" - } - return ft, data.Aspect, data.Family, variant, nil + data := DescriptionToFont(metadata.Metadata(ld)) + return ft, data, nil } // Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper. @@ -107,16 +116,11 @@ func (f Face) Face() font.Face { // BUG(whereswaldon): the only Variant that can be detected automatically is // "Mono". func (f Face) Font() giofont.Font { - return giofont.Font{ - Typeface: giofont.Typeface(f.family), - Style: f.style(), - Weight: f.weight(), - Variant: giofont.Variant(f.variant), - } + return f.font } -func (f Face) style() giofont.Style { - switch f.aspect.Style { +func gioStyle(s metadata.Style) giofont.Style { + switch s { case metadata.StyleItalic: return giofont.Italic case metadata.StyleNormal: @@ -126,8 +130,19 @@ func (f Face) style() giofont.Style { } } -func (f Face) weight() giofont.Weight { - switch f.aspect.Weight { +func mdStyle(g giofont.Style) metadata.Style { + switch g { + case giofont.Italic: + return metadata.StyleItalic + case giofont.Regular: + fallthrough + default: + return metadata.StyleNormal + } +} + +func gioWeight(w metadata.Weight) giofont.Weight { + switch w { case metadata.WeightThin: return giofont.Thin case metadata.WeightExtraLight: @@ -150,3 +165,28 @@ func (f Face) weight() giofont.Weight { return giofont.Normal } } + +func mdWeight(g giofont.Weight) metadata.Weight { + switch g { + case giofont.Thin: + return metadata.WeightThin + case giofont.ExtraLight: + return metadata.WeightExtraLight + case giofont.Light: + return metadata.WeightLight + case giofont.Normal: + return metadata.WeightNormal + case giofont.Medium: + return metadata.WeightMedium + case giofont.SemiBold: + return metadata.WeightSemibold + case giofont.Bold: + return metadata.WeightBold + case giofont.ExtraBold: + return metadata.WeightExtraBold + case giofont.Black: + return metadata.WeightBlack + default: + return metadata.WeightNormal + } +} diff --git a/text/gotext.go b/text/gotext.go index 538a5ec0..a358ea8d 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -895,7 +895,7 @@ func closestFont(lookup giofont.Font, available []giofont.Font) (giofont.Font, b if cf == lookup { return lookup, true } - if cf.Typeface != lookup.Typeface || cf.Variant != lookup.Variant || cf.Style != lookup.Style { + if cf.Typeface != lookup.Typeface || cf.Style != lookup.Style { continue } if !found {