...

Source file src/github.com/golang/freetype/truetype/truetype.go

Documentation: github.com/golang/freetype/truetype

     1  // Copyright 2010 The Freetype-Go Authors. All rights reserved.
     2  // Use of this source code is governed by your choice of either the
     3  // FreeType License or the GNU General Public License version 2 (or
     4  // 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  // Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
     8  // and http://www.microsoft.com/typography/otspec/
     9  //
    10  // Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
    11  // metrics and control points. All these methods take a scale parameter, which
    12  // is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
    13  // example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
    14  // fixed.Int26_6(10 << 6).
    15  //
    16  // To measure a TrueType font in ideal FUnit space, use scale equal to
    17  // font.FUnitsPerEm().
    18  package truetype // import "github.com/golang/freetype/truetype"
    19  
    20  import (
    21  	"fmt"
    22  
    23  	"golang.org/x/image/math/fixed"
    24  )
    25  
    26  // An Index is a Font's index of a rune.
    27  type Index uint16
    28  
    29  // A NameID identifies a name table entry.
    30  //
    31  // See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
    32  type NameID uint16
    33  
    34  const (
    35  	NameIDCopyright          NameID = 0
    36  	NameIDFontFamily                = 1
    37  	NameIDFontSubfamily             = 2
    38  	NameIDUniqueSubfamilyID         = 3
    39  	NameIDFontFullName              = 4
    40  	NameIDNameTableVersion          = 5
    41  	NameIDPostscriptName            = 6
    42  	NameIDTrademarkNotice           = 7
    43  	NameIDManufacturerName          = 8
    44  	NameIDDesignerName              = 9
    45  	NameIDFontDescription           = 10
    46  	NameIDFontVendorURL             = 11
    47  	NameIDFontDesignerURL           = 12
    48  	NameIDFontLicense               = 13
    49  	NameIDFontLicenseURL            = 14
    50  	NameIDPreferredFamily           = 16
    51  	NameIDPreferredSubfamily        = 17
    52  	NameIDCompatibleName            = 18
    53  	NameIDSampleText                = 19
    54  )
    55  
    56  const (
    57  	// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
    58  	// least-significant 16-bit Platform Specific ID. The magic numbers are
    59  	// specified at https://www.microsoft.com/typography/otspec/name.htm
    60  	unicodeEncodingBMPOnly  = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0 BMP Only)
    61  	unicodeEncodingFull     = 0x00000004 // PID = 0 (Unicode), PSID = 4 (Unicode 2.0 Full Repertoire)
    62  	microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
    63  	microsoftUCS2Encoding   = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
    64  	microsoftUCS4Encoding   = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
    65  )
    66  
    67  // An HMetric holds the horizontal metrics of a single glyph.
    68  type HMetric struct {
    69  	AdvanceWidth, LeftSideBearing fixed.Int26_6
    70  }
    71  
    72  // A VMetric holds the vertical metrics of a single glyph.
    73  type VMetric struct {
    74  	AdvanceHeight, TopSideBearing fixed.Int26_6
    75  }
    76  
    77  // A FormatError reports that the input is not a valid TrueType font.
    78  type FormatError string
    79  
    80  func (e FormatError) Error() string {
    81  	return "freetype: invalid TrueType format: " + string(e)
    82  }
    83  
    84  // An UnsupportedError reports that the input uses a valid but unimplemented
    85  // TrueType feature.
    86  type UnsupportedError string
    87  
    88  func (e UnsupportedError) Error() string {
    89  	return "freetype: unsupported TrueType feature: " + string(e)
    90  }
    91  
    92  // u32 returns the big-endian uint32 at b[i:].
    93  func u32(b []byte, i int) uint32 {
    94  	return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
    95  }
    96  
    97  // u16 returns the big-endian uint16 at b[i:].
    98  func u16(b []byte, i int) uint16 {
    99  	return uint16(b[i])<<8 | uint16(b[i+1])
   100  }
   101  
   102  // readTable returns a slice of the TTF data given by a table's directory entry.
   103  func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
   104  	offset := int(u32(offsetLength, 0))
   105  	if offset < 0 {
   106  		return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
   107  	}
   108  	length := int(u32(offsetLength, 4))
   109  	if length < 0 {
   110  		return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
   111  	}
   112  	end := offset + length
   113  	if end < 0 || end > len(ttf) {
   114  		return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
   115  	}
   116  	return ttf[offset:end], nil
   117  }
   118  
   119  // parseSubtables returns the offset and platformID of the best subtable in
   120  // table, where best favors a Unicode cmap encoding, and failing that, a
   121  // Microsoft cmap encoding. offset is the offset of the first subtable in
   122  // table, and size is the size of each subtable.
   123  //
   124  // If pred is non-nil, then only subtables that satisfy that predicate will be
   125  // considered.
   126  func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
   127  	bestOffset int, bestPID uint32, retErr error) {
   128  
   129  	if len(table) < 4 {
   130  		return 0, 0, FormatError(name + " too short")
   131  	}
   132  	nSubtables := int(u16(table, 2))
   133  	if len(table) < size*nSubtables+offset {
   134  		return 0, 0, FormatError(name + " too short")
   135  	}
   136  	ok := false
   137  	for i := 0; i < nSubtables; i, offset = i+1, offset+size {
   138  		if pred != nil && !pred(table[offset:]) {
   139  			continue
   140  		}
   141  		// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
   142  		// All values are big-endian.
   143  		pidPsid := u32(table, offset)
   144  		// We prefer the Unicode cmap encoding. Failing to find that, we fall
   145  		// back onto the Microsoft cmap encoding.
   146  		if pidPsid == unicodeEncodingBMPOnly || pidPsid == unicodeEncodingFull {
   147  			bestOffset, bestPID, ok = offset, pidPsid>>16, true
   148  			break
   149  
   150  		} else if pidPsid == microsoftSymbolEncoding ||
   151  			pidPsid == microsoftUCS2Encoding ||
   152  			pidPsid == microsoftUCS4Encoding {
   153  
   154  			bestOffset, bestPID, ok = offset, pidPsid>>16, true
   155  			// We don't break out of the for loop, so that Unicode can override Microsoft.
   156  		}
   157  	}
   158  	if !ok {
   159  		return 0, 0, UnsupportedError(name + " encoding")
   160  	}
   161  	return bestOffset, bestPID, nil
   162  }
   163  
   164  const (
   165  	locaOffsetFormatUnknown int = iota
   166  	locaOffsetFormatShort
   167  	locaOffsetFormatLong
   168  )
   169  
   170  // A cm holds a parsed cmap entry.
   171  type cm struct {
   172  	start, end, delta, offset uint32
   173  }
   174  
   175  // A Font represents a Truetype font.
   176  type Font struct {
   177  	// Tables sliced from the TTF data. The different tables are documented
   178  	// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
   179  	cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
   180  
   181  	cmapIndexes []byte
   182  
   183  	// Cached values derived from the raw ttf data.
   184  	cm                      []cm
   185  	locaOffsetFormat        int
   186  	nGlyph, nHMetric, nKern int
   187  	fUnitsPerEm             int32
   188  	ascent                  int32               // In FUnits.
   189  	descent                 int32               // In FUnits; typically negative.
   190  	bounds                  fixed.Rectangle26_6 // In FUnits.
   191  	// Values from the maxp section.
   192  	maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
   193  }
   194  
   195  func (f *Font) parseCmap() error {
   196  	const (
   197  		cmapFormat4         = 4
   198  		cmapFormat12        = 12
   199  		languageIndependent = 0
   200  	)
   201  
   202  	offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	offset = int(u32(f.cmap, offset+4))
   207  	if offset <= 0 || offset > len(f.cmap) {
   208  		return FormatError("bad cmap offset")
   209  	}
   210  
   211  	cmapFormat := u16(f.cmap, offset)
   212  	switch cmapFormat {
   213  	case cmapFormat4:
   214  		language := u16(f.cmap, offset+4)
   215  		if language != languageIndependent {
   216  			return UnsupportedError(fmt.Sprintf("language: %d", language))
   217  		}
   218  		segCountX2 := int(u16(f.cmap, offset+6))
   219  		if segCountX2%2 == 1 {
   220  			return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
   221  		}
   222  		segCount := segCountX2 / 2
   223  		offset += 14
   224  		f.cm = make([]cm, segCount)
   225  		for i := 0; i < segCount; i++ {
   226  			f.cm[i].end = uint32(u16(f.cmap, offset))
   227  			offset += 2
   228  		}
   229  		offset += 2
   230  		for i := 0; i < segCount; i++ {
   231  			f.cm[i].start = uint32(u16(f.cmap, offset))
   232  			offset += 2
   233  		}
   234  		for i := 0; i < segCount; i++ {
   235  			f.cm[i].delta = uint32(u16(f.cmap, offset))
   236  			offset += 2
   237  		}
   238  		for i := 0; i < segCount; i++ {
   239  			f.cm[i].offset = uint32(u16(f.cmap, offset))
   240  			offset += 2
   241  		}
   242  		f.cmapIndexes = f.cmap[offset:]
   243  		return nil
   244  
   245  	case cmapFormat12:
   246  		if u16(f.cmap, offset+2) != 0 {
   247  			return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
   248  		}
   249  		length := u32(f.cmap, offset+4)
   250  		language := u32(f.cmap, offset+8)
   251  		if language != languageIndependent {
   252  			return UnsupportedError(fmt.Sprintf("language: %d", language))
   253  		}
   254  		nGroups := u32(f.cmap, offset+12)
   255  		if length != 12*nGroups+16 {
   256  			return FormatError("inconsistent cmap length")
   257  		}
   258  		offset += 16
   259  		f.cm = make([]cm, nGroups)
   260  		for i := uint32(0); i < nGroups; i++ {
   261  			f.cm[i].start = u32(f.cmap, offset+0)
   262  			f.cm[i].end = u32(f.cmap, offset+4)
   263  			f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
   264  			offset += 12
   265  		}
   266  		return nil
   267  	}
   268  	return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
   269  }
   270  
   271  func (f *Font) parseHead() error {
   272  	if len(f.head) != 54 {
   273  		return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
   274  	}
   275  	f.fUnitsPerEm = int32(u16(f.head, 18))
   276  	f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
   277  	f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
   278  	f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
   279  	f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
   280  	switch i := u16(f.head, 50); i {
   281  	case 0:
   282  		f.locaOffsetFormat = locaOffsetFormatShort
   283  	case 1:
   284  		f.locaOffsetFormat = locaOffsetFormatLong
   285  	default:
   286  		return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
   287  	}
   288  	return nil
   289  }
   290  
   291  func (f *Font) parseHhea() error {
   292  	if len(f.hhea) != 36 {
   293  		return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
   294  	}
   295  	f.ascent = int32(int16(u16(f.hhea, 4)))
   296  	f.descent = int32(int16(u16(f.hhea, 6)))
   297  	f.nHMetric = int(u16(f.hhea, 34))
   298  	if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
   299  		return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
   300  	}
   301  	return nil
   302  }
   303  
   304  func (f *Font) parseKern() error {
   305  	// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
   306  	// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
   307  	// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
   308  	// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
   309  	// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
   310  	// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
   311  	// and Windows should use the old format."
   312  	// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
   313  	// just like the C Freetype implementation.
   314  	if len(f.kern) == 0 {
   315  		if f.nKern != 0 {
   316  			return FormatError("bad kern table length")
   317  		}
   318  		return nil
   319  	}
   320  	if len(f.kern) < 18 {
   321  		return FormatError("kern data too short")
   322  	}
   323  	version, offset := u16(f.kern, 0), 2
   324  	if version != 0 {
   325  		return UnsupportedError(fmt.Sprintf("kern version: %d", version))
   326  	}
   327  
   328  	n, offset := u16(f.kern, offset), offset+2
   329  	if n == 0 {
   330  		return UnsupportedError("kern nTables: 0")
   331  	}
   332  	// TODO: support multiple subtables. In practice, almost all .ttf files
   333  	// have only one subtable, if they have a kern table at all. But it's not
   334  	// impossible. Xolonium Regular (https://fontlibrary.org/en/font/xolonium)
   335  	// has 3 subtables. Those subtables appear to be disjoint, rather than
   336  	// being the same kerning pairs encoded in three different ways.
   337  	//
   338  	// For now, we'll use only the first subtable.
   339  
   340  	offset += 2 // Skip the version.
   341  	length, offset := int(u16(f.kern, offset)), offset+2
   342  	coverage, offset := u16(f.kern, offset), offset+2
   343  	if coverage != 0x0001 {
   344  		// We only support horizontal kerning.
   345  		return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
   346  	}
   347  	f.nKern, offset = int(u16(f.kern, offset)), offset+2
   348  	if 6*f.nKern != length-14 {
   349  		return FormatError("bad kern table length")
   350  	}
   351  	return nil
   352  }
   353  
   354  func (f *Font) parseMaxp() error {
   355  	if len(f.maxp) != 32 {
   356  		return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
   357  	}
   358  	f.nGlyph = int(u16(f.maxp, 4))
   359  	f.maxTwilightPoints = u16(f.maxp, 16)
   360  	f.maxStorage = u16(f.maxp, 18)
   361  	f.maxFunctionDefs = u16(f.maxp, 20)
   362  	f.maxStackElements = u16(f.maxp, 24)
   363  	return nil
   364  }
   365  
   366  // scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
   367  func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
   368  	if x >= 0 {
   369  		x += fixed.Int26_6(f.fUnitsPerEm) / 2
   370  	} else {
   371  		x -= fixed.Int26_6(f.fUnitsPerEm) / 2
   372  	}
   373  	return x / fixed.Int26_6(f.fUnitsPerEm)
   374  }
   375  
   376  // Bounds returns the union of a Font's glyphs' bounds.
   377  func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
   378  	b := f.bounds
   379  	b.Min.X = f.scale(scale * b.Min.X)
   380  	b.Min.Y = f.scale(scale * b.Min.Y)
   381  	b.Max.X = f.scale(scale * b.Max.X)
   382  	b.Max.Y = f.scale(scale * b.Max.Y)
   383  	return b
   384  }
   385  
   386  // FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
   387  func (f *Font) FUnitsPerEm() int32 {
   388  	return f.fUnitsPerEm
   389  }
   390  
   391  // Index returns a Font's index for the given rune.
   392  func (f *Font) Index(x rune) Index {
   393  	c := uint32(x)
   394  	for i, j := 0, len(f.cm); i < j; {
   395  		h := i + (j-i)/2
   396  		cm := &f.cm[h]
   397  		if c < cm.start {
   398  			j = h
   399  		} else if cm.end < c {
   400  			i = h + 1
   401  		} else if cm.offset == 0 {
   402  			return Index(c + cm.delta)
   403  		} else {
   404  			offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
   405  			return Index(u16(f.cmapIndexes, offset))
   406  		}
   407  	}
   408  	return 0
   409  }
   410  
   411  // Name returns the Font's name value for the given NameID. It returns "" if
   412  // there was an error, or if that name was not found.
   413  func (f *Font) Name(id NameID) string {
   414  	x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
   415  		return NameID(u16(b, 6)) == id
   416  	})
   417  	if err != nil {
   418  		return ""
   419  	}
   420  	offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
   421  	// Return the ASCII value of the encoded string.
   422  	// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
   423  	src := f.name[offset : offset+length]
   424  	var dst []byte
   425  	if platformID != 1 { // UTF-16.
   426  		if len(src)&1 != 0 {
   427  			return ""
   428  		}
   429  		dst = make([]byte, len(src)/2)
   430  		for i := range dst {
   431  			dst[i] = printable(u16(src, 2*i))
   432  		}
   433  	} else { // ASCII.
   434  		dst = make([]byte, len(src))
   435  		for i, c := range src {
   436  			dst[i] = printable(uint16(c))
   437  		}
   438  	}
   439  	return string(dst)
   440  }
   441  
   442  func printable(r uint16) byte {
   443  	if 0x20 <= r && r < 0x7f {
   444  		return byte(r)
   445  	}
   446  	return '?'
   447  }
   448  
   449  // unscaledHMetric returns the unscaled horizontal metrics for the glyph with
   450  // the given index.
   451  func (f *Font) unscaledHMetric(i Index) (h HMetric) {
   452  	j := int(i)
   453  	if j < 0 || f.nGlyph <= j {
   454  		return HMetric{}
   455  	}
   456  	if j >= f.nHMetric {
   457  		p := 4 * (f.nHMetric - 1)
   458  		return HMetric{
   459  			AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, p)),
   460  			LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
   461  		}
   462  	}
   463  	return HMetric{
   464  		AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, 4*j)),
   465  		LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
   466  	}
   467  }
   468  
   469  // HMetric returns the horizontal metrics for the glyph with the given index.
   470  func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
   471  	h := f.unscaledHMetric(i)
   472  	h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
   473  	h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
   474  	return h
   475  }
   476  
   477  // unscaledVMetric returns the unscaled vertical metrics for the glyph with
   478  // the given index. yMax is the top of the glyph's bounding box.
   479  func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
   480  	j := int(i)
   481  	if j < 0 || f.nGlyph <= j {
   482  		return VMetric{}
   483  	}
   484  	if 4*j+4 <= len(f.vmtx) {
   485  		return VMetric{
   486  			AdvanceHeight:  fixed.Int26_6(u16(f.vmtx, 4*j)),
   487  			TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
   488  		}
   489  	}
   490  	// The OS/2 table has grown over time.
   491  	// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
   492  	// says that it was originally 68 bytes. Optional fields, including
   493  	// the ascender and descender, are described at
   494  	// http://www.microsoft.com/typography/otspec/os2.htm
   495  	if len(f.os2) >= 72 {
   496  		sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
   497  		sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
   498  		return VMetric{
   499  			AdvanceHeight:  sTypoAscender - sTypoDescender,
   500  			TopSideBearing: sTypoAscender - yMax,
   501  		}
   502  	}
   503  	return VMetric{
   504  		AdvanceHeight:  fixed.Int26_6(f.fUnitsPerEm),
   505  		TopSideBearing: 0,
   506  	}
   507  }
   508  
   509  // VMetric returns the vertical metrics for the glyph with the given index.
   510  func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
   511  	// TODO: should 0 be bounds.YMax?
   512  	v := f.unscaledVMetric(i, 0)
   513  	v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
   514  	v.TopSideBearing = f.scale(scale * v.TopSideBearing)
   515  	return v
   516  }
   517  
   518  // Kern returns the horizontal adjustment for the given glyph pair. A positive
   519  // kern means to move the glyphs further apart.
   520  func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
   521  	if f.nKern == 0 {
   522  		return 0
   523  	}
   524  	g := uint32(i0)<<16 | uint32(i1)
   525  	lo, hi := 0, f.nKern
   526  	for lo < hi {
   527  		i := (lo + hi) / 2
   528  		ig := u32(f.kern, 18+6*i)
   529  		if ig < g {
   530  			lo = i + 1
   531  		} else if ig > g {
   532  			hi = i
   533  		} else {
   534  			return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
   535  		}
   536  	}
   537  	return 0
   538  }
   539  
   540  // Parse returns a new Font for the given TTF or TTC data.
   541  //
   542  // For TrueType Collections, the first font in the collection is parsed.
   543  func Parse(ttf []byte) (font *Font, err error) {
   544  	return parse(ttf, 0)
   545  }
   546  
   547  func parse(ttf []byte, offset int) (font *Font, err error) {
   548  	if len(ttf)-offset < 12 {
   549  		err = FormatError("TTF data is too short")
   550  		return
   551  	}
   552  	originalOffset := offset
   553  	magic, offset := u32(ttf, offset), offset+4
   554  	switch magic {
   555  	case 0x00010000:
   556  		// No-op.
   557  	case 0x74746366: // "ttcf" as a big-endian uint32.
   558  		if originalOffset != 0 {
   559  			err = FormatError("recursive TTC")
   560  			return
   561  		}
   562  		ttcVersion, offset := u32(ttf, offset), offset+4
   563  		if ttcVersion != 0x00010000 && ttcVersion != 0x00020000 {
   564  			err = FormatError("bad TTC version")
   565  			return
   566  		}
   567  		numFonts, offset := int(u32(ttf, offset)), offset+4
   568  		if numFonts <= 0 {
   569  			err = FormatError("bad number of TTC fonts")
   570  			return
   571  		}
   572  		if len(ttf[offset:])/4 < numFonts {
   573  			err = FormatError("TTC offset table is too short")
   574  			return
   575  		}
   576  		// TODO: provide an API to select which font in a TrueType collection to return,
   577  		// not just the first one. This may require an API to parse a TTC's name tables,
   578  		// so users of this package can select the font in a TTC by name.
   579  		offset = int(u32(ttf, offset))
   580  		if offset <= 0 || offset > len(ttf) {
   581  			err = FormatError("bad TTC offset")
   582  			return
   583  		}
   584  		return parse(ttf, offset)
   585  	default:
   586  		err = FormatError("bad TTF version")
   587  		return
   588  	}
   589  	n, offset := int(u16(ttf, offset)), offset+2
   590  	offset += 6 // Skip the searchRange, entrySelector and rangeShift.
   591  	if len(ttf) < 16*n+offset {
   592  		err = FormatError("TTF data is too short")
   593  		return
   594  	}
   595  	f := new(Font)
   596  	// Assign the table slices.
   597  	for i := 0; i < n; i++ {
   598  		x := 16*i + offset
   599  		switch string(ttf[x : x+4]) {
   600  		case "cmap":
   601  			f.cmap, err = readTable(ttf, ttf[x+8:x+16])
   602  		case "cvt ":
   603  			f.cvt, err = readTable(ttf, ttf[x+8:x+16])
   604  		case "fpgm":
   605  			f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
   606  		case "glyf":
   607  			f.glyf, err = readTable(ttf, ttf[x+8:x+16])
   608  		case "hdmx":
   609  			f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
   610  		case "head":
   611  			f.head, err = readTable(ttf, ttf[x+8:x+16])
   612  		case "hhea":
   613  			f.hhea, err = readTable(ttf, ttf[x+8:x+16])
   614  		case "hmtx":
   615  			f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
   616  		case "kern":
   617  			f.kern, err = readTable(ttf, ttf[x+8:x+16])
   618  		case "loca":
   619  			f.loca, err = readTable(ttf, ttf[x+8:x+16])
   620  		case "maxp":
   621  			f.maxp, err = readTable(ttf, ttf[x+8:x+16])
   622  		case "name":
   623  			f.name, err = readTable(ttf, ttf[x+8:x+16])
   624  		case "OS/2":
   625  			f.os2, err = readTable(ttf, ttf[x+8:x+16])
   626  		case "prep":
   627  			f.prep, err = readTable(ttf, ttf[x+8:x+16])
   628  		case "vmtx":
   629  			f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
   630  		}
   631  		if err != nil {
   632  			return
   633  		}
   634  	}
   635  	// Parse and sanity-check the TTF data.
   636  	if err = f.parseHead(); err != nil {
   637  		return
   638  	}
   639  	if err = f.parseMaxp(); err != nil {
   640  		return
   641  	}
   642  	if err = f.parseCmap(); err != nil {
   643  		return
   644  	}
   645  	if err = f.parseKern(); err != nil {
   646  		return
   647  	}
   648  	if err = f.parseHhea(); err != nil {
   649  		return
   650  	}
   651  	font = f
   652  	return
   653  }
   654  

View as plain text