truetype.go 16.3 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 29 30
type Index uint16

// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
31
	AdvanceWidth, LeftSideBearing fixed.Int26_6
32 33 34 35
}

// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
36
	AdvanceHeight, TopSideBearing fixed.Int26_6
37 38 39 40 41
}

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

N
Nigel Tao 已提交
42
func (e FormatError) Error() string {
43 44 45 46 47 48 49
	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 已提交
50
func (e UnsupportedError) Error() string {
51 52 53
	return "freetype: unsupported TrueType feature: " + string(e)
}

54 55 56
// 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])
57 58
}

59 60 61
// 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])
62 63 64
}

// readTable returns a slice of the TTF data given by a table's directory entry.
N
Nigel Tao 已提交
65
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
66
	offset := int(u32(offsetLength, 0))
67 68
	if offset < 0 {
		return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
69
	}
70
	length := int(u32(offsetLength, 4))
71 72
	if length < 0 {
		return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
73
	}
74 75 76 77 78
	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
79 80 81 82 83 84 85 86 87 88
}

const (
	locaOffsetFormatUnknown int = iota
	locaOffsetFormatShort
	locaOffsetFormatLong
)

// A cm holds a parsed cmap entry.
type cm struct {
89
	start, end, delta, offset uint32
90 91 92 93 94 95
}

// 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
96
	cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, os2, prep, vmtx []byte
97 98

	cmapIndexes []byte
99 100 101 102 103

	// Cached values derived from the raw ttf data.
	cm                      []cm
	locaOffsetFormat        int
	nGlyph, nHMetric, nKern int
104
	fUnitsPerEm             int32
105
	bounds                  fixed.Rectangle26_6
106 107
	// Values from the maxp section.
	maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
108 109
}

N
Nigel Tao 已提交
110
func (f *Font) parseCmap() error {
111 112
	const (
		cmapFormat4         = 4
113
		cmapFormat12        = 12
114 115 116
		languageIndependent = 0

		// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
117 118 119 120 121 122
		// 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)
123 124 125 126 127
	)

	if len(f.cmap) < 4 {
		return FormatError("cmap too short")
	}
128
	nsubtab := int(u16(f.cmap, 2))
129 130 131
	if len(f.cmap) < 8*nsubtab+4 {
		return FormatError("cmap too short")
	}
132
	offset, found, x := 0, false, 4
133 134 135
	for i := 0; i < nsubtab; i++ {
		// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
		// All values are big-endian.
136 137
		pidPsid, o := u32(f.cmap, x), u32(f.cmap, x+4)
		x += 8
138 139 140 141 142
		// We prefer the Unicode cmap encoding. Failing to find that, we fall
		// back onto the Microsoft cmap encoding.
		if pidPsid == unicodeEncoding {
			offset, found = int(o), true
			break
143 144 145 146 147

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

148 149 150 151 152 153 154 155 156 157 158
			offset, found = int(o), true
			// We don't break out of the for loop, so that Unicode can override Microsoft.
		}
	}
	if !found {
		return UnsupportedError("cmap encoding")
	}
	if offset <= 0 || offset > len(f.cmap) {
		return FormatError("bad cmap offset")
	}

159
	cmapFormat := u16(f.cmap, offset)
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
	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
		}
177
		offset += 2
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
		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
215
	}
216
	return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
217 218
}

N
Nigel Tao 已提交
219
func (f *Font) parseHead() error {
220 221 222
	if len(f.head) != 54 {
		return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
	}
223
	f.fUnitsPerEm = int32(u16(f.head, 18))
224 225 226 227
	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)))
228
	switch i := u16(f.head, 50); i {
229 230 231 232 233 234 235 236 237 238
	case 0:
		f.locaOffsetFormat = locaOffsetFormatShort
	case 1:
		f.locaOffsetFormat = locaOffsetFormatLong
	default:
		return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
	}
	return nil
}

N
Nigel Tao 已提交
239
func (f *Font) parseHhea() error {
240 241 242
	if len(f.hhea) != 36 {
		return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
	}
243
	f.nHMetric = int(u16(f.hhea, 34))
244 245 246 247 248 249
	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 已提交
250
func (f *Font) parseKern() error {
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	// 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")
	}
269
	version, offset := u16(f.kern, 0), 2
270 271 272
	if version != 0 {
		return UnsupportedError(fmt.Sprintf("kern version: %d", version))
	}
273
	n, offset := u16(f.kern, offset), offset+2
274 275 276
	if n != 1 {
		return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
	}
277 278 279
	offset += 2
	length, offset := int(u16(f.kern, offset)), offset+2
	coverage, offset := u16(f.kern, offset), offset+2
280 281 282 283
	if coverage != 0x0001 {
		// We only support horizontal kerning.
		return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
	}
284
	f.nKern, offset = int(u16(f.kern, offset)), offset+2
285 286 287 288 289 290
	if 6*f.nKern != length-14 {
		return FormatError("bad kern table length")
	}
	return nil
}

N
Nigel Tao 已提交
291
func (f *Font) parseMaxp() error {
292 293 294
	if len(f.maxp) != 32 {
		return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
	}
295
	f.nGlyph = int(u16(f.maxp, 4))
296 297 298 299
	f.maxTwilightPoints = u16(f.maxp, 16)
	f.maxStorage = u16(f.maxp, 18)
	f.maxFunctionDefs = u16(f.maxp, 20)
	f.maxStackElements = u16(f.maxp, 24)
300 301 302
	return nil
}

303
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
304
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
305
	if x >= 0 {
306
		x += fixed.Int26_6(f.fUnitsPerEm) / 2
307
	} else {
308
		x -= fixed.Int26_6(f.fUnitsPerEm) / 2
309
	}
310
	return x / fixed.Int26_6(f.fUnitsPerEm)
311 312
}

313
// Bounds returns the union of a Font's glyphs' bounds.
314
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
315
	b := f.bounds
316 317 318 319
	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)
320
	return b
321 322
}

323 324 325
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
func (f *Font) FUnitsPerEm() int32 {
	return f.fUnitsPerEm
326 327
}

N
Nigel Tao 已提交
328 329
// Index returns a Font's index for the given rune.
func (f *Font) Index(x rune) Index {
330
	c := uint32(x)
331 332 333 334 335 336 337 338 339 340 341
	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))
342
			return Index(u16(f.cmapIndexes, offset))
343 344
		}
	}
345
	return 0
346 347
}

348 349 350
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
351
	j := int(i)
352
	if j < 0 || f.nGlyph <= j {
353 354 355 356
		return HMetric{}
	}
	if j >= f.nHMetric {
		p := 4 * (f.nHMetric - 1)
357
		return HMetric{
358 359
			AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, p)),
			LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
360 361 362
		}
	}
	return HMetric{
363 364
		AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, 4*j)),
		LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
365
	}
366 367 368
}

// HMetric returns the horizontal metrics for the glyph with the given index.
369
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
370
	h := f.unscaledHMetric(i)
371 372 373
	h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
	h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
	return h
374 375
}

376
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
377
// the given index. yMax is the top of the glyph's bounding box.
378
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
379 380 381 382 383 384
	j := int(i)
	if j < 0 || f.nGlyph <= j {
		return VMetric{}
	}
	if 4*j+4 <= len(f.vmtx) {
		return VMetric{
385 386
			AdvanceHeight:  fixed.Int26_6(u16(f.vmtx, 4*j)),
			TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
387 388
		}
	}
389 390 391 392 393 394
	// 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 {
395 396
		sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
		sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
397 398 399 400 401
		return VMetric{
			AdvanceHeight:  sTypoAscender - sTypoDescender,
			TopSideBearing: sTypoAscender - yMax,
		}
	}
402
	return VMetric{
403
		AdvanceHeight:  fixed.Int26_6(f.fUnitsPerEm),
404 405 406 407 408
		TopSideBearing: 0,
	}
}

// VMetric returns the vertical metrics for the glyph with the given index.
409
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
410 411
	// TODO: should 0 be bounds.YMax?
	v := f.unscaledVMetric(i, 0)
412 413 414 415 416
	v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
	v.TopSideBearing = f.scale(scale * v.TopSideBearing)
	return v
}

N
Nigel Tao 已提交
417 418 419
// 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 {
420 421 422 423 424 425 426
	if f.nKern == 0 {
		return 0
	}
	g := uint32(i0)<<16 | uint32(i1)
	lo, hi := 0, f.nKern
	for lo < hi {
		i := (lo + hi) / 2
427
		ig := u32(f.kern, 18+6*i)
428 429 430 431 432
		if ig < g {
			lo = i + 1
		} else if ig > g {
			hi = i
		} else {
433
			return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
434 435 436 437 438
		}
	}
	return 0
}

439 440 441
// 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 已提交
442
func Parse(ttf []byte) (font *Font, err error) {
443 444 445 446 447
	return parse(ttf, 0)
}

func parse(ttf []byte, offset int) (font *Font, err error) {
	if len(ttf)-offset < 12 {
448 449 450
		err = FormatError("TTF data is too short")
		return
	}
451 452 453
	originalOffset := offset
	magic, offset := u32(ttf, offset), offset+4
	switch magic {
454 455 456
	case 0x00010000:
		// No-op.
	case 0x74746366: // "ttcf" as a big-endian uint32.
457
		if originalOffset != 0 {
458 459 460
			err = FormatError("recursive TTC")
			return
		}
461 462
		ttcVersion, offset := u32(ttf, offset), offset+4
		if ttcVersion != 0x00010000 {
463 464 465 466
			// TODO: support TTC version 2.0, once I have such a .ttc file to test with.
			err = FormatError("bad TTC version")
			return
		}
467
		numFonts, offset := int(u32(ttf, offset)), offset+4
468 469 470 471
		if numFonts <= 0 {
			err = FormatError("bad number of TTC fonts")
			return
		}
472
		if len(ttf[offset:])/4 < numFonts {
473 474 475 476 477 478
			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.
479
		offset = int(u32(ttf, offset))
480 481 482 483 484 485 486
		if offset <= 0 || offset > len(ttf) {
			err = FormatError("bad TTC offset")
			return
		}
		return parse(ttf, offset)
	default:
		err = FormatError("bad TTF version")
487 488
		return
	}
489
	n, offset := int(u16(ttf, offset)), offset+2
490 491 492 493 494 495 496 497 498 499 500
	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])
501 502 503 504
		case "cvt ":
			f.cvt, err = readTable(ttf, ttf[x+8:x+16])
		case "fpgm":
			f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
505 506
		case "glyf":
			f.glyf, err = readTable(ttf, ttf[x+8:x+16])
507 508
		case "hdmx":
			f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
509 510 511 512 513 514 515 516 517 518 519 520
		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])
521 522
		case "OS/2":
			f.os2, err = readTable(ttf, ttf[x+8:x+16])
523 524
		case "prep":
			f.prep, err = readTable(ttf, ttf[x+8:x+16])
525 526
		case "vmtx":
			f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
		}
		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
}