git clone https://git.lucas.co/go_mono.git
font/sfnt/proprietary_test.go (48.2K)
1 // Copyright 2017 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 popular, high quality, proprietary fonts,
9 made by companies such as Adobe and Microsoft. These fonts are generally
10 available, but copies are not explicitly included in this repository due to
11 licensing differences or file size concerns. To opt-in, run:
12
13 go test golang.org/x/image/font/sfnt -args -proprietary
14
15 Not all tests pass out-of-the-box on all systems. For example, the Microsoft
16 Times New Roman font is downloadable gratis even on non-Windows systems, but as
17 per the ttf-mscorefonts-installer Debian package, this requires accepting an
18 End User License Agreement (EULA) and a CAB format decoder. These tests assume
19 that such fonts have already been installed. You may need to specify the
20 directories for these fonts:
21
22 go test golang.org/x/image/font/sfnt -args -proprietary \
23 -adobeDir=$HOME/fonts/adobe \
24 -appleDir=$HOME/fonts/apple \
25 -dejavuDir=$HOME/fonts/dejavu \
26 -microsoftDir=$HOME/fonts/microsoft \
27 -notoDir=$HOME/fonts/noto
28
29 To only run those tests for the Microsoft fonts:
30
31 go test golang.org/x/image/font/sfnt -test.run=ProprietaryMicrosoft -args -proprietary etc
32 */
33
34 // TODO: enable Apple/Microsoft tests by default on Darwin/Windows?
35
36 import (
37 "errors"
38 "flag"
39 "io/ioutil"
40 "path/filepath"
41 "strconv"
42 "strings"
43 "testing"
44
45 "golang.org/x/image/font"
46 "golang.org/x/image/math/fixed"
47 )
48
49 var (
50 proprietary = flag.Bool("proprietary", false, "test proprietary fonts not included in this repository")
51
52 adobeDir = flag.String(
53 "adobeDir",
54 // This needs to be set explicitly. There is no default dir on Debian:
55 // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=736680
56 //
57 // Get the fonts from https://github.com/adobe-fonts, e.g.:
58 // - https://github.com/adobe-fonts/source-code-pro/releases/latest
59 // - https://github.com/adobe-fonts/source-han-sans/releases/latest
60 // - https://github.com/adobe-fonts/source-sans-pro/releases/latest
61 //
62 // Copy all of the TTF and OTF files to the one directory, such as
63 // $HOME/adobe-fonts, and pass that as the -adobeDir flag here.
64 "",
65 "directory name for the Adobe proprietary fonts",
66 )
67
68 appleDir = flag.String(
69 "appleDir",
70 // This needs to be set explicitly. These fonts come with macOS, which
71 // is widely available but not freely available.
72 //
73 // On a Mac, set this to "/System/Library/Fonts/".
74 "",
75 "directory name for the Apple proprietary fonts",
76 )
77
78 dejavuDir = flag.String(
79 "dejavuDir",
80 // Get the fonts from https://dejavu-fonts.github.io/
81 "",
82 "directory name for the DejaVu proprietary fonts",
83 )
84
85 microsoftDir = flag.String(
86 "microsoftDir",
87 "/usr/share/fonts/truetype/msttcorefonts",
88 "directory name for the Microsoft proprietary fonts",
89 )
90
91 notoDir = flag.String(
92 "notoDir",
93 // Get the fonts from https://www.google.com/get/noto/
94 "",
95 "directory name for the Noto proprietary fonts",
96 )
97 )
98
99 func TestProprietaryAdobeSourceCodeProRegularOTF(t *testing.T) {
100 testProprietary(t, "adobe", "SourceCodePro-Regular.otf", 1500, -1)
101 }
102
103 func TestProprietaryAdobeSourceCodeProRegularTTF(t *testing.T) {
104 testProprietary(t, "adobe", "SourceCodePro-Regular.ttf", 1500, -1)
105 }
106
107 func TestProprietaryAdobeSourceHanSansSCRegularOTF(t *testing.T) {
108 testProprietary(t, "adobe", "SourceHanSansSC-Regular.otf", 65535, -1)
109 }
110
111 func TestProprietaryAdobeSourceSansProBlackOTF(t *testing.T) {
112 testProprietary(t, "adobe", "SourceSansPro-Black.otf", 1900, -1)
113 }
114
115 func TestProprietaryAdobeSourceSansProBlackTTF(t *testing.T) {
116 testProprietary(t, "adobe", "SourceSansPro-Black.ttf", 1900, -1)
117 }
118
119 func TestProprietaryAdobeSourceSansProRegularOTF(t *testing.T) {
120 testProprietary(t, "adobe", "SourceSansPro-Regular.otf", 1900, -1)
121 }
122
123 func TestProprietaryAdobeSourceSansProRegularTTF(t *testing.T) {
124 testProprietary(t, "adobe", "SourceSansPro-Regular.ttf", 1900, -1)
125 }
126
127 func TestProprietaryAppleAppleSymbols(t *testing.T) {
128 testProprietary(t, "apple", "Apple Symbols.ttf", 4600, -1)
129 }
130
131 func TestProprietaryAppleGeezaPro0(t *testing.T) {
132 testProprietary(t, "apple", "GeezaPro.ttc?0", 1700, -1)
133 }
134
135 func TestProprietaryAppleGeezaPro1(t *testing.T) {
136 testProprietary(t, "apple", "GeezaPro.ttc?1", 1700, -1)
137 }
138
139 func TestProprietaryAppleHelvetica0(t *testing.T) {
140 testProprietary(t, "apple", "Helvetica.dfont?0", 2100, -1)
141 }
142
143 func TestProprietaryAppleHelvetica1(t *testing.T) {
144 testProprietary(t, "apple", "Helvetica.dfont?1", 2100, -1)
145 }
146
147 func TestProprietaryAppleHelvetica2(t *testing.T) {
148 testProprietary(t, "apple", "Helvetica.dfont?2", 2100, -1)
149 }
150
151 func TestProprietaryAppleHelvetica3(t *testing.T) {
152 testProprietary(t, "apple", "Helvetica.dfont?3", 2100, -1)
153 }
154
155 func TestProprietaryAppleHelvetica4(t *testing.T) {
156 testProprietary(t, "apple", "Helvetica.dfont?4", 1300, -1)
157 }
158
159 func TestProprietaryAppleHelvetica5(t *testing.T) {
160 testProprietary(t, "apple", "Helvetica.dfont?5", 1300, -1)
161 }
162
163 func TestProprietaryAppleHiragino0(t *testing.T) {
164 testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?0", 9000, -1)
165 }
166
167 func TestProprietaryAppleHiragino1(t *testing.T) {
168 testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?1", 9000, -1)
169 }
170
171 func TestProprietaryDejaVuSans(t *testing.T) {
172 testProprietary(t, "dejavu", "DejaVuSans.ttf", 3300, -1)
173 }
174
175 func TestProprietaryDejaVuSansExtraLight(t *testing.T) {
176 testProprietary(t, "dejavu", "DejaVuSans-ExtraLight.ttf", 2000, -1)
177 }
178
179 func TestProprietaryDejaVuSansMono(t *testing.T) {
180 testProprietary(t, "dejavu", "DejaVuSansMono.ttf", 3300, -1)
181 }
182
183 func TestProprietaryDejaVuSerif(t *testing.T) {
184 testProprietary(t, "dejavu", "DejaVuSerif.ttf", 3500, -1)
185 }
186
187 func TestProprietaryMicrosoftArial(t *testing.T) {
188 testProprietary(t, "microsoft", "Arial.ttf", 1200, -1)
189 }
190
191 func TestProprietaryMicrosoftArialAsACollection(t *testing.T) {
192 testProprietary(t, "microsoft", "Arial.ttf?0", 1200, -1)
193 }
194
195 func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
196 testProprietary(t, "microsoft", "Comic_Sans_MS.ttf", 550, -1)
197 }
198
199 func TestProprietaryMicrosoftTimesNewRoman(t *testing.T) {
200 testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, -1)
201 }
202
203 func TestProprietaryMicrosoftWebdings(t *testing.T) {
204 testProprietary(t, "microsoft", "Webdings.ttf", 200, -1)
205 }
206
207 func TestProprietaryNotoColorEmoji(t *testing.T) {
208 testProprietary(t, "noto", "NotoColorEmoji.ttf", 2300, -1)
209 }
210
211 func TestProprietaryNotoSansRegular(t *testing.T) {
212 testProprietary(t, "noto", "NotoSans-Regular.ttf", 2400, -1)
213 }
214
215 // testProprietary tests that we can load every glyph in the named font.
216 //
217 // The exact number of glyphs in the font can differ across its various
218 // versions, but as a sanity check, there should be at least minNumGlyphs.
219 //
220 // While this package is a work-in-progress, not every glyph can be loaded. The
221 // firstUnsupportedGlyph argument, if non-negative, is the index of the first
222 // unsupported glyph in the font. This number should increase over time (or set
223 // negative), as the TODO's in this package are done.
224 func testProprietary(t *testing.T, proprietor, filename string, minNumGlyphs, firstUnsupportedGlyph int) {
225 if !*proprietary {
226 t.Skip("skipping proprietary font test")
227 }
228
229 basename, fontIndex, err := filename, -1, error(nil)
230 if i := strings.IndexByte(filename, '?'); i >= 0 {
231 fontIndex, err = strconv.Atoi(filename[i+1:])
232 if err != nil {
233 t.Fatalf("could not parse collection font index from filename %q", filename)
234 }
235 basename = filename[:i]
236 }
237
238 dir := ""
239 switch proprietor {
240 case "adobe":
241 dir = *adobeDir
242 case "apple":
243 dir = *appleDir
244 case "dejavu":
245 dir = *dejavuDir
246 case "microsoft":
247 dir = *microsoftDir
248 case "noto":
249 dir = *notoDir
250 default:
251 panic("unreachable")
252 }
253 file, err := ioutil.ReadFile(filepath.Join(dir, basename))
254 if err != nil {
255 t.Fatalf("%v\nPerhaps you need to set the -%sDir flag?", err, proprietor)
256 }
257 qualifiedFilename := proprietor + "/" + filename
258
259 f := (*Font)(nil)
260 if fontIndex >= 0 {
261 c, err := ParseCollection(file)
262 if err != nil {
263 t.Fatalf("ParseCollection: %v", err)
264 }
265 if want, ok := proprietaryNumFonts[qualifiedFilename]; ok {
266 if got := c.NumFonts(); got != want {
267 t.Fatalf("NumFonts: got %d, want %d", got, want)
268 }
269 }
270 f, err = c.Font(fontIndex)
271 if err != nil {
272 t.Fatalf("Font: %v", err)
273 }
274 } else {
275 f, err = Parse(file)
276 if err != nil {
277 t.Fatalf("Parse: %v", err)
278 }
279 }
280
281 ppem := fixed.Int26_6(f.UnitsPerEm())
282 var buf Buffer
283
284 // Some of the tests below, such as which glyph index a particular rune
285 // maps to, can depend on the specific version of the proprietary font. If
286 // tested against a different version of that font, the test might (but not
287 // necessarily will) fail, even though the Go code is good. If so, log a
288 // message, but don't automatically fail (i.e. dont' call t.Fatalf).
289 gotVersion, err := f.Name(&buf, NameIDVersion)
290 if err != nil {
291 t.Fatalf("Name(Version): %v", err)
292 }
293 wantVersion := proprietaryVersions[qualifiedFilename]
294 if gotVersion != wantVersion {
295 t.Logf("font version provided differs from the one the tests were written against:"+
296 "\ngot %q\nwant %q", gotVersion, wantVersion)
297 }
298
299 gotFull, err := f.Name(&buf, NameIDFull)
300 if err != nil {
301 t.Fatalf("Name(Full): %v", err)
302 }
303 wantFull := proprietaryFullNames[qualifiedFilename]
304 if gotFull != wantFull {
305 t.Fatalf("Name(Full):\ngot %q\nwant %q", gotFull, wantFull)
306 }
307
308 numGlyphs := f.NumGlyphs()
309 if numGlyphs < minNumGlyphs {
310 t.Fatalf("NumGlyphs: got %d, want at least %d", numGlyphs, minNumGlyphs)
311 }
312
313 iMax := numGlyphs
314 if firstUnsupportedGlyph >= 0 {
315 iMax = firstUnsupportedGlyph
316 }
317 for i, numErrors := 0, 0; i < iMax; i++ {
318 if _, err := f.LoadGlyph(&buf, GlyphIndex(i), ppem, nil); err != nil && err != ErrColoredGlyph {
319 t.Errorf("LoadGlyph(%d): %v", i, err)
320 numErrors++
321 }
322 if numErrors == 10 {
323 t.Fatal("LoadGlyph: too many errors")
324 }
325 }
326
327 for r, want := range proprietaryGlyphIndexTestCases[qualifiedFilename] {
328 got, err := f.GlyphIndex(&buf, r)
329 if err != nil {
330 t.Errorf("GlyphIndex(%q): %v", r, err)
331 continue
332 }
333 if got != want {
334 t.Errorf("GlyphIndex(%q): got %d, want %d", r, got, want)
335 continue
336 }
337 }
338
339 for r, want := range proprietaryGlyphTestCases[qualifiedFilename] {
340 x, err := f.GlyphIndex(&buf, r)
341 if err != nil {
342 t.Errorf("GlyphIndex(%q): %v", r, err)
343 continue
344 }
345 got, err := f.LoadGlyph(&buf, x, ppem, nil)
346 if err != nil {
347 t.Errorf("LoadGlyph(%q): %v", r, err)
348 continue
349 }
350 if err := checkSegmentsEqual(got, want); err != nil {
351 t.Errorf("LoadGlyph(%q): %v", r, err)
352 continue
353 }
354 }
355
356 for r, tc := range proprietaryGlyphBoundsTestCases[qualifiedFilename] {
357 ppem := fixed.Int26_6(f.UnitsPerEm())
358 x, err := f.GlyphIndex(&buf, r)
359 if err != nil {
360 t.Errorf("GlyphIndex(%q): %v", r, err)
361 continue
362 }
363 gotBounds, gotAdv, err := f.GlyphBounds(&buf, x, ppem, font.HintingNone)
364 if err != nil {
365 t.Errorf("GlyphBounds(%q): %v", r, err)
366 continue
367 }
368 if gotBounds != tc.wantBounds {
369 t.Errorf("GlyphBounds(%q): got %#v, want %#v", r, gotBounds, tc.wantBounds)
370 }
371 if gotAdv != tc.wantAdv {
372 t.Errorf("GlyphBounds(%q): got %#v, want %#v", r, gotAdv, tc.wantAdv)
373 }
374 }
375
376 kernLoop:
377 for _, tc := range proprietaryKernTestCases[qualifiedFilename] {
378 var indexes [2]GlyphIndex
379 for i := range indexes {
380 x, err := f.GlyphIndex(&buf, tc.runes[i])
381 if x == 0 && err == nil {
382 err = errors.New("no glyph index found")
383 }
384 if err != nil {
385 t.Errorf("GlyphIndex(%q): %v", tc.runes[0], err)
386 continue kernLoop
387 }
388 indexes[i] = x
389 }
390 kern, err := f.Kern(&buf, indexes[0], indexes[1], tc.ppem, tc.hinting)
391 if err != nil {
392 t.Errorf("Kern(%q, %q, ppem=%d, hinting=%v): %v",
393 tc.runes[0], tc.runes[1], tc.ppem, tc.hinting, err)
394 continue
395 }
396 if got := Units(kern); got != tc.want {
397 t.Errorf("Kern(%q, %q, ppem=%d, hinting=%v): got %d, want %d",
398 tc.runes[0], tc.runes[1], tc.ppem, tc.hinting, got, tc.want)
399 continue
400 }
401 }
402
403 for x, want := range proprietaryFDSelectTestCases[qualifiedFilename] {
404 got, err := f.cached.glyphData.fdSelect.lookup(f, &buf, x)
405 if err != nil {
406 t.Errorf("fdSelect.lookup(%d): %v", x, err)
407 continue
408 }
409 if got != want {
410 t.Errorf("fdSelect.lookup(%d): got %d, want %d", x, got, want)
411 continue
412 }
413 }
414 }
415
416 // proprietaryNumFonts holds the expected number of fonts in each collection,
417 // or 1 for a single font. It is not necessarily an exhaustive list of all
418 // proprietary fonts tested.
419 var proprietaryNumFonts = map[string]int{
420 "apple/Helvetica.dfont?0": 6,
421 "apple/ヒラギノ角ゴシック W0.ttc?0": 2,
422 "microsoft/Arial.ttf?0": 1,
423 }
424
425 // proprietaryVersions holds the expected version string of each proprietary
426 // font tested. If third parties such as Adobe or Microsoft update their fonts,
427 // and the tests subsequently fail, these versions should be updated too.
428 //
429 // Updates are expected to be infrequent. For example, as of 2017, the fonts
430 // installed by the Debian ttf-mscorefonts-installer package have last modified
431 // times no later than 2001.
432 var proprietaryVersions = map[string]string{
433 "adobe/SourceCodePro-Regular.otf": "Version 2.030;PS 1.0;hotconv 16.6.51;makeotf.lib2.5.65220",
434 "adobe/SourceCodePro-Regular.ttf": "Version 2.030;PS 1.000;hotconv 16.6.51;makeotf.lib2.5.65220",
435 "adobe/SourceHanSansSC-Regular.otf": "Version 1.004;PS 1.004;hotconv 1.0.82;makeotf.lib2.5.63406",
436 "adobe/SourceSansPro-Black.otf": "Version 2.020;PS 2.0;hotconv 1.0.86;makeotf.lib2.5.63406",
437 "adobe/SourceSansPro-Black.ttf": "Version 2.020;PS 2.000;hotconv 1.0.86;makeotf.lib2.5.63406",
438 "adobe/SourceSansPro-Regular.otf": "Version 2.020;PS 2.0;hotconv 1.0.86;makeotf.lib2.5.63406",
439 "adobe/SourceSansPro-Regular.ttf": "Version 2.020;PS 2.000;hotconv 1.0.86;makeotf.lib2.5.63406",
440
441 "apple/Apple Symbols.ttf": "12.0d3e10",
442 "apple/GeezaPro.ttc?0": "12.0d1e3",
443 "apple/GeezaPro.ttc?1": "12.0d1e3",
444 "apple/Helvetica.dfont?0": "12.0d1e3",
445 "apple/Helvetica.dfont?1": "12.0d1e3",
446 "apple/Helvetica.dfont?2": "12.0d1e3",
447 "apple/Helvetica.dfont?3": "12.0d1e3",
448 "apple/Helvetica.dfont?4": "12.0d1e3",
449 "apple/Helvetica.dfont?5": "12.0d1e3",
450 "apple/ヒラギノ角ゴシック W0.ttc?0": "11.0d7e1",
451 "apple/ヒラギノ角ゴシック W0.ttc?1": "11.0d7e1",
452
453 "dejavu/DejaVuSans-ExtraLight.ttf": "Version 2.37",
454 "dejavu/DejaVuSans.ttf": "Version 2.37",
455 "dejavu/DejaVuSansMono.ttf": "Version 2.37",
456 "dejavu/DejaVuSerif.ttf": "Version 2.37",
457
458 "microsoft/Arial.ttf": "Version 2.82",
459 "microsoft/Arial.ttf?0": "Version 2.82",
460 "microsoft/Comic_Sans_MS.ttf": "Version 2.10",
461 "microsoft/Times_New_Roman.ttf": "Version 2.82",
462 "microsoft/Webdings.ttf": "Version 1.03",
463
464 "noto/NotoColorEmoji.ttf": "Version 1.33",
465 "noto/NotoSans-Regular.ttf": "Version 1.06",
466 }
467
468 // proprietaryFullNames holds the expected full name of each proprietary font
469 // tested.
470 var proprietaryFullNames = map[string]string{
471 "adobe/SourceCodePro-Regular.otf": "Source Code Pro",
472 "adobe/SourceCodePro-Regular.ttf": "Source Code Pro",
473 "adobe/SourceHanSansSC-Regular.otf": "Source Han Sans SC Regular",
474 "adobe/SourceSansPro-Black.otf": "Source Sans Pro Black",
475 "adobe/SourceSansPro-Black.ttf": "Source Sans Pro Black",
476 "adobe/SourceSansPro-Regular.otf": "Source Sans Pro",
477 "adobe/SourceSansPro-Regular.ttf": "Source Sans Pro",
478
479 "apple/Apple Symbols.ttf": "Apple Symbols",
480 "apple/GeezaPro.ttc?0": "Geeza Pro Regular",
481 "apple/GeezaPro.ttc?1": "Geeza Pro Bold",
482 "apple/Helvetica.dfont?0": "Helvetica",
483 "apple/Helvetica.dfont?1": "Helvetica Bold",
484 "apple/Helvetica.dfont?2": "Helvetica Oblique",
485 "apple/Helvetica.dfont?3": "Helvetica Bold Oblique",
486 "apple/Helvetica.dfont?4": "Helvetica Light",
487 "apple/Helvetica.dfont?5": "Helvetica Light Oblique",
488 "apple/ヒラギノ角ゴシック W0.ttc?0": "Hiragino Sans W0",
489 "apple/ヒラギノ角ゴシック W0.ttc?1": ".Hiragino Kaku Gothic Interface W0",
490
491 "dejavu/DejaVuSans-ExtraLight.ttf": "DejaVu Sans ExtraLight",
492 "dejavu/DejaVuSans.ttf": "DejaVu Sans",
493 "dejavu/DejaVuSansMono.ttf": "DejaVu Sans Mono",
494 "dejavu/DejaVuSerif.ttf": "DejaVu Serif",
495
496 "microsoft/Arial.ttf": "Arial",
497 "microsoft/Arial.ttf?0": "Arial",
498 "microsoft/Comic_Sans_MS.ttf": "Comic Sans MS",
499 "microsoft/Times_New_Roman.ttf": "Times New Roman",
500 "microsoft/Webdings.ttf": "Webdings",
501
502 "noto/NotoColorEmoji.ttf": "Noto Color Emoji",
503 "noto/NotoSans-Regular.ttf": "Noto Sans",
504 }
505
506 // proprietaryGlyphIndexTestCases hold a sample of each font's rune to glyph
507 // index cmap. The numerical values can be verified by running the ttx tool.
508 var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
509 "adobe/SourceCodePro-Regular.otf": {
510 '\u0030': 877, // U+0030 DIGIT ZERO
511 '\u0041': 2, // U+0041 LATIN CAPITAL LETTER A
512 '\u0061': 28, // U+0061 LATIN SMALL LETTER A
513 '\u0104': 64, // U+0104 LATIN CAPITAL LETTER A WITH OGONEK
514 '\u0125': 323, // U+0125 LATIN SMALL LETTER H WITH CIRCUMFLEX
515 '\u01f4': 111, // U+01F4 LATIN CAPITAL LETTER G WITH ACUTE
516 '\u03a3': 623, // U+03A3 GREEK CAPITAL LETTER SIGMA
517 '\u2569': 1500, // U+2569 BOX DRAWINGS DOUBLE UP AND HORIZONTAL
518 '\U0001f100': 0, // U+0001F100 DIGIT ZERO FULL STOP
519 },
520 "adobe/SourceCodePro-Regular.ttf": {
521 '\u0030': 877, // U+0030 DIGIT ZERO
522 '\u0041': 2, // U+0041 LATIN CAPITAL LETTER A
523 '\u01f4': 111, // U+01F4 LATIN CAPITAL LETTER G WITH ACUTE
524 },
525 "adobe/SourceHanSansSC-Regular.otf": {
526 '\u0030': 17, // U+0030 DIGIT ZERO
527 '\u0041': 34, // U+0041 LATIN CAPITAL LETTER A
528 '\u00d7': 150, // U+00D7 MULTIPLICATION SIGN
529 '\u1100': 365, // U+1100 HANGUL CHOSEONG KIYEOK
530 '\u25ca': 1254, // U+25CA LOZENGE
531 '\u2e9c': 1359, // U+2E9C CJK RADICAL SUN
532 '\u304b': 1463, // U+304B HIRAGANA LETTER KA
533 '\u4e2d': 9893, // U+4E2D <CJK Ideograph>, 中
534 '\ua960': 47537, // U+A960 HANGUL CHOSEONG TIKEUT-MIEUM
535 '\ufb00': 58919, // U+FB00 LATIN SMALL LIGATURE FF
536 '\uffee': 59213, // U+FFEE HALFWIDTH WHITE CIRCLE
537 '\U0001f100': 59214, // U+0001F100 DIGIT ZERO FULL STOP
538 '\U0001f248': 59449, // U+0001F248 TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557
539 '\U0002f9f4': 61768, // U+0002F9F4 CJK COMPATIBILITY IDEOGRAPH-2F9F4
540 },
541 "adobe/SourceSansPro-Regular.otf": {
542 '\u0041': 2, // U+0041 LATIN CAPITAL LETTER A
543 '\u03a3': 592, // U+03A3 GREEK CAPITAL LETTER SIGMA
544 '\u0435': 999, // U+0435 CYRILLIC SMALL LETTER IE
545 '\u2030': 1728, // U+2030 PER MILLE SIGN
546 },
547 "adobe/SourceSansPro-Regular.ttf": {
548 '\u0041': 2, // U+0041 LATIN CAPITAL LETTER A
549 '\u03a3': 592, // U+03A3 GREEK CAPITAL LETTER SIGMA
550 '\u0435': 999, // U+0435 CYRILLIC SMALL LETTER IE
551 '\u2030': 1728, // U+2030 PER MILLE SIGN
552 },
553
554 "apple/Helvetica.dfont?0": {
555 '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
556 '\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
557 '\u0401': 473, // U+0401 CYRILLIC CAPITAL LETTER IO
558 '\u200d': 611, // U+200D ZERO WIDTH JOINER
559 '\u20ab': 1743, // U+20AB DONG SIGN
560 '\u2229': 0, // U+2229 INTERSECTION
561 '\u04e9': 1208, // U+04E9 CYRILLIC SMALL LETTER BARRED O
562 '\U0001f100': 0, // U+0001F100 DIGIT ZERO FULL STOP
563 },
564
565 "dejavu/DejaVuSerif.ttf": {
566 '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
567 '\u1e00': 1418, // U+1E00 LATIN CAPITAL LETTER A WITH RING BELOW
568 },
569
570 "microsoft/Arial.ttf": {
571 '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
572 '\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
573 '\u0401': 556, // U+0401 CYRILLIC CAPITAL LETTER IO
574 '\u200d': 745, // U+200D ZERO WIDTH JOINER
575 '\u20ab': 1150, // U+20AB DONG SIGN
576 '\u2229': 320, // U+2229 INTERSECTION
577 '\u04e9': 1319, // U+04E9 CYRILLIC SMALL LETTER BARRED O
578 '\U0001f100': 0, // U+0001F100 DIGIT ZERO FULL STOP
579 },
580 "microsoft/Comic_Sans_MS.ttf": {
581 '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
582 '\u03af': 573, // U+03AF GREEK SMALL LETTER IOTA WITH TONOS
583 },
584 "microsoft/Times_New_Roman.ttf": {
585 '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
586 '\u0042': 37, // U+0041 LATIN CAPITAL LETTER B
587 '\u266a': 392, // U+266A EIGHTH NOTE
588 '\uf041': 0, // PRIVATE USE AREA
589 '\uf042': 0, // PRIVATE USE AREA
590 },
591 "microsoft/Webdings.ttf": {
592 '\u0041': 0, // U+0041 LATIN CAPITAL LETTER A
593 '\u0042': 0, // U+0041 LATIN CAPITAL LETTER B
594 '\u266a': 0, // U+266A EIGHTH NOTE
595 '\uf041': 36, // PRIVATE USE AREA
596 '\uf042': 37, // PRIVATE USE AREA
597 },
598 }
599
600 // proprietaryGlyphTestCases hold a sample of each font's glyph vectors. The
601 // numerical values can be verified by running the ttx tool, remembering that:
602 // - for PostScript glyphs, ttx coordinates are relative.
603 // - for TrueType glyphs, ttx coordinates are absolute, and consecutive
604 // off-curve points implies an on-curve point at the midpoint.
605 var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
606 "adobe/SourceHanSansSC-Regular.otf": {
607 '!': {
608 // -312 123 callsubr # 123 + bias = 230
609 // : # Arg stack is [-312].
610 // : -13 140 -119 -21 return
611 // : # Arg stack is [-312 -13 140 -119 -21].
612 // 120 callsubr # 120 + bias = 227
613 // : # Arg stack is [-312 -13 140 -119 -21].
614 // : hstemhm
615 // : 95 132 -103 75 return
616 // : # Arg stack is [95 132 -103 75].
617 // hintmask 01010000
618 // 8 callsubr # 8 + bias = 115
619 // : # Arg stack is [].
620 // : 130 221 rmoveto
621 moveTo(130, 221),
622 // : 63 hlineto
623 lineTo(193, 221),
624 // : 12 424 3 -735 callgsubr # -735 + bias = 396
625 // : : # Arg stack is [12 424 3].
626 // : : 104 rlineto
627 lineTo(205, 645),
628 lineTo(208, 749),
629 // : : -93 hlineto
630 lineTo(115, 749),
631 // : : 3 -104 rlineto
632 lineTo(118, 645),
633 // : : return
634 // : : # Arg stack is [].
635 // : return
636 // : # Arg stack is [].
637 // hintmask 01100000
638 // 106 callsubr # 106 + bias = 213
639 // : # Arg stack is [].
640 // : 43 -658 rmoveto
641 lineTo(130, 221),
642 moveTo(161, -13),
643 // : 37 29 28 41 return
644 // : # Arg stack is [37 29 28 41].
645 // hvcurveto
646 cubeTo(198, -13, 227, 15, 227, 56),
647 // hintmask 10100000
648 // 41 -29 30 -37 -36 -30 -30 -41 vhcurveto
649 cubeTo(227, 97, 198, 127, 161, 127),
650 cubeTo(125, 127, 95, 97, 95, 56),
651 // hintmask 01100000
652 // 111 callsubr # 111 + bias = 218
653 // : # Arg stack is [].
654 // : -41 30 -28 36 vhcurveto
655 cubeTo(95, 15, 125, -13, 161, -13),
656 // : endchar
657 },
658
659 '二': { // U+4E8C <CJK Ideograph> "two; twice"
660 // 23 81 510 79 hstem
661 // 60 881 cntrmask 11000000
662 // 144 693 rmoveto
663 moveTo(144, 693),
664 // -79 713 79 vlineto
665 lineTo(144, 614),
666 lineTo(857, 614),
667 lineTo(857, 693),
668 // -797 -589 rmoveto
669 lineTo(144, 693),
670 moveTo(60, 104),
671 // -81 881 81 vlineto
672 lineTo(60, 23),
673 lineTo(941, 23),
674 lineTo(941, 104),
675 // endchar
676 lineTo(60, 104),
677 },
678 },
679
680 "adobe/SourceSansPro-Black.otf": {
681 '¤': { // U+00A4 CURRENCY SIGN
682 // -45 147 99 168 98 hstem
683 // 44 152 148 152 vstem
684 // 102 76 rmoveto
685 moveTo(102, 76),
686 // 71 71 rlineto
687 lineTo(173, 147),
688 // 31 -13 33 -6 33 32 34 6 31 hflex1
689 cubeTo(204, 134, 237, 128, 270, 128),
690 cubeTo(302, 128, 336, 134, 367, 147),
691 // 71 -71 85 85 -61 60 rlineto
692 lineTo(438, 76),
693 lineTo(523, 161),
694 lineTo(462, 221),
695 // 21 30 13 36 43 vvcurveto
696 cubeTo(483, 251, 496, 287, 496, 330),
697 // 42 -12 36 -21 29 vhcurveto
698 cubeTo(496, 372, 484, 408, 463, 437),
699 // 60 60 -85 85 -70 -70 rlineto
700 lineTo(523, 497),
701 lineTo(438, 582),
702 lineTo(368, 512),
703 // -31 13 -34 7 -33 -33 -34 -7 -31 hflex1
704 cubeTo(337, 525, 303, 532, 270, 532),
705 cubeTo(237, 532, 203, 525, 172, 512),
706 // -70 70 -85 -85 59 -60 rlineto
707 lineTo(102, 582),
708 lineTo(17, 497),
709 lineTo(76, 437),
710 // -20 -29 -12 -36 -42 vvcurveto
711 cubeTo(56, 408, 44, 372, 44, 330),
712 // -43 12 -36 21 -30 vhcurveto
713 cubeTo(44, 287, 56, 251, 77, 221),
714 // -60 -60 rlineto
715 lineTo(17, 161),
716 // 253 85 rmoveto
717 lineTo(102, 76),
718 moveTo(270, 246),
719 // -42 -32 32 52 52 32 32 42 42 32 -32 -52 -52 -32 -32 -42 hvcurveto
720 cubeTo(228, 246, 196, 278, 196, 330),
721 cubeTo(196, 382, 228, 414, 270, 414),
722 cubeTo(312, 414, 344, 382, 344, 330),
723 cubeTo(344, 278, 312, 246, 270, 246),
724 // endchar
725 },
726 },
727
728 "adobe/SourceSansPro-Regular.otf": {
729 ',': {
730 // -309 -1 115 hstem
731 // 137 61 vstem
732 // 67 -170 rmoveto
733 moveTo(67, -170),
734 // 81 34 50 67 86 vvcurveto
735 cubeTo(148, -136, 198, -69, 198, 17),
736 // 60 -26 37 -43 -33 -28 -22 -36 -37 27 -20 32 3 4 0 1 3 vhcurveto
737 cubeTo(198, 77, 172, 114, 129, 114),
738 cubeTo(96, 114, 68, 92, 68, 56),
739 cubeTo(68, 19, 95, -1, 127, -1),
740 cubeTo(130, -1, 134, -1, 137, 0),
741 // 1 -53 -34 -44 -57 -25 rrcurveto
742 cubeTo(138, -53, 104, -97, 47, -122),
743 // endchar
744 lineTo(67, -170),
745 },
746
747 'Q': {
748 // 106 -165 70 87 65 538 73 hstem
749 // 52 86 388 87 vstem
750 // 332 57 rmoveto
751 moveTo(332, 57),
752 // -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto
753 cubeTo(215, 57, 138, 163, 138, 331),
754 cubeTo(138, 494, 215, 595, 332, 595),
755 cubeTo(449, 595, 526, 494, 526, 331),
756 cubeTo(526, 163, 449, 57, 332, 57),
757 // 201 -222 rmoveto
758 moveTo(533, -165),
759 // 39 35 7 8 20 hvcurveto
760 cubeTo(572, -165, 607, -158, 627, -150),
761 // -16 64 rlineto
762 lineTo(611, -86),
763 // -5 -18 -22 -4 -29 hhcurveto
764 cubeTo(593, -91, 571, -95, 542, -95),
765 // -71 -60 29 58 -30 hvcurveto
766 cubeTo(471, -95, 411, -66, 381, -8),
767 // 139 24 93 126 189 vvcurveto
768 cubeTo(520, 16, 613, 142, 613, 331),
769 // 209 -116 128 -165 -165 -115 -127 -210 -193 96 -127 143 -20 vhcurveto
770 cubeTo(613, 540, 497, 668, 332, 668),
771 cubeTo(167, 668, 52, 541, 52, 331),
772 cubeTo(52, 138, 148, 11, 291, -9),
773 // -90 38 83 -66 121 hhcurveto
774 cubeTo(329, -99, 412, -165, 533, -165),
775 // endchar
776 },
777
778 'ĩ': { // U+0129 LATIN SMALL LETTER I WITH TILDE
779 // 92 callgsubr # 92 + bias = 199.
780 // : # Arg stack is [].
781 // : -312 21 85 callgsubr # 85 + bias = 192.
782 // : : # Arg stack is [-312 21].
783 // : : -21 486 -20 return
784 // : : # Arg stack is [-312 21 -21 486 -20].
785 // : return
786 // : # Arg stack is [-312 21 -21 486 -20].
787 // 111 45 callsubr # 45 + bias = 152
788 // : # Arg stack is [-312 21 -21 486 -20 111].
789 // : 60 24 60 -9 216 callgsubr # 216 + bias = 323
790 // : : # Arg stack is [-312 21 -21 486 -20 111 60 24 60 -9].
791 // : : -20 24 -20 hstemhm
792 // : : return
793 // : : # Arg stack is [].
794 // : return
795 // : # Arg stack is [].
796 // -50 55 77 82 77 55 hintmask 1101000100000000
797 // 134 callsubr # 134 + bias = 241
798 // : # Arg stack is [].
799 // : 82 hmoveto
800 moveTo(82, 0),
801 // : 82 127 callsubr # 127 + bias = 234
802 // : : # Arg stack is [82].
803 // : : 486 -82 hlineto
804 lineTo(164, 0),
805 lineTo(164, 486),
806 lineTo(82, 486),
807 // : : return
808 // : : # Arg stack is [].
809 // : return
810 // : # Arg stack is [].
811 // hintmask 1110100110000000
812 // 113 91 15 callgsubr # 15 + bias = 122
813 // : # Arg stack is [113 91].
814 // : rmoveto
815 lineTo(82, 0),
816 moveTo(195, 577),
817 // : 69 29 58 77 3 hvcurveto
818 cubeTo(264, 577, 293, 635, 296, 712),
819 // : return
820 // : # Arg stack is [].
821 // hintmask 1110010110000000
822 // -58 callsubr # -58 + bias = 49
823 // : # Arg stack is [].
824 // : -55 4 rlineto
825 lineTo(241, 716),
826 // : -46 -3 -14 -33 -29 -47 -26 84 -71 hhcurveto
827 cubeTo(238, 670, 224, 637, 195, 637),
828 cubeTo(148, 637, 122, 721, 51, 721),
829 // : return
830 // : # Arg stack is [].
831 // hintmask 1101001100000000
832 // -70 callgsubr # -70 + bias = 37
833 // : # Arg stack is [].
834 // : -69 -29 -58 -78 -3 hvcurveto
835 cubeTo(-18, 721, -47, 663, -50, 585),
836 // : 55 -3 rlineto
837 lineTo(5, 582),
838 // : 47 3 14 32 30 hhcurveto
839 cubeTo(8, 629, 22, 661, 52, 661),
840 // : return
841 // : # Arg stack is [].
842 // hintmask 1110100110000000
843 // 51 callsubr # 51 + bias = 158
844 // : # Arg stack is [].
845 // : 46 26 -84 71 hhcurveto
846 cubeTo(98, 661, 124, 577, 195, 577),
847 // : endchar
848 },
849
850 'ī': { // U+012B LATIN SMALL LETTER I WITH MACRON
851 // 92 callgsubr # 92 + bias = 199.
852 // : # Arg stack is [].
853 // : -312 21 85 callgsubr # 85 + bias = 192.
854 // : : # Arg stack is [-312 21].
855 // : : -21 486 -20 return
856 // : : # Arg stack is [-312 21 -21 486 -20].
857 // : return
858 // : # Arg stack is [-312 21 -21 486 -20].
859 // 135 57 112 callgsubr # 112 + bias = 219
860 // : # Arg stack is [-312 21 -21 486 -20 135 57].
861 // : hstem
862 // : 82 82 vstem
863 // : 134 callsubr # 134 + bias = 241
864 // : : # Arg stack is [].
865 // : : 82 hmoveto
866 moveTo(82, 0),
867 // : : 82 127 callsubr # 127 + bias = 234
868 // : : : # Arg stack is [82].
869 // : : : 486 -82 hlineto
870 lineTo(164, 0),
871 lineTo(164, 486),
872 lineTo(82, 486),
873 // : : : return
874 // : : : # Arg stack is [].
875 // : : return
876 // : : # Arg stack is [].
877 // : return
878 // : # Arg stack is [].
879 // -92 115 -60 callgsubr # -60 + bias = 47
880 // : # Arg stack is [-92 115].
881 // : rmoveto
882 lineTo(82, 0),
883 moveTo(-10, 601),
884 // : 266 57 -266 hlineto
885 lineTo(256, 601),
886 lineTo(256, 658),
887 lineTo(-10, 658),
888 // : endchar
889 lineTo(-10, 601),
890 },
891
892 'ĭ': { // U+012D LATIN SMALL LETTER I WITH BREVE
893 // 92 callgsubr # 92 + bias = 199.
894 // : # Arg stack is [].
895 // : -312 21 85 callgsubr # 85 + bias = 192.
896 // : : # Arg stack is [-312 21].
897 // : : -21 486 -20 return
898 // : : # Arg stack is [-312 21 -21 486 -20].
899 // : return
900 // : # Arg stack is [-312 21 -21 486 -20].
901 // 105 55 96 -20 hstem
902 // -32 51 63 82 65 51 vstem
903 // 134 callsubr # 134 + bias = 241
904 // : # Arg stack is [].
905 // : 82 hmoveto
906 moveTo(82, 0),
907 // : 82 127 callsubr # 127 + bias = 234
908 // : : # Arg stack is [82].
909 // : : 486 -82 hlineto
910 lineTo(164, 0),
911 lineTo(164, 486),
912 lineTo(82, 486),
913 // : : return
914 // : : # Arg stack is [].
915 // : return
916 // : # Arg stack is [].
917 // 42 85 143 callsubr # 143 + bias = 250
918 // : # Arg stack is [42 85].
919 // : rmoveto
920 lineTo(82, 0),
921 moveTo(124, 571),
922 // : -84 callsubr # -84 + bias = 23
923 // : : # Arg stack is [].
924 // : : 107 44 77 74 5 hvcurveto
925 cubeTo(231, 571, 275, 648, 280, 722),
926 // : : -51 8 rlineto
927 lineTo(229, 730),
928 // : : -51 -8 -32 -53 -65 hhcurveto
929 cubeTo(221, 679, 189, 626, 124, 626),
930 // : : -65 -32 53 51 -8 hvcurveto
931 cubeTo(59, 626, 27, 679, 19, 730),
932 // : : -51 -22 callsubr # -22 + bias = 85
933 // : : : # Arg stack is [-51].
934 // : : : -8 rlineto
935 lineTo(-32, 722),
936 // : : : -74 5 44 -77 107 hhcurveto
937 cubeTo(-27, 648, 17, 571, 124, 571),
938 // : : : return
939 // : : : # Arg stack is [].
940 // : : return
941 // : : # Arg stack is [].
942 // : return
943 // : # Arg stack is [].
944 // endchar
945 },
946
947 'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA
948 // -43 21 -21 572 84 hstem
949 // 0 515 vstem
950 // 0 vmoveto
951 moveTo(0, 0),
952 // 85 hlineto
953 lineTo(85, 0),
954 // 105 355 23 77 16 63 24 77 rlinecurve
955 lineTo(190, 355),
956 cubeTo(213, 432, 229, 495, 253, 572),
957 // 4 hlineto
958 lineTo(257, 572),
959 // 25 -77 16 -63 23 -77 106 -355 rcurveline
960 cubeTo(282, 495, 298, 432, 321, 355),
961 lineTo(427, 0),
962 // 88 hlineto
963 lineTo(515, 0),
964 // -210 656 rlineto
965 lineTo(305, 656),
966 // -96 hlineto
967 lineTo(209, 656),
968 // endchar
969 lineTo(0, 0),
970 },
971
972 'Ḫ': { // U+1E2A LATIN CAPITAL LETTER H WITH BREVE BELOW
973 // 94 -231 55 197 157 callgsubr # 157 + bias = 264
974 // : # Arg stack is [94 -231 55 197].
975 // : -21 309 72 return
976 // : # Arg stack is [94 -231 55 197 -21 309 72].
977 // 275 254 callgsubr # 254 + bias = 361
978 // : # Arg stack is [94 -231 55 197 -21 309 72 275].
979 // : -20 hstemhm
980 // : 90 83 return
981 // : # Arg stack is [90 83].
982 // -4 352 callsubr # 352 + bias = 459
983 // : # Arg stack is [90 83 -4].
984 // : 51 210 51 return
985 // : # Arg stack is [90 83 -4 51 210 51].
986 // -3 84 hintmask 11111001
987 // 90 -40 callsubr # -40 + bias = 67
988 // : # Arg stack is [90].
989 // : -27 callgsubr # -27 + bias = 80
990 // : : # Arg stack is [90].
991 // : : hmoveto
992 moveTo(90, 0),
993 // : : 83 309 305 -309 84 return
994 // : : # Arg stack is [83 309 305 -309 84].
995 // : -41 callgsubr # -41 + bias = 66
996 // : : # Arg stack is [83 309 305 -309 84].
997 // : : 656 -84 -275 -305 275 -83 return
998 // : : # Arg stack is [83 309 305 -309 84 656 -84 -275 -305 275 -83].
999 // : hlineto
1000 lineTo(173, 0),
1001 lineTo(173, 309),
1002 lineTo(478, 309),
1003 lineTo(478, 0),
1004 lineTo(562, 0),
1005 lineTo(562, 656),
1006 lineTo(478, 656),
1007 lineTo(478, 381),
1008 lineTo(173, 381),
1009 lineTo(173, 656),
1010 lineTo(90, 656),
1011 // : return
1012 // : # Arg stack is [].
1013 // hintmask 11110110
1014 // 235 -887 143 callsubr # 143 + bias = 250
1015 // : # Arg stack is [235 -887].
1016 // : rmoveto
1017 lineTo(90, 0),
1018 moveTo(325, -231),
1019 // : -84 callsubr # -84 + bias = 23
1020 // : : # Arg stack is [].
1021 // : : 107 44 77 74 5 hvcurveto
1022 cubeTo(432, -231, 476, -154, 481, -80),
1023 // : : -51 8 rlineto
1024 lineTo(430, -72),
1025 // : : -51 -8 -32 -53 -65 hhcurveto
1026 cubeTo(422, -123, 390, -176, 325, -176),
1027 // : : -65 -32 53 51 -8 hvcurveto
1028 cubeTo(260, -176, 228, -123, 220, -72),
1029 // : : -51 -22 callsubr # -22 + bias = 85
1030 // : : : # Arg stack is [-51].
1031 // : : : -8 rlineto
1032 lineTo(169, -80),
1033 // : : : -74 5 44 -77 107 hhcurveto
1034 cubeTo(174, -154, 218, -231, 325, -231),
1035 // : : : return
1036 // : : : # Arg stack is [].
1037 // : : return
1038 // : : # Arg stack is [].
1039 // : return
1040 // : # Arg stack is [].
1041 // endchar
1042 },
1043 },
1044
1045 "apple/Helvetica.dfont?0": {
1046 'i': {
1047 // - contour #0
1048 moveTo(132, 1066),
1049 lineTo(315, 1066),
1050 lineTo(315, 0),
1051 lineTo(132, 0),
1052 lineTo(132, 1066),
1053 // - contour #1
1054 moveTo(132, 1469),
1055 lineTo(315, 1469),
1056 lineTo(315, 1265),
1057 lineTo(132, 1265),
1058 lineTo(132, 1469),
1059 },
1060 },
1061
1062 "apple/Helvetica.dfont?1": {
1063 'i': {
1064 // - contour #0
1065 moveTo(426, 1220),
1066 lineTo(137, 1220),
1067 lineTo(137, 1483),
1068 lineTo(426, 1483),
1069 lineTo(426, 1220),
1070 // - contour #1
1071 moveTo(137, 1090),
1072 lineTo(426, 1090),
1073 lineTo(426, 0),
1074 lineTo(137, 0),
1075 lineTo(137, 1090),
1076 },
1077 },
1078
1079 "dejavu/DejaVuSans-ExtraLight.ttf": {
1080 'i': {
1081 // - contour #0
1082 moveTo(230, 1120),
1083 lineTo(322, 1120),
1084 lineTo(322, 0),
1085 lineTo(230, 0),
1086 lineTo(230, 1120),
1087 // - contour #1
1088 moveTo(230, 1556),
1089 lineTo(322, 1556),
1090 lineTo(322, 1430),
1091 lineTo(230, 1430),
1092 lineTo(230, 1556),
1093 },
1094 },
1095
1096 "microsoft/Arial.ttf": {
1097 ',': {
1098 // - contour #0
1099 moveTo(182, 0),
1100 lineTo(182, 205),
1101 lineTo(387, 205),
1102 lineTo(387, 0),
1103 quadTo(387, -113, 347, -182),
1104 quadTo(307, -252, 220, -290),
1105 lineTo(170, -213),
1106 quadTo(227, -188, 254, -139),
1107 quadTo(281, -91, 284, 0),
1108 lineTo(182, 0),
1109 },
1110
1111 'i': {
1112 // - contour #0
1113 moveTo(136, 1259),
1114 lineTo(136, 1466),
1115 lineTo(316, 1466),
1116 lineTo(316, 1259),
1117 lineTo(136, 1259),
1118 // - contour #1
1119 moveTo(136, 0),
1120 lineTo(136, 1062),
1121 lineTo(316, 1062),
1122 lineTo(316, 0),
1123 lineTo(136, 0),
1124 },
1125
1126 'o': {
1127 // - contour #0
1128 moveTo(68, 531),
1129 quadTo(68, 826, 232, 968),
1130 quadTo(369, 1086, 566, 1086),
1131 quadTo(785, 1086, 924, 942),
1132 quadTo(1063, 799, 1063, 546),
1133 quadTo(1063, 341, 1001, 223),
1134 quadTo(940, 106, 822, 41),
1135 quadTo(705, -24, 566, -24),
1136 quadTo(343, -24, 205, 119),
1137 quadTo(68, 262, 68, 531),
1138 // - contour #1
1139 moveTo(253, 531),
1140 quadTo(253, 327, 342, 225),
1141 quadTo(431, 124, 566, 124),
1142 quadTo(700, 124, 789, 226),
1143 quadTo(878, 328, 878, 537),
1144 quadTo(878, 734, 788, 835),
1145 quadTo(699, 937, 566, 937),
1146 quadTo(431, 937, 342, 836),
1147 quadTo(253, 735, 253, 531),
1148 },
1149
1150 'í': { // U+00ED LATIN SMALL LETTER I WITH ACUTE
1151 // - contour #0
1152 translate(0, 0, moveTo(198, 0)),
1153 translate(0, 0, lineTo(198, 1062)),
1154 translate(0, 0, lineTo(378, 1062)),
1155 translate(0, 0, lineTo(378, 0)),
1156 translate(0, 0, lineTo(198, 0)),
1157 // - contour #1
1158 translate(-33, 0, moveTo(222, 1194)),
1159 translate(-33, 0, lineTo(355, 1474)),
1160 translate(-33, 0, lineTo(591, 1474)),
1161 translate(-33, 0, lineTo(371, 1194)),
1162 translate(-33, 0, lineTo(222, 1194)),
1163 },
1164
1165 'Ī': { // U+012A LATIN CAPITAL LETTER I WITH MACRON
1166 // - contour #0
1167 translate(0, 0, moveTo(191, 0)),
1168 translate(0, 0, lineTo(191, 1466)),
1169 translate(0, 0, lineTo(385, 1466)),
1170 translate(0, 0, lineTo(385, 0)),
1171 translate(0, 0, lineTo(191, 0)),
1172 // - contour #1
1173 translate(-57, 336, moveTo(29, 1227)),
1174 translate(-57, 336, lineTo(29, 1375)),
1175 translate(-57, 336, lineTo(653, 1375)),
1176 translate(-57, 336, lineTo(653, 1227)),
1177 translate(-57, 336, lineTo(29, 1227)),
1178 },
1179
1180 // Ǻ is a compound glyph whose elements are also compound glyphs.
1181 'Ǻ': { // U+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
1182 // - contour #0
1183 translate(0, 0, moveTo(-3, 0)),
1184 translate(0, 0, lineTo(560, 1466)),
1185 translate(0, 0, lineTo(769, 1466)),
1186 translate(0, 0, lineTo(1369, 0)),
1187 translate(0, 0, lineTo(1148, 0)),
1188 translate(0, 0, lineTo(977, 444)),
1189 translate(0, 0, lineTo(364, 444)),
1190 translate(0, 0, lineTo(203, 0)),
1191 translate(0, 0, lineTo(-3, 0)),
1192 // - contour #1
1193 translate(0, 0, moveTo(420, 602)),
1194 translate(0, 0, lineTo(917, 602)),
1195 translate(0, 0, lineTo(764, 1008)),
1196 translate(0, 0, quadTo(694, 1193, 660, 1312)),
1197 translate(0, 0, quadTo(632, 1171, 581, 1032)),
1198 translate(0, 0, lineTo(420, 602)),
1199 // - contour #2
1200 translate(319, 263, moveTo(162, 1338)),
1201 translate(319, 263, quadTo(162, 1411, 215, 1464)),
1202 translate(319, 263, quadTo(269, 1517, 342, 1517)),
1203 translate(319, 263, quadTo(416, 1517, 469, 1463)),
1204 translate(319, 263, quadTo(522, 1410, 522, 1334)),
1205 translate(319, 263, quadTo(522, 1257, 469, 1204)),
1206 translate(319, 263, quadTo(416, 1151, 343, 1151)),
1207 translate(319, 263, quadTo(268, 1151, 215, 1204)),
1208 translate(319, 263, quadTo(162, 1258, 162, 1338)),
1209 // - contour #3
1210 translate(319, 263, moveTo(238, 1337)),
1211 translate(319, 263, quadTo(238, 1290, 269, 1258)),
1212 translate(319, 263, quadTo(301, 1226, 344, 1226)),
1213 translate(319, 263, quadTo(387, 1226, 418, 1258)),
1214 translate(319, 263, quadTo(450, 1290, 450, 1335)),
1215 translate(319, 263, quadTo(450, 1380, 419, 1412)),
1216 translate(319, 263, quadTo(388, 1444, 344, 1444)),
1217 translate(319, 263, quadTo(301, 1444, 269, 1412)),
1218 translate(319, 263, quadTo(238, 1381, 238, 1337)),
1219 // - contour #4
1220 translate(339, 650, moveTo(222, 1194)),
1221 translate(339, 650, lineTo(355, 1474)),
1222 translate(339, 650, lineTo(591, 1474)),
1223 translate(339, 650, lineTo(371, 1194)),
1224 translate(339, 650, lineTo(222, 1194)),
1225 },
1226
1227 '﴾': { // U+FD3E ORNATE LEFT PARENTHESIS.
1228 // - contour #0
1229 moveTo(560, -384),
1230 lineTo(516, -429),
1231 quadTo(412, -304, 361, -226),
1232 quadTo(258, -68, 201, 106),
1233 quadTo(127, 334, 127, 595),
1234 quadTo(127, 845, 201, 1069),
1235 quadTo(259, 1246, 361, 1404),
1236 quadTo(414, 1487, 514, 1608),
1237 lineTo(560, 1566),
1238 quadTo(452, 1328, 396, 1094),
1239 quadTo(336, 845, 336, 603),
1240 quadTo(336, 359, 370, 165),
1241 quadTo(398, 8, 454, -142),
1242 quadTo(482, -217, 560, -384),
1243 },
1244
1245 '﴿': { // U+FD3F ORNATE RIGHT PARENTHESIS
1246 // - contour #0
1247 transform(-1<<14, 0, 0, +1<<14, 653, 0, moveTo(560, -384)),
1248 transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(516, -429)),
1249 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(412, -304, 361, -226)),
1250 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(258, -68, 201, 106)),
1251 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 334, 127, 595)),
1252 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 845, 201, 1069)),
1253 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(259, 1246, 361, 1404)),
1254 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(414, 1487, 514, 1608)),
1255 transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(560, 1566)),
1256 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(452, 1328, 396, 1094)),
1257 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 845, 336, 603)),
1258 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 359, 370, 165)),
1259 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(398, 8, 454, -142)),
1260 transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(482, -217, 560, -384)),
1261 },
1262 },
1263
1264 "noto/NotoSans-Regular.ttf": {
1265 'i': {
1266 // - contour #0
1267 moveTo(354, 0),
1268 lineTo(174, 0),
1269 lineTo(174, 1098),
1270 lineTo(354, 1098),
1271 lineTo(354, 0),
1272 // - contour #1
1273 moveTo(160, 1395),
1274 quadTo(160, 1455, 190, 1482),
1275 quadTo(221, 1509, 266, 1509),
1276 quadTo(308, 1509, 339, 1482),
1277 quadTo(371, 1455, 371, 1395),
1278 quadTo(371, 1336, 339, 1308),
1279 quadTo(308, 1280, 266, 1280),
1280 quadTo(221, 1280, 190, 1308),
1281 quadTo(160, 1336, 160, 1395),
1282 },
1283 },
1284 }
1285
1286 type boundsTestCase struct {
1287 wantBounds fixed.Rectangle26_6
1288 wantAdv fixed.Int26_6
1289 }
1290
1291 // proprietaryGlyphBoundsTestCases hold expected GlyphBounds. The
1292 // numerical values can be verified by running the ttx tool.
1293 // - Advance from hmtx width
1294 // - Bounds from TTGlyph (with flipped Y axis)
1295 var proprietaryGlyphBoundsTestCases = map[string]map[rune]boundsTestCase{
1296 "adobe/SourceHanSansSC-Regular.otf": {
1297 '!': {
1298 wantBounds: fixed.Rectangle26_6{
1299 Min: fixed.Point26_6{X: 95, Y: -749},
1300 Max: fixed.Point26_6{X: 227, Y: 13},
1301 },
1302 wantAdv: 323,
1303 },
1304 },
1305 "apple/Helvetica.dfont?0": {
1306 'i': {
1307 wantBounds: fixed.Rectangle26_6{
1308 Min: fixed.Point26_6{X: 132, Y: -1469},
1309 Max: fixed.Point26_6{X: 315, Y: 0},
1310 },
1311 wantAdv: 455,
1312 },
1313 },
1314 "microsoft/Arial.ttf": {
1315 'A': {
1316 wantBounds: fixed.Rectangle26_6{
1317 Min: fixed.Point26_6{X: -3, Y: -1466},
1318 Max: fixed.Point26_6{X: 1369, Y: 0},
1319 },
1320 wantAdv: 1366,
1321 },
1322 // U+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE is a
1323 // compound glyph whose elements are also compound glyphs.
1324 'Ǻ': {
1325 wantBounds: fixed.Rectangle26_6{
1326 Min: fixed.Point26_6{X: -3, Y: -2124},
1327 Max: fixed.Point26_6{X: 1369, Y: 0},
1328 },
1329 wantAdv: 1366,
1330 },
1331 // U+FD3E ORNATE LEFT PARENTHESIS.
1332 '﴾': {
1333 wantBounds: fixed.Rectangle26_6{
1334 Min: fixed.Point26_6{X: 127, Y: -1608},
1335 Max: fixed.Point26_6{X: 560, Y: 429},
1336 },
1337 wantAdv: 653,
1338 },
1339 // U+FD3F ORNATE RIGHT PARENTHESIS is a transformed version of left parenthesis
1340 '﴿': {
1341 wantBounds: fixed.Rectangle26_6{
1342 Min: fixed.Point26_6{X: 93, Y: -1608},
1343 Max: fixed.Point26_6{X: 526, Y: 429},
1344 },
1345 wantAdv: 653,
1346 },
1347 },
1348 "noto/NotoSans-Regular.ttf": {
1349 'i': {
1350 wantBounds: fixed.Rectangle26_6{
1351 Min: fixed.Point26_6{X: 160, Y: -1509},
1352 Max: fixed.Point26_6{X: 371, Y: 0},
1353 },
1354 wantAdv: 528,
1355 },
1356 },
1357 }
1358
1359 type kernTestCase struct {
1360 ppem fixed.Int26_6
1361 hinting font.Hinting
1362 runes [2]rune
1363 want Units
1364 }
1365
1366 // proprietaryKernTestCases hold a sample of each font's kerning pairs. The
1367 // numerical values can be verified by running the ttx tool.
1368 var proprietaryKernTestCases = map[string][]kernTestCase{
1369 "adobe/SourceSansPro-Regular.otf": {
1370 {1000, font.HintingNone, [2]rune{'A', 'V'}, -14},
1371 {1000, font.HintingNone, [2]rune{'F', '\u0129'}, 36},
1372 },
1373 "adobe/SourceHanSansSC-Regular.otf": {
1374 // U+3043 HIRAGANA LETTER SMALL I
1375 // U+3067 HIRAGANA LETTER DE
1376 {1000, font.HintingNone, [2]rune{'\u3043', '\u3067'}, -20},
1377 },
1378 "dejavu/DejaVuSans-ExtraLight.ttf": {
1379 {2048, font.HintingNone, [2]rune{'A', 'A'}, 57},
1380 {2048, font.HintingNone, [2]rune{'W', 'A'}, -112},
1381 // U+00C1 LATIN CAPITAL LETTER A WITH ACUTE
1382 // U+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
1383 // U+1E82 LATIN CAPITAL LETTER W WITH ACUTE
1384 {2048, font.HintingNone, [2]rune{'\u00c1', 'A'}, 57},
1385 {2048, font.HintingNone, [2]rune{'\u01fa', 'A'}, 57},
1386 {2048, font.HintingNone, [2]rune{'\u1e82', 'A'}, -112},
1387 },
1388 "dejavu/DejaVuSans.ttf": {
1389 // U+EF13 + U+EF19 are private use codes, but DejaVuSans has these
1390 // codes in a rarely used ClassDef format 1
1391 {2048, font.HintingNone, [2]rune{'\uef13', '\uef19'}, -40},
1392 {2048, font.HintingNone, [2]rune{'\uef13', '\uef13'}, 0},
1393 // Use U+EF13 to trigger default class in ClassDef format 2
1394 {2048, font.HintingNone, [2]rune{'A', '\uef13'}, 0},
1395 },
1396 "microsoft/Arial.ttf": {
1397 {2048, font.HintingNone, [2]rune{'A', 'V'}, -152},
1398 // U+03B8 GREEK SMALL LETTER THETA
1399 // U+03BB GREEK SMALL LETTER LAMDA
1400 {2048, font.HintingNone, [2]rune{'\u03b8', '\u03bb'}, -39},
1401 {2048, font.HintingNone, [2]rune{'\u03bb', '\u03b8'}, -0},
1402 },
1403 "microsoft/Comic_Sans_MS.ttf": {
1404 {2048, font.HintingNone, [2]rune{'A', 'V'}, 0},
1405 },
1406 "microsoft/Times_New_Roman.ttf": {
1407 {768, font.HintingNone, [2]rune{'A', 'V'}, -99},
1408 {768, font.HintingFull, [2]rune{'A', 'V'}, -128},
1409 {2048, font.HintingNone, [2]rune{'A', 'A'}, 0},
1410 {2048, font.HintingNone, [2]rune{'A', 'T'}, -227},
1411 {2048, font.HintingNone, [2]rune{'A', 'V'}, -264},
1412 {2048, font.HintingNone, [2]rune{'T', 'A'}, -164},
1413 {2048, font.HintingNone, [2]rune{'T', 'T'}, 0},
1414 {2048, font.HintingNone, [2]rune{'T', 'V'}, 0},
1415 {2048, font.HintingNone, [2]rune{'V', 'A'}, -264},
1416 {2048, font.HintingNone, [2]rune{'V', 'T'}, 0},
1417 {2048, font.HintingNone, [2]rune{'V', 'V'}, 0},
1418 // U+0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
1419 // U+0393 GREEK CAPITAL LETTER GAMMA
1420 {2048, font.HintingNone, [2]rune{'\u0390', '\u0393'}, 0},
1421 {2048, font.HintingNone, [2]rune{'\u0393', '\u0390'}, 76},
1422 },
1423 "microsoft/Webdings.ttf": {
1424 {2048, font.HintingNone, [2]rune{'\uf041', '\uf042'}, 0},
1425 },
1426 "noto/NotoSans-Regular.ttf": {
1427 {2048, font.HintingNone, [2]rune{'A', 'V'}, -40},
1428 {2048, font.HintingNone, [2]rune{'A', 'W'}, -40},
1429 {2048, font.HintingNone, [2]rune{'A', 'T'}, -70},
1430 {2048, font.HintingNone, [2]rune{'V', 'A'}, -40},
1431 {2048, font.HintingNone, [2]rune{'W', 'A'}, -40},
1432 {2048, font.HintingNone, [2]rune{'T', 'A'}, -70},
1433 // U+00C1 LATIN CAPITAL LETTER A WITH ACUTE
1434 // U+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
1435 // U+1E82 LATIN CAPITAL LETTER W WITH ACUTE
1436 {2048, font.HintingNone, [2]rune{'\u00c1', 'T'}, -70},
1437 {2048, font.HintingNone, [2]rune{'\u01fa', 'T'}, -70},
1438 {2048, font.HintingNone, [2]rune{'\u1e82', 'A'}, -40},
1439
1440 // NotoSans-Regular.ttf uses two PairPos format=1 tables (with ExtensionPos).
1441 // Test first and last pairs in both tables.
1442 // First pair in first table
1443 {2048, font.HintingNone, [2]rune{'F', '?'}, 20},
1444 // U+04c3 CYRILLIC CAPITAL LETTER KA WITH HOOK
1445 // U+0424 CYRILLIC CAPITAL LETTER EF
1446 // Last pair in first table
1447 {2048, font.HintingNone, [2]rune{'\u04c3', '\u0424'}, -20},
1448 // First pair in second table
1449 {2048, font.HintingNone, [2]rune{'"', 'A'}, -70},
1450 // <!-- GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA -->
1451 // <!-- GREEK UPSILON WITH HOOK SYMBOL -->
1452 // Last pair in second table
1453 {2048, font.HintingNone, [2]rune{'\u1f4d', '\u03d2'}, -10},
1454 },
1455 }
1456
1457 // proprietaryFDSelectTestCases hold a sample of each font's Font Dict Select
1458 // (FDSelect) map. The numerical values can be verified by grepping the output
1459 // of the ttx tool:
1460 //
1461 // grep CharString.*fdSelectIndex SourceHanSansSC-Regular.ttx
1462 //
1463 // will print lines like this:
1464 //
1465 // <CharString name="cid00100" fdSelectIndex="15">
1466 // <CharString name="cid00101" fdSelectIndex="15">
1467 // <CharString name="cid00102" fdSelectIndex="3">
1468 // <CharString name="cid00103" fdSelectIndex="15">
1469 //
1470 // As for what the values like 3 or 15 actually mean, grepping that ttx file
1471 // for "FontName" gives this list:
1472 //
1473 // 0: <FontName value="SourceHanSansSC-Regular-Alphabetic"/>
1474 // 1: <FontName value="SourceHanSansSC-Regular-AlphabeticDigits"/>
1475 // 2: <FontName value="SourceHanSansSC-Regular-Bopomofo"/>
1476 // 3: <FontName value="SourceHanSansSC-Regular-Dingbats"/>
1477 // 4: <FontName value="SourceHanSansSC-Regular-DingbatsDigits"/>
1478 // 5: <FontName value="SourceHanSansSC-Regular-Generic"/>
1479 // 6: <FontName value="SourceHanSansSC-Regular-HDingbats"/>
1480 // 7: <FontName value="SourceHanSansSC-Regular-HHangul"/>
1481 // 8: <FontName value="SourceHanSansSC-Regular-HKana"/>
1482 // 9: <FontName value="SourceHanSansSC-Regular-HWidth"/>
1483 // 10: <FontName value="SourceHanSansSC-Regular-HWidthCJK"/>
1484 // 11: <FontName value="SourceHanSansSC-Regular-HWidthDigits"/>
1485 // 12: <FontName value="SourceHanSansSC-Regular-Hangul"/>
1486 // 13: <FontName value="SourceHanSansSC-Regular-Ideographs"/>
1487 // 14: <FontName value="SourceHanSansSC-Regular-Kana"/>
1488 // 15: <FontName value="SourceHanSansSC-Regular-Proportional"/>
1489 // 16: <FontName value="SourceHanSansSC-Regular-ProportionalCJK"/>
1490 // 17: <FontName value="SourceHanSansSC-Regular-ProportionalDigits"/>
1491 // 18: <FontName value="SourceHanSansSC-Regular-VKana"/>
1492 //
1493 // As a sanity check, the cmap table maps U+3127 BOPOMOFO LETTER I to the glyph
1494 // named "cid65353", proprietaryFDSelectTestCases here maps 65353 to Font Dict
1495 // 2, and the list immediately above maps 2 to "Bopomofo".
1496 var proprietaryFDSelectTestCases = map[string]map[GlyphIndex]int{
1497 "adobe/SourceHanSansSC-Regular.otf": {
1498 0: 5,
1499 1: 15,
1500 2: 15,
1501 16: 15,
1502 17: 17,
1503 26: 17,
1504 27: 15,
1505 100: 15,
1506 101: 15,
1507 102: 3,
1508 103: 15,
1509 777: 4,
1510 1000: 3,
1511 2000: 3,
1512 3000: 13,
1513 4000: 13,
1514 20000: 13,
1515 48000: 12,
1516 59007: 1,
1517 59024: 0,
1518 59087: 8,
1519 59200: 7,
1520 59211: 6,
1521 60000: 13,
1522 63000: 16,
1523 63039: 9,
1524 63060: 11,
1525 63137: 10,
1526 65353: 2,
1527 65486: 14,
1528 65505: 18,
1529 65506: 5,
1530 65533: 5,
1531 65534: 5,
1532 },
1533 }