2012-06-16 02:19:07 +00:00
|
|
|
|
// 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 (
|
2013-08-13 11:08:54 +00:00
|
|
|
|
"bufio"
|
2012-06-16 02:19:07 +00:00
|
|
|
|
"fmt"
|
2013-08-13 11:08:54 +00:00
|
|
|
|
"io"
|
2012-06-16 02:19:07 +00:00
|
|
|
|
"io/ioutil"
|
2013-08-13 11:08:54 +00:00
|
|
|
|
"os"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
2012-06-16 02:19:07 +00:00
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2013-10-10 20:29:40 +00:00
|
|
|
|
func parseTestdataFont(name string) (font *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)
|
|
|
|
|
}
|
|
|
|
|
font, err = Parse(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, true, fmt.Errorf("%s: Parse: %v", name, err)
|
|
|
|
|
}
|
|
|
|
|
return font, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-16 02:19:07 +00:00
|
|
|
|
// 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) {
|
2013-10-10 20:29:40 +00:00
|
|
|
|
font, _, err := parseTestdataFont("luxisr")
|
2012-06-16 02:19:07 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
|
if got, want := font.FUnitsPerEm(), int32(2048); got != want {
|
|
|
|
|
t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
|
2012-06-16 02:19:07 +00:00
|
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
|
fupe := font.FUnitsPerEm()
|
|
|
|
|
if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want {
|
|
|
|
|
t.Errorf("Bounds: got %v, want %v", got, want)
|
2012-06-16 02:19:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i0 := font.Index('A')
|
|
|
|
|
i1 := font.Index('V')
|
|
|
|
|
if i0 != 36 || i1 != 57 {
|
|
|
|
|
t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
|
|
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
|
if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
|
2012-06-16 02:19:07 +00:00
|
|
|
|
t.Errorf("HMetric: got %v, want %v", got, want)
|
|
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
|
if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
|
2012-06-16 02:19:07 +00:00
|
|
|
|
t.Errorf("Kerning: got %v, want %v", got, want)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g0 := NewGlyphBuf()
|
2012-07-25 12:10:25 +00:00
|
|
|
|
err = g0.Load(font, fupe, i0, nil)
|
2012-06-16 02:19:07 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
g1 := &GlyphBuf{
|
|
|
|
|
B: Bounds{19, 0, 1342, 1480},
|
|
|
|
|
Point: []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},
|
|
|
|
|
},
|
|
|
|
|
End: []int{8, 11},
|
|
|
|
|
}
|
|
|
|
|
if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
|
|
|
|
|
t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-13 11:08:54 +00:00
|
|
|
|
|
2013-10-10 20:29:40 +00:00
|
|
|
|
func TestIndex(t *testing.T) {
|
|
|
|
|
testCases := map[string]map[rune]Index{
|
|
|
|
|
"luxisr": {
|
|
|
|
|
' ': 3,
|
|
|
|
|
'!': 4,
|
|
|
|
|
'A': 36,
|
|
|
|
|
'V': 57,
|
|
|
|
|
'É': 101,
|
|
|
|
|
'fl': 193,
|
|
|
|
|
'\u22c5': 385,
|
|
|
|
|
'中': 0,
|
|
|
|
|
},
|
|
|
|
|
"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,
|
|
|
|
|
'⊕': 3109,
|
|
|
|
|
'fl': 4560,
|
|
|
|
|
'\ufb03': 4561,
|
|
|
|
|
'\ufffd': 4645,
|
|
|
|
|
// 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 {
|
|
|
|
|
font, testdataIsOptional, err := parseTestdataFont(name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if testdataIsOptional {
|
|
|
|
|
t.Log(err)
|
|
|
|
|
} else {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for r, want := range wants {
|
|
|
|
|
if got := font.Index(r); got != want {
|
2013-10-11 22:29:59 +00:00
|
|
|
|
t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want)
|
2013-10-10 20:29:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 09:07:32 +00:00
|
|
|
|
var scalingTestCases = []struct {
|
|
|
|
|
name string
|
|
|
|
|
size int32
|
|
|
|
|
// hintingBrokenAt, if non-negative, is the glyph index n for which
|
|
|
|
|
// only the first n glyphs are known to be correctly hinted.
|
|
|
|
|
// TODO: remove this field, when hinting is completely implemented.
|
|
|
|
|
hintingBrokenAt int
|
|
|
|
|
}{
|
|
|
|
|
{"luxisr", 12, -1},
|
|
|
|
|
// TODO: uncomment the fonts below, once they get past Parse and
|
|
|
|
|
// GlyphBuf.Load, and the unhinted values match C Freetype.
|
2013-10-09 20:43:32 +00:00
|
|
|
|
{"x-arial-bold", 11, 0},
|
2013-10-09 09:07:32 +00:00
|
|
|
|
//{"x-deja-vu-sans-oblique", 17, 0},
|
2013-10-10 20:29:40 +00:00
|
|
|
|
{"x-droid-sans-japanese", 9, 0},
|
2013-10-09 09:07:32 +00:00
|
|
|
|
//{"x-times-new-roman", 13, 0},
|
|
|
|
|
}
|
2013-08-13 11:08:54 +00:00
|
|
|
|
|
2013-10-09 09:07:32 +00:00
|
|
|
|
func testScaling(t *testing.T, hinter *Hinter) {
|
|
|
|
|
loop:
|
|
|
|
|
for _, tc := range scalingTestCases {
|
2013-10-10 20:29:40 +00:00
|
|
|
|
font, testdataIsOptional, err := parseTestdataFont(tc.name)
|
2013-10-09 09:07:32 +00:00
|
|
|
|
if err != nil {
|
2013-10-10 20:29:40 +00:00
|
|
|
|
if testdataIsOptional {
|
|
|
|
|
t.Log(err)
|
2013-10-09 09:07:32 +00:00
|
|
|
|
} else {
|
2013-10-10 20:29:40 +00:00
|
|
|
|
t.Error(err)
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
2013-10-09 09:07:32 +00:00
|
|
|
|
continue loop
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
2013-10-09 09:07:32 +00:00
|
|
|
|
hinting := "sans"
|
|
|
|
|
if hinter != nil {
|
|
|
|
|
hinting = "with"
|
|
|
|
|
}
|
|
|
|
|
f, err := os.Open(fmt.Sprintf(
|
|
|
|
|
"../../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hinting))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: Open: %v", tc.name, err)
|
|
|
|
|
continue loop
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
2013-08-13 11:08:54 +00:00
|
|
|
|
|
2013-10-09 09:07:32 +00:00
|
|
|
|
wants := [][]Point{}
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
text := scanner.Text()
|
|
|
|
|
if text == "" {
|
|
|
|
|
wants = append(wants, []Point{})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ss := strings.Split(text, ",")
|
|
|
|
|
points := make([]Point, len(ss))
|
|
|
|
|
for i, s := range ss {
|
|
|
|
|
p := &points[i]
|
|
|
|
|
if _, err := fmt.Sscanf(s, "%d %d %d", &p.X, &p.Y, &p.Flags); err != nil {
|
|
|
|
|
t.Errorf("%s: Sscanf: %v", tc.name, err)
|
|
|
|
|
continue loop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
wants = append(wants, points)
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
2013-10-09 09:07:32 +00:00
|
|
|
|
if err := scanner.Err(); err != nil && err != io.EOF {
|
|
|
|
|
t.Errorf("%s: Scanner: %v", tc.name, err)
|
|
|
|
|
continue loop
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
2013-10-09 09:07:32 +00:00
|
|
|
|
|
|
|
|
|
glyphBuf := NewGlyphBuf()
|
|
|
|
|
for i, want := range wants {
|
|
|
|
|
// TODO: completely implement hinting. For now, only the first
|
|
|
|
|
// tc.hintingBrokenAt glyphs of the test case's font are correctly hinted.
|
|
|
|
|
if hinter != nil && i == tc.hintingBrokenAt {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = glyphBuf.Load(font, tc.size*64, Index(i), hinter); err != nil {
|
|
|
|
|
t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
|
|
|
|
|
continue loop
|
|
|
|
|
}
|
|
|
|
|
got := glyphBuf.Point
|
|
|
|
|
for i := range got {
|
|
|
|
|
got[i].Flags &= 0x01
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(got, want) {
|
|
|
|
|
t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\n", tc.name, i, got, want)
|
|
|
|
|
}
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScalingSansHinting(t *testing.T) {
|
2013-10-09 09:07:32 +00:00
|
|
|
|
testScaling(t, nil)
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScalingWithHinting(t *testing.T) {
|
2013-10-09 09:07:32 +00:00
|
|
|
|
testScaling(t, &Hinter{})
|
2013-08-13 11:08:54 +00:00
|
|
|
|
}
|