mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
text/shaper: lookup closest font by weight
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
This commit is contained in:
committed by
Elias Naur
parent
23f6dcb868
commit
49a7b2e6f4
+33
-15
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user