blob: 163dac70eb2e93f80cf81a834997f2f98b7da517 [file] [log] [blame]
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sfnt
/*
This file contains opt-in tests for kerning in user provided fonts.
Kerning information in kern and GPOS tables can be quite complex. These tests
recursively load all fonts from -bulkFontDirs and try to kern all possible
glyph pairs.
These tests only check if there are no errors during kerning. Tests of actual
kerning values are in proprietary_test.go.
Note: CJK fonts can contain billions of posible kerning pairs. Testing for
these fonts stops after -bulkMaxKernPairs.
To opt-in:
go test golang.org/x/image/font/sfnt -test.run=BulkKern -args -bulk -bulkFontDirs /Library/Fonts:./myfonts
*/
import (
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
var (
bulk = flag.Bool("bulk", false, "")
fontDirs = flag.String(
"bulkFontDirs",
"./",
"separated directories to search for fonts",
)
maxKernPairs = flag.Int(
"bulkMaxKernPairs",
20000000,
"skip testing of kerning after this many tested pairs",
)
)
func TestBulkKern(t *testing.T) {
if !*bulk {
t.Skip("skipping bulk font test")
}
for _, fontDir := range filepath.SplitList(*fontDirs) {
err := filepath.Walk(fontDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if strings.HasSuffix(path, ".ttf") || strings.HasSuffix(path, ".otf") {
t.Run(info.Name(), testFontKerning(filepath.Join(path)))
}
return nil
})
if err != nil {
t.Fatal("error finding fonts", err)
}
}
}
func testFontKerning(fname string) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
b, err := ioutil.ReadFile(fname)
if err != nil {
t.Fatal(err)
}
fnt, err := Parse(b)
if err != nil {
t.Fatal(err)
}
buf := &Buffer{}
// collect all GlyphIndex
glyphs := make([]GlyphIndex, 1, fnt.NumGlyphs())
glyphs[0] = GlyphIndex(0)
r := rune(0)
for r < 0xffff {
g, err := fnt.GlyphIndex(buf, r)
r++
if g == 0 || err == ErrNotFound {
continue
}
if err != nil {
t.Fatal(err)
}
glyphs = append(glyphs, g)
if len(glyphs) == fnt.NumGlyphs() {
break
}
}
var kerned, tested int
for _, g1 := range glyphs {
for _, g2 := range glyphs {
if tested >= *maxKernPairs {
log.Printf("stop testing after %d or %d kerning pairs (found %d pairs)",
tested, len(glyphs)*len(glyphs), kerned)
return
}
tested++
adv, err := fnt.Kern(buf, g1, g2, fixed.I(20), font.HintingNone)
if err == ErrNotFound {
continue
}
if err != nil {
t.Fatal(err)
}
if adv != 0 {
kerned++
}
}
}
log.Printf("found %d kerning pairs for %d glyphs (%.1f%%) in %q",
kerned,
len(glyphs),
100*float64(kerned)/float64(len(glyphs)*len(glyphs)),
fname,
)
}
}