// 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, ) } }