git clone https://git.lucas.co/go_mono.git
font/sfnt/kern_test.go (2.9K)
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package sfnt
6
7 /*
8 This file contains opt-in tests for kerning in user provided fonts.
9
10 Kerning information in kern and GPOS tables can be quite complex. These tests
11 recursively load all fonts from -bulkFontDirs and try to kern all possible
12 glyph pairs.
13
14 These tests only check if there are no errors during kerning. Tests of actual
15 kerning values are in proprietary_test.go.
16
17 Note: CJK fonts can contain billions of possible kerning pairs. Testing for
18 these fonts stops after -bulkMaxKernPairs.
19
20 To opt-in:
21
22 go test golang.org/x/image/font/sfnt -test.run=BulkKern -args -bulk -bulkFontDirs /Library/Fonts:./myfonts
23 */
24
25 import (
26 "flag"
27 "io/ioutil"
28 "log"
29 "os"
30 "path/filepath"
31 "strings"
32 "testing"
33
34 "golang.org/x/image/font"
35 "golang.org/x/image/math/fixed"
36 )
37
38 var (
39 bulk = flag.Bool("bulk", false, "")
40
41 fontDirs = flag.String(
42 "bulkFontDirs",
43 "./",
44 "separated directories to search for fonts",
45 )
46 maxKernPairs = flag.Int(
47 "bulkMaxKernPairs",
48 20000000,
49 "skip testing of kerning after this many tested pairs",
50 )
51 )
52
53 func TestBulkKern(t *testing.T) {
54 if !*bulk {
55 t.Skip("skipping bulk font test")
56 }
57
58 for _, fontDir := range filepath.SplitList(*fontDirs) {
59 err := filepath.Walk(fontDir, func(path string, info os.FileInfo, err error) error {
60 if err != nil {
61 return err
62 }
63 if info.IsDir() {
64 return nil
65 }
66 if strings.HasSuffix(path, ".ttf") || strings.HasSuffix(path, ".otf") {
67 t.Run(info.Name(), testFontKerning(filepath.Join(path)))
68 }
69 return nil
70 })
71 if err != nil {
72 t.Fatal("error finding fonts", err)
73 }
74 }
75
76 }
77
78 func testFontKerning(fname string) func(*testing.T) {
79 return func(t *testing.T) {
80 t.Parallel()
81 b, err := ioutil.ReadFile(fname)
82 if err != nil {
83 t.Fatal(err)
84 }
85 fnt, err := Parse(b)
86 if err != nil {
87 t.Fatal(err)
88 }
89
90 buf := &Buffer{}
91
92 // collect all GlyphIndex
93 glyphs := make([]GlyphIndex, 1, fnt.NumGlyphs())
94 glyphs[0] = GlyphIndex(0)
95 r := rune(0)
96 for r < 0xffff {
97 g, err := fnt.GlyphIndex(buf, r)
98 r++
99 if g == 0 || err == ErrNotFound {
100 continue
101 }
102 if err != nil {
103 t.Fatal(err)
104 }
105 glyphs = append(glyphs, g)
106 if len(glyphs) == fnt.NumGlyphs() {
107 break
108 }
109 }
110
111 var kerned, tested int
112 for _, g1 := range glyphs {
113 for _, g2 := range glyphs {
114 if tested >= *maxKernPairs {
115 log.Printf("stop testing after %d or %d kerning pairs (found %d pairs)",
116 tested, len(glyphs)*len(glyphs), kerned)
117 return
118 }
119
120 tested++
121 adv, err := fnt.Kern(buf, g1, g2, fixed.I(20), font.HintingNone)
122 if err == ErrNotFound {
123 continue
124 }
125 if err != nil {
126 t.Fatal(err)
127 }
128 if adv != 0 {
129 kerned++
130 }
131 }
132 }
133
134 log.Printf("found %d kerning pairs for %d glyphs (%.1f%%) in %q",
135 kerned,
136 len(glyphs),
137 100*float64(kerned)/float64(len(glyphs)*len(glyphs)),
138 fname,
139 )
140 }
141 }