// Copyright 2016 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

import (
	"bytes"
	"fmt"
	"image"
	"io/ioutil"
	"path/filepath"
	"testing"

	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gobold"
	"golang.org/x/image/font/gofont/gomono"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/math/fixed"
)

func pt(x, y fixed.Int26_6) fixed.Point26_6 {
	return fixed.Point26_6{X: x, Y: y}
}

func moveTo(xa, ya fixed.Int26_6) Segment {
	return Segment{
		Op:   SegmentOpMoveTo,
		Args: [3]fixed.Point26_6{pt(xa, ya)},
	}
}

func lineTo(xa, ya fixed.Int26_6) Segment {
	return Segment{
		Op:   SegmentOpLineTo,
		Args: [3]fixed.Point26_6{pt(xa, ya)},
	}
}

func quadTo(xa, ya, xb, yb fixed.Int26_6) Segment {
	return Segment{
		Op:   SegmentOpQuadTo,
		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb)},
	}
}

func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment {
	return Segment{
		Op:   SegmentOpCubeTo,
		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb), pt(xc, yc)},
	}
}

func translate(dx, dy fixed.Int26_6, s Segment) Segment {
	translateArgs(&s.Args, dx, dy)
	return s
}

func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segment {
	transformArgs(&s.Args, txx, txy, tyx, tyy, dx, dy)
	return s
}

func checkSegmentsEqual(got, want []Segment) error {
	// Flip got's Y axis. The test cases' coordinates are given with the Y axis
	// increasing up, as that is what the ttx tool gives, and is the model for
	// the underlying font format. The Go API returns coordinates with the Y
	// axis increasing down, the same as the standard graphics libraries.
	for i := range got {
		for j := range got[i].Args {
			got[i].Args[j].Y *= -1
		}
	}

	if len(got) != len(want) {
		return fmt.Errorf("got %d elements, want %d\noverall:\ngot  %v\nwant %v",
			len(got), len(want), got, want)
	}
	for i, g := range got {
		if w := want[i]; g != w {
			return fmt.Errorf("element %d:\ngot  %v\nwant %v\noverall:\ngot  %v\nwant %v",
				i, g, w, got, want)
		}
	}

	// Check that every contour is closed.
	if len(got) == 0 {
		return nil
	}
	if got[0].Op != SegmentOpMoveTo {
		return fmt.Errorf("segments do not start with a moveTo")
	}
	var (
		first, last fixed.Point26_6
		firstI      int
	)
	checkClosed := func(lastI int) error {
		if first != last {
			return fmt.Errorf("segments[%d:%d] not closed:\nfirst %v\nlast  %v", firstI, lastI, first, last)
		}
		return nil
	}
	for i, g := range got {
		switch g.Op {
		case SegmentOpMoveTo:
			if i != 0 {
				if err := checkClosed(i); err != nil {
					return err
				}
			}
			firstI, first, last = i, g.Args[0], g.Args[0]
		case SegmentOpLineTo:
			last = g.Args[0]
		case SegmentOpQuadTo:
			last = g.Args[1]
		case SegmentOpCubeTo:
			last = g.Args[2]
		}
	}
	return checkClosed(len(got))
}

func TestTrueTypeParse(t *testing.T) {
	f, err := Parse(goregular.TTF)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	testTrueType(t, f, goregular.TTF)
}

func TestTrueTypeParseReaderAt(t *testing.T) {
	f, err := ParseReaderAt(bytes.NewReader(goregular.TTF))
	if err != nil {
		t.Fatalf("ParseReaderAt: %v", err)
	}
	testTrueType(t, f, goregular.TTF)
}

func testTrueType(t *testing.T, f *Font, wantSrc []byte) {
	if got, want := f.UnitsPerEm(), Units(2048); got != want {
		t.Errorf("UnitsPerEm: got %d, want %d", got, want)
	}
	// The exact number of glyphs in goregular.TTF can vary, and future
	// versions may add more glyphs, but https://blog.golang.org/go-fonts says
	// that "The WGL4 character set... [has] more than 650 characters in all.
	if got, want := f.NumGlyphs(), 650; got <= want {
		t.Errorf("NumGlyphs: got %d, want > %d", got, want)
	}
	buf := &bytes.Buffer{}
	if n, err := f.WriteSourceTo(nil, buf); err != nil {
		t.Fatalf("WriteSourceTo: %v", err)
	} else if n != int64(len(wantSrc)) {
		t.Fatalf("WriteSourceTo: got %d, want %d", n, len(wantSrc))
	} else if gotSrc := buf.Bytes(); !bytes.Equal(gotSrc, wantSrc) {
		t.Fatalf("WriteSourceTo: contents differ")
	}
}

func fontData(name string) []byte {
	switch name {
	case "gobold":
		return gobold.TTF
	case "gomono":
		return gomono.TTF
	case "goregular":
		return goregular.TTF
	}
	panic("unreachable")
}

func TestBounds(t *testing.T) {
	testCases := map[string]fixed.Rectangle26_6{
		"gobold": {
			Min: fixed.Point26_6{
				X: -452,
				Y: -2193,
			},
			Max: fixed.Point26_6{
				X: 2190,
				Y: 432,
			},
		},
		"gomono": {
			Min: fixed.Point26_6{
				X: 0,
				Y: -2227,
			},
			Max: fixed.Point26_6{
				X: 1229,
				Y: 432,
			},
		},
		"goregular": {
			Min: fixed.Point26_6{
				X: -440,
				Y: -2118,
			},
			Max: fixed.Point26_6{
				X: 2160,
				Y: 543,
			},
		},
	}

	var b Buffer
	for name, want := range testCases {
		f, err := Parse(fontData(name))
		if err != nil {
			t.Errorf("Parse(%q): %v", name, err)
			continue
		}
		ppem := fixed.Int26_6(f.UnitsPerEm())

		got, err := f.Bounds(&b, ppem, font.HintingNone)
		if err != nil {
			t.Errorf("name=%q: Bounds: %v", name, err)
			continue
		}
		if got != want {
			t.Errorf("name=%q: Bounds: got %v, want %v", name, got, want)
			continue
		}
	}
}

func TestMetrics(t *testing.T) {
	cmapFont, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
	if err != nil {
		t.Fatal(err)
	}
	testCases := map[string]struct {
		font []byte
		want font.Metrics
	}{
		"goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432, XHeight: 1086, CapHeight: 1480,
			CaretSlope: image.Point{X: 0, Y: 1}}},
		// cmapTest.ttf has a non-zero lineGap.
		"cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0, XHeight: 800, CapHeight: 800,
			CaretSlope: image.Point{X: 20, Y: 100}}},
	}
	var b Buffer
	for name, tc := range testCases {
		f, err := Parse(tc.font)
		if err != nil {
			t.Errorf("name=%q: Parse: %v", name, err)
			continue
		}
		ppem := fixed.Int26_6(f.UnitsPerEm())

		got, err := f.Metrics(&b, ppem, font.HintingNone)
		if err != nil {
			t.Errorf("name=%q: Metrics: %v", name, err)
			continue
		}
		if got != tc.want {
			t.Errorf("name=%q: Metrics: got %v, want %v", name, got, tc.want)
			continue
		}
	}
}

func TestGlyphBounds(t *testing.T) {
	f, err := Parse(goregular.TTF)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	ppem := fixed.Int26_6(f.UnitsPerEm())

	testCases := []struct {
		r          rune
		wantBounds fixed.Rectangle26_6
		wantAdv    fixed.Int26_6
	}{{
		r: ' ',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 0, Y: 0},
			Max: fixed.Point26_6{X: 0, Y: 0},
		},
		wantAdv: 569,
	}, {
		r: 'A',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 19, Y: -1480},
			Max: fixed.Point26_6{X: 1342, Y: 0},
		},
		wantAdv: 1366,
	}, {
		r: 'Á',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 19, Y: -1935},
			Max: fixed.Point26_6{X: 1342, Y: 0},
		},
		wantAdv: 1366,
	}, {
		r: 'Æ',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 19, Y: -1480},
			Max: fixed.Point26_6{X: 1990, Y: 0},
		},
		wantAdv: 2048,
	}, {
		r: 'i',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 144, Y: -1500},
			Max: fixed.Point26_6{X: 361, Y: 0}},
		wantAdv: 505,
	}, {
		r: 'j',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: -84, Y: -1500},
			Max: fixed.Point26_6{X: 387, Y: 419},
		},
		wantAdv: 519,
	}, {
		r: 'x',
		wantBounds: fixed.Rectangle26_6{
			Min: fixed.Point26_6{X: 28, Y: -1086},
			Max: fixed.Point26_6{X: 993, Y: 0},
		},
		wantAdv: 1024,
	}}

	var b Buffer
	for _, tc := range testCases {
		gi, err := f.GlyphIndex(&b, tc.r)
		if err != nil {
			t.Errorf("r=%q: %v", tc.r, err)
			continue
		}

		gotBounds, gotAdv, err := f.GlyphBounds(&b, gi, ppem, font.HintingNone)
		if err != nil {
			t.Errorf("r=%q: GlyphBounds: %v", tc.r, err)
			continue
		}
		if gotBounds != tc.wantBounds {
			t.Errorf("r=%q: Bounds: got %#v, want %#v", tc.r, gotBounds, tc.wantBounds)
		}
		if gotAdv != tc.wantAdv {
			t.Errorf("r=%q: Adv: got %#v, want %#v", tc.r, gotAdv, tc.wantAdv)
		}
	}
}

func TestGlyphAdvance(t *testing.T) {
	testCases := map[string][]struct {
		r    rune
		want fixed.Int26_6
	}{
		"gobold": {
			{' ', 569},
			{'A', 1479},
			{'Á', 1479},
			{'Æ', 2048},
			{'i', 592},
			{'x', 1139},
		},
		"gomono": {
			{' ', 1229},
			{'A', 1229},
			{'Á', 1229},
			{'Æ', 1229},
			{'i', 1229},
			{'x', 1229},
		},
		"goregular": {
			{' ', 569},
			{'A', 1366},
			{'Á', 1366},
			{'Æ', 2048},
			{'i', 505},
			{'x', 1024},
		},
	}

	var b Buffer
	for name, testCases1 := range testCases {
		f, err := Parse(fontData(name))
		if err != nil {
			t.Errorf("Parse(%q): %v", name, err)
			continue
		}
		ppem := fixed.Int26_6(f.UnitsPerEm())

		for _, tc := range testCases1 {
			x, err := f.GlyphIndex(&b, tc.r)
			if err != nil {
				t.Errorf("name=%q, r=%q: GlyphIndex: %v", name, tc.r, err)
				continue
			}
			got, err := f.GlyphAdvance(&b, x, ppem, font.HintingNone)
			if err != nil {
				t.Errorf("name=%q, r=%q: GlyphAdvance: %v", name, tc.r, err)
				continue
			}
			if got != tc.want {
				t.Errorf("name=%q, r=%q: GlyphAdvance: got %d, want %d", name, tc.r, got, tc.want)
				continue
			}
		}
	}
}

func TestGoRegularGlyphIndex(t *testing.T) {
	f, err := Parse(goregular.TTF)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}

	testCases := []struct {
		r    rune
		want GlyphIndex
	}{
		// Glyphs that aren't present in Go Regular.
		{'\u001f', 0}, // U+001F <control>
		{'\u0200', 0}, // U+0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
		{'\u2000', 0}, // U+2000 EN QUAD

		// The want values below can be verified by running the ttx tool on
		// Go-Regular.ttf.
		//
		// The actual values are ad hoc, and result from whatever tools the
		// Bigelow & Holmes type foundry used and the order in which they
		// crafted the glyphs. They may change over time as newer versions of
		// the font are released.

		{'\u0020', 3},  // U+0020 SPACE
		{'\u0021', 4},  // U+0021 EXCLAMATION MARK
		{'\u0022', 5},  // U+0022 QUOTATION MARK
		{'\u0023', 6},  // U+0023 NUMBER SIGN
		{'\u0024', 7},  // U+0024 DOLLAR SIGN
		{'\u0025', 8},  // U+0025 PERCENT SIGN
		{'\u0026', 9},  // U+0026 AMPERSAND
		{'\u0027', 10}, // U+0027 APOSTROPHE

		{'\u03bd', 396}, // U+03BD GREEK SMALL LETTER NU
		{'\u03be', 397}, // U+03BE GREEK SMALL LETTER XI
		{'\u03bf', 398}, // U+03BF GREEK SMALL LETTER OMICRON
		{'\u03c0', 399}, // U+03C0 GREEK SMALL LETTER PI
		{'\u03c1', 400}, // U+03C1 GREEK SMALL LETTER RHO
		{'\u03c2', 401}, // U+03C2 GREEK SMALL LETTER FINAL SIGMA
	}

	var b Buffer
	for _, tc := range testCases {
		got, err := f.GlyphIndex(&b, tc.r)
		if err != nil {
			t.Errorf("r=%q: %v", tc.r, err)
			continue
		}
		if got != tc.want {
			t.Errorf("r=%q: got %d, want %d", tc.r, got, tc.want)
			continue
		}
	}
}

func TestGlyphIndex(t *testing.T) {
	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
	if err != nil {
		t.Fatal(err)
	}

	for _, format := range []int{-1, 0, 4, 12} {
		testGlyphIndex(t, data, format)
	}
}

func testGlyphIndex(t *testing.T, data []byte, cmapFormat int) {
	if cmapFormat >= 0 {
		originalSupportedCmapFormat := supportedCmapFormat
		defer func() {
			supportedCmapFormat = originalSupportedCmapFormat
		}()
		supportedCmapFormat = func(format, pid, psid uint16) bool {
			return int(format) == cmapFormat && originalSupportedCmapFormat(format, pid, psid)
		}
	}

	f, err := Parse(data)
	if err != nil {
		t.Errorf("cmapFormat=%d: %v", cmapFormat, err)
		return
	}

	testCases := []struct {
		r    rune
		want GlyphIndex
	}{
		// Glyphs that aren't present in cmapTest.ttf.
		{'?', 0},
		{'\ufffd', 0},
		{'\U0001f4a9', 0},

		// For a .TTF file, FontForge maps:
		//	- ".notdef"          to glyph index 0.
		//	- ".null"            to glyph index 1.
		//	- "nonmarkingreturn" to glyph index 2.

		{'/', 0},
		{'0', 3},
		{'1', 4},
		{'2', 5},
		{'3', 0},

		{'@', 0},
		{'A', 6},
		{'B', 7},
		{'C', 0},

		{'`', 0},
		{'a', 8},
		{'b', 0},

		// Of the remaining runes, only U+00FF LATIN SMALL LETTER Y WITH
		// DIAERESIS is in both the Mac Roman encoding and the cmapTest.ttf
		// font file.
		{'\u00fe', 0},
		{'\u00ff', 9},
		{'\u0100', 10},
		{'\u0101', 11},
		{'\u0102', 0},

		{'\u4e2c', 0},
		{'\u4e2d', 12},
		{'\u4e2e', 0},

		{'\U0001f0a0', 0},
		{'\U0001f0a1', 13},
		{'\U0001f0a2', 0},

		{'\U0001f0b0', 0},
		{'\U0001f0b1', 14},
		{'\U0001f0b2', 15},
		{'\U0001f0b3', 0},
	}

	var b Buffer
	for _, tc := range testCases {
		want := tc.want
		switch {
		case cmapFormat == 0 && tc.r > '\u007f' && tc.r != '\u00ff':
			// cmap format 0, with the Macintosh Roman encoding, can only
			// represent a limited set of non-ASCII runes, e.g. U+00FF.
			want = 0
		case cmapFormat == 4 && tc.r > '\uffff':
			// cmap format 4 only supports the Basic Multilingual Plane (BMP).
			want = 0
		}

		got, err := f.GlyphIndex(&b, tc.r)
		if err != nil {
			t.Errorf("cmapFormat=%d, r=%q: %v", cmapFormat, tc.r, err)
			continue
		}
		if got != want {
			t.Errorf("cmapFormat=%d, r=%q: got %d, want %d", cmapFormat, tc.r, got, want)
			continue
		}
	}
}

func TestPostScriptSegments(t *testing.T) {
	// wants' vectors correspond 1-to-1 to what's in the CFFTest.sfd file,
	// although OpenType/CFF and FontForge's SFD have reversed orders.
	// https://fontforge.github.io/validation.html says that "All paths must be
	// drawn in a consistent direction. Clockwise for external paths,
	// anti-clockwise for internal paths. (Actually PostScript requires the
	// exact opposite, but FontForge reverses PostScript contours when it loads
	// them so that everything is consistant internally -- and reverses them
	// again when it saves them, of course)."
	//
	// The .notdef glyph isn't explicitly in the SFD file, but for some unknown
	// reason, FontForge generates it in the OpenType/CFF file.
	wants := [][]Segment{{
		// .notdef
		// - contour #0
		moveTo(50, 0),
		lineTo(450, 0),
		lineTo(450, 533),
		lineTo(50, 533),
		lineTo(50, 0),
		// - contour #1
		moveTo(100, 50),
		lineTo(100, 483),
		lineTo(400, 483),
		lineTo(400, 50),
		lineTo(100, 50),
	}, {
		// zero
		// - contour #0
		moveTo(300, 700),
		cubeTo(380, 700, 420, 580, 420, 500),
		cubeTo(420, 350, 390, 100, 300, 100),
		cubeTo(220, 100, 180, 220, 180, 300),
		cubeTo(180, 450, 210, 700, 300, 700),
		// - contour #1
		moveTo(300, 800),
		cubeTo(200, 800, 100, 580, 100, 400),
		cubeTo(100, 220, 200, 0, 300, 0),
		cubeTo(400, 0, 500, 220, 500, 400),
		cubeTo(500, 580, 400, 800, 300, 800),
	}, {
		// one
		// - contour #0
		moveTo(100, 0),
		lineTo(300, 0),
		lineTo(300, 800),
		lineTo(100, 800),
		lineTo(100, 0),
	}, {
		// Q
		// - contour #0
		moveTo(657, 237),
		lineTo(289, 387),
		lineTo(519, 615),
		lineTo(657, 237),
		// - contour #1
		moveTo(792, 169),
		cubeTo(867, 263, 926, 502, 791, 665),
		cubeTo(645, 840, 380, 831, 228, 673),
		cubeTo(71, 509, 110, 231, 242, 93),
		cubeTo(369, -39, 641, 18, 722, 93),
		lineTo(802, 3),
		lineTo(864, 83),
		lineTo(792, 169),
	}, {
		// uni4E2D
		// - contour #0
		moveTo(141, 520),
		lineTo(137, 356),
		lineTo(245, 400),
		lineTo(331, 26),
		lineTo(355, 414),
		lineTo(463, 434),
		lineTo(453, 620),
		lineTo(341, 592),
		lineTo(331, 758),
		lineTo(243, 752),
		lineTo(235, 562),
		lineTo(141, 520),
	}}

	testSegments(t, "CFFTest.otf", wants)
}

func TestTrueTypeSegments(t *testing.T) {
	// wants' vectors correspond 1-to-1 to what's in the glyfTest.sfd file,
	// although FontForge's SFD format stores quadratic Bézier curves as cubics
	// with duplicated off-curve points. quadTo(bx, by, cx, cy) is stored as
	// "bx by bx by cx cy".
	//
	// The .notdef, .null and nonmarkingreturn glyphs aren't explicitly in the
	// SFD file, but for some unknown reason, FontForge generates them in the
	// TrueType file.
	wants := [][]Segment{{
		// .notdef
		// - contour #0
		moveTo(68, 0),
		lineTo(68, 1365),
		lineTo(612, 1365),
		lineTo(612, 0),
		lineTo(68, 0),
		// - contour #1
		moveTo(136, 68),
		lineTo(544, 68),
		lineTo(544, 1297),
		lineTo(136, 1297),
		lineTo(136, 68),
	}, {
		// .null
		// Empty glyph.
	}, {
		// nonmarkingreturn
		// Empty glyph.
	}, {
		// zero
		// - contour #0
		moveTo(614, 1434),
		quadTo(369, 1434, 369, 614),
		quadTo(369, 471, 435, 338),
		quadTo(502, 205, 614, 205),
		quadTo(860, 205, 860, 1024),
		quadTo(860, 1167, 793, 1300),
		quadTo(727, 1434, 614, 1434),
		// - contour #1
		moveTo(614, 1638),
		quadTo(1024, 1638, 1024, 819),
		quadTo(1024, 0, 614, 0),
		quadTo(205, 0, 205, 819),
		quadTo(205, 1638, 614, 1638),
	}, {
		// one
		// - contour #0
		moveTo(205, 0),
		lineTo(205, 1638),
		lineTo(614, 1638),
		lineTo(614, 0),
		lineTo(205, 0),
	}, {
		// five
		// - contour #0
		moveTo(0, 0),
		lineTo(0, 100),
		lineTo(400, 100),
		lineTo(400, 0),
		lineTo(0, 0),
	}, {
		// six
		// - contour #0
		moveTo(0, 0),
		lineTo(0, 100),
		lineTo(400, 100),
		lineTo(400, 0),
		lineTo(0, 0),
		// - contour #1
		translate(111, 234, moveTo(205, 0)),
		translate(111, 234, lineTo(205, 1638)),
		translate(111, 234, lineTo(614, 1638)),
		translate(111, 234, lineTo(614, 0)),
		translate(111, 234, lineTo(205, 0)),
	}, {
		// seven
		// - contour #0
		moveTo(0, 0),
		lineTo(0, 100),
		lineTo(400, 100),
		lineTo(400, 0),
		lineTo(0, 0),
		// - contour #1
		transform(1<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)),
		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)),
		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)),
		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)),
		transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)),
	}, {
		// eight
		// - contour #0
		moveTo(0, 0),
		lineTo(0, 100),
		lineTo(400, 100),
		lineTo(400, 0),
		lineTo(0, 0),
		// - contour #1
		transform(3<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)),
		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)),
		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)),
		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)),
		transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)),
	}, {
		// nine
		// - contour #0
		moveTo(0, 0),
		lineTo(0, 100),
		lineTo(400, 100),
		lineTo(400, 0),
		lineTo(0, 0),
		// - contour #1
		transform(22381, 8192, 5996, 14188, 237, 258, moveTo(205, 0)),
		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 1638)),
		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 1638)),
		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 0)),
		transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 0)),
	}}

	testSegments(t, "glyfTest.ttf", wants)
}

func testSegments(t *testing.T, filename string, wants [][]Segment) {
	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/" + filename))
	if err != nil {
		t.Fatalf("ReadFile: %v", err)
	}
	f, err := Parse(data)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	ppem := fixed.Int26_6(f.UnitsPerEm())

	if ng := f.NumGlyphs(); ng != len(wants) {
		t.Fatalf("NumGlyphs: got %d, want %d", ng, len(wants))
	}
	var b Buffer
	for i, want := range wants {
		got, err := f.LoadGlyph(&b, GlyphIndex(i), ppem, nil)
		if err != nil {
			t.Errorf("i=%d: LoadGlyph: %v", i, err)
			continue
		}
		if err := checkSegmentsEqual(got, want); err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
	}
	if _, err := f.LoadGlyph(nil, 0xffff, ppem, nil); err != ErrNotFound {
		t.Errorf("LoadGlyph(..., 0xffff, ...):\ngot  %v\nwant %v", err, ErrNotFound)
	}

	name, err := f.Name(nil, NameIDFamily)
	if err != nil {
		t.Errorf("Name: %v", err)
	} else if want := filename[:len(filename)-len(".ttf")]; name != want {
		t.Errorf("Name:\ngot  %q\nwant %q", name, want)
	}
}

func TestPPEM(t *testing.T) {
	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/glyfTest.ttf"))
	if err != nil {
		t.Fatalf("ReadFile: %v", err)
	}
	f, err := Parse(data)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	var b Buffer
	x, err := f.GlyphIndex(&b, '1')
	if err != nil {
		t.Fatalf("GlyphIndex: %v", err)
	}
	if x == 0 {
		t.Fatalf("GlyphIndex: no glyph index found for the rune '1'")
	}

	testCases := []struct {
		ppem fixed.Int26_6
		want []Segment
	}{{
		ppem: fixed.Int26_6(12 << 6),
		want: []Segment{
			moveTo(77, 0),
			lineTo(77, 614),
			lineTo(230, 614),
			lineTo(230, 0),
			lineTo(77, 0),
		},
	}, {
		ppem: fixed.Int26_6(2048),
		want: []Segment{
			moveTo(205, 0),
			lineTo(205, 1638),
			lineTo(614, 1638),
			lineTo(614, 0),
			lineTo(205, 0),
		},
	}}

	for i, tc := range testCases {
		got, err := f.LoadGlyph(&b, x, tc.ppem, nil)
		if err != nil {
			t.Errorf("i=%d: LoadGlyph: %v", i, err)
			continue
		}
		if err := checkSegmentsEqual(got, tc.want); err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
	}
}

func TestPostInfo(t *testing.T) {
	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/glyfTest.ttf"))
	if err != nil {
		t.Fatalf("ReadFile: %v", err)
	}
	f, err := Parse(data)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}
	post := f.PostTable()
	if post.ItalicAngle != -11.25 {
		t.Error("ItalicAngle:", post.ItalicAngle)
	}
	if post.UnderlinePosition != -255 {
		t.Error("UnderlinePosition:", post.UnderlinePosition)
	}
	if post.UnderlineThickness != 102 {
		t.Error("UnderlineThickness:", post.UnderlineThickness)
	}
	if post.IsFixedPitch {
		t.Error("IsFixedPitch:", post.IsFixedPitch)
	}
}

func TestGlyphName(t *testing.T) {
	f, err := Parse(goregular.TTF)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}

	testCases := []struct {
		r    rune
		want string
	}{
		{'\x00', "uni0000"},
		{'!', "exclam"},
		{'A', "A"},
		{'{', "braceleft"},
		{'\u00c4', "Adieresis"}, // U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
		{'\u2020', "dagger"},    // U+2020 DAGGER
		{'\u2660', "spade"},     // U+2660 BLACK SPADE SUIT
		{'\uf800', "gopher"},    // U+F800 <Private Use>
		{'\ufffe', ".notdef"},   // Not in the Go Regular font, so GlyphIndex returns (0, nil).
	}

	var b Buffer
	for _, tc := range testCases {
		x, err := f.GlyphIndex(&b, tc.r)
		if err != nil {
			t.Errorf("r=%q: GlyphIndex: %v", tc.r, err)
			continue
		}
		got, err := f.GlyphName(&b, x)
		if err != nil {
			t.Errorf("r=%q: GlyphName: %v", tc.r, err)
			continue
		}
		if got != tc.want {
			t.Errorf("r=%q: got %q, want %q", tc.r, got, tc.want)
			continue
		}
	}
}

func TestBuiltInPostNames(t *testing.T) {
	testCases := []struct {
		x    GlyphIndex
		want string
	}{
		{0, ".notdef"},
		{1, ".null"},
		{2, "nonmarkingreturn"},
		{13, "asterisk"},
		{36, "A"},
		{93, "z"},
		{123, "ocircumflex"},
		{202, "Edieresis"},
		{255, "Ccaron"},
		{256, "ccaron"},
		{257, "dcroat"},
		{258, ""},
		{999, ""},
		{0xffff, ""},
	}

	for _, tc := range testCases {
		if tc.x >= numBuiltInPostNames {
			continue
		}
		i := builtInPostNamesOffsets[tc.x+0]
		j := builtInPostNamesOffsets[tc.x+1]
		got := builtInPostNamesData[i:j]
		if got != tc.want {
			t.Errorf("x=%d: got %q, want %q", tc.x, got, tc.want)
		}
	}
}
