summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golang/freetype/truetype
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golang/freetype/truetype')
-rw-r--r--vendor/github.com/golang/freetype/truetype/face_test.go48
-rw-r--r--vendor/github.com/golang/freetype/truetype/hint_test.go675
-rw-r--r--vendor/github.com/golang/freetype/truetype/truetype.go2
-rw-r--r--vendor/github.com/golang/freetype/truetype/truetype_test.go400
4 files changed, 1124 insertions, 1 deletions
diff --git a/vendor/github.com/golang/freetype/truetype/face_test.go b/vendor/github.com/golang/freetype/truetype/face_test.go
new file mode 100644
index 000000000..856581dff
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/face_test.go
@@ -0,0 +1,48 @@
+// Copyright 2015 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "image"
+ "image/draw"
+ "io/ioutil"
+ "strings"
+ "testing"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func BenchmarkDrawString(b *testing.B) {
+ data, err := ioutil.ReadFile("../licenses/gpl.txt")
+ if err != nil {
+ b.Fatal(err)
+ }
+ lines := strings.Split(string(data), "\n")
+ data, err = ioutil.ReadFile("../testdata/luxisr.ttf")
+ if err != nil {
+ b.Fatal(err)
+ }
+ f, err := Parse(data)
+ if err != nil {
+ b.Fatal(err)
+ }
+ dst := image.NewRGBA(image.Rect(0, 0, 800, 600))
+ draw.Draw(dst, dst.Bounds(), image.White, image.ZP, draw.Src)
+ d := &font.Drawer{
+ Dst: dst,
+ Src: image.Black,
+ Face: NewFace(f, nil),
+ }
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for j, line := range lines {
+ d.Dot = fixed.P(0, (j*16)%600)
+ d.DrawString(line)
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/truetype/hint_test.go b/vendor/github.com/golang/freetype/truetype/hint_test.go
new file mode 100644
index 000000000..7eb43dde0
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/hint_test.go
@@ -0,0 +1,675 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "golang.org/x/image/math/fixed"
+)
+
+func TestBytecode(t *testing.T) {
+ testCases := []struct {
+ desc string
+ prog []byte
+ want []int32
+ errStr string
+ }{
+ {
+ "underflow",
+ []byte{
+ opDUP,
+ },
+ nil,
+ "underflow",
+ },
+ {
+ "infinite loop",
+ []byte{
+ opPUSHW000, // [-1]
+ 0xff,
+ 0xff,
+ opDUP, // [-1, -1]
+ opJMPR, // [-1]
+ },
+ nil,
+ "too many steps",
+ },
+ {
+ "unbalanced if/else",
+ []byte{
+ opPUSHB000, // [0]
+ 0,
+ opIF,
+ },
+ nil,
+ "unbalanced",
+ },
+ {
+ "vector set/gets",
+ []byte{
+ opSVTCA1, // []
+ opGPV, // [0x4000, 0]
+ opSVTCA0, // [0x4000, 0]
+ opGFV, // [0x4000, 0, 0, 0x4000]
+ opNEG, // [0x4000, 0, 0, -0x4000]
+ opSPVFS, // [0x4000, 0]
+ opSFVTPV, // [0x4000, 0]
+ opPUSHB000, // [0x4000, 0, 1]
+ 1,
+ opGFV, // [0x4000, 0, 1, 0, -0x4000]
+ opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2]
+ 2,
+ },
+ []int32{0x4000, 0, 1, 0, -0x4000, 2},
+ "",
+ },
+ {
+ "jumps",
+ []byte{
+ opPUSHB001, // [10, 2]
+ 10,
+ 2,
+ opJMPR, // [10]
+ opDUP, // not executed
+ opDUP, // [10, 10]
+ opPUSHB010, // [10, 10, 20, 2, 1]
+ 20,
+ 2,
+ 1,
+ opJROT, // [10, 10, 20]
+ opDUP, // not executed
+ opDUP, // [10, 10, 20, 20]
+ opPUSHB010, // [10, 10, 20, 20, 30, 2, 1]
+ 30,
+ 2,
+ 1,
+ opJROF, // [10, 10, 20, 20, 30]
+ opDUP, // [10, 10, 20, 20, 30, 30]
+ opDUP, // [10, 10, 20, 20, 30, 30, 30]
+ },
+ []int32{10, 10, 20, 20, 30, 30, 30},
+ "",
+ },
+ {
+ "stack ops",
+ []byte{
+ opPUSHB010, // [10, 20, 30]
+ 10,
+ 20,
+ 30,
+ opCLEAR, // []
+ opPUSHB010, // [40, 50, 60]
+ 40,
+ 50,
+ 60,
+ opSWAP, // [40, 60, 50]
+ opDUP, // [40, 60, 50, 50]
+ opDUP, // [40, 60, 50, 50, 50]
+ opPOP, // [40, 60, 50, 50]
+ opDEPTH, // [40, 60, 50, 50, 4]
+ opCINDEX, // [40, 60, 50, 50, 40]
+ opPUSHB000, // [40, 60, 50, 50, 40, 4]
+ 4,
+ opMINDEX, // [40, 50, 50, 40, 60]
+ },
+ []int32{40, 50, 50, 40, 60},
+ "",
+ },
+ {
+ "push ops",
+ []byte{
+ opPUSHB000, // [255]
+ 255,
+ opPUSHW001, // [255, -2, 253]
+ 255,
+ 254,
+ 0,
+ 253,
+ opNPUSHB, // [1, -2, 253, 1, 2]
+ 2,
+ 1,
+ 2,
+ opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809]
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ },
+ []int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809},
+ "",
+ },
+ {
+ "store ops",
+ []byte{
+ opPUSHB011, // [1, 22, 3, 44]
+ 1,
+ 22,
+ 3,
+ 44,
+ opWS, // [1, 22]
+ opWS, // []
+ opPUSHB000, // [3]
+ 3,
+ opRS, // [44]
+ },
+ []int32{44},
+ "",
+ },
+ {
+ "comparison ops",
+ []byte{
+ opPUSHB001, // [10, 20]
+ 10,
+ 20,
+ opLT, // [1]
+ opPUSHB001, // [1, 10, 20]
+ 10,
+ 20,
+ opLTEQ, // [1, 1]
+ opPUSHB001, // [1, 1, 10, 20]
+ 10,
+ 20,
+ opGT, // [1, 1, 0]
+ opPUSHB001, // [1, 1, 0, 10, 20]
+ 10,
+ 20,
+ opGTEQ, // [1, 1, 0, 0]
+ opEQ, // [1, 1, 1]
+ opNEQ, // [1, 0]
+ },
+ []int32{1, 0},
+ "",
+ },
+ {
+ "odd/even",
+ // Calculate odd(2+31/64), odd(2+32/64), even(2), even(1).
+ []byte{
+ opPUSHB000, // [159]
+ 159,
+ opODD, // [0]
+ opPUSHB000, // [0, 160]
+ 160,
+ opODD, // [0, 1]
+ opPUSHB000, // [0, 1, 128]
+ 128,
+ opEVEN, // [0, 1, 1]
+ opPUSHB000, // [0, 1, 1, 64]
+ 64,
+ opEVEN, // [0, 1, 1, 0]
+ },
+ []int32{0, 1, 1, 0},
+ "",
+ },
+ {
+ "if true",
+ []byte{
+ opPUSHB001, // [255, 1]
+ 255,
+ 1,
+ opIF,
+ opPUSHB000, // [255, 2]
+ 2,
+ opEIF,
+ opPUSHB000, // [255, 2, 254]
+ 254,
+ },
+ []int32{255, 2, 254},
+ "",
+ },
+ {
+ "if false",
+ []byte{
+ opPUSHB001, // [255, 0]
+ 255,
+ 0,
+ opIF,
+ opPUSHB000, // [255]
+ 2,
+ opEIF,
+ opPUSHB000, // [255, 254]
+ 254,
+ },
+ []int32{255, 254},
+ "",
+ },
+ {
+ "if/else true",
+ []byte{
+ opPUSHB000, // [1]
+ 1,
+ opIF,
+ opPUSHB000, // [2]
+ 2,
+ opELSE,
+ opPUSHB000, // not executed
+ 3,
+ opEIF,
+ },
+ []int32{2},
+ "",
+ },
+ {
+ "if/else false",
+ []byte{
+ opPUSHB000, // [0]
+ 0,
+ opIF,
+ opPUSHB000, // not executed
+ 2,
+ opELSE,
+ opPUSHB000, // [3]
+ 3,
+ opEIF,
+ },
+ []int32{3},
+ "",
+ },
+ {
+ "if/else true if/else false",
+ // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
+ []byte{
+ opPUSHB010, // [255, 0, 1]
+ 255,
+ 0,
+ 1,
+ opIF,
+ opIF,
+ opPUSHB001, // not executed
+ 0x58,
+ 0x58,
+ opELSE,
+ opPUSHW000, // [255, 0x5858]
+ 0x58,
+ 0x58,
+ opEIF,
+ opELSE,
+ opIF,
+ opNPUSHB, // not executed
+ 3,
+ 0x58,
+ 0x58,
+ 0x58,
+ opELSE,
+ opNPUSHW, // not executed
+ 2,
+ 0x58,
+ 0x58,
+ 0x58,
+ 0x58,
+ opEIF,
+ opEIF,
+ opPUSHB000, // [255, 0x5858, 254]
+ 254,
+ },
+ []int32{255, 0x5858, 254},
+ "",
+ },
+ {
+ "if/else false if/else true",
+ // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
+ []byte{
+ opPUSHB010, // [255, 1, 0]
+ 255,
+ 1,
+ 0,
+ opIF,
+ opIF,
+ opPUSHB001, // not executed
+ 0x58,
+ 0x58,
+ opELSE,
+ opPUSHW000, // not executed
+ 0x58,
+ 0x58,
+ opEIF,
+ opELSE,
+ opIF,
+ opNPUSHB, // [255, 0x58, 0x58, 0x58]
+ 3,
+ 0x58,
+ 0x58,
+ 0x58,
+ opELSE,
+ opNPUSHW, // not executed
+ 2,
+ 0x58,
+ 0x58,
+ 0x58,
+ 0x58,
+ opEIF,
+ opEIF,
+ opPUSHB000, // [255, 0x58, 0x58, 0x58, 254]
+ 254,
+ },
+ []int32{255, 0x58, 0x58, 0x58, 254},
+ "",
+ },
+ {
+ "logical ops",
+ []byte{
+ opPUSHB010, // [0, 10, 20]
+ 0,
+ 10,
+ 20,
+ opAND, // [0, 1]
+ opOR, // [1]
+ opNOT, // [0]
+ },
+ []int32{0},
+ "",
+ },
+ {
+ "arithmetic ops",
+ // Calculate abs((-(1 - (2*3)))/2 + 1/64).
+ // The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math.
+ []byte{
+ opPUSHB010, // [64, 128, 192]
+ 1 << 6,
+ 2 << 6,
+ 3 << 6,
+ opMUL, // [64, 384]
+ opSUB, // [-320]
+ opNEG, // [320]
+ opPUSHB000, // [320, 128]
+ 2 << 6,
+ opDIV, // [160]
+ opPUSHB000, // [160, 1]
+ 1,
+ opADD, // [161]
+ opABS, // [161]
+ },
+ []int32{161},
+ "",
+ },
+ {
+ "floor, ceiling",
+ []byte{
+ opPUSHB000, // [96]
+ 96,
+ opFLOOR, // [64]
+ opPUSHB000, // [64, 96]
+ 96,
+ opCEILING, // [64, 128]
+ },
+ []int32{64, 128},
+ "",
+ },
+ {
+ "rounding",
+ // Round 1.40625 (which is 90/64) under various rounding policies.
+ // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+ []byte{
+ opROFF, // []
+ opPUSHB000, // [90]
+ 90,
+ opROUND00, // [90]
+ opRTG, // [90]
+ opPUSHB000, // [90, 90]
+ 90,
+ opROUND00, // [90, 64]
+ opRTHG, // [90, 64]
+ opPUSHB000, // [90, 64, 90]
+ 90,
+ opROUND00, // [90, 64, 96]
+ opRDTG, // [90, 64, 96]
+ opPUSHB000, // [90, 64, 96, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64]
+ opRUTG, // [90, 64, 96, 64]
+ opPUSHB000, // [90, 64, 96, 64, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64, 128]
+ opRTDG, // [90, 64, 96, 64, 128]
+ opPUSHB000, // [90, 64, 96, 64, 128, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64, 128, 96]
+ },
+ []int32{90, 64, 96, 64, 128, 96},
+ "",
+ },
+ {
+ "super-rounding",
+ // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+ // and the sign preservation steps of the "Order of rounding operations" section.
+ []byte{
+ opPUSHB000, // [0x58]
+ 0x58,
+ opSROUND, // []
+ opPUSHW000, // [-81]
+ 0xff,
+ 0xaf,
+ opROUND00, // [-80]
+ opPUSHW000, // [-80, -80]
+ 0xff,
+ 0xb0,
+ opROUND00, // [-80, -80]
+ opPUSHW000, // [-80, -80, -17]
+ 0xff,
+ 0xef,
+ opROUND00, // [-80, -80, -16]
+ opPUSHW000, // [-80, -80, -16, -16]
+ 0xff,
+ 0xf0,
+ opROUND00, // [-80, -80, -16, -16]
+ opPUSHB000, // [-80, -80, -16, -16, 0]
+ 0,
+ opROUND00, // [-80, -80, -16, -16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16]
+ 16,
+ opROUND00, // [-80, -80, -16, -16, 16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47]
+ 47,
+ opROUND00, // [-80, -80, -16, -16, 16, 16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48]
+ 48,
+ opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80]
+ },
+ []int32{-80, -80, -16, -16, 16, 16, 16, 80},
+ "",
+ },
+ {
+ "roll",
+ []byte{
+ opPUSHB010, // [1, 2, 3]
+ 1,
+ 2,
+ 3,
+ opROLL, // [2, 3, 1]
+ },
+ []int32{2, 3, 1},
+ "",
+ },
+ {
+ "max/min",
+ []byte{
+ opPUSHW001, // [-2, -3]
+ 0xff,
+ 0xfe,
+ 0xff,
+ 0xfd,
+ opMAX, // [-2]
+ opPUSHW001, // [-2, -4, -5]
+ 0xff,
+ 0xfc,
+ 0xff,
+ 0xfb,
+ opMIN, // [-2, -5]
+ },
+ []int32{-2, -5},
+ "",
+ },
+ {
+ "functions",
+ []byte{
+ opPUSHB011, // [3, 7, 0, 3]
+ 3,
+ 7,
+ 0,
+ 3,
+
+ opFDEF, // Function #3 (not called)
+ opPUSHB000,
+ 98,
+ opENDF,
+
+ opFDEF, // Function #0
+ opDUP,
+ opADD,
+ opENDF,
+
+ opFDEF, // Function #7
+ opPUSHB001,
+ 10,
+ 0,
+ opCALL,
+ opDUP,
+ opENDF,
+
+ opFDEF, // Function #3 (again)
+ opPUSHB000,
+ 99,
+ opENDF,
+
+ opPUSHB001, // [2, 0]
+ 2,
+ 0,
+ opCALL, // [4]
+ opPUSHB000, // [4, 3]
+ 3,
+ opLOOPCALL, // [99, 99, 99, 99]
+ opPUSHB000, // [99, 99, 99, 99, 7]
+ 7,
+ opCALL, // [99, 99, 99, 99, 20, 20]
+ },
+ []int32{99, 99, 99, 99, 20, 20},
+ "",
+ },
+ }
+
+ for _, tc := range testCases {
+ h := &hinter{}
+ h.init(&Font{
+ maxStorage: 32,
+ maxStackElements: 100,
+ }, 768)
+ err, errStr := h.run(tc.prog, nil, nil, nil, nil), ""
+ if err != nil {
+ errStr = err.Error()
+ }
+ if tc.errStr != "" {
+ if errStr == "" {
+ t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr)
+ } else if !strings.Contains(errStr, tc.errStr) {
+ t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr)
+ }
+ continue
+ }
+ if errStr != "" {
+ t.Errorf("%s: got error %q, want none", tc.desc, errStr)
+ continue
+ }
+ got := h.stack[:len(tc.want)]
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want)
+ continue
+ }
+ }
+}
+
+// TestMove tests that the hinter.move method matches the output of the C
+// Freetype implementation.
+func TestMove(t *testing.T) {
+ h, p := hinter{}, Point{}
+ testCases := []struct {
+ pvX, pvY, fvX, fvY f2dot14
+ wantX, wantY fixed.Int26_6
+ }{
+ {+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0},
+ {+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0},
+ {-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0},
+ {-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0},
+ {+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000},
+ {+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000},
+ {+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000},
+ {+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000},
+ {+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000},
+ {+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000},
+ {-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000},
+ {-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000},
+ {-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000},
+ {-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000},
+ {+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732},
+ {-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732},
+ {+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732},
+ {-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732},
+ {-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732},
+ {+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0},
+ {+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000},
+ }
+ for _, tc := range testCases {
+ p = Point{}
+ h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY}
+ h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY}
+ h.move(&p, 1000, true)
+ tx := p.Flags&flagTouchedX != 0
+ ty := p.Flags&flagTouchedY != 0
+ wantTX := tc.fvX != 0
+ wantTY := tc.fvY != 0
+ if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY {
+ t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t",
+ h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY)
+ continue
+ }
+
+ // Check that p is aligned with the freedom vector.
+ a := int64(p.X) * int64(tc.fvY)
+ b := int64(p.Y) * int64(tc.fvX)
+ if a != b {
+ t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p)
+ continue
+ }
+
+ // Check that the projected p is 1000 away from the origin.
+ dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14
+ if dotProd != 1000 {
+ t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p)
+ continue
+ }
+ }
+}
+
+// TestNormalize tests that the normalize function matches the output of the C
+// Freetype implementation.
+func TestNormalize(t *testing.T) {
+ testCases := [][2]f2dot14{
+ {-15895, 3974},
+ {-15543, 5181},
+ {-14654, 7327},
+ {-11585, 11585},
+ {0, 16384},
+ {11585, 11585},
+ {14654, 7327},
+ {15543, 5181},
+ {15895, 3974},
+ {16066, 3213},
+ {16161, 2694},
+ {16219, 2317},
+ {16257, 2032},
+ {16284, 1809},
+ }
+ for i, want := range testCases {
+ got := normalize(f2dot14(i)-4, 1)
+ if got != want {
+ t.Errorf("i=%d: got %v, want %v", i, got, want)
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/truetype/truetype.go b/vendor/github.com/golang/freetype/truetype/truetype.go
index 9e943d3ec..613fc17e2 100644
--- a/vendor/github.com/golang/freetype/truetype/truetype.go
+++ b/vendor/github.com/golang/freetype/truetype/truetype.go
@@ -15,7 +15,7 @@
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
-package truetype
+package truetype // import "github.com/golang/freetype/truetype"
import (
"fmt"
diff --git a/vendor/github.com/golang/freetype/truetype/truetype_test.go b/vendor/github.com/golang/freetype/truetype/truetype_test.go
new file mode 100644
index 000000000..bd62d1da1
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/truetype_test.go
@@ -0,0 +1,400 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func parseTestdataFont(name string) (f *Font, testdataIsOptional bool, err error) {
+ b, err := ioutil.ReadFile(fmt.Sprintf("../testdata/%s.ttf", name))
+ if err != nil {
+ // The "x-foo" fonts are optional tests, as they are not checked
+ // in for copyright or file size reasons.
+ return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err)
+ }
+ f, err = Parse(b)
+ if err != nil {
+ return nil, true, fmt.Errorf("%s: Parse: %v", name, err)
+ }
+ return f, false, nil
+}
+
+func mkBounds(minX, minY, maxX, maxY fixed.Int26_6) fixed.Rectangle26_6 {
+ return fixed.Rectangle26_6{
+ Min: fixed.Point26_6{
+ X: minX,
+ Y: minY,
+ },
+ Max: fixed.Point26_6{
+ X: maxX,
+ Y: maxY,
+ },
+ }
+}
+
+// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
+// The numerical values can be manually verified by examining luxisr.ttx.
+func TestParse(t *testing.T) {
+ f, _, err := parseTestdataFont("luxisr")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := f.FUnitsPerEm(), int32(2048); got != want {
+ t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
+ }
+ fupe := fixed.Int26_6(f.FUnitsPerEm())
+ if got, want := f.Bounds(fupe), mkBounds(-441, -432, 2024, 2033); got != want {
+ t.Errorf("Bounds: got %v, want %v", got, want)
+ }
+
+ i0 := f.Index('A')
+ i1 := f.Index('V')
+ if i0 != 36 || i1 != 57 {
+ t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
+ }
+ if got, want := f.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
+ t.Errorf("HMetric: got %v, want %v", got, want)
+ }
+ if got, want := f.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
+ t.Errorf("VMetric: got %v, want %v", got, want)
+ }
+ if got, want := f.Kern(fupe, i0, i1), fixed.Int26_6(-144); got != want {
+ t.Errorf("Kern: got %v, want %v", got, want)
+ }
+
+ g := &GlyphBuf{}
+ err = g.Load(f, fupe, i0, font.HintingNone)
+ if err != nil {
+ t.Fatalf("Load: %v", err)
+ }
+ g0 := &GlyphBuf{
+ Bounds: g.Bounds,
+ Points: g.Points,
+ Ends: g.Ends,
+ }
+ g1 := &GlyphBuf{
+ Bounds: mkBounds(19, 0, 1342, 1480),
+ Points: []Point{
+ {19, 0, 51},
+ {581, 1480, 1},
+ {789, 1480, 51},
+ {1342, 0, 1},
+ {1116, 0, 35},
+ {962, 410, 3},
+ {368, 410, 33},
+ {214, 0, 3},
+ {428, 566, 19},
+ {904, 566, 33},
+ {667, 1200, 3},
+ },
+ Ends: []int{8, 11},
+ }
+ if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
+ t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want)
+ }
+}
+
+func TestIndex(t *testing.T) {
+ testCases := map[string]map[rune]Index{
+ "luxisr": {
+ ' ': 3,
+ '!': 4,
+ 'A': 36,
+ 'V': 57,
+ 'É': 101,
+ 'fl': 193,
+ '\u22c5': 385,
+ '中': 0,
+ },
+
+ // The x-etc test cases use those versions of the .ttf files provided
+ // by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details.
+
+ "x-arial-bold": {
+ ' ': 3,
+ '+': 14,
+ '0': 19,
+ '_': 66,
+ 'w': 90,
+ '~': 97,
+ 'Ä': 98,
+ 'fl': 192,
+ '½': 242,
+ 'σ': 305,
+ 'λ': 540,
+ 'ỹ': 1275,
+ '\u04e9': 1319,
+ '中': 0,
+ },
+ "x-deja-vu-sans-oblique": {
+ ' ': 3,
+ '*': 13,
+ 'Œ': 276,
+ 'ω': 861,
+ '‡': 2571,
+ '⊕': 3110,
+ 'fl': 4728,
+ '\ufb03': 4729,
+ '\ufffd': 4813,
+ // TODO: '\U0001f640': ???,
+ '中': 0,
+ },
+ "x-droid-sans-japanese": {
+ ' ': 0,
+ '\u3000': 3,
+ '\u3041': 25,
+ '\u30fe': 201,
+ '\uff61': 202,
+ '\uff67': 208,
+ '\uff9e': 263,
+ '\uff9f': 264,
+ '\u4e00': 265,
+ '\u557e': 1000,
+ '\u61b6': 2024,
+ '\u6ede': 3177,
+ '\u7505': 3555,
+ '\u81e3': 4602,
+ '\u81e5': 4603,
+ '\u81e7': 4604,
+ '\u81e8': 4605,
+ '\u81ea': 4606,
+ '\u81ed': 4607,
+ '\u81f3': 4608,
+ '\u81f4': 4609,
+ '\u91c7': 5796,
+ '\u9fa0': 6620,
+ '\u203e': 12584,
+ },
+ "x-times-new-roman": {
+ ' ': 3,
+ ':': 29,
+ 'fl': 192,
+ 'Ŀ': 273,
+ '♠': 388,
+ 'Ŗ': 451,
+ 'Σ': 520,
+ '\u200D': 745,
+ 'Ẽ': 1216,
+ '\u04e9': 1319,
+ '中': 0,
+ },
+ }
+ for name, wants := range testCases {
+ f, testdataIsOptional, err := parseTestdataFont(name)
+ if err != nil {
+ if testdataIsOptional {
+ t.Log(err)
+ } else {
+ t.Fatal(err)
+ }
+ continue
+ }
+ for r, want := range wants {
+ if got := f.Index(r); got != want {
+ t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want)
+ }
+ }
+ }
+}
+
+func TestName(t *testing.T) {
+ testCases := map[string]string{
+ "luximr": "Luxi Mono",
+ "luxirr": "Luxi Serif",
+ "luxisr": "Luxi Sans",
+ }
+
+ for name, want := range testCases {
+ f, testdataIsOptional, err := parseTestdataFont(name)
+ if err != nil {
+ if testdataIsOptional {
+ t.Log(err)
+ } else {
+ t.Fatal(err)
+ }
+ continue
+ }
+ if got := f.Name(NameIDFontFamily); got != want {
+ t.Errorf("%s: got %q, want %q", name, got, want)
+ }
+ }
+}
+
+type scalingTestData struct {
+ advanceWidth fixed.Int26_6
+ bounds fixed.Rectangle26_6
+ points []Point
+}
+
+// scalingTestParse parses a line of points like
+// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1
+// The line will not have a trailing "\n".
+func scalingTestParse(line string) (ret scalingTestData) {
+ next := func(s string) (string, fixed.Int26_6) {
+ t, i := "", strings.Index(s, " ")
+ if i != -1 {
+ s, t = s[:i], s[i+1:]
+ }
+ x, _ := strconv.Atoi(s)
+ return t, fixed.Int26_6(x)
+ }
+
+ i := strings.Index(line, ";")
+ prefix, line := line[:i], line[i+1:]
+
+ prefix, ret.advanceWidth = next(prefix)
+ prefix, ret.bounds.Min.X = next(prefix)
+ prefix, ret.bounds.Min.Y = next(prefix)
+ prefix, ret.bounds.Max.X = next(prefix)
+ prefix, ret.bounds.Max.Y = next(prefix)
+
+ ret.points = make([]Point, 0, 1+strings.Count(line, ","))
+ for len(line) > 0 {
+ s := line
+ if i := strings.Index(line, ","); i != -1 {
+ s, line = line[:i], line[i+1:]
+ for len(line) > 0 && line[0] == ' ' {
+ line = line[1:]
+ }
+ } else {
+ line = ""
+ }
+ s, x := next(s)
+ s, y := next(s)
+ s, f := next(s)
+ ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)})
+ }
+ return ret
+}
+
+// scalingTestEquals is equivalent to, but faster than, calling
+// reflect.DeepEquals(a, b), and also returns the index of the first non-equal
+// element. It also treats a nil []Point and an empty non-nil []Point as equal.
+// a and b must have equal length.
+func scalingTestEquals(a, b []Point) (index int, equals bool) {
+ for i, p := range a {
+ if p != b[i] {
+ return i, false
+ }
+ }
+ return 0, true
+}
+
+var scalingTestCases = []struct {
+ name string
+ size int
+}{
+ {"luxisr", 12},
+ {"x-arial-bold", 11},
+ {"x-deja-vu-sans-oblique", 17},
+ {"x-droid-sans-japanese", 9},
+ {"x-times-new-roman", 13},
+}
+
+func testScaling(t *testing.T, h font.Hinting) {
+ for _, tc := range scalingTestCases {
+ f, testdataIsOptional, err := parseTestdataFont(tc.name)
+ if err != nil {
+ if testdataIsOptional {
+ t.Log(err)
+ } else {
+ t.Error(err)
+ }
+ continue
+ }
+ hintingStr := "sans"
+ if h != font.HintingNone {
+ hintingStr = "with"
+ }
+ testFile, err := os.Open(fmt.Sprintf(
+ "../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr))
+ if err != nil {
+ t.Errorf("%s: Open: %v", tc.name, err)
+ continue
+ }
+ defer testFile.Close()
+
+ wants := []scalingTestData{}
+ scanner := bufio.NewScanner(testFile)
+ if scanner.Scan() {
+ major, minor, patch := 0, 0, 0
+ _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch)
+ if err != nil {
+ t.Errorf("%s: version information: %v", tc.name, err)
+ }
+ if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) {
+ t.Errorf("%s: need freetype version >= 2.5.1.\n"+
+ "Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+
+ "and re-running testdata/make-other-hinting-txts.sh",
+ tc.name)
+ continue
+ }
+ } else {
+ t.Errorf("%s: no version information", tc.name)
+ continue
+ }
+ for scanner.Scan() {
+ wants = append(wants, scalingTestParse(scanner.Text()))
+ }
+ if err := scanner.Err(); err != nil && err != io.EOF {
+ t.Errorf("%s: Scanner: %v", tc.name, err)
+ continue
+ }
+
+ glyphBuf := &GlyphBuf{}
+ for i, want := range wants {
+ if err = glyphBuf.Load(f, fixed.I(tc.size), Index(i), h); err != nil {
+ t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
+ continue
+ }
+ got := scalingTestData{
+ advanceWidth: glyphBuf.AdvanceWidth,
+ bounds: glyphBuf.Bounds,
+ points: glyphBuf.Points,
+ }
+
+ if got.advanceWidth != want.advanceWidth {
+ t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v",
+ tc.name, i, got.advanceWidth, want.advanceWidth)
+ continue
+ }
+
+ if got.bounds != want.bounds {
+ t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v",
+ tc.name, i, got.bounds, want.bounds)
+ continue
+ }
+
+ for i := range got.points {
+ got.points[i].Flags &= 0x01
+ }
+ if len(got.points) != len(want.points) {
+ t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d",
+ tc.name, i, got.points, want.points, len(got.points), len(want.points))
+ continue
+ }
+ if j, equals := scalingTestEquals(got.points, want.points); !equals {
+ t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v",
+ tc.name, i, got.points, want.points, j, got.points[j], want.points[j])
+ continue
+ }
+ }
+ }
+}
+
+func TestScalingHintingNone(t *testing.T) { testScaling(t, font.HintingNone) }
+func TestScalingHintingFull(t *testing.T) { testScaling(t, font.HintingFull) }