text/shaper: lookup closest font by weight

Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
This commit is contained in:
Walter Werner SCHNEIDER
2021-09-02 22:20:38 +03:00
committed by Elias Naur
parent 23f6dcb868
commit 49a7b2e6f4
3 changed files with 163 additions and 15 deletions
+33 -15
View File
@@ -56,24 +56,42 @@ func (c *Cache) lookup(font Font) *faceCache {
}
func (c *Cache) faceForStyle(font Font) *faceCache {
tf := c.faces[font]
if tf == nil {
font := font
font.Weight = Normal
tf = c.faces[font]
if closest, ok := c.closestFont(font); ok {
return c.faces[closest]
}
if tf == nil {
font := font
font.Style = Regular
tf = c.faces[font]
font.Style = Regular
if closest, ok := c.closestFont(font); ok {
return c.faces[closest]
}
if tf == nil {
font := font
font.Style = Regular
font.Weight = Normal
tf = c.faces[font]
return nil
}
// closestFont returns the closest Font by weight, in case of equality the
// lighter weight will be returned.
func (c *Cache) closestFont(lookup Font) (Font, bool) {
if c.faces[lookup] != nil {
return lookup, true
}
return tf
found := false
var match Font
for cf := range c.faces {
if cf.Typeface != lookup.Typeface || cf.Variant != lookup.Variant || cf.Style != lookup.Style {
continue
}
if !found {
found = true
match = cf
continue
}
cDist := weightDistance(lookup.Weight, cf.Weight)
mDist := weightDistance(lookup.Weight, match.Weight)
if cDist < mDist {
match = cf
} else if cDist == mDist && cf.Weight < match.Weight {
match = cf
}
}
return match, found
}
func NewCache(collection []FontFace) *Cache {
+118
View File
@@ -0,0 +1,118 @@
package text
import (
"testing"
)
var (
testTF1 Typeface = "MockFace"
testTF2 Typeface = "TestFace"
testTF3 Typeface = "AnotherFace"
)
func TestClosestFontByWeight(t *testing.T) {
c := newTestCache(
Font{Style: Regular, Weight: Normal},
Font{Style: Regular, Weight: Light},
Font{Style: Regular, Weight: Bold},
Font{Style: Italic, Weight: Thin},
)
weightOnlyTests := []struct {
Lookup Weight
Expected Weight
}{
// Test for existing weights.
{Lookup: Normal, Expected: Normal},
{Lookup: Light, Expected: Light},
{Lookup: Bold, Expected: Bold},
// Test for missing weights.
{Lookup: Thin, Expected: Light},
{Lookup: ExtraLight, Expected: Light},
{Lookup: Medium, Expected: Normal},
{Lookup: SemiBold, Expected: Bold},
{Lookup: ExtraBlack, Expected: Bold},
}
for _, test := range weightOnlyTests {
got, ok := c.closestFont(Font{Typeface: testTF1, Weight: test.Lookup})
if !ok {
t.Fatalf("expected closest font for %v to exist", test.Lookup)
}
if got.Weight != test.Expected {
t.Fatalf("got weight %v, expected %v", got.Weight, test.Expected)
}
}
c = newTestCache(
Font{Style: Regular, Weight: Light},
Font{Style: Regular, Weight: Bold},
Font{Style: Italic, Weight: Normal},
Font{Typeface: testTF3, Style: Italic, Weight: Bold},
)
otherTests := []struct {
Lookup Font
Expected Font
ExpectedToFail bool
}{
// Test for existing fonts.
{
Lookup: Font{Typeface: testTF1, Weight: Light},
Expected: Font{Typeface: testTF1, Style: Regular, Weight: Light},
},
{
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
},
// Test for missing fonts.
{
Lookup: Font{Typeface: testTF1, Weight: Normal},
Expected: Font{Typeface: testTF1, Style: Regular, Weight: Light},
},
{
Lookup: Font{Typeface: testTF3, Style: Italic, Weight: Normal},
Expected: Font{Typeface: testTF3, Style: Italic, Weight: Bold},
},
{
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Thin},
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
},
{
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Bold},
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
},
{
Lookup: Font{Typeface: testTF2, Weight: Normal},
ExpectedToFail: true,
},
{
Lookup: Font{Typeface: testTF2, Style: Italic, Weight: Normal},
ExpectedToFail: true,
},
}
for _, test := range otherTests {
got, ok := c.closestFont(test.Lookup)
if test.ExpectedToFail {
if ok {
t.Fatalf("expected closest font for %v to not exist", test.Lookup)
} else {
continue
}
}
if !ok {
t.Fatalf("expected closest font for %v to exist", test.Lookup)
}
if got != test.Expected {
t.Fatalf("got %v, expected %v", got, test.Expected)
}
}
}
func newTestCache(fonts ...Font) *Cache {
c := &Cache{faces: make(map[Font]*faceCache)}
c.def = testTF1
for _, font := range fonts {
if font.Typeface == "" {
font.Typeface = testTF1
}
c.faces[font] = &faceCache{face: nil}
}
return c
}
+12
View File
@@ -114,3 +114,15 @@ func (s Style) String() string {
panic("invalid Style")
}
}
// weightDistance returns the distance value between two font weights.
func weightDistance(wa Weight, wb Weight) int {
// Avoid dealing with negative Weight values.
a := int(wa) + 400
b := int(wb) + 400
diff := a - b
if diff < 0 {
return -diff
}
return diff
}