truetype.go 18.9 KB
Newer Older
1 2
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
3 4
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
5

6
// Package truetype provides a parser for the TTF and TTC file formats.
7 8
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
// and http://www.microsoft.com/typography/otspec/
9
//
10 11
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
// metrics and control points. All these methods take a scale parameter, which
12 13 14
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
// fixed.Int26_6(10 << 6).
15 16 17
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
N
Nigel Tao 已提交
18
package truetype // import "github.com/golang/freetype/truetype"
19 20 21

import (
	"fmt"
22 23

	"golang.org/x/image/math/fixed"
24 25
)

N
Nigel Tao 已提交
26
// An Index is a Font's index of a rune.
27 28
type Index uint16

N
Nigel Tao 已提交
29 30 31
// A NameID identifies a name table entry.
//
// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
S
Stephen Edwards 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
type NameID uint16

const (
	NameIDCopyright          NameID = 0
	NameIDFontFamily                = 1
	NameIDFontSubfamily             = 2
	NameIDUniqueSubfamilyID         = 3
	NameIDFontFullName              = 4
	NameIDNameTableVersion          = 5
	NameIDPostscriptName            = 6
	NameIDTrademarkNotice           = 7
	NameIDManufacturerName          = 8
	NameIDDesignerName              = 9
	NameIDFontDescription           = 10
	NameIDFontVendorURL             = 11
	NameIDFontDesignerURL           = 12
	NameIDFontLicense               = 13
	NameIDFontLicenseURL            = 14
	NameIDPreferredFamily           = 16
	NameIDPreferredSubfamily        = 17
	NameIDCompatibleName            = 18
	NameIDSampleText                = 19
)

const (
	// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
	// least-significant 16-bit Platform Specific ID. The magic numbers are
	// specified at https://www.microsoft.com/typography/otspec/name.htm
	unicodeEncoding         = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
	microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
	microsoftUCS2Encoding   = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
	microsoftUCS4Encoding   = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
)

66 67
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
68
	AdvanceWidth, LeftSideBearing fixed.Int26_6
69 70 71 72
}

// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
73
	AdvanceHeight, TopSideBearing fixed.Int26_6
74 75 76 77 78
}

// A FormatError reports that the input is not a valid TrueType font.
type FormatError string

N
Nigel Tao 已提交
79
func (e FormatError) Error() string {
80 81 82 83 84 85 86
	return "freetype: invalid TrueType format: " + string(e)
}

// An UnsupportedError reports that the input uses a valid but unimplemented
// TrueType feature.
type UnsupportedError string

N
Nigel Tao 已提交
87
func (e UnsupportedError) Error() string {
88 89 90
	return "freetype: unsupported TrueType feature: " + string(e)
}

91 92 93
// u32 returns the big-endian uint32 at b[i:].
func u32(b []byte, i int) uint32 {
	return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
94 95
}

96 97 98
// u16 returns the big-endian uint16 at b[i:].
func u16(b []byte, i int) uint16 {
	return uint16(b[i])<<8 | uint16(b[i+1])
99 100 101
}

// readTable returns a slice of the TTF data given by a table's directory entry.
N
Nigel Tao 已提交
102
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
103
	offset := int(u32(offsetLength, 0))
104 105
	if offset < 0 {
		return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
106
	}
107
	length := int(u32(offsetLength, 4))
108 109
	if length < 0 {
		return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
110
	}
111 112 113 114 115
	end := offset + length
	if end < 0 || end > len(ttf) {
		return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
	}
	return ttf[offset:end], nil
116 117
}

N
Nigel Tao 已提交
118 119 120 121 122 123 124 125 126 127 128
// parseSubtables returns the offset and platformID of the best subtable in
// table, where best favors a Unicode cmap encoding, and failing that, a
// Microsoft cmap encoding. offset is the offset of the first subtable in
// table, and size is the size of each subtable.
//
// If pred is non-nil, then only subtables that satisfy that predicate will be
// considered.
func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
	bestOffset int, bestPID uint32, retErr error) {

	if len(table) < 4 {
S
Stephen Edwards 已提交
129 130
		return 0, 0, FormatError(name + " too short")
	}
N
Nigel Tao 已提交
131 132
	nSubtables := int(u16(table, 2))
	if len(table) < size*nSubtables+offset {
S
Stephen Edwards 已提交
133 134
		return 0, 0, FormatError(name + " too short")
	}
N
Nigel Tao 已提交
135 136 137
	ok := false
	for i := 0; i < nSubtables; i, offset = i+1, offset+size {
		if pred != nil && !pred(table[offset:]) {
S
Stephen Edwards 已提交
138 139 140 141
			continue
		}
		// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
		// All values are big-endian.
N
Nigel Tao 已提交
142
		pidPsid := u32(table, offset)
S
Stephen Edwards 已提交
143 144 145
		// We prefer the Unicode cmap encoding. Failing to find that, we fall
		// back onto the Microsoft cmap encoding.
		if pidPsid == unicodeEncoding {
N
Nigel Tao 已提交
146
			bestOffset, bestPID, ok = offset, pidPsid>>16, true
S
Stephen Edwards 已提交
147 148 149 150 151 152
			break

		} else if pidPsid == microsoftSymbolEncoding ||
			pidPsid == microsoftUCS2Encoding ||
			pidPsid == microsoftUCS4Encoding {

N
Nigel Tao 已提交
153
			bestOffset, bestPID, ok = offset, pidPsid>>16, true
S
Stephen Edwards 已提交
154 155 156
			// We don't break out of the for loop, so that Unicode can override Microsoft.
		}
	}
N
Nigel Tao 已提交
157 158 159 160
	if !ok {
		return 0, 0, UnsupportedError(name + " encoding")
	}
	return bestOffset, bestPID, nil
S
Stephen Edwards 已提交
161 162
}

163 164 165 166 167 168 169 170
const (
	locaOffsetFormatUnknown int = iota
	locaOffsetFormatShort
	locaOffsetFormatLong
)

// A cm holds a parsed cmap entry.
type cm struct {
171
	start, end, delta, offset uint32
172 173 174 175 176 177
}

// A Font represents a Truetype font.
type Font struct {
	// Tables sliced from the TTF data. The different tables are documented
	// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
S
Stephen Edwards 已提交
178
	cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
179 180

	cmapIndexes []byte
181 182 183 184 185

	// Cached values derived from the raw ttf data.
	cm                      []cm
	locaOffsetFormat        int
	nGlyph, nHMetric, nKern int
186
	fUnitsPerEm             int32
187
	bounds                  fixed.Rectangle26_6
188 189
	// Values from the maxp section.
	maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
190 191
}

N
Nigel Tao 已提交
192
func (f *Font) parseCmap() error {
193 194
	const (
		cmapFormat4         = 4
195
		cmapFormat12        = 12
196 197 198
		languageIndependent = 0
	)

N
Nigel Tao 已提交
199
	offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
S
Stephen Edwards 已提交
200 201
	if err != nil {
		return err
202
	}
N
Nigel Tao 已提交
203
	offset = int(u32(f.cmap, offset+4))
204 205 206 207
	if offset <= 0 || offset > len(f.cmap) {
		return FormatError("bad cmap offset")
	}

208
	cmapFormat := u16(f.cmap, offset)
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
	switch cmapFormat {
	case cmapFormat4:
		language := u16(f.cmap, offset+4)
		if language != languageIndependent {
			return UnsupportedError(fmt.Sprintf("language: %d", language))
		}
		segCountX2 := int(u16(f.cmap, offset+6))
		if segCountX2%2 == 1 {
			return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
		}
		segCount := segCountX2 / 2
		offset += 14
		f.cm = make([]cm, segCount)
		for i := 0; i < segCount; i++ {
			f.cm[i].end = uint32(u16(f.cmap, offset))
			offset += 2
		}
226
		offset += 2
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
		for i := 0; i < segCount; i++ {
			f.cm[i].start = uint32(u16(f.cmap, offset))
			offset += 2
		}
		for i := 0; i < segCount; i++ {
			f.cm[i].delta = uint32(u16(f.cmap, offset))
			offset += 2
		}
		for i := 0; i < segCount; i++ {
			f.cm[i].offset = uint32(u16(f.cmap, offset))
			offset += 2
		}
		f.cmapIndexes = f.cmap[offset:]
		return nil

	case cmapFormat12:
		if u16(f.cmap, offset+2) != 0 {
			return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
		}
		length := u32(f.cmap, offset+4)
		language := u32(f.cmap, offset+8)
		if language != languageIndependent {
			return UnsupportedError(fmt.Sprintf("language: %d", language))
		}
		nGroups := u32(f.cmap, offset+12)
		if length != 12*nGroups+16 {
			return FormatError("inconsistent cmap length")
		}
		offset += 16
		f.cm = make([]cm, nGroups)
		for i := uint32(0); i < nGroups; i++ {
			f.cm[i].start = u32(f.cmap, offset+0)
			f.cm[i].end = u32(f.cmap, offset+4)
			f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
			offset += 12
		}
		return nil
264
	}
265
	return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
266 267
}

N
Nigel Tao 已提交
268
func (f *Font) parseHead() error {
269 270 271
	if len(f.head) != 54 {
		return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
	}
272
	f.fUnitsPerEm = int32(u16(f.head, 18))
273 274 275 276
	f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
	f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
	f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
	f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
277
	switch i := u16(f.head, 50); i {
278 279 280 281 282 283 284 285 286 287
	case 0:
		f.locaOffsetFormat = locaOffsetFormatShort
	case 1:
		f.locaOffsetFormat = locaOffsetFormatLong
	default:
		return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
	}
	return nil
}

N
Nigel Tao 已提交
288
func (f *Font) parseHhea() error {
289 290 291
	if len(f.hhea) != 36 {
		return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
	}
292
	f.nHMetric = int(u16(f.hhea, 34))
293 294 295 296 297 298
	if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
		return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
	}
	return nil
}

N
Nigel Tao 已提交
299
func (f *Font) parseKern() error {
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
	// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
	// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
	// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
	// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
	// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
	// and Windows should use the old format."
	// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
	// just like the C Freetype implementation.
	if len(f.kern) == 0 {
		if f.nKern != 0 {
			return FormatError("bad kern table length")
		}
		return nil
	}
	if len(f.kern) < 18 {
		return FormatError("kern data too short")
	}
318
	version, offset := u16(f.kern, 0), 2
319 320 321
	if version != 0 {
		return UnsupportedError(fmt.Sprintf("kern version: %d", version))
	}
322
	n, offset := u16(f.kern, offset), offset+2
323 324 325
	if n != 1 {
		return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
	}
326 327 328
	offset += 2
	length, offset := int(u16(f.kern, offset)), offset+2
	coverage, offset := u16(f.kern, offset), offset+2
329 330 331 332
	if coverage != 0x0001 {
		// We only support horizontal kerning.
		return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
	}
333
	f.nKern, offset = int(u16(f.kern, offset)), offset+2
334 335 336 337 338 339
	if 6*f.nKern != length-14 {
		return FormatError("bad kern table length")
	}
	return nil
}

N
Nigel Tao 已提交
340
func (f *Font) parseMaxp() error {
341 342 343
	if len(f.maxp) != 32 {
		return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
	}
344
	f.nGlyph = int(u16(f.maxp, 4))
345 346 347 348
	f.maxTwilightPoints = u16(f.maxp, 16)
	f.maxStorage = u16(f.maxp, 18)
	f.maxFunctionDefs = u16(f.maxp, 20)
	f.maxStackElements = u16(f.maxp, 24)
349 350 351
	return nil
}

352
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
353
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
354
	if x >= 0 {
355
		x += fixed.Int26_6(f.fUnitsPerEm) / 2
356
	} else {
357
		x -= fixed.Int26_6(f.fUnitsPerEm) / 2
358
	}
359
	return x / fixed.Int26_6(f.fUnitsPerEm)
360 361
}

362
// Bounds returns the union of a Font's glyphs' bounds.
363
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
364
	b := f.bounds
365 366 367 368
	b.Min.X = f.scale(scale * b.Min.X)
	b.Min.Y = f.scale(scale * b.Min.Y)
	b.Max.X = f.scale(scale * b.Max.X)
	b.Max.Y = f.scale(scale * b.Max.Y)
369
	return b
370 371
}

372 373 374
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
func (f *Font) FUnitsPerEm() int32 {
	return f.fUnitsPerEm
375 376
}

N
Nigel Tao 已提交
377 378
// Index returns a Font's index for the given rune.
func (f *Font) Index(x rune) Index {
379
	c := uint32(x)
380 381 382 383 384 385 386 387 388 389 390
	for i, j := 0, len(f.cm); i < j; {
		h := i + (j-i)/2
		cm := &f.cm[h]
		if c < cm.start {
			j = h
		} else if cm.end < c {
			i = h + 1
		} else if cm.offset == 0 {
			return Index(c + cm.delta)
		} else {
			offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
391
			return Index(u16(f.cmapIndexes, offset))
392 393
		}
	}
394
	return 0
395 396
}

N
Nigel Tao 已提交
397 398
// Name returns the Font's name value for the given NameID. It returns "" if
// there was an error, or if that name was not found.
S
Stephen Edwards 已提交
399 400 401 402 403 404 405
func (f *Font) Name(id NameID) string {
	x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
		return NameID(u16(b, 6)) == id
	})
	if err != nil {
		return ""
	}
N
Nigel Tao 已提交
406
	offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
S
Stephen Edwards 已提交
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
	// Return the ASCII value of the encoded string.
	// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
	src := f.name[offset : offset+length]
	var dst []byte
	if platformID != 1 { // UTF-16.
		if len(src)&1 != 0 {
			return ""
		}
		dst = make([]byte, len(src)/2)
		for i := range dst {
			dst[i] = printable(u16(src, 2*i))
		}
	} else { // ASCII.
		dst = make([]byte, len(src))
		for i, c := range src {
			dst[i] = printable(uint16(c))
		}
	}
	return string(dst)
}

func printable(r uint16) byte {
N
Nigel Tao 已提交
429
	if 0x20 <= r && r < 0x7f {
S
Stephen Edwards 已提交
430 431 432 433 434
		return byte(r)
	}
	return '?'
}

435 436 437
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
438
	j := int(i)
439
	if j < 0 || f.nGlyph <= j {
440 441 442 443
		return HMetric{}
	}
	if j >= f.nHMetric {
		p := 4 * (f.nHMetric - 1)
444
		return HMetric{
445 446
			AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, p)),
			LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
447 448 449
		}
	}
	return HMetric{
450 451
		AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, 4*j)),
		LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
452
	}
453 454 455
}

// HMetric returns the horizontal metrics for the glyph with the given index.
456
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
457
	h := f.unscaledHMetric(i)
458 459 460
	h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
	h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
	return h
461 462
}

463
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
464
// the given index. yMax is the top of the glyph's bounding box.
465
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
466 467 468 469 470 471
	j := int(i)
	if j < 0 || f.nGlyph <= j {
		return VMetric{}
	}
	if 4*j+4 <= len(f.vmtx) {
		return VMetric{
472 473
			AdvanceHeight:  fixed.Int26_6(u16(f.vmtx, 4*j)),
			TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
474 475
		}
	}
476 477 478 479 480 481
	// The OS/2 table has grown over time.
	// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
	// says that it was originally 68 bytes. Optional fields, including
	// the ascender and descender, are described at
	// http://www.microsoft.com/typography/otspec/os2.htm
	if len(f.os2) >= 72 {
482 483
		sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
		sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
484 485 486 487 488
		return VMetric{
			AdvanceHeight:  sTypoAscender - sTypoDescender,
			TopSideBearing: sTypoAscender - yMax,
		}
	}
489
	return VMetric{
490
		AdvanceHeight:  fixed.Int26_6(f.fUnitsPerEm),
491 492 493 494 495
		TopSideBearing: 0,
	}
}

// VMetric returns the vertical metrics for the glyph with the given index.
496
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
497 498
	// TODO: should 0 be bounds.YMax?
	v := f.unscaledVMetric(i, 0)
499 500 501 502 503
	v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
	v.TopSideBearing = f.scale(scale * v.TopSideBearing)
	return v
}

N
Nigel Tao 已提交
504 505 506
// Kern returns the horizontal adjustment for the given glyph pair. A positive
// kern means to move the glyphs further apart.
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
507 508 509 510 511 512 513
	if f.nKern == 0 {
		return 0
	}
	g := uint32(i0)<<16 | uint32(i1)
	lo, hi := 0, f.nKern
	for lo < hi {
		i := (lo + hi) / 2
514
		ig := u32(f.kern, 18+6*i)
515 516 517 518 519
		if ig < g {
			lo = i + 1
		} else if ig > g {
			hi = i
		} else {
520
			return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
521 522 523 524 525
		}
	}
	return 0
}

526 527 528
// Parse returns a new Font for the given TTF or TTC data.
//
// For TrueType Collections, the first font in the collection is parsed.
N
Nigel Tao 已提交
529
func Parse(ttf []byte) (font *Font, err error) {
530 531 532 533 534
	return parse(ttf, 0)
}

func parse(ttf []byte, offset int) (font *Font, err error) {
	if len(ttf)-offset < 12 {
535 536 537
		err = FormatError("TTF data is too short")
		return
	}
538 539 540
	originalOffset := offset
	magic, offset := u32(ttf, offset), offset+4
	switch magic {
541 542 543
	case 0x00010000:
		// No-op.
	case 0x74746366: // "ttcf" as a big-endian uint32.
544
		if originalOffset != 0 {
545 546 547
			err = FormatError("recursive TTC")
			return
		}
548 549
		ttcVersion, offset := u32(ttf, offset), offset+4
		if ttcVersion != 0x00010000 {
550 551 552 553
			// TODO: support TTC version 2.0, once I have such a .ttc file to test with.
			err = FormatError("bad TTC version")
			return
		}
554
		numFonts, offset := int(u32(ttf, offset)), offset+4
555 556 557 558
		if numFonts <= 0 {
			err = FormatError("bad number of TTC fonts")
			return
		}
559
		if len(ttf[offset:])/4 < numFonts {
560 561 562 563 564 565
			err = FormatError("TTC offset table is too short")
			return
		}
		// TODO: provide an API to select which font in a TrueType collection to return,
		// not just the first one. This may require an API to parse a TTC's name tables,
		// so users of this package can select the font in a TTC by name.
566
		offset = int(u32(ttf, offset))
567 568 569 570 571 572 573
		if offset <= 0 || offset > len(ttf) {
			err = FormatError("bad TTC offset")
			return
		}
		return parse(ttf, offset)
	default:
		err = FormatError("bad TTF version")
574 575
		return
	}
576
	n, offset := int(u16(ttf, offset)), offset+2
577 578 579 580 581 582 583 584 585 586 587
	if len(ttf) < 16*n+12 {
		err = FormatError("TTF data is too short")
		return
	}
	f := new(Font)
	// Assign the table slices.
	for i := 0; i < n; i++ {
		x := 16*i + 12
		switch string(ttf[x : x+4]) {
		case "cmap":
			f.cmap, err = readTable(ttf, ttf[x+8:x+16])
588 589 590 591
		case "cvt ":
			f.cvt, err = readTable(ttf, ttf[x+8:x+16])
		case "fpgm":
			f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
592 593
		case "glyf":
			f.glyf, err = readTable(ttf, ttf[x+8:x+16])
594 595
		case "hdmx":
			f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
596 597 598 599 600 601 602 603 604 605 606 607
		case "head":
			f.head, err = readTable(ttf, ttf[x+8:x+16])
		case "hhea":
			f.hhea, err = readTable(ttf, ttf[x+8:x+16])
		case "hmtx":
			f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
		case "kern":
			f.kern, err = readTable(ttf, ttf[x+8:x+16])
		case "loca":
			f.loca, err = readTable(ttf, ttf[x+8:x+16])
		case "maxp":
			f.maxp, err = readTable(ttf, ttf[x+8:x+16])
S
Stephen Edwards 已提交
608 609
		case "name":
			f.name, err = readTable(ttf, ttf[x+8:x+16])
610 611
		case "OS/2":
			f.os2, err = readTable(ttf, ttf[x+8:x+16])
612 613
		case "prep":
			f.prep, err = readTable(ttf, ttf[x+8:x+16])
614 615
		case "vmtx":
			f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
		}
		if err != nil {
			return
		}
	}
	// Parse and sanity-check the TTF data.
	if err = f.parseHead(); err != nil {
		return
	}
	if err = f.parseMaxp(); err != nil {
		return
	}
	if err = f.parseCmap(); err != nil {
		return
	}
	if err = f.parseKern(); err != nil {
		return
	}
	if err = f.parseHhea(); err != nil {
		return
	}
	font = f
	return
}