sfnt_test.go 19 KB

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