Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ttc fonts supported #5210

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions internal/painter/font.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,17 @@ func lookupRuneFont(r rune, family string, aspect font.Aspect) *font.Face {
func lookupFaces(theme, fallback, emoji fyne.Resource, family string, style fyne.TextStyle) (faces *dynamicFontMap) {
f1 := loadMeasureFont(theme)
if theme == fallback {
if f1 == nil {
return nil
}
faces = &dynamicFontMap{family: family, faces: []*font.Face{f1}}
} else {
f2 := loadMeasureFont(fallback)
faces = &dynamicFontMap{family: family, faces: []*font.Face{f1, f2}}
if f1 == nil {
faces = &dynamicFontMap{family: family, faces: []*font.Face{f2}}
} else {
faces = &dynamicFontMap{family: family, faces: []*font.Face{f1, f2}}
}
}

aspect := font.Aspect{Style: font.StyleNormal}
Expand Down Expand Up @@ -152,6 +159,9 @@ func CachedFontFace(style fyne.TextStyle, source fyne.Resource, o fyne.CanvasObj
default:
faces = lookupFaces(font1, theme.DefaultTextFont(), emoji, fontscan.SansSerif, style)
}
if faces == nil {
return nil
}

val = &FontCacheItem{Fonts: faces}
fontCache.Store(cacheID{style: style, scope: scope}, val)
Expand Down Expand Up @@ -192,13 +202,13 @@ func DrawString(dst draw.Image, s string, color color.Color, f shaping.Fontmap,
}

func loadMeasureFont(data fyne.Resource) *font.Face {
loaded, err := font.ParseTTF(bytes.NewReader(data.Content()))
loaded, err := font.ParseTTC(bytes.NewReader(data.Content()))
if err != nil {
fyne.LogError("font load error", err)
return nil
}

return loaded
return loaded[0]
}

// MeasureString returns how far dot would advance by drawing s with f.
Expand Down Expand Up @@ -230,6 +240,9 @@ func float32ToFixed266(f float32) fixed.Int26_6 {

func measureText(text string, fontSize float32, style fyne.TextStyle, source fyne.Resource) (fyne.Size, float32) {
face := CachedFontFace(style, source, nil)
if face == nil {
return fyne.NewSize(0, 0), 0
}
return MeasureString(face.Fonts, text, fontSize, style)
}

Expand Down
3 changes: 3 additions & 0 deletions internal/painter/font_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestCachedFontFace(t *testing.T) {
} {
t.Run(name, func(t *testing.T) {
got := painter.CachedFontFace(tt.style, nil, nil)
assert.NotNil(t, got, "Font not loaded")
for _, r := range tt.runes {
f := got.Fonts.ResolveFace(r)
assert.NotNil(t, f, "symbol Font should include: %c", r)
Expand Down Expand Up @@ -78,6 +79,7 @@ func TestDrawString(t *testing.T) {
t.Run(name, func(t *testing.T) {
img := image.NewNRGBA(image.Rect(0, 0, 300, 100))
f := painter.CachedFontFace(tt.style, nil, nil)
assert.NotNil(t, f, "Font not loaded")

fontMap := &intTest.FontMap{f.Fonts.ResolveFace(' ')} // first (ascii) font
painter.DrawString(img, tt.string, tt.color, fontMap, tt.size, 1, fyne.TextStyle{TabWidth: tt.tabWidth})
Expand Down Expand Up @@ -118,6 +120,7 @@ func TestMeasureString(t *testing.T) {
} {
t.Run(name, func(t *testing.T) {
faces := painter.CachedFontFace(tt.style, nil, nil)
assert.NotNil(t, faces, "Font not loaded")
fontMap := &intTest.FontMap{faces.Fonts.ResolveFace(' ')} // first (ascii) font
got, _ := painter.MeasureString(fontMap, tt.string, tt.size, fyne.TextStyle{TabWidth: tt.tabWidth})
assert.Equal(t, tt.want, got.Width)
Expand Down
5 changes: 3 additions & 2 deletions internal/painter/gl/texture.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ func (p *painter) newGlTextTexture(obj fyne.CanvasObject) Texture {
height := int(math.Ceil(float64(p.textureScale(bounds.Height))))
img := image.NewNRGBA(image.Rect(0, 0, width, height))

face := paint.CachedFontFace(text.TextStyle, text.FontSource, text)
paint.DrawString(img, text.Text, color, face.Fonts, text.TextSize, p.pixScale, text.TextStyle)
if face := paint.CachedFontFace(text.TextStyle, text.FontSource, text); face != nil {
paint.DrawString(img, text.Text, color, face.Fonts, text.TextSize, p.pixScale, text.TextStyle)
}
return p.imgToTexture(img, canvas.ImageScaleSmooth)
}

Expand Down
5 changes: 3 additions & 2 deletions internal/painter/software/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,9 @@ func drawText(c fyne.Canvas, text *canvas.Text, pos fyne.Position, base *image.N
color = theme.Color(theme.ColorNameForeground)
}

face := painter.CachedFontFace(text.TextStyle, text.FontSource, text)
painter.DrawString(txtImg, text.Text, color, face.Fonts, text.TextSize, c.Scale(), text.TextStyle)
if face := painter.CachedFontFace(text.TextStyle, text.FontSource, text); face != nil {
painter.DrawString(txtImg, text.Text, color, face.Fonts, text.TextSize, c.Scale(), text.TextStyle)
}

size := text.Size()
offsetX := float32(0)
Expand Down
4 changes: 2 additions & 2 deletions internal/test/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ func TestAssertImageMatches(t *testing.T) {
draw.Draw(img, bounds, image.NewUniform(color.White), image.Point{}, draw.Src)

txtImg := image.NewNRGBA(bounds)
face, err := font.ParseTTF(bytes.NewReader(theme.TextFont().Content()))
face, err := font.ParseTTC(bytes.NewReader(theme.TextFont().Content()))
assert.Nil(t, err)

painter.DrawString(txtImg, "Hello!", color.Black, &test.FontMap{face}, 25, 1, fyne.TextStyle{TabWidth: 4})
painter.DrawString(txtImg, "Hello!", color.Black, &test.FontMap{face[0]}, 25, 1, fyne.TextStyle{TabWidth: 4})
draw.Draw(img, bounds, txtImg, image.Point{}, draw.Over)

tt := &testing.T{}
Expand Down
9 changes: 9 additions & 0 deletions internal/theme/theme.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package theme

import (
"bytes"
"image/color"

"fyne.io/fyne/v2"
"github.com/go-text/typesetting/font"
)

// Primary color names.
Expand Down Expand Up @@ -91,3 +93,10 @@ func PrimaryColorNamed(name string) color.Color {
// There is no need to have it in the switch above.
return colorLightPrimaryBlue
}

// CheckFontParsable checks whether a resource is a valid font.
// It mimics internal/painter.loadMeasureFont
func CheckFontParsable(data fyne.Resource) error {
_, err := font.ParseTTC(bytes.NewReader(data.Content()))
return err
}
Binary file added theme/testdata/Sitka.ttc
Binary file not shown.
Binary file added theme/testdata/smaller.fon
Binary file not shown.
Binary file added theme/testdata/teamviewer14.otf
Binary file not shown.
5 changes: 5 additions & 0 deletions theme/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ func CurrentForWidget(w fyne.CanvasObject) fyne.Theme {
return Current()
}

// CheckFontParsable checks whether a resource is a valid font.
func CheckFontParsable(data fyne.Resource) error {
return internaltheme.CheckFontParsable(data)
}

func currentVariant() fyne.ThemeVariant {
if std, ok := Current().(*builtinTheme); ok {
if std.variant != internaltheme.VariantNameUserPreference {
Expand Down
22 changes: 22 additions & 0 deletions theme/theme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,25 @@ func (e *emptyTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
func (e *emptyTheme) Size(n fyne.ThemeSizeName) float32 {
return 0
}

func TestCheckFontParsable(t *testing.T) {
for name, tt := range map[string]struct {
fn string
ok bool
}{
"ttf": {"./testdata/NotoMono-Regular.ttf", true},
"ttc": {"./testdata/Sitka.ttc", true},
"otf": {"./testdata/teamviewer14.otf", true},
"fon": {"./testdata/smaller.fon", false},
} {
t.Run(name, func(t *testing.T) {
res, err := fyne.LoadResourceFromPath(tt.fn)
assert.Nil(t, err)
checkFunc := assert.Nil
if !tt.ok {
checkFunc = assert.NotNil
}
checkFunc(t, theme.CheckFontParsable(res))
})
}
}
3 changes: 3 additions & 0 deletions widget/richtext.go
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,9 @@ func splitLines(seg *TextSegment) []rowBoundary {

func truncateLimit(s string, text *canvas.Text, limit int, ellipsis []rune) (int, bool) {
face := paint.CachedFontFace(text.TextStyle, text.FontSource, text)
if face == nil {
return 0, true
}

runes := []rune(s)
in := shaping.Input{
Expand Down