git.lucas.co / go_mono
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 }