github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/cff/parser.go (about)

     1  package cff
     2  
     3  // code is adapted from golang.org/x/image/font/sfnt
     4  
     5  import (
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"math"
    10  
    11  	"github.com/benoitkugler/textlayout/fonts"
    12  	ps "github.com/benoitkugler/textlayout/fonts/psinterpreter"
    13  	"github.com/benoitkugler/textlayout/fonts/simpleencodings"
    14  )
    15  
    16  var (
    17  	errInvalidCFFTable                = errors.New("invalid CFF font file")
    18  	errUnsupportedCFFVersion          = errors.New("unsupported CFF version")
    19  	errUnsupportedRealNumberEncoding  = errors.New("unsupported real number encoding")
    20  	errUnsupportedCFFFDSelectTable    = errors.New("unsupported FD Select version")
    21  	errUnsupportedNumberOfSubroutines = errors.New("unsupported number of subroutines")
    22  
    23  	be = binary.BigEndian
    24  )
    25  
    26  const (
    27  	// since SID = 0 means .notdef, we use a reserved value
    28  	// to mean unset
    29  	unsetSID = uint16(0xFFFF)
    30  )
    31  
    32  type userStrings [][]byte
    33  
    34  // return either the predefined string or the user defined one
    35  func (u userStrings) getString(sid uint16) (string, error) {
    36  	if sid == unsetSID {
    37  		return "", nil
    38  	}
    39  	if sid < 391 {
    40  		return stdStrings[sid], nil
    41  	}
    42  	sid -= 391
    43  	if int(sid) >= len(u) {
    44  		return "", fmt.Errorf("invalid glyph index %d", sid)
    45  	}
    46  	return string(u[sid]), nil
    47  }
    48  
    49  // Compact Font Format (CFF) fonts are written in PostScript, a stack-based
    50  // programming language.
    51  //
    52  // A fundamental concept is a DICT, or a key-value map, expressed in reverse
    53  // Polish notation. For example, this sequence of operations:
    54  //	- push the number 379
    55  //	- version operator
    56  //	- push the number 392
    57  //	- Notice operator
    58  //	- etc
    59  //	- push the number 100
    60  //	- push the number 0
    61  //	- push the number 500
    62  //	- push the number 800
    63  //	- FontBBox operator
    64  //	- etc
    65  // defines a DICT that maps "version" to the String ID (SID) 379, "Notice" to
    66  // the SID 392, "FontBBox" to the four numbers [100, 0, 500, 800], etc.
    67  //
    68  // The first 391 String IDs (starting at 0) are predefined as per the CFF spec
    69  // Appendix A, in 5176.CFF.pdf referenced below. For example, 379 means
    70  // "001.000". String ID 392 is not predefined, and is mapped by a separate
    71  // structure, the "String INDEX", inside the CFF data. (String ID 391 is also
    72  // not predefined. Specifically for ../testdata/CFFTest.otf, 391 means
    73  // "uni4E2D", as this font contains a glyph for U+4E2D).
    74  //
    75  // The actual glyph vectors are similarly encoded (in PostScript), in a format
    76  // called Type 2 Charstrings. The wire encoding is similar to but not exactly
    77  // the same as CFF's. For example, the byte 0x05 means FontBBox for CFF DICTs,
    78  // but means rlineto (relative line-to) for Type 2 Charstrings. See
    79  // 5176.CFF.pdf Appendix H and 5177.Type2.pdf Appendix A in the PDF files
    80  // referenced below.
    81  //
    82  // The relevant specifications are:
    83  // 	- http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
    84  // 	- http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
    85  type cffParser struct {
    86  	src    []byte // whole input
    87  	offset int    // current position
    88  }
    89  
    90  func (p *cffParser) parse() ([]Font, error) {
    91  	// header was checked prior to this call
    92  
    93  	// Parse the Name INDEX.
    94  	fontNames, err := p.parseNames()
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	topDicts, err := p.parseTopDicts()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	// 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
   104  	// should match the count of the Name INDEX
   105  	if len(topDicts) != len(fontNames) {
   106  		return nil, fmt.Errorf("top DICT length doest not match Names (%d, %d)", len(topDicts),
   107  			len(fontNames))
   108  	}
   109  
   110  	// parse the String INDEX.
   111  	strs, err := p.parseUserStrings()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	out := make([]Font, len(topDicts))
   117  
   118  	// use the strings to fetch the PSInfo
   119  	for i, topDict := range topDicts {
   120  		out[i].fontName = fontNames[i]
   121  		out[i].userStrings = strs
   122  		out[i].PSInfo, err = topDict.toInfo(strs)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  		out[i].cidFontName, err = strs.getString(topDict.cidFontName)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		out[i].FontMatrix = topDict.fontMatrix
   131  	}
   132  
   133  	// Parse the Global Subrs [Subroutines] INDEX,
   134  	// shared among all fonts.
   135  	globalSubrs, err := p.parseIndex()
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	for i, topDict := range topDicts {
   141  		out[i].globalSubrs = globalSubrs
   142  
   143  		// Parse the CharStrings INDEX, whose location was found in the Top DICT.
   144  		if err = p.seek(topDict.charStringsOffset); err != nil {
   145  			return nil, err
   146  		}
   147  		out[i].charstrings, err = p.parseIndex()
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  		numGlyphs := uint16(len(out[i].charstrings))
   152  
   153  		out[i].charset, err = p.parseCharset(topDict.charsetOffset, numGlyphs)
   154  		if err != nil {
   155  			return nil, err
   156  		}
   157  
   158  		out[i].Encoding, err = p.parseEncoding(topDict.encodingOffset, numGlyphs, out[i].charset, strs)
   159  		if err != nil {
   160  			return nil, err
   161  		}
   162  
   163  		if !topDict.isCIDFont {
   164  			// Parse the Private DICT, whose location was found in the Top DICT.
   165  			var localSubrs [][]byte
   166  			var priv privateDict
   167  			priv, localSubrs, err = p.parsePrivateDICT(topDict.privateDictOffset, topDict.privateDictLength)
   168  			if err != nil {
   169  				return nil, err
   170  			}
   171  			out[i].localSubrs = [][][]byte{localSubrs}
   172  			out[i].priv = []privateDict{priv}
   173  		} else {
   174  			// Parse the Font Dict Select data, whose location was found in the Top
   175  			// DICT.
   176  			out[i].fdSelect, err = p.parseFDSelect(topDict.fdSelect, numGlyphs)
   177  			if err != nil {
   178  				return nil, err
   179  			}
   180  			indexExtent := out[i].fdSelect.extent()
   181  
   182  			// Parse the Font Dicts. Each one contains its own Private DICT.
   183  			if err = p.seek(topDict.fdArray); err != nil {
   184  				return nil, err
   185  			}
   186  			topDicts, err := p.parseTopDicts()
   187  			if err != nil {
   188  				return nil, err
   189  			}
   190  			if len(topDicts) < indexExtent {
   191  				return nil, fmt.Errorf("invalid number of font dicts: %d (for %d)",
   192  					len(topDicts), indexExtent)
   193  			}
   194  			out[i].localSubrs = make([][][]byte, len(topDicts))
   195  			out[i].priv = make([]privateDict, len(topDicts))
   196  			for j, topDict := range topDicts {
   197  				out[i].priv[j], out[i].localSubrs[j], err = p.parsePrivateDICT(topDict.privateDictOffset, topDict.privateDictLength)
   198  				if err != nil {
   199  					return nil, err
   200  				}
   201  			}
   202  		}
   203  	}
   204  
   205  	return out, nil
   206  }
   207  
   208  func (p *cffParser) parseTopDicts() ([]topDictData, error) {
   209  	// Parse the Top DICT INDEX.
   210  	instructions, err := p.parseIndex()
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	out := make([]topDictData, len(instructions)) // guarded by uint16 max size
   216  	var psi ps.Machine
   217  	for i, buf := range instructions {
   218  		topDict := &out[i]
   219  
   220  		// set default value before parsing
   221  		topDict.underlinePosition = -100
   222  		topDict.underlineThickness = 50
   223  		topDict.version = unsetSID
   224  		topDict.notice = unsetSID
   225  		topDict.fullName = unsetSID
   226  		topDict.familyName = unsetSID
   227  		topDict.weight = unsetSID
   228  		topDict.cidFontName = unsetSID
   229  		topDict.fontMatrix = []float32{0.001, 0, 0, 0.001, 0, 0}
   230  
   231  		if err = psi.Run(buf, nil, nil, topDict); err != nil {
   232  			return nil, err
   233  		}
   234  	}
   235  	return out, nil
   236  }
   237  
   238  // parse the general form of an index
   239  func (p *cffParser) parseIndex() ([][]byte, error) {
   240  	count, offSize, err := p.parseIndexHeader()
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if count == 0 {
   245  		return nil, nil
   246  	}
   247  
   248  	out := make([][]byte, count)
   249  
   250  	stringsLocations := make([]uint32, int(count)+1)
   251  	if err := p.parseIndexLocations(stringsLocations, offSize); err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	for i := range out {
   256  		length := stringsLocations[i+1] - stringsLocations[i]
   257  		buf, err := p.read(int(length))
   258  		if err != nil {
   259  			return nil, err
   260  		}
   261  		out[i] = buf
   262  	}
   263  	return out, nil
   264  }
   265  
   266  // parse the Name INDEX
   267  func (p *cffParser) parseNames() ([][]byte, error) {
   268  	return p.parseIndex()
   269  }
   270  
   271  // parse the String INDEX
   272  func (p *cffParser) parseUserStrings() (userStrings, error) {
   273  	index, err := p.parseIndex()
   274  	return userStrings(index), err
   275  }
   276  
   277  // Parse the charset data, whose location was found in the Top DICT.
   278  func (p *cffParser) parseCharset(charsetOffset int32, numGlyphs uint16) ([]uint16, error) {
   279  	// Predefined charset may have offset of 0 to 2 // Table 22
   280  	var charset []uint16
   281  	switch charsetOffset {
   282  	case 0: // ISOAdobe
   283  		charset = charsetISOAdobe[:]
   284  	case 1: // Expert
   285  		charset = charsetExpert[:]
   286  	case 2: // ExpertSubset
   287  		charset = charsetExpertSubset[:]
   288  	default: // custom
   289  		if err := p.seek(charsetOffset); err != nil {
   290  			return nil, err
   291  		}
   292  		buf, err := p.read(1)
   293  		if err != nil {
   294  			return nil, err
   295  		}
   296  		charset = make([]uint16, numGlyphs)
   297  		switch buf[0] { // format
   298  		case 0:
   299  			buf, err = p.read(2 * (int(numGlyphs) - 1)) // ".notdef" is omited, and has an implicit SID of 0
   300  			if err != nil {
   301  				return nil, err
   302  			}
   303  			for i := uint16(1); i < numGlyphs; i++ {
   304  				charset[i] = be.Uint16(buf[2*i-2:])
   305  			}
   306  		case 1:
   307  			for i := uint16(1); i < numGlyphs; {
   308  				buf, err = p.read(3)
   309  				if err != nil {
   310  					return nil, err
   311  				}
   312  				first, nLeft := be.Uint16(buf), uint16(buf[2])
   313  				for j := uint16(0); j <= nLeft && i < numGlyphs; j++ {
   314  					charset[i] = first + j
   315  					i++
   316  				}
   317  			}
   318  		case 2:
   319  			for i := uint16(1); i < numGlyphs; {
   320  				buf, err = p.read(4)
   321  				if err != nil {
   322  					return nil, err
   323  				}
   324  				first, nLeft := be.Uint16(buf), be.Uint16(buf[2:])
   325  				for j := uint16(0); j <= nLeft && i < numGlyphs; j++ {
   326  					charset[i] = first + j
   327  					i++
   328  				}
   329  			}
   330  		default:
   331  			return nil, fmt.Errorf("invalid custom charset format %d", buf[0])
   332  		}
   333  	}
   334  	return charset, nil
   335  }
   336  
   337  // Parse the encoding data, whose location was found in the Top DICT.
   338  func (p *cffParser) parseEncoding(encodingOffset int32, numGlyphs uint16, charset []uint16, strs userStrings) (*simpleencodings.Encoding, error) {
   339  	// Predefined encoding may have offset of 0 to 1 // Table 16
   340  	switch encodingOffset {
   341  	case 0: // Standard
   342  		return &simpleencodings.AdobeStandard, nil
   343  	case 1: // Expert
   344  		return &expertEncoding, nil
   345  	default: // custom
   346  		var encoding simpleencodings.Encoding
   347  		if err := p.seek(encodingOffset); err != nil {
   348  			return nil, err
   349  		}
   350  		buf, err := p.read(2)
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  		format, size, charL := buf[0], int32(buf[1]), int32(len(charset))
   355  		// high order bit may be set for supplemental encoding data
   356  		switch f := format & 0xf; f { // 0111_1111
   357  		case 0:
   358  			if size > int32(numGlyphs) { // truncate
   359  				size = int32(numGlyphs)
   360  			}
   361  			buf, err = p.read(int(size))
   362  			if err != nil {
   363  				return nil, err
   364  			}
   365  			for i := int32(1); i < size && i < charL; i++ {
   366  				c := buf[i-1]
   367  				encoding[c], err = strs.getString(charset[i])
   368  				if err != nil {
   369  					return nil, err
   370  				}
   371  			}
   372  		case 1:
   373  			buf, err = p.read(2 * int(size))
   374  			if err != nil {
   375  				return nil, err
   376  			}
   377  			nCodes := int32(1)
   378  			for i := int32(0); i < size; i++ {
   379  				c, nLeft := buf[2*i], buf[2*i+1]
   380  				for j := byte(0); j < nLeft && nCodes < int32(numGlyphs) && nCodes < charL; j++ {
   381  					encoding[c], err = strs.getString(charset[nCodes])
   382  					if err != nil {
   383  						return nil, err
   384  					}
   385  					nCodes++
   386  					c++
   387  				}
   388  			}
   389  		default:
   390  			return nil, fmt.Errorf("invalid custom encoding format %d", f)
   391  		}
   392  
   393  		if format&0x80 != 0 { // 1000_000
   394  			nSupsBuf, err := p.read(1)
   395  			if err != nil {
   396  				return nil, err
   397  			}
   398  			buf, err := p.read(3 * int(nSupsBuf[0]))
   399  			if err != nil {
   400  				return nil, err
   401  			}
   402  			for i := byte(0); i < nSupsBuf[0]; i++ {
   403  				code, sid := buf[3*i], be.Uint16(buf[3*i+1:])
   404  				encoding[code], err = strs.getString(sid)
   405  				if err != nil {
   406  					return nil, err
   407  				}
   408  			}
   409  		}
   410  		return &encoding, nil
   411  	}
   412  }
   413  
   414  // fdSelect holds a CFF font's Font Dict Select data.
   415  type fdSelect interface {
   416  	fontDictIndex(glyph fonts.GID) (byte, error)
   417  	// return the maximum index + 1 (it's the length of an array
   418  	// which can be safely indexed by the indexes)
   419  	extent() int
   420  }
   421  
   422  type fdSelect0 []byte
   423  
   424  func (fds fdSelect0) fontDictIndex(glyph fonts.GID) (byte, error) {
   425  	if int(glyph) >= len(fds) {
   426  		return 0, errors.New("invalid glyph index")
   427  	}
   428  	return fds[glyph], nil
   429  }
   430  
   431  func (fds fdSelect0) extent() int {
   432  	max := -1
   433  	for _, b := range fds {
   434  		if int(b) > max {
   435  			max = int(b)
   436  		}
   437  	}
   438  	return max + 1
   439  }
   440  
   441  type range3 struct {
   442  	first fonts.GID
   443  	fd    byte
   444  }
   445  
   446  type fdSelect3 struct {
   447  	ranges   []range3
   448  	sentinel fonts.GID // = numGlyphs
   449  }
   450  
   451  func (fds fdSelect3) fontDictIndex(x fonts.GID) (byte, error) {
   452  	lo, hi := 0, len(fds.ranges)
   453  	for lo < hi {
   454  		i := (lo + hi) / 2
   455  		r := fds.ranges[i]
   456  		xlo := r.first
   457  		if x < xlo {
   458  			hi = i
   459  			continue
   460  		}
   461  		xhi := fds.sentinel
   462  		if i < len(fds.ranges)-1 {
   463  			xhi = fds.ranges[i+1].first
   464  		}
   465  		if xhi <= x {
   466  			lo = i + 1
   467  			continue
   468  		}
   469  		return r.fd, nil
   470  	}
   471  	return 0, errors.New("invalid glyph index")
   472  }
   473  
   474  func (fds fdSelect3) extent() int {
   475  	max := -1
   476  	for _, b := range fds.ranges {
   477  		if int(b.fd) > max {
   478  			max = int(b.fd)
   479  		}
   480  	}
   481  	return max + 1
   482  }
   483  
   484  // parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section
   485  // 19 "FDSelect".
   486  func (p *cffParser) parseFDSelect(offset int32, numGlyphs uint16) (fdSelect, error) {
   487  	if err := p.seek(offset); err != nil {
   488  		return nil, err
   489  	}
   490  	buf, err := p.read(1)
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  	switch buf[0] { // format
   495  	case 0:
   496  		if len(p.src) < p.offset+int(numGlyphs) {
   497  			return nil, errors.New("invalid FDSelect data")
   498  		}
   499  		return fdSelect0(p.src[p.offset : p.offset+int(numGlyphs)]), nil
   500  	case 3:
   501  		buf, err = p.read(2)
   502  		if err != nil {
   503  			return nil, err
   504  		}
   505  		numRanges := be.Uint16(buf)
   506  		if len(p.src) < p.offset+3*int(numRanges)+2 {
   507  			return nil, errors.New("invalid FDSelect data")
   508  		}
   509  		out := fdSelect3{
   510  			sentinel: fonts.GID(numGlyphs),
   511  			ranges:   make([]range3, numRanges),
   512  		}
   513  		for i := range out.ranges {
   514  			// 	buf holds the range [xlo, xhi).
   515  			out.ranges[i].first = fonts.GID(be.Uint16(p.src[p.offset+3*i:]))
   516  			out.ranges[i].fd = p.src[p.offset+3*i+2]
   517  		}
   518  		return out, nil
   519  	}
   520  	return nil, errUnsupportedCFFFDSelectTable
   521  }
   522  
   523  // Parse Private DICT and the Local Subrs [Subroutines] INDEX
   524  func (p *cffParser) parsePrivateDICT(offset, length int32) (priv privateDict, subrs [][]byte, err error) {
   525  	if length == 0 {
   526  		return privateDict{}, nil, nil
   527  	}
   528  	if err := p.seek(offset); err != nil {
   529  		return privateDict{}, nil, err
   530  	}
   531  	buf, err := p.read(int(length))
   532  	if err != nil {
   533  		return privateDict{}, nil, err
   534  	}
   535  	var psi ps.Machine
   536  	if err = psi.Run(buf, nil, nil, &priv); err != nil {
   537  		return privateDict{}, nil, err
   538  	}
   539  
   540  	if priv.subrsOffset == 0 {
   541  		return priv, nil, nil
   542  	}
   543  
   544  	// "The local subrs offset is relative to the beginning of the Private DICT data"
   545  	if err = p.seek(offset + priv.subrsOffset); err != nil {
   546  		return privateDict{}, nil, errors.New("invalid local subroutines offset")
   547  	}
   548  	subrs, err = p.parseIndex()
   549  	if err != nil {
   550  		return privateDict{}, nil, err
   551  	}
   552  	return priv, subrs, nil
   553  }
   554  
   555  // read returns the n bytes from p.offset and advances p.offset by n.
   556  func (p *cffParser) read(n int) ([]byte, error) {
   557  	if n < 0 || len(p.src) < p.offset+n {
   558  		return nil, errors.New("invalid CFF font file (EOF)")
   559  	}
   560  	out := p.src[p.offset : p.offset+n]
   561  	p.offset += n
   562  	return out, nil
   563  }
   564  
   565  // skip advances p.offset by n.
   566  func (p *cffParser) skip(n int) error {
   567  	if len(p.src) < p.offset+n {
   568  		return errors.New("invalid CFF font file (EOF)")
   569  	}
   570  	p.offset += n
   571  	return nil
   572  }
   573  
   574  func (p *cffParser) seek(offset int32) error {
   575  	if offset < 0 || len(p.src) < int(offset) {
   576  		return errors.New("invalid CFF font file (EOF)")
   577  	}
   578  	p.offset = int(offset)
   579  	return nil
   580  }
   581  
   582  func bigEndian(b []byte) uint32 {
   583  	switch len(b) {
   584  	case 1:
   585  		return uint32(b[0])
   586  	case 2:
   587  		return uint32(b[0])<<8 | uint32(b[1])
   588  	case 3:
   589  		return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
   590  	case 4:
   591  		return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
   592  	}
   593  	panic("unreachable")
   594  }
   595  
   596  func (p *cffParser) parseIndexHeader() (count uint16, offSize int32, err error) {
   597  	buf, err := p.read(2)
   598  	if err != nil {
   599  		return 0, 0, err
   600  	}
   601  	count = be.Uint16(buf)
   602  	// 5176.CFF.pdf section 5 "INDEX Data" says that "An empty INDEX is
   603  	// represented by a count field with a 0 value and no additional fields.
   604  	// Thus, the total size of an empty INDEX is 2 bytes".
   605  	if count == 0 {
   606  		return count, 0, nil
   607  	}
   608  	buf, err = p.read(1)
   609  	if err != nil {
   610  		return 0, 0, err
   611  	}
   612  	offSize = int32(buf[0])
   613  	if offSize < 1 || 4 < offSize {
   614  		return 0, 0, fmt.Errorf("invalid offset size %d", offSize)
   615  	}
   616  	return count, offSize, nil
   617  }
   618  
   619  func (p *cffParser) parseIndexLocations(dst []uint32, offSize int32) error {
   620  	if len(dst) == 0 {
   621  		return nil
   622  	}
   623  	buf, err := p.read(len(dst) * int(offSize))
   624  	if err != nil {
   625  		return err
   626  	}
   627  
   628  	prev := uint32(0)
   629  	for i := range dst {
   630  		loc := bigEndian(buf[:offSize])
   631  		buf = buf[offSize:]
   632  
   633  		// Locations are off by 1 byte. 5176.CFF.pdf section 5 "INDEX Data"
   634  		// says that "Offsets in the offset array are relative to the byte that
   635  		// precedes the object data... This ensures that every object has a
   636  		// corresponding offset which is always nonzero".
   637  		if loc == 0 {
   638  			return errors.New("invalid CFF index locations (0)")
   639  		}
   640  		loc--
   641  
   642  		// In the same paragraph, "Therefore the first element of the offset
   643  		// array is always 1" before correcting for the off-by-1.
   644  		if i == 0 {
   645  			if loc != 0 {
   646  				return errors.New("invalid CFF index locations (not 0)")
   647  			}
   648  		} else if loc < prev { // Check that locations are increasing
   649  			return errors.New("invalid CFF index locations (not increasing)")
   650  		}
   651  
   652  		// Check that locations are in bounds.
   653  		if uint32(len(p.src)-p.offset) < loc {
   654  			return errors.New("invalid CFF index locations (out of bounds)")
   655  		}
   656  
   657  		dst[i] = uint32(p.offset) + loc
   658  		prev = loc
   659  	}
   660  	return nil
   661  }
   662  
   663  // topDictData contains fields specific to the Top DICT context.
   664  type topDictData struct {
   665  	// SIDs, to be decoded using the string index
   666  	version, notice, fullName, familyName, weight      uint16
   667  	isFixedPitch                                       bool
   668  	italicAngle, underlinePosition, underlineThickness float32
   669  	charsetOffset                                      int32
   670  	encodingOffset                                     int32
   671  	charStringsOffset                                  int32
   672  	fdArray                                            int32
   673  	fdSelect                                           int32
   674  	isCIDFont                                          bool
   675  	cidFontName                                        uint16
   676  	privateDictOffset                                  int32
   677  	privateDictLength                                  int32
   678  	fontMatrix                                         []float32
   679  }
   680  
   681  // resolve the strings
   682  func (topDict topDictData) toInfo(strs userStrings) (out fonts.PSInfo, err error) {
   683  	out.Version, err = strs.getString(topDict.version)
   684  	if err != nil {
   685  		return out, err
   686  	}
   687  	out.Notice, err = strs.getString(topDict.notice)
   688  	if err != nil {
   689  		return out, err
   690  	}
   691  	out.FullName, err = strs.getString(topDict.fullName)
   692  	if err != nil {
   693  		return out, err
   694  	}
   695  	out.FamilyName, err = strs.getString(topDict.familyName)
   696  	if err != nil {
   697  		return out, err
   698  	}
   699  	out.Weight, err = strs.getString(topDict.weight)
   700  	if err != nil {
   701  		return out, err
   702  	}
   703  	out.IsFixedPitch = topDict.isFixedPitch
   704  	out.ItalicAngle = int(topDict.italicAngle)
   705  	out.UnderlinePosition = int(topDict.underlinePosition)
   706  	out.UnderlineThickness = int(topDict.underlineThickness)
   707  	return out, nil
   708  }
   709  
   710  func (topDict *topDictData) Context() ps.PsContext { return ps.TopDict }
   711  
   712  func (topDict *topDictData) Apply(op ps.PsOperator, state *ps.Machine) error {
   713  	ops := topDictOperators[0]
   714  	if op.IsEscaped {
   715  		ops = topDictOperators[1]
   716  	}
   717  	if int(op.Operator) >= len(ops) {
   718  		return fmt.Errorf("invalid operator %s in Top Dict", op)
   719  	}
   720  	opFunc := ops[op.Operator]
   721  	if opFunc.run == nil {
   722  		return fmt.Errorf("invalid operator %s in Top Dict", op)
   723  	}
   724  	if state.ArgStack.Top < opFunc.numPop {
   725  		return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op)
   726  	}
   727  	err := opFunc.run(topDict, state)
   728  	if err != nil {
   729  		return err
   730  	}
   731  	err = state.ArgStack.PopN(opFunc.numPop)
   732  	return err
   733  }
   734  
   735  // The Top DICT operators are defined by 5176.CFF.pdf Table 9 "Top DICT
   736  // Operator Entries" and Table 10 "CIDFont Operator Extensions".
   737  type topDictOperator struct {
   738  	// run is the function that implements the operator. Nil means that we
   739  	// ignore the operator, other than popping its arguments off the stack.
   740  	run func(*topDictData, *ps.Machine) error
   741  
   742  	// numPop is the number of stack values to pop. -1 means "array" and -2
   743  	// means "delta" as per 5176.CFF.pdf Table 6 "Operand Types".
   744  	numPop int32
   745  }
   746  
   747  func topDictNoOp(*topDictData, *ps.Machine) error { return nil }
   748  
   749  var topDictOperators = [2][]topDictOperator{
   750  	// 1-byte operators.
   751  	{
   752  		0: {func(t *topDictData, s *ps.Machine) error {
   753  			t.version = s.ArgStack.Uint16()
   754  			return nil
   755  		}, +1 /*version*/},
   756  		1: {func(t *topDictData, s *ps.Machine) error {
   757  			t.notice = s.ArgStack.Uint16()
   758  			return nil
   759  		}, +1 /*Notice*/},
   760  		2: {func(t *topDictData, s *ps.Machine) error {
   761  			t.fullName = s.ArgStack.Uint16()
   762  			return nil
   763  		}, +1 /*FullName*/},
   764  		3: {func(t *topDictData, s *ps.Machine) error {
   765  			t.familyName = s.ArgStack.Uint16()
   766  			return nil
   767  		}, +1 /*FamilyName*/},
   768  		4: {func(t *topDictData, s *ps.Machine) error {
   769  			t.weight = s.ArgStack.Uint16()
   770  			return nil
   771  		}, +1 /*Weight*/},
   772  		5:  {topDictNoOp, -1 /*FontBBox*/},
   773  		13: {topDictNoOp, +1 /*UniqueID*/},
   774  		14: {topDictNoOp, -1 /*XUID*/},
   775  		15: {func(t *topDictData, s *ps.Machine) error {
   776  			t.charsetOffset = s.ArgStack.Vals[s.ArgStack.Top-1]
   777  			return nil
   778  		}, +1 /*charset*/},
   779  		16: {func(t *topDictData, s *ps.Machine) error {
   780  			t.encodingOffset = s.ArgStack.Vals[s.ArgStack.Top-1]
   781  			return nil
   782  		}, +1 /*Encoding*/},
   783  		17: {func(t *topDictData, s *ps.Machine) error {
   784  			t.charStringsOffset = s.ArgStack.Vals[s.ArgStack.Top-1]
   785  			return nil
   786  		}, +1 /*CharStrings*/},
   787  		18: {func(t *topDictData, s *ps.Machine) error {
   788  			t.privateDictLength = s.ArgStack.Vals[s.ArgStack.Top-2]
   789  			t.privateDictOffset = s.ArgStack.Vals[s.ArgStack.Top-1]
   790  			return nil
   791  		}, +2 /*Private*/},
   792  	},
   793  	// 2-byte operators. The first byte is the escape byte.
   794  	{
   795  		0: {topDictNoOp, +1 /*Copyright*/},
   796  		1: {func(t *topDictData, s *ps.Machine) error {
   797  			t.isFixedPitch = s.ArgStack.Vals[s.ArgStack.Top-1] == 1
   798  			return nil
   799  		}, +1 /*isFixedPitch*/},
   800  		2: {func(t *topDictData, s *ps.Machine) error {
   801  			t.italicAngle = s.ArgStack.Float()
   802  			return nil
   803  		}, +1 /*ItalicAngle*/},
   804  		3: {func(t *topDictData, s *ps.Machine) error {
   805  			t.underlinePosition = s.ArgStack.Float()
   806  			return nil
   807  		}, +1 /*UnderlinePosition*/},
   808  		4: {func(t *topDictData, s *ps.Machine) error {
   809  			t.underlineThickness = s.ArgStack.Float()
   810  			return nil
   811  		}, +1 /*UnderlineThickness*/},
   812  		5: {topDictNoOp, +1 /*PaintType*/},
   813  		6: {func(_ *topDictData, i *ps.Machine) error {
   814  			if version := i.ArgStack.Vals[i.ArgStack.Top-1]; version != 2 {
   815  				return fmt.Errorf("charstring type %d not supported", version)
   816  			}
   817  			return nil
   818  		}, +1 /*CharstringType*/},
   819  		7: {func(t *topDictData, s *ps.Machine) error {
   820  			if s.ArgStack.Top != 6 {
   821  				return fmt.Errorf("wrong number of arguments for FontMatrix: got %d, want 6", s.ArgStack.Top)
   822  			}
   823  			t.fontMatrix = make([]float32, 6)
   824  			for i := range t.fontMatrix {
   825  				t.fontMatrix[i] = math.Float32frombits(uint32(s.ArgStack.Vals[i]))
   826  			}
   827  			return nil
   828  		}, -1 /*FontMatrix*/},
   829  		8:  {topDictNoOp, +1 /*StrokeWidth*/},
   830  		20: {topDictNoOp, +1 /*SyntheticBase*/},
   831  		21: {topDictNoOp, +1 /*PostScript*/},
   832  		22: {topDictNoOp, +1 /*BaseFontName*/},
   833  		23: {topDictNoOp, -2 /*BaseFontBlend*/},
   834  		30: {func(t *topDictData, _ *ps.Machine) error {
   835  			t.isCIDFont = true
   836  			return nil
   837  		}, +3 /*ROS*/},
   838  		31: {topDictNoOp, +1 /*CIDFontVersion*/},
   839  		32: {topDictNoOp, +1 /*CIDFontRevision*/},
   840  		33: {topDictNoOp, +1 /*CIDFontType*/},
   841  		34: {topDictNoOp, +1 /*CIDCount*/},
   842  		35: {topDictNoOp, +1 /*UIDBase*/},
   843  		36: {func(t *topDictData, s *ps.Machine) error {
   844  			t.fdArray = s.ArgStack.Vals[s.ArgStack.Top-1]
   845  			return nil
   846  		}, +1 /*FDArray*/},
   847  		37: {func(t *topDictData, s *ps.Machine) error {
   848  			t.fdSelect = s.ArgStack.Vals[s.ArgStack.Top-1]
   849  			return nil
   850  		}, +1 /*FDSelect*/},
   851  		38: {func(t *topDictData, s *ps.Machine) error {
   852  			t.cidFontName = s.ArgStack.Uint16()
   853  			return nil
   854  		}, +1 /*FontName*/},
   855  	},
   856  }
   857  
   858  // privateDict contains fields specific to the Private DICT context.
   859  type privateDict struct {
   860  	subrsOffset                  int32
   861  	defaultWidthX, nominalWidthX int32
   862  }
   863  
   864  func (privateDict) Context() ps.PsContext { return ps.PrivateDict }
   865  
   866  // The Private DICT operators are defined by 5176.CFF.pdf Table 23 "Private
   867  // DICT Operators".
   868  func (priv *privateDict) Apply(op ps.PsOperator, state *ps.Machine) error {
   869  	if !op.IsEscaped { // 1-byte operators.
   870  		switch op.Operator {
   871  		case 6, 7, 8, 9: // "BlueValues" "OtherBlues" "FamilyBlues" "FamilyOtherBlues"
   872  			return state.ArgStack.PopN(-2)
   873  		case 10, 11: // "StdHW" "StdVW"
   874  			return state.ArgStack.PopN(1)
   875  		case 20: // "defaultWidthX"
   876  			if state.ArgStack.Top < 1 {
   877  				return errors.New("invalid stack size for 'defaultWidthX' in private Dict charstring")
   878  			}
   879  			priv.defaultWidthX = state.ArgStack.Vals[state.ArgStack.Top-1]
   880  			return state.ArgStack.PopN(1)
   881  		case 21: // "nominalWidthX"
   882  			if state.ArgStack.Top < 1 {
   883  				return errors.New("invalid stack size for 'nominalWidthX' in private Dict charstring")
   884  			}
   885  			priv.nominalWidthX = state.ArgStack.Vals[state.ArgStack.Top-1]
   886  			return state.ArgStack.PopN(1)
   887  		case 19: // "Subrs" pop 1
   888  			if state.ArgStack.Top < 1 {
   889  				return errors.New("invalid stack size for 'subrs' in private Dict charstring")
   890  			}
   891  			priv.subrsOffset = state.ArgStack.Vals[state.ArgStack.Top-1]
   892  			return state.ArgStack.PopN(1)
   893  		}
   894  	} else { // 2-byte operators. The first byte is the escape byte.
   895  		switch op.Operator {
   896  		case 9, 10, 11, 14, 17, 18, 19: // "BlueScale" "BlueShift" "BlueFuzz" "ForceBold" "LanguageGroup" "ExpansionFactor" "initialRandomSeed"
   897  			return state.ArgStack.PopN(1)
   898  		case 12, 13: //  "StemSnapH"  "StemSnapV"
   899  			return state.ArgStack.PopN(-2)
   900  		}
   901  	}
   902  	return errors.New("invalid operand in private Dict charstring")
   903  }