git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

font/sfnt/sfnt_test.go (22.7K)

  1 // Copyright 2016 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 import (
  8 	"bytes"
  9 	"fmt"
 10 	"image"
 11 	"io/ioutil"
 12 	"path/filepath"
 13 	"testing"
 14 
 15 	"golang.org/x/image/font"
 16 	"golang.org/x/image/font/gofont/gobold"
 17 	"golang.org/x/image/font/gofont/gomono"
 18 	"golang.org/x/image/font/gofont/goregular"
 19 	"golang.org/x/image/math/fixed"
 20 )
 21 
 22 func pt(x, y fixed.Int26_6) fixed.Point26_6 {
 23 	return fixed.Point26_6{X: x, Y: y}
 24 }
 25 
 26 func moveTo(xa, ya fixed.Int26_6) Segment {
 27 	return Segment{
 28 		Op:   SegmentOpMoveTo,
 29 		Args: [3]fixed.Point26_6{pt(xa, ya)},
 30 	}
 31 }
 32 
 33 func lineTo(xa, ya fixed.Int26_6) Segment {
 34 	return Segment{
 35 		Op:   SegmentOpLineTo,
 36 		Args: [3]fixed.Point26_6{pt(xa, ya)},
 37 	}
 38 }
 39 
 40 func quadTo(xa, ya, xb, yb fixed.Int26_6) Segment {
 41 	return Segment{
 42 		Op:   SegmentOpQuadTo,
 43 		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb)},
 44 	}
 45 }
 46 
 47 func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment {
 48 	return Segment{
 49 		Op:   SegmentOpCubeTo,
 50 		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb), pt(xc, yc)},
 51 	}
 52 }
 53 
 54 func translate(dx, dy fixed.Int26_6, s Segment) Segment {
 55 	translateArgs(&s.Args, dx, dy)
 56 	return s
 57 }
 58 
 59 func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segment {
 60 	transformArgs(&s.Args, txx, txy, tyx, tyy, dx, dy)
 61 	return s
 62 }
 63 
 64 func checkSegmentsEqual(got, want []Segment) error {
 65 	// Flip got's Y axis. The test cases' coordinates are given with the Y axis
 66 	// increasing up, as that is what the ttx tool gives, and is the model for
 67 	// the underlying font format. The Go API returns coordinates with the Y
 68 	// axis increasing down, the same as the standard graphics libraries.
 69 	for i := range got {
 70 		for j := range got[i].Args {
 71 			got[i].Args[j].Y *= -1
 72 		}
 73 	}
 74 
 75 	if len(got) != len(want) {
 76 		return fmt.Errorf("got %d elements, want %d\noverall:\ngot  %v\nwant %v",
 77 			len(got), len(want), got, want)
 78 	}
 79 	for i, g := range got {
 80 		if w := want[i]; g != w {
 81 			return fmt.Errorf("element %d:\ngot  %v\nwant %v\noverall:\ngot  %v\nwant %v",
 82 				i, g, w, got, want)
 83 		}
 84 	}
 85 
 86 	// Check that every contour is closed.
 87 	if len(got) == 0 {
 88 		return nil
 89 	}
 90 	if got[0].Op != SegmentOpMoveTo {
 91 		return fmt.Errorf("segments do not start with a moveTo")
 92 	}
 93 	var (
 94 		first, last fixed.Point26_6
 95 		firstI      int
 96 	)
 97 	checkClosed := func(lastI int) error {
 98 		if first != last {
 99 			return fmt.Errorf("segments[%d:%d] not closed:\nfirst %v\nlast  %v", firstI, lastI, first, last)
100 		}
101 		return nil
102 	}
103 	for i, g := range got {
104 		switch g.Op {
105 		case SegmentOpMoveTo:
106 			if i != 0 {
107 				if err := checkClosed(i); err != nil {
108 					return err
109 				}
110 			}
111 			firstI, first, last = i, g.Args[0], g.Args[0]
112 		case SegmentOpLineTo:
113 			last = g.Args[0]
114 		case SegmentOpQuadTo:
115 			last = g.Args[1]
116 		case SegmentOpCubeTo:
117 			last = g.Args[2]
118 		}
119 	}
120 	return checkClosed(len(got))
121 }
122 
123 func TestTrueTypeParse(t *testing.T) {
124 	f, err := Parse(goregular.TTF)
125 	if err != nil {
126 		t.Fatalf("Parse: %v", err)
127 	}
128 	testTrueType(t, f, goregular.TTF)
129 }
130 
131 func TestTrueTypeParseReaderAt(t *testing.T) {
132 	f, err := ParseReaderAt(bytes.NewReader(goregular.TTF))
133 	if err != nil {
134 		t.Fatalf("ParseReaderAt: %v", err)
135 	}
136 	testTrueType(t, f, goregular.TTF)
137 }
138 
139 func testTrueType(t *testing.T, f *Font, wantSrc []byte) {
140 	if got, want := f.UnitsPerEm(), Units(2048); got != want {
141 		t.Errorf("UnitsPerEm: got %d, want %d", got, want)
142 	}
143 	// The exact number of glyphs in goregular.TTF can vary, and future
144 	// versions may add more glyphs, but https://blog.golang.org/go-fonts says
145 	// that "The WGL4 character set... [has] more than 650 characters in all.
146 	if got, want := f.NumGlyphs(), 650; got <= want {
147 		t.Errorf("NumGlyphs: got %d, want > %d", got, want)
148 	}
149 
150 	buf := &bytes.Buffer{}
151 	n, err := f.WriteSourceTo(nil, buf)
152 	if err != nil {
153 		t.Fatalf("WriteSourceTo: %v", err)
154 	}
155 
156 	// Some TTF tools pad their output so that the file sizes are multiples of
157 	// 4. Round up before comparison.
158 	got := (n + 3) &^ 3
159 	have := (int64(len(wantSrc)) + 3) &^ 3
160 
161 	if (n > int64(len(wantSrc))) || (got != have) {
162 		t.Fatalf("WriteSourceTo: got %d, want %d (with rounding)", n, len(wantSrc))
163 	} else if gotSrc := buf.Bytes(); !bytes.Equal(gotSrc, wantSrc[:n]) {
164 		t.Fatalf("WriteSourceTo: contents differ")
165 	}
166 }
167 
168 func fontData(name string) []byte {
169 	switch name {
170 	case "gobold":
171 		return gobold.TTF
172 	case "gomono":
173 		return gomono.TTF
174 	case "goregular":
175 		return goregular.TTF
176 	}
177 	panic("unreachable")
178 }
179 
180 func TestBounds(t *testing.T) {
181 	testCases := map[string]fixed.Rectangle26_6{
182 		"gobold": {
183 			Min: fixed.Point26_6{
184 				X: -452,
185 				Y: -2291,
186 			},
187 			Max: fixed.Point26_6{
188 				X: 2190,
189 				Y: 492,
190 			},
191 		},
192 		"gomono": {
193 			Min: fixed.Point26_6{
194 				X: 0,
195 				Y: -2291,
196 			},
197 			Max: fixed.Point26_6{
198 				X: 1229,
199 				Y: 432,
200 			},
201 		},
202 		"goregular": {
203 			Min: fixed.Point26_6{
204 				X: -440,
205 				Y: -2291,
206 			},
207 			Max: fixed.Point26_6{
208 				X: 2160,
209 				Y: 543,
210 			},
211 		},
212 	}
213 
214 	var b Buffer
215 	for name, want := range testCases {
216 		f, err := Parse(fontData(name))
217 		if err != nil {
218 			t.Errorf("Parse(%q): %v", name, err)
219 			continue
220 		}
221 		ppem := fixed.Int26_6(f.UnitsPerEm())
222 
223 		got, err := f.Bounds(&b, ppem, font.HintingNone)
224 		if err != nil {
225 			t.Errorf("name=%q: Bounds: %v", name, err)
226 			continue
227 		}
228 		if got != want {
229 			t.Errorf("name=%q: Bounds: got %v, want %v", name, got, want)
230 			continue
231 		}
232 	}
233 }
234 
235 func TestMetrics(t *testing.T) {
236 	cmapFont, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
237 	if err != nil {
238 		t.Fatal(err)
239 	}
240 	testCases := map[string]struct {
241 		font []byte
242 		want font.Metrics
243 	}{
244 		"goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432, XHeight: 1086, CapHeight: 1480,
245 			CaretSlope: image.Point{X: 0, Y: 1}}},
246 		// cmapTest.ttf has a non-zero lineGap.
247 		"cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0, XHeight: 800, CapHeight: 800,
248 			CaretSlope: image.Point{X: 20, Y: 100}}},
249 	}
250 	var b Buffer
251 	for name, tc := range testCases {
252 		f, err := Parse(tc.font)
253 		if err != nil {
254 			t.Errorf("name=%q: Parse: %v", name, err)
255 			continue
256 		}
257 		ppem := fixed.Int26_6(f.UnitsPerEm())
258 
259 		got, err := f.Metrics(&b, ppem, font.HintingNone)
260 		if err != nil {
261 			t.Errorf("name=%q: Metrics: %v", name, err)
262 			continue
263 		}
264 		if got != tc.want {
265 			t.Errorf("name=%q: Metrics: got %v, want %v", name, got, tc.want)
266 			continue
267 		}
268 	}
269 }
270 
271 func TestGlyphBounds(t *testing.T) {
272 	f, err := Parse(goregular.TTF)
273 	if err != nil {
274 		t.Fatalf("Parse: %v", err)
275 	}
276 	ppem := fixed.Int26_6(f.UnitsPerEm())
277 
278 	testCases := []struct {
279 		r          rune
280 		wantBounds fixed.Rectangle26_6
281 		wantAdv    fixed.Int26_6
282 	}{{
283 		r: ' ',
284 		wantBounds: fixed.Rectangle26_6{
285 			Min: fixed.Point26_6{X: 0, Y: 0},
286 			Max: fixed.Point26_6{X: 0, Y: 0},
287 		},
288 		wantAdv: 569,
289 	}, {
290 		r: 'A',
291 		wantBounds: fixed.Rectangle26_6{
292 			Min: fixed.Point26_6{X: 19, Y: -1480},
293 			Max: fixed.Point26_6{X: 1342, Y: 0},
294 		},
295 		wantAdv: 1366,
296 	}, {
297 		r: 'Á',
298 		wantBounds: fixed.Rectangle26_6{
299 			Min: fixed.Point26_6{X: 19, Y: -1935},
300 			Max: fixed.Point26_6{X: 1342, Y: 0},
301 		},
302 		wantAdv: 1366,
303 	}, {
304 		r: 'Æ',
305 		wantBounds: fixed.Rectangle26_6{
306 			Min: fixed.Point26_6{X: 19, Y: -1480},
307 			Max: fixed.Point26_6{X: 1990, Y: 0},
308 		},
309 		wantAdv: 2048,
310 	}, {
311 		r: 'i',
312 		wantBounds: fixed.Rectangle26_6{
313 			Min: fixed.Point26_6{X: 144, Y: -1500},
314 			Max: fixed.Point26_6{X: 361, Y: 0}},
315 		wantAdv: 505,
316 	}, {
317 		r: 'j',
318 		wantBounds: fixed.Rectangle26_6{
319 			Min: fixed.Point26_6{X: -84, Y: -1500},
320 			Max: fixed.Point26_6{X: 387, Y: 419},
321 		},
322 		wantAdv: 519,
323 	}, {
324 		r: 'x',
325 		wantBounds: fixed.Rectangle26_6{
326 			Min: fixed.Point26_6{X: 28, Y: -1086},
327 			Max: fixed.Point26_6{X: 993, Y: 0},
328 		},
329 		wantAdv: 1024,
330 	}}
331 
332 	var b Buffer
333 	for _, tc := range testCases {
334 		gi, err := f.GlyphIndex(&b, tc.r)
335 		if err != nil {
336 			t.Errorf("r=%q: %v", tc.r, err)
337 			continue
338 		}
339 
340 		gotBounds, gotAdv, err := f.GlyphBounds(&b, gi, ppem, font.HintingNone)
341 		if err != nil {
342 			t.Errorf("r=%q: GlyphBounds: %v", tc.r, err)
343 			continue
344 		}
345 		if gotBounds != tc.wantBounds {
346 			t.Errorf("r=%q: Bounds: got %#v, want %#v", tc.r, gotBounds, tc.wantBounds)
347 		}
348 		if gotAdv != tc.wantAdv {
349 			t.Errorf("r=%q: Adv: got %#v, want %#v", tc.r, gotAdv, tc.wantAdv)
350 		}
351 	}
352 }
353 
354 func TestGlyphAdvance(t *testing.T) {
355 	testCases := map[string][]struct {
356 		r    rune
357 		want fixed.Int26_6
358 	}{
359 		"gobold": {
360 			{' ', 569},
361 			{'A', 1479},
362 			{'Á', 1479},
363 			{'Æ', 2048},
364 			{'i', 592},
365 			{'x', 1139},
366 		},
367 		"gomono": {
368 			{' ', 1229},
369 			{'A', 1229},
370 			{'Á', 1229},
371 			{'Æ', 1229},
372 			{'i', 1229},
373 			{'x', 1229},
374 		},
375 		"goregular": {
376 			{' ', 569},
377 			{'A', 1366},
378 			{'Á', 1366},
379 			{'Æ', 2048},
380 			{'i', 505},
381 			{'x', 1024},
382 		},
383 	}
384 
385 	var b Buffer
386 	for name, testCases1 := range testCases {
387 		f, err := Parse(fontData(name))
388 		if err != nil {
389 			t.Errorf("Parse(%q): %v", name, err)
390 			continue
391 		}
392 		ppem := fixed.Int26_6(f.UnitsPerEm())
393 
394 		for _, tc := range testCases1 {
395 			x, err := f.GlyphIndex(&b, tc.r)
396 			if err != nil {
397 				t.Errorf("name=%q, r=%q: GlyphIndex: %v", name, tc.r, err)
398 				continue
399 			}
400 			got, err := f.GlyphAdvance(&b, x, ppem, font.HintingNone)
401 			if err != nil {
402 				t.Errorf("name=%q, r=%q: GlyphAdvance: %v", name, tc.r, err)
403 				continue
404 			}
405 			if got != tc.want {
406 				t.Errorf("name=%q, r=%q: GlyphAdvance: got %d, want %d", name, tc.r, got, tc.want)
407 				continue
408 			}
409 		}
410 	}
411 }
412 
413 func TestGoRegularGlyphIndex(t *testing.T) {
414 	f, err := Parse(goregular.TTF)
415 	if err != nil {
416 		t.Fatalf("Parse: %v", err)
417 	}
418 
419 	testCases := []struct {
420 		r    rune
421 		want GlyphIndex
422 	}{
423 		// Glyphs that aren't present in Go Regular.
424 		{'\u001f', 0}, // U+001F <control>
425 		{'\u0200', 0}, // U+0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
426 		{'\u2000', 0}, // U+2000 EN QUAD
427 
428 		// The want values below can be verified by running the ttx tool on
429 		// Go-Regular.ttf.
430 		//
431 		// The actual values are ad hoc, and result from whatever tools the
432 		// Bigelow & Holmes type foundry used, the order in which they crafted
433 		// the glyphs and post-processing tools such as
434 		// (https://github.com/nigeltao/fontscripts/tree/master/cmd/ttfreindex).
435 		// They may change over time as newer versions of the font are
436 		// released.
437 
438 		{'\u0020', 3},  // U+0020 SPACE
439 		{'\u0021', 4},  // U+0021 EXCLAMATION MARK
440 		{'\u0022', 5},  // U+0022 QUOTATION MARK
441 		{'\u0023', 6},  // U+0023 NUMBER SIGN
442 		{'\u0024', 7},  // U+0024 DOLLAR SIGN
443 		{'\u0025', 8},  // U+0025 PERCENT SIGN
444 		{'\u0026', 9},  // U+0026 AMPERSAND
445 		{'\u0027', 10}, // U+0027 APOSTROPHE
446 
447 		{'\u03bd', 413}, // U+03BD GREEK SMALL LETTER NU
448 		{'\u03be', 414}, // U+03BE GREEK SMALL LETTER XI
449 		{'\u03bf', 415}, // U+03BF GREEK SMALL LETTER OMICRON
450 		{'\u03c0', 416}, // U+03C0 GREEK SMALL LETTER PI
451 		{'\u03c1', 417}, // U+03C1 GREEK SMALL LETTER RHO
452 		{'\u03c2', 418}, // U+03C2 GREEK SMALL LETTER FINAL SIGMA
453 	}
454 
455 	var b Buffer
456 	for _, tc := range testCases {
457 		got, err := f.GlyphIndex(&b, tc.r)
458 		if err != nil {
459 			t.Errorf("r=%q: %v", tc.r, err)
460 			continue
461 		}
462 		if got != tc.want {
463 			t.Errorf("r=%q: got %d, want %d", tc.r, got, tc.want)
464 			continue
465 		}
466 	}
467 }
468 
469 func TestGlyphIndex(t *testing.T) {
470 	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
471 	if err != nil {
472 		t.Fatal(err)
473 	}
474 
475 	for _, format := range []int{-1, 0, 4, 12} {
476 		testGlyphIndex(t, data, format)
477 	}
478 }
479 
480 func testGlyphIndex(t *testing.T, data []byte, cmapFormat int) {
481 	if cmapFormat >= 0 {
482 		originalSupportedCmapFormat := supportedCmapFormat
483 		defer func() {
484 			supportedCmapFormat = originalSupportedCmapFormat
485 		}()
486 		supportedCmapFormat = func(format, pid, psid uint16) bool {
487 			return int(format) == cmapFormat && originalSupportedCmapFormat(format, pid, psid)
488 		}
489 	}
490 
491 	f, err := Parse(data)
492 	if err != nil {
493 		t.Errorf("cmapFormat=%d: %v", cmapFormat, err)
494 		return
495 	}
496 
497 	testCases := []struct {
498 		r    rune
499 		want GlyphIndex
500 	}{
501 		// Glyphs that aren't present in cmapTest.ttf.
502 		{'?', 0},
503 		{'\ufffd', 0},
504 		{'\U0001f4a9', 0},
505 
506 		// For a .TTF file, FontForge maps:
507 		//	- ".notdef"          to glyph index 0.
508 		//	- ".null"            to glyph index 1.
509 		//	- "nonmarkingreturn" to glyph index 2.
510 
511 		{'/', 0},
512 		{'0', 3},
513 		{'1', 4},
514 		{'2', 5},
515 		{'3', 0},
516 
517 		{'@', 0},
518 		{'A', 6},
519 		{'B', 7},
520 		{'C', 0},
521 
522 		{'`', 0},
523 		{'a', 8},
524 		{'b', 0},
525 
526 		// Of the remaining runes, only U+00FF LATIN SMALL LETTER Y WITH
527 		// DIAERESIS is in both the Mac Roman encoding and the cmapTest.ttf
528 		// font file.
529 		{'\u00fe', 0},
530 		{'\u00ff', 9},
531 		{'\u0100', 10},
532 		{'\u0101', 11},
533 		{'\u0102', 0},
534 
535 		{'\u4e2c', 0},
536 		{'\u4e2d', 12},
537 		{'\u4e2e', 0},
538 
539 		{'\U0001f0a0', 0},
540 		{'\U0001f0a1', 13},
541 		{'\U0001f0a2', 0},
542 
543 		{'\U0001f0b0', 0},
544 		{'\U0001f0b1', 14},
545 		{'\U0001f0b2', 15},
546 		{'\U0001f0b3', 0},
547 	}
548 
549 	var b Buffer
550 	for _, tc := range testCases {
551 		want := tc.want
552 		switch {
553 		case cmapFormat == 0 && tc.r > '\u007f' && tc.r != '\u00ff':
554 			// cmap format 0, with the Macintosh Roman encoding, can only
555 			// represent a limited set of non-ASCII runes, e.g. U+00FF.
556 			want = 0
557 		case cmapFormat == 4 && tc.r > '\uffff':
558 			// cmap format 4 only supports the Basic Multilingual Plane (BMP).
559 			want = 0
560 		}
561 
562 		got, err := f.GlyphIndex(&b, tc.r)
563 		if err != nil {
564 			t.Errorf("cmapFormat=%d, r=%q: %v", cmapFormat, tc.r, err)
565 			continue
566 		}
567 		if got != want {
568 			t.Errorf("cmapFormat=%d, r=%q: got %d, want %d", cmapFormat, tc.r, got, want)
569 			continue
570 		}
571 	}
572 }
573 
574 func TestPostScriptSegments(t *testing.T) {
575 	// wants' vectors correspond 1-to-1 to what's in the CFFTest.sfd file,
576 	// although OpenType/CFF and FontForge's SFD have reversed orders.
577 	// https://fontforge.github.io/validation.html says that "All paths must be
578 	// drawn in a consistent direction. Clockwise for external paths,
579 	// anti-clockwise for internal paths. (Actually PostScript requires the
580 	// exact opposite, but FontForge reverses PostScript contours when it loads
581 	// them so that everything is consistent internally -- and reverses them
582 	// again when it saves them, of course)."
583 	//
584 	// The .notdef glyph isn't explicitly in the SFD file, but for some unknown
585 	// reason, FontForge generates it in the OpenType/CFF file.
586 	wants := [][]Segment{{
587 		// .notdef
588 		// - contour #0
589 		moveTo(50, 0),
590 		lineTo(450, 0),
591 		lineTo(450, 533),
592 		lineTo(50, 533),
593 		lineTo(50, 0),
594 		// - contour #1
595 		moveTo(100, 50),
596 		lineTo(100, 483),
597 		lineTo(400, 483),
598 		lineTo(400, 50),
599 		lineTo(100, 50),
600 	}, {
601 		// zero
602 		// - contour #0
603 		moveTo(300, 700),
604 		cubeTo(380, 700, 420, 580, 420, 500),
605 		cubeTo(420, 350, 390, 100, 300, 100),
606 		cubeTo(220, 100, 180, 220, 180, 300),
607 		cubeTo(180, 450, 210, 700, 300, 700),
608 		// - contour #1
609 		moveTo(300, 800),
610 		cubeTo(200, 800, 100, 580, 100, 400),
611 		cubeTo(100, 220, 200, 0, 300, 0),
612 		cubeTo(400, 0, 500, 220, 500, 400),
613 		cubeTo(500, 580, 400, 800, 300, 800),
614 	}, {
615 		// one
616 		// - contour #0
617 		moveTo(100, 0),
618 		lineTo(300, 0),
619 		lineTo(300, 800),
620 		lineTo(100, 800),
621 		lineTo(100, 0),
622 	}, {
623 		// Q
624 		// - contour #0
625 		moveTo(657, 237),
626 		lineTo(289, 387),
627 		lineTo(519, 615),
628 		lineTo(657, 237),
629 		// - contour #1
630 		moveTo(792, 169),
631 		cubeTo(867, 263, 926, 502, 791, 665),
632 		cubeTo(645, 840, 380, 831, 228, 673),
633 		cubeTo(71, 509, 110, 231, 242, 93),
634 		cubeTo(369, -39, 641, 18, 722, 93),
635 		lineTo(802, 3),
636 		lineTo(864, 83),
637 		lineTo(792, 169),
638 	}, {
639 		// uni4E2D
640 		// - contour #0
641 		moveTo(141, 520),
642 		lineTo(137, 356),
643 		lineTo(245, 400),
644 		lineTo(331, 26),
645 		lineTo(355, 414),
646 		lineTo(463, 434),
647 		lineTo(453, 620),
648 		lineTo(341, 592),
649 		lineTo(331, 758),
650 		lineTo(243, 752),
651 		lineTo(235, 562),
652 		lineTo(141, 520),
653 	}}
654 
655 	testSegments(t, "CFFTest.otf", wants)
656 }
657 
658 func TestTrueTypeSegments(t *testing.T) {
659 	// wants' vectors correspond 1-to-1 to what's in the glyfTest.sfd file,
660 	// although FontForge's SFD format stores quadratic Bézier curves as cubics
661 	// with duplicated off-curve points. quadTo(bx, by, cx, cy) is stored as
662 	// "bx by bx by cx cy".
663 	//
664 	// The .notdef, .null and nonmarkingreturn glyphs aren't explicitly in the
665 	// SFD file, but for some unknown reason, FontForge generates them in the
666 	// TrueType file.
667 	wants := [][]Segment{{
668 		// .notdef
669 		// - contour #0
670 		moveTo(68, 0),
671 		lineTo(68, 1365),
672 		lineTo(612, 1365),
673 		lineTo(612, 0),
674 		lineTo(68, 0),
675 		// - contour #1
676 		moveTo(136, 68),
677 		lineTo(544, 68),
678 		lineTo(544, 1297),
679 		lineTo(136, 1297),
680 		lineTo(136, 68),
681 	}, {
682 		// .null
683 		// Empty glyph.
684 	}, {
685 		// nonmarkingreturn
686 		// Empty glyph.
687 	}, {
688 		// zero
689 		// - contour #0
690 		moveTo(614, 1434),
691 		quadTo(369, 1434, 369, 614),
692 		quadTo(369, 471, 435, 338),
693 		quadTo(502, 205, 614, 205),
694 		quadTo(860, 205, 860, 1024),
695 		quadTo(860, 1167, 793, 1300),
696 		quadTo(727, 1434, 614, 1434),
697 		// - contour #1
698 		moveTo(614, 1638),
699 		quadTo(1024, 1638, 1024, 819),
700 		quadTo(1024, 0, 614, 0),
701 		quadTo(205, 0, 205, 819),
702 		quadTo(205, 1638, 614, 1638),
703 	}, {
704 		// one
705 		// - contour #0
706 		moveTo(205, 0),
707 		lineTo(205, 1638),
708 		lineTo(614, 1638),
709 		lineTo(614, 0),
710 		lineTo(205, 0),
711 	}, {
712 		// five
713 		// - contour #0
714 		moveTo(0, 0),
715 		lineTo(0, 100),
716 		lineTo(400, 100),
717 		lineTo(400, 0),
718 		lineTo(0, 0),
719 	}, {
720 		// six
721 		// - contour #0
722 		moveTo(0, 0),
723 		lineTo(0, 100),
724 		lineTo(400, 100),
725 		lineTo(400, 0),
726 		lineTo(0, 0),
727 		// - contour #1
728 		translate(111, 234, moveTo(205, 0)),
729 		translate(111, 234, lineTo(205, 1638)),
730 		translate(111, 234, lineTo(614, 1638)),
731 		translate(111, 234, lineTo(614, 0)),
732 		translate(111, 234, lineTo(205, 0)),
733 	}, {
734 		// seven
735 		// - contour #0
736 		moveTo(0, 0),
737 		lineTo(0, 100),
738 		lineTo(400, 100),
739 		lineTo(400, 0),
740 		lineTo(0, 0),
741 		// - contour #1
742 		transform(1<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)),
743 		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)),
744 		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)),
745 		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)),
746 		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)),
747 	}, {
748 		// eight
749 		// - contour #0
750 		moveTo(0, 0),
751 		lineTo(0, 100),
752 		lineTo(400, 100),
753 		lineTo(400, 0),
754 		lineTo(0, 0),
755 		// - contour #1
756 		transform(3<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)),
757 		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)),
758 		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)),
759 		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)),
760 		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)),
761 	}, {
762 		// nine
763 		// - contour #0
764 		moveTo(0, 0),
765 		lineTo(0, 100),
766 		lineTo(400, 100),
767 		lineTo(400, 0),
768 		lineTo(0, 0),
769 		// - contour #1
770 		transform(22381, 8192, 5996, 14188, 237, 258, moveTo(205, 0)),
771 		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 1638)),
772 		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 1638)),
773 		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 0)),
774 		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 0)),
775 	}}
776 
777 	testSegments(t, "glyfTest.ttf", wants)
778 }
779 
780 func testSegments(t *testing.T, filename string, wants [][]Segment) {
781 	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/" + filename))
782 	if err != nil {
783 		t.Fatalf("ReadFile: %v", err)
784 	}
785 	f, err := Parse(data)
786 	if err != nil {
787 		t.Fatalf("Parse: %v", err)
788 	}
789 	ppem := fixed.Int26_6(f.UnitsPerEm())
790 
791 	if ng := f.NumGlyphs(); ng != len(wants) {
792 		t.Fatalf("NumGlyphs: got %d, want %d", ng, len(wants))
793 	}
794 	var b Buffer
795 	for i, want := range wants {
796 		got, err := f.LoadGlyph(&b, GlyphIndex(i), ppem, nil)
797 		if err != nil {
798 			t.Errorf("i=%d: LoadGlyph: %v", i, err)
799 			continue
800 		}
801 		if err := checkSegmentsEqual(got, want); err != nil {
802 			t.Errorf("i=%d: %v", i, err)
803 			continue
804 		}
805 	}
806 	if _, err := f.LoadGlyph(nil, 0xffff, ppem, nil); err != ErrNotFound {
807 		t.Errorf("LoadGlyph(..., 0xffff, ...):\ngot  %v\nwant %v", err, ErrNotFound)
808 	}
809 
810 	name, err := f.Name(nil, NameIDFamily)
811 	if err != nil {
812 		t.Errorf("Name: %v", err)
813 	} else if want := filename[:len(filename)-len(".ttf")]; name != want {
814 		t.Errorf("Name:\ngot  %q\nwant %q", name, want)
815 	}
816 }
817 
818 func TestPPEM(t *testing.T) {
819 	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/glyfTest.ttf"))
820 	if err != nil {
821 		t.Fatalf("ReadFile: %v", err)
822 	}
823 	f, err := Parse(data)
824 	if err != nil {
825 		t.Fatalf("Parse: %v", err)
826 	}
827 	var b Buffer
828 	x, err := f.GlyphIndex(&b, '1')
829 	if err != nil {
830 		t.Fatalf("GlyphIndex: %v", err)
831 	}
832 	if x == 0 {
833 		t.Fatalf("GlyphIndex: no glyph index found for the rune '1'")
834 	}
835 
836 	testCases := []struct {
837 		ppem fixed.Int26_6
838 		want []Segment
839 	}{{
840 		ppem: fixed.Int26_6(12 << 6),
841 		want: []Segment{
842 			moveTo(77, 0),
843 			lineTo(77, 614),
844 			lineTo(230, 614),
845 			lineTo(230, 0),
846 			lineTo(77, 0),
847 		},
848 	}, {
849 		ppem: fixed.Int26_6(2048),
850 		want: []Segment{
851 			moveTo(205, 0),
852 			lineTo(205, 1638),
853 			lineTo(614, 1638),
854 			lineTo(614, 0),
855 			lineTo(205, 0),
856 		},
857 	}}
858 
859 	for i, tc := range testCases {
860 		got, err := f.LoadGlyph(&b, x, tc.ppem, nil)
861 		if err != nil {
862 			t.Errorf("i=%d: LoadGlyph: %v", i, err)
863 			continue
864 		}
865 		if err := checkSegmentsEqual(got, tc.want); err != nil {
866 			t.Errorf("i=%d: %v", i, err)
867 			continue
868 		}
869 	}
870 }
871 
872 func TestPostInfo(t *testing.T) {
873 	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/glyfTest.ttf"))
874 	if err != nil {
875 		t.Fatalf("ReadFile: %v", err)
876 	}
877 	f, err := Parse(data)
878 	if err != nil {
879 		t.Fatalf("Parse: %v", err)
880 	}
881 	post := f.PostTable()
882 	if post.ItalicAngle != -11.25 {
883 		t.Error("ItalicAngle:", post.ItalicAngle)
884 	}
885 	if post.UnderlinePosition != -255 {
886 		t.Error("UnderlinePosition:", post.UnderlinePosition)
887 	}
888 	if post.UnderlineThickness != 102 {
889 		t.Error("UnderlineThickness:", post.UnderlineThickness)
890 	}
891 	if post.IsFixedPitch {
892 		t.Error("IsFixedPitch:", post.IsFixedPitch)
893 	}
894 }
895 
896 func TestGlyphName(t *testing.T) {
897 	f, err := Parse(goregular.TTF)
898 	if err != nil {
899 		t.Fatalf("Parse: %v", err)
900 	}
901 
902 	testCases := []struct {
903 		r    rune
904 		want string
905 	}{
906 		{'\x00', "uni0000"},
907 		{'!', "exclam"},
908 		{'A', "A"},
909 		{'{', "braceleft"},
910 		{'\u00c4', "Adieresis"}, // U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
911 		{'\u2020', "dagger"},    // U+2020 DAGGER
912 		{'\u2660', "spade"},     // U+2660 BLACK SPADE SUIT
913 		{'\uf800', "gopher"},    // U+F800 <Private Use>
914 		{'\ufffe', ".notdef"},   // Not in the Go Regular font, so GlyphIndex returns (0, nil).
915 	}
916 
917 	var b Buffer
918 	for _, tc := range testCases {
919 		x, err := f.GlyphIndex(&b, tc.r)
920 		if err != nil {
921 			t.Errorf("r=%q: GlyphIndex: %v", tc.r, err)
922 			continue
923 		}
924 		got, err := f.GlyphName(&b, x)
925 		if err != nil {
926 			t.Errorf("r=%q: GlyphName: %v", tc.r, err)
927 			continue
928 		}
929 		if got != tc.want {
930 			t.Errorf("r=%q: got %q, want %q", tc.r, got, tc.want)
931 			continue
932 		}
933 	}
934 }
935 
936 func TestBuiltInPostNames(t *testing.T) {
937 	testCases := []struct {
938 		x    GlyphIndex
939 		want string
940 	}{
941 		{0, ".notdef"},
942 		{1, ".null"},
943 		{2, "nonmarkingreturn"},
944 		{13, "asterisk"},
945 		{36, "A"},
946 		{93, "z"},
947 		{123, "ocircumflex"},
948 		{202, "Edieresis"},
949 		{255, "Ccaron"},
950 		{256, "ccaron"},
951 		{257, "dcroat"},
952 		{258, ""},
953 		{999, ""},
954 		{0xffff, ""},
955 	}
956 
957 	for _, tc := range testCases {
958 		if tc.x >= numBuiltInPostNames {
959 			continue
960 		}
961 		i := builtInPostNamesOffsets[tc.x+0]
962 		j := builtInPostNamesOffsets[tc.x+1]
963 		got := builtInPostNamesData[i:j]
964 		if got != tc.want {
965 			t.Errorf("x=%d: got %q, want %q", tc.x, got, tc.want)
966 		}
967 	}
968 }