// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sfnt

// Compact Font Format (CFF) fonts are written in PostScript, a stack-based
// programming language.
//
// A fundamental concept is a DICT, or a key-value map, expressed in reverse
// Polish notation. For example, this sequence of operations:
//	- push the number 379
//	- version operator
//	- push the number 392
//	- Notice operator
//	- etc
//	- push the number 100
//	- push the number 0
//	- push the number 500
//	- push the number 800
//	- FontBBox operator
//	- etc
// defines a DICT that maps "version" to the String ID (SID) 379, "Notice" to
// the SID 392, "FontBBox" to the four numbers [100, 0, 500, 800], etc.
//
// The first 391 String IDs (starting at 0) are predefined as per the CFF spec
// Appendix A, in 5176.CFF.pdf referenced below. For example, 379 means
// "001.000". String ID 392 is not predefined, and is mapped by a separate
// structure, the "String INDEX", inside the CFF data. (String ID 391 is also
// not predefined. Specifically for ../testdata/CFFTest.otf, 391 means
// "uni4E2D", as this font contains a glyph for U+4E2D).
//
// The actual glyph vectors are similarly encoded (in PostScript), in a format
// called Type 2 Charstrings. The wire encoding is similar to but not exactly
// the same as CFF's. For example, the byte 0x05 means FontBBox for CFF DICTs,
// but means rlineto (relative line-to) for Type 2 Charstrings. See
// 5176.CFF.pdf Appendix H and 5177.Type2.pdf Appendix A in the PDF files
// referenced below.
//
// CFF is a stand-alone format, but CFF as used in SFNT fonts have further
// restrictions. For example, a stand-alone CFF can contain multiple fonts, but
// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The Name
// INDEX in the CFF must contain only one entry; that is, there must be only
// one font in the CFF FontSet".
//
// The relevant specifications are:
// 	- http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
// 	- http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf

import (
	
	
	

	
)

const (
	// psArgStackSize is the argument stack size for a PostScript interpreter.
	// 5176.CFF.pdf section 4 "DICT Data" says that "An operator may be
	// preceded by up to a maximum of 48 operands". 5177.Type2.pdf Appendix B
	// "Type 2 Charstring Implementation Limits" says that "Argument stack 48".
	psArgStackSize = 48

	// Similarly, Appendix B says "Subr nesting, stack limit 10".
	psCallStackSize = 10
)

func bigEndian( []byte) uint32 {
	switch len() {
	case 1:
		return uint32([0])
	case 2:
		return uint32([0])<<8 | uint32([1])
	case 3:
		return uint32([0])<<16 | uint32([1])<<8 | uint32([2])
	case 4:
		return uint32([0])<<24 | uint32([1])<<16 | uint32([2])<<8 | uint32([3])
	}
	panic("unreachable")
}

// fdSelect holds a CFF font's Font Dict Select data.
type fdSelect struct {
	format    uint8
	numRanges uint16
	offset    int32
}

func ( *fdSelect) ( *Font,  *Buffer,  GlyphIndex) (int, error) {
	switch .format {
	case 0:
		,  := .view(&.src, int(.offset)+int(), 1)
		if  != nil {
			return 0, 
		}
		return int([0]), nil
	case 3:
		,  := 0, int(.numRanges)
		for  <  {
			 := ( + ) / 2
			,  := .view(&.src, int(.offset)+3*, 3+2)
			if  != nil {
				return 0, 
			}
			// buf holds the range [xlo, xhi).
			if  := GlyphIndex(u16([0:]));  <  {
				 = 
				continue
			}
			if  := GlyphIndex(u16([3:]));  <=  {
				 =  + 1
				continue
			}
			return int([2]), nil
		}
	}
	return 0, ErrNotFound
}

// cffParser parses the CFF table from an SFNT font.
type cffParser struct {
	src    *source
	base   int
	offset int
	end    int
	err    error

	buf    []byte
	locBuf [2]uint32

	psi psInterpreter
}

func ( *cffParser) ( int32) ( glyphData,  error) {
	// Parse the header.
	{
		if !.read(4) {
			return glyphData{}, .err
		}
		if .buf[0] != 1 || .buf[1] != 0 || .buf[2] != 4 {
			return glyphData{}, errUnsupportedCFFVersion
		}
	}

	// Parse the Name INDEX.
	{
		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The
		// Name INDEX in the CFF must contain only one entry".
		if  != 1 {
			return glyphData{}, errInvalidCFFTable
		}
		if !.parseIndexLocations(.locBuf[:2], , ) {
			return glyphData{}, .err
		}
		.offset = int(.locBuf[1])
	}

	// Parse the Top DICT INDEX.
	.psi.topDict.initialize()
	{
		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		// 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
		// should match the count of the Name INDEX, which is 1.
		if  != 1 {
			return glyphData{}, errInvalidCFFTable
		}
		if !.parseIndexLocations(.locBuf[:2], , ) {
			return glyphData{}, .err
		}
		if !.read(int(.locBuf[1] - .locBuf[0])) {
			return glyphData{}, .err
		}
		if .err = .psi.run(psContextTopDict, .buf, 0, 0); .err != nil {
			return glyphData{}, .err
		}
	}

	// Skip the String INDEX.
	{
		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		if  != 0 {
			// Read the last location. Locations are off by 1 byte. See the
			// comment in parseIndexLocations.
			if !.skip(int( * )) {
				return glyphData{}, .err
			}
			if !.read(int()) {
				return glyphData{}, .err
			}
			 := bigEndian(.buf) - 1
			// Check that locations are in bounds.
			if uint32(.end-.offset) <  {
				return glyphData{}, errInvalidCFFTable
			}
			// Skip the index data.
			if !.skip(int()) {
				return glyphData{}, .err
			}
		}
	}

	// Parse the Global Subrs [Subroutines] INDEX.
	{
		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		if  != 0 {
			if  > maxNumSubroutines {
				return glyphData{}, errUnsupportedNumberOfSubroutines
			}
			.gsubrs = make([]uint32, +1)
			if !.parseIndexLocations(.gsubrs, , ) {
				return glyphData{}, .err
			}
		}
	}

	// Parse the CharStrings INDEX, whose location was found in the Top DICT.
	{
		if !.seekFromBase(.psi.topDict.charStringsOffset) {
			return glyphData{}, errInvalidCFFTable
		}
		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		if  == 0 || int32() !=  {
			return glyphData{}, errInvalidCFFTable
		}
		.locations = make([]uint32, +1)
		if !.parseIndexLocations(.locations, , ) {
			return glyphData{}, .err
		}
	}

	if !.psi.topDict.isCIDFont {
		// Parse the Private DICT, whose location was found in the Top DICT.
		.singleSubrs,  = .parsePrivateDICT(
			.psi.topDict.privateDictOffset,
			.psi.topDict.privateDictLength,
		)
		if  != nil {
			return glyphData{}, 
		}

	} else {
		// Parse the Font Dict Select data, whose location was found in the Top
		// DICT.
		.fdSelect,  = .parseFDSelect(.psi.topDict.fdSelect, )
		if  != nil {
			return glyphData{}, 
		}

		// Parse the Font Dicts. Each one contains its own Private DICT.
		if !.seekFromBase(.psi.topDict.fdArray) {
			return glyphData{}, errInvalidCFFTable
		}

		, ,  := .parseIndexHeader()
		if ! {
			return glyphData{}, .err
		}
		if  > maxNumFontDicts {
			return glyphData{}, errUnsupportedNumberOfFontDicts
		}

		 := make([]uint32, +1)
		if !.parseIndexLocations(, , ) {
			return glyphData{}, .err
		}

		 := make([]struct {
			,  int32
		}, )

		for  := range  {
			 := [+1] - []
			if !.read(int()) {
				return glyphData{}, errInvalidCFFTable
			}
			.psi.topDict.initialize()
			if .err = .psi.run(psContextTopDict, .buf, 0, 0); .err != nil {
				return glyphData{}, .err
			}
			[]. = .psi.topDict.privateDictOffset
			[]. = .psi.topDict.privateDictLength
		}

		.multiSubrs = make([][]uint32, )
		for ,  := range  {
			.multiSubrs[],  = .parsePrivateDICT(., .)
			if  != nil {
				return glyphData{}, 
			}
		}
	}

	return , 
}

// parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section
// 19 "FDSelect".
func ( *cffParser) ( int32,  int32) ( fdSelect,  error) {
	if !.seekFromBase(.psi.topDict.fdSelect) {
		return fdSelect{}, errInvalidCFFTable
	}
	if !.read(1) {
		return fdSelect{}, .err
	}
	.format = .buf[0]
	switch .format {
	case 0:
		if .end-.offset < int() {
			return fdSelect{}, errInvalidCFFTable
		}
		.offset = int32(.offset)
		return , nil
	case 3:
		if !.read(2) {
			return fdSelect{}, .err
		}
		.numRanges = u16(.buf)
		if .end-.offset < 3*int(.numRanges)+2 {
			return fdSelect{}, errInvalidCFFTable
		}
		.offset = int32(.offset)
		return , nil
	}
	return fdSelect{}, errUnsupportedCFFFDSelectTable
}

func ( *cffParser) (,  int32) ( []uint32,  error) {
	.psi.privateDict.initialize()
	if  != 0 {
		 := int32(.end - .base)
		if  <= 0 ||  <  || - <  ||  < 0 {
			return nil, errInvalidCFFTable
		}
		.offset = .base + int()
		if !.read(int()) {
			return nil, .err
		}
		if .err = .psi.run(psContextPrivateDict, .buf, 0, 0); .err != nil {
			return nil, .err
		}
	}

	// Parse the Local Subrs [Subroutines] INDEX, whose location was found in
	// the Private DICT.
	if .psi.privateDict.subrsOffset != 0 {
		if !.seekFromBase( + .psi.privateDict.subrsOffset) {
			return nil, errInvalidCFFTable
		}
		, ,  := .parseIndexHeader()
		if ! {
			return nil, .err
		}
		if  != 0 {
			if  > maxNumSubroutines {
				return nil, errUnsupportedNumberOfSubroutines
			}
			 = make([]uint32, +1)
			if !.parseIndexLocations(, , ) {
				return nil, .err
			}
		}
	}

	return , 
}

// read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
// advances p.offset by n.
//
// As per the source.view method, the caller should not modify the contents of
// p.buf after read returns, other than by calling read again.
//
// The caller should also avoid modifying the pointer / length / capacity of
// the p.buf slice, not just avoid modifying the slice's contents, in order to
// maximize the opportunity to re-use p.buf's allocated memory when viewing the
// underlying source data for subsequent read calls.
func ( *cffParser) ( int) ( bool) {
	if  < 0 || .end-.offset <  {
		.err = errInvalidCFFTable
		return false
	}
	.buf, .err = .src.view(.buf, .offset, )
	// TODO: if p.err == io.EOF, change that to a different error??
	.offset += 
	return .err == nil
}

func ( *cffParser) ( int) ( bool) {
	if .end-.offset <  {
		.err = errInvalidCFFTable
		return false
	}
	.offset += 
	return true
}

func ( *cffParser) ( int32) ( bool) {
	if  < 0 || int32(.end-.base) <  {
		return false
	}
	.offset = .base + int()
	return true
}

func ( *cffParser) () (,  int32,  bool) {
	if !.read(2) {
		return 0, 0, false
	}
	 = int32(u16(.buf[:2]))
	// 5176.CFF.pdf section 5 "INDEX Data" says that "An empty INDEX is
	// represented by a count field with a 0 value and no additional fields.
	// Thus, the total size of an empty INDEX is 2 bytes".
	if  == 0 {
		return , 0, true
	}
	if !.read(1) {
		return 0, 0, false
	}
	 = int32(.buf[0])
	if  < 1 || 4 <  {
		.err = errInvalidCFFTable
		return 0, 0, false
	}
	return , , true
}

func ( *cffParser) ( []uint32, ,  int32) ( bool) {
	if  == 0 {
		return true
	}
	if len() != int(+1) {
		panic("unreachable")
	}
	if !.read(len() * int()) {
		return false
	}

	,  := .buf, uint32(0)
	for  := range  {
		 := bigEndian([:])
		 = [:]

		// Locations are off by 1 byte. 5176.CFF.pdf section 5 "INDEX Data"
		// says that "Offsets in the offset array are relative to the byte that
		// precedes the object data... This ensures that every object has a
		// corresponding offset which is always nonzero".
		if  == 0 {
			.err = errInvalidCFFTable
			return false
		}
		--

		// In the same paragraph, "Therefore the first element of the offset
		// array is always 1" before correcting for the off-by-1.
		if  == 0 {
			if  != 0 {
				.err = errInvalidCFFTable
				break
			}
		} else if  <=  { // Check that locations are increasing.
			.err = errInvalidCFFTable
			break
		}

		// Check that locations are in bounds.
		if uint32(.end-.offset) <  {
			.err = errInvalidCFFTable
			break
		}

		[] = uint32(.offset) + 
		 = 
	}
	return .err == nil
}

type psCallStackEntry struct {
	offset, length uint32
}

type psContext uint32

const (
	psContextTopDict psContext = iota
	psContextPrivateDict
	psContextType2Charstring
)

// psTopDictData contains fields specific to the Top DICT context.
type psTopDictData struct {
	charStringsOffset int32
	fdArray           int32
	fdSelect          int32
	isCIDFont         bool
	privateDictOffset int32
	privateDictLength int32
}

func ( *psTopDictData) () {
	* = psTopDictData{}
}

// psPrivateDictData contains fields specific to the Private DICT context.
type psPrivateDictData struct {
	subrsOffset int32
}

func ( *psPrivateDictData) () {
	* = psPrivateDictData{}
}

// psType2CharstringsData contains fields specific to the Type 2 Charstrings
// context.
type psType2CharstringsData struct {
	f          *Font
	b          *Buffer
	x          int32
	y          int32
	firstX     int32
	firstY     int32
	hintBits   int32
	seenWidth  bool
	ended      bool
	glyphIndex GlyphIndex
	// fdSelectIndexPlusOne is the result of the Font Dict Select lookup, plus
	// one. That plus one lets us use the zero value to denote either unused
	// (for CFF fonts with a single Font Dict) or lazily evaluated.
	fdSelectIndexPlusOne int32
}

func ( *psType2CharstringsData) ( *Font,  *Buffer,  GlyphIndex) {
	* = psType2CharstringsData{
		f:          ,
		b:          ,
		glyphIndex: ,
	}
}

func ( *psType2CharstringsData) () {
	if .x != .firstX || .y != .firstY {
		.b.segments = append(.b.segments, Segment{
			Op: SegmentOpLineTo,
			Args: [3]fixed.Point26_6{{
				X: fixed.Int26_6(.firstX),
				Y: fixed.Int26_6(.firstY),
			}},
		})
	}
}

func ( *psType2CharstringsData) (,  int32) {
	.closePath()
	.x += 
	.y += 
	.b.segments = append(.b.segments, Segment{
		Op: SegmentOpMoveTo,
		Args: [3]fixed.Point26_6{{
			X: fixed.Int26_6(.x),
			Y: fixed.Int26_6(.y),
		}},
	})
	.firstX = .x
	.firstY = .y
}

func ( *psType2CharstringsData) (,  int32) {
	.x += 
	.y += 
	.b.segments = append(.b.segments, Segment{
		Op: SegmentOpLineTo,
		Args: [3]fixed.Point26_6{{
			X: fixed.Int26_6(.x),
			Y: fixed.Int26_6(.y),
		}},
	})
}

func ( *psType2CharstringsData) (, , , , ,  int32) {
	.x += 
	.y += 
	 := fixed.Int26_6(.x)
	 := fixed.Int26_6(.y)
	.x += 
	.y += 
	 := fixed.Int26_6(.x)
	 := fixed.Int26_6(.y)
	.x += 
	.y += 
	 := fixed.Int26_6(.x)
	 := fixed.Int26_6(.y)
	.b.segments = append(.b.segments, Segment{
		Op:   SegmentOpCubeTo,
		Args: [3]fixed.Point26_6{{X: , Y: }, {X: , Y: }, {X: , Y: }},
	})
}

// psInterpreter is a PostScript interpreter.
type psInterpreter struct {
	ctx          psContext
	instructions []byte
	instrOffset  uint32
	instrLength  uint32
	argStack     struct {
		a   [psArgStackSize]int32
		top int32
	}
	callStack struct {
		a   [psCallStackSize]psCallStackEntry
		top int32
	}
	parseNumberBuf [maxRealNumberStrLen]byte

	topDict          psTopDictData
	privateDict      psPrivateDictData
	type2Charstrings psType2CharstringsData
}

func ( *psInterpreter) () bool {
	if len(.instructions) != 0 {
		return true
	}
	for  := int32(0);  < .callStack.top; ++ {
		if .callStack.a[].length != 0 {
			return true
		}
	}
	return false
}

// run runs the instructions in the given PostScript context. For the
// psContextType2Charstring context, offset and length give the location of the
// instructions in p.type2Charstrings.f.src.
func ( *psInterpreter) ( psContext,  []byte, ,  uint32) error {
	.ctx = 
	.instructions = 
	.instrOffset = 
	.instrLength = 
	.argStack.top = 0
	.callStack.top = 0

:
	for len(.instructions) > 0 {
		// Push a numeric operand on the stack, if applicable.
		if ,  := .parseNumber();  {
			if  != nil {
				return 
			}
			continue
		}

		// Otherwise, execute an operator.
		 := .instructions[0]
		.instructions = .instructions[1:]

		for ,  := false, psOperators[][0]; ; {
			if  == escapeByte && ! {
				if len(.instructions) <= 0 {
					return errInvalidCFFTable
				}
				 = .instructions[0]
				.instructions = .instructions[1:]
				 = true
				 = psOperators[][1]
				continue
			}

			if int() < len() {
				if  := []; .name != "" {
					if .argStack.top < .numPop {
						return errInvalidCFFTable
					}
					if .run != nil {
						if  := .run();  != nil {
							return 
						}
					}
					if .numPop < 0 {
						.argStack.top = 0
					} else {
						.argStack.top -= .numPop
					}
					continue 
				}
			}

			if  {
				return fmt.Errorf("sfnt: unrecognized CFF 2-byte operator (12 %d)", )
			} else {
				return fmt.Errorf("sfnt: unrecognized CFF 1-byte operator (%d)", )
			}
		}
	}
	return nil
}

// See 5176.CFF.pdf section 4 "DICT Data".
func ( *psInterpreter) () ( bool,  error) {
	 := int32(0)
	switch  := .instructions[0]; {
	case  == 28:
		if len(.instructions) < 3 {
			return true, errInvalidCFFTable
		}
		,  = int32(int16(u16(.instructions[1:]))), true
		.instructions = .instructions[3:]

	case  == 29 && .ctx != psContextType2Charstring:
		if len(.instructions) < 5 {
			return true, errInvalidCFFTable
		}
		,  = int32(u32(.instructions[1:])), true
		.instructions = .instructions[5:]

	case  == 30 && .ctx != psContextType2Charstring:
		// Parse a real number. This isn't listed in 5176.CFF.pdf Table 3
		// "Operand Encoding" but that table lists integer encodings. Further
		// down the page it says "A real number operand is provided in addition
		// to integer operands. This operand begins with a byte value of 30
		// followed by a variable-length sequence of bytes."

		 := .parseNumberBuf[:0]
		.instructions = .instructions[1:]
	:
		for {
			if len(.instructions) == 0 {
				return true, errInvalidCFFTable
			}
			 := .instructions[0]
			.instructions = .instructions[1:]
			// Process b's two nibbles, high then low.
			for  := 0;  < 2; ++ {
				 :=  >> 4
				 =  << 4
				if  == 0x0f {
					,  := strconv.ParseFloat(string(), 32)
					if  != nil {
						return true, errInvalidCFFTable
					}
					,  = int32(math.Float32bits(float32())), true
					break 
				}
				if  == 0x0d {
					return true, errInvalidCFFTable
				}
				if len()+maxNibbleDefsLength > len(.parseNumberBuf) {
					return true, errUnsupportedRealNumberEncoding
				}
				 = append(, nibbleDefs[]...)
			}
		}

	case  < 32:
		// No-op.

	case  < 247:
		.instructions = .instructions[1:]
		,  = int32()-139, true

	case  < 251:
		if len(.instructions) < 2 {
			return true, errInvalidCFFTable
		}
		 := .instructions[1]
		.instructions = .instructions[2:]
		,  = +int32(-247)*256+int32()+108, true

	case  < 255:
		if len(.instructions) < 2 {
			return true, errInvalidCFFTable
		}
		 := .instructions[1]
		.instructions = .instructions[2:]
		,  = -int32(-251)*256-int32()-108, true

	case  == 255 && .ctx == psContextType2Charstring:
		if len(.instructions) < 5 {
			return true, errInvalidCFFTable
		}
		,  = int32(u32(.instructions[1:])), true
		.instructions = .instructions[5:]
		// 5177.Type2.pdf section 3.2 "Charstring Number Encoding" says "If the
		// charstring byte contains the value 255... [this] number is
		// interpreted as a Fixed; that is, a signed number with 16 bits of
		// fraction".
		//
		// TODO: change the psType2CharstringsData.b.segments and
		// psInterpreter.argStack data structures to optionally hold fixed
		// point values, not just integer values. That's a substantial
		// re-design, though. Until then, just round the 16.16 fixed point
		// number to the closest integer value. This isn't just "number =
		// ((number + 0x8000) >> 16)" because of potential overflow.
		 = ( >> 16) + (1 & ( >> 15))
	}

	if  {
		if .argStack.top == psArgStackSize {
			return true, errInvalidCFFTable
		}
		.argStack.a[.argStack.top] = 
		.argStack.top++
	}
	return , nil
}

const maxNibbleDefsLength = len("E-")

// nibbleDefs encodes 5176.CFF.pdf Table 5 "Nibble Definitions".
var nibbleDefs = [16]string{
	0x00: "0",
	0x01: "1",
	0x02: "2",
	0x03: "3",
	0x04: "4",
	0x05: "5",
	0x06: "6",
	0x07: "7",
	0x08: "8",
	0x09: "9",
	0x0a: ".",
	0x0b: "E",
	0x0c: "E-",
	0x0d: "",
	0x0e: "-",
	0x0f: "",
}

type psOperator struct {
	// numPop is the number of stack values to pop. -1 means "array" and -2
	// means "delta" as per 5176.CFF.pdf Table 6 "Operand Types".
	numPop int32
	// name is the operator name. An empty name (i.e. the zero value for the
	// struct overall) means an unrecognized 1-byte operator.
	name string
	// run is the function that implements the operator. Nil means that we
	// ignore the operator, other than popping its arguments off the stack.
	run func(*psInterpreter) error
}

// psOperators holds the 1-byte and 2-byte operators for PostScript interpreter
// contexts.
var psOperators = [...][2][]psOperator{
	// The Top DICT operators are defined by 5176.CFF.pdf Table 9 "Top DICT
	// Operator Entries" and Table 10 "CIDFont Operator Extensions".
	psContextTopDict: {{
		// 1-byte operators.
		0:  {+1, "version", nil},
		1:  {+1, "Notice", nil},
		2:  {+1, "FullName", nil},
		3:  {+1, "FamilyName", nil},
		4:  {+1, "Weight", nil},
		5:  {-1, "FontBBox", nil},
		13: {+1, "UniqueID", nil},
		14: {-1, "XUID", nil},
		15: {+1, "charset", nil},
		16: {+1, "Encoding", nil},
		17: {+1, "CharStrings", func( *psInterpreter) error {
			.topDict.charStringsOffset = .argStack.a[.argStack.top-1]
			return nil
		}},
		18: {+2, "Private", func( *psInterpreter) error {
			.topDict.privateDictLength = .argStack.a[.argStack.top-2]
			.topDict.privateDictOffset = .argStack.a[.argStack.top-1]
			return nil
		}},
	}, {
		// 2-byte operators. The first byte is the escape byte.
		0:  {+1, "Copyright", nil},
		1:  {+1, "isFixedPitch", nil},
		2:  {+1, "ItalicAngle", nil},
		3:  {+1, "UnderlinePosition", nil},
		4:  {+1, "UnderlineThickness", nil},
		5:  {+1, "PaintType", nil},
		6:  {+1, "CharstringType", nil},
		7:  {-1, "FontMatrix", nil},
		8:  {+1, "StrokeWidth", nil},
		20: {+1, "SyntheticBase", nil},
		21: {+1, "PostScript", nil},
		22: {+1, "BaseFontName", nil},
		23: {-2, "BaseFontBlend", nil},
		30: {+3, "ROS", func( *psInterpreter) error {
			.topDict.isCIDFont = true
			return nil
		}},
		31: {+1, "CIDFontVersion", nil},
		32: {+1, "CIDFontRevision", nil},
		33: {+1, "CIDFontType", nil},
		34: {+1, "CIDCount", nil},
		35: {+1, "UIDBase", nil},
		36: {+1, "FDArray", func( *psInterpreter) error {
			.topDict.fdArray = .argStack.a[.argStack.top-1]
			return nil
		}},
		37: {+1, "FDSelect", func( *psInterpreter) error {
			.topDict.fdSelect = .argStack.a[.argStack.top-1]
			return nil
		}},
		38: {+1, "FontName", nil},
	}},

	// The Private DICT operators are defined by 5176.CFF.pdf Table 23 "Private
	// DICT Operators".
	psContextPrivateDict: {{
		// 1-byte operators.
		6:  {-2, "BlueValues", nil},
		7:  {-2, "OtherBlues", nil},
		8:  {-2, "FamilyBlues", nil},
		9:  {-2, "FamilyOtherBlues", nil},
		10: {+1, "StdHW", nil},
		11: {+1, "StdVW", nil},
		19: {+1, "Subrs", func( *psInterpreter) error {
			.privateDict.subrsOffset = .argStack.a[.argStack.top-1]
			return nil
		}},
		20: {+1, "defaultWidthX", nil},
		21: {+1, "nominalWidthX", nil},
	}, {
		// 2-byte operators. The first byte is the escape byte.
		9:  {+1, "BlueScale", nil},
		10: {+1, "BlueShift", nil},
		11: {+1, "BlueFuzz", nil},
		12: {-2, "StemSnapH", nil},
		13: {-2, "StemSnapV", nil},
		14: {+1, "ForceBold", nil},
		17: {+1, "LanguageGroup", nil},
		18: {+1, "ExpansionFactor", nil},
		19: {+1, "initialRandomSeed", nil},
	}},

	// The Type 2 Charstring operators are defined by 5177.Type2.pdf Appendix A
	// "Type 2 Charstring Command Codes".
	psContextType2Charstring: {{
		// 1-byte operators.
		0:  {}, // Reserved.
		1:  {-1, "hstem", t2CStem},
		2:  {}, // Reserved.
		3:  {-1, "vstem", t2CStem},
		4:  {-1, "vmoveto", t2CVmoveto},
		5:  {-1, "rlineto", t2CRlineto},
		6:  {-1, "hlineto", t2CHlineto},
		7:  {-1, "vlineto", t2CVlineto},
		8:  {-1, "rrcurveto", t2CRrcurveto},
		9:  {}, // Reserved.
		10: {+1, "callsubr", t2CCallsubr},
		11: {+0, "return", t2CReturn},
		12: {}, // escape.
		13: {}, // Reserved.
		14: {-1, "endchar", t2CEndchar},
		15: {}, // Reserved.
		16: {}, // Reserved.
		17: {}, // Reserved.
		18: {-1, "hstemhm", t2CStem},
		19: {-1, "hintmask", t2CMask},
		20: {-1, "cntrmask", t2CMask},
		21: {-1, "rmoveto", t2CRmoveto},
		22: {-1, "hmoveto", t2CHmoveto},
		23: {-1, "vstemhm", t2CStem},
		24: {-1, "rcurveline", t2CRcurveline},
		25: {-1, "rlinecurve", t2CRlinecurve},
		26: {-1, "vvcurveto", t2CVvcurveto},
		27: {-1, "hhcurveto", t2CHhcurveto},
		28: {}, // shortint.
		29: {+1, "callgsubr", t2CCallgsubr},
		30: {-1, "vhcurveto", t2CVhcurveto},
		31: {-1, "hvcurveto", t2CHvcurveto},
	}, {
		// 2-byte operators. The first byte is the escape byte.
		34: {+7, "hflex", t2CHflex},
		36: {+9, "hflex1", t2CHflex1},
		// TODO: more operators.
	}},
}

// 5176.CFF.pdf section 4 "DICT Data" says that "Two-byte operators have an
// initial escape byte of 12".
const escapeByte = 12

// t2CReadWidth reads the optional width adjustment. If present, it is on the
// bottom of the arg stack. nArgs is the expected number of arguments on the
// stack. A negative nArgs means a multiple of 2.
//
// 5177.Type2.pdf page 16 Note 4 says: "The first stack-clearing operator,
// which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask,
// hmoveto, vmoveto, rmoveto, or endchar, takes an additional argument — the
// width... which may be expressed as zero or one numeric argument."
func t2CReadWidth( *psInterpreter,  int32) {
	if .type2Charstrings.seenWidth {
		return
	}
	.type2Charstrings.seenWidth = true
	if  >= 0 {
		if .argStack.top != +1 {
			return
		}
	} else if .argStack.top&1 == 0 {
		return
	}
	// When parsing a standalone CFF, we'd save the value of p.argStack.a[0]
	// here as it defines the glyph's width (horizontal advance). Specifically,
	// if present, it is a delta to the font-global nominalWidthX value found
	// in the Private DICT. If absent, the glyph's width is the defaultWidthX
	// value in that dict. See 5176.CFF.pdf section 15 "Private DICT Data".
	//
	// For a CFF embedded in an SFNT font (i.e. an OpenType font), glyph widths
	// are already stored in the hmtx table, separate to the CFF table, and it
	// is simpler to parse that table for all OpenType fonts (PostScript and
	// TrueType). We therefore ignore the width value here, and just remove it
	// from the bottom of the argStack.
	copy(.argStack.a[:.argStack.top-1], .argStack.a[1:.argStack.top])
	.argStack.top--
}

func t2CStem( *psInterpreter) error {
	t2CReadWidth(, -1)
	if .argStack.top%2 != 0 {
		return errInvalidCFFTable
	}
	// We update the number of hintBits need to parse hintmask and cntrmask
	// instructions, but this Type 2 Charstring implementation otherwise
	// ignores the stem hints.
	.type2Charstrings.hintBits += .argStack.top / 2
	if .type2Charstrings.hintBits > maxHintBits {
		return errUnsupportedNumberOfHints
	}
	return nil
}

func t2CMask( *psInterpreter) error {
	// 5176.CFF.pdf section 4.3 "Hint Operators" says that "If hstem and vstem
	// hints are both declared at the beginning of a charstring, and this
	// sequence is followed directly by the hintmask or cntrmask operators, the
	// vstem hint operator need not be included."
	//
	// What we implement here is more permissive (but the same as what the
	// FreeType implementation does, and simpler than tracking the previous
	// operator and other hinting state): if a hintmask is given any arguments
	// (i.e. the argStack is non-empty), we run an implicit vstem operator.
	//
	// Note that the vstem operator consumes from p.argStack, but the hintmask
	// or cntrmask operators consume from p.instructions.
	if .argStack.top != 0 {
		if  := t2CStem();  != nil {
			return 
		}
	} else if !.type2Charstrings.seenWidth {
		.type2Charstrings.seenWidth = true
	}

	 := (.type2Charstrings.hintBits + 7) / 8
	if len(.instructions) < int() {
		return errInvalidCFFTable
	}
	.instructions = .instructions[:]
	return nil
}

func t2CHmoveto( *psInterpreter) error {
	t2CReadWidth(, 1)
	if .argStack.top != 1 {
		return errInvalidCFFTable
	}
	.type2Charstrings.moveTo(.argStack.a[0], 0)
	return nil
}

func t2CVmoveto( *psInterpreter) error {
	t2CReadWidth(, 1)
	if .argStack.top != 1 {
		return errInvalidCFFTable
	}
	.type2Charstrings.moveTo(0, .argStack.a[0])
	return nil
}

func t2CRmoveto( *psInterpreter) error {
	t2CReadWidth(, 2)
	if .argStack.top != 2 {
		return errInvalidCFFTable
	}
	.type2Charstrings.moveTo(.argStack.a[0], .argStack.a[1])
	return nil
}

func t2CHlineto( *psInterpreter) error { return t2CLineto(, false) }
func t2CVlineto( *psInterpreter) error { return t2CLineto(, true) }

func t2CLineto( *psInterpreter,  bool) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 1 {
		return errInvalidCFFTable
	}
	for  := int32(0);  < .argStack.top; ,  = +1, ! {
		,  := .argStack.a[], int32(0)
		if  {
			,  = , 
		}
		.type2Charstrings.lineTo(, )
	}
	return nil
}

func t2CRlineto( *psInterpreter) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 2 || .argStack.top%2 != 0 {
		return errInvalidCFFTable
	}
	for  := int32(0);  < .argStack.top;  += 2 {
		.type2Charstrings.lineTo(.argStack.a[], .argStack.a[+1])
	}
	return nil
}

// As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
//
// rcurveline is:
//	- {dxa dya dxb dyb dxc dyc}+ dxd dyd
//
// rlinecurve is:
//	- {dxa dya}+ dxb dyb dxc dyc dxd dyd

func t2CRcurveline( *psInterpreter) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 8 || .argStack.top%6 != 2 {
		return errInvalidCFFTable
	}
	 := int32(0)
	for  := .argStack.top - 2;  < ;  += 6 {
		.type2Charstrings.cubeTo(
			.argStack.a[+0],
			.argStack.a[+1],
			.argStack.a[+2],
			.argStack.a[+3],
			.argStack.a[+4],
			.argStack.a[+5],
		)
	}
	.type2Charstrings.lineTo(.argStack.a[], .argStack.a[+1])
	return nil
}

func t2CRlinecurve( *psInterpreter) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 8 || .argStack.top%2 != 0 {
		return errInvalidCFFTable
	}
	 := int32(0)
	for  := .argStack.top - 6;  < ;  += 2 {
		.type2Charstrings.lineTo(.argStack.a[], .argStack.a[+1])
	}
	.type2Charstrings.cubeTo(
		.argStack.a[+0],
		.argStack.a[+1],
		.argStack.a[+2],
		.argStack.a[+3],
		.argStack.a[+4],
		.argStack.a[+5],
	)
	return nil
}

// As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
//
// hhcurveto is:
//	- dy1 {dxa dxb dyb dxc}+
//
// vvcurveto is:
//	- dx1 {dya dxb dyb dyc}+
//
// hvcurveto is one of:
//	- dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
//	- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
//
// vhcurveto is one of:
//	- dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
//	- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?

func t2CHhcurveto( *psInterpreter) error { return t2CCurveto(, false, false) }
func t2CVvcurveto( *psInterpreter) error { return t2CCurveto(, false, true) }
func t2CHvcurveto( *psInterpreter) error { return t2CCurveto(, true, false) }
func t2CVhcurveto( *psInterpreter) error { return t2CCurveto(, true, true) }

// t2CCurveto implements the hh / vv / hv / vh xxcurveto operators. N relative
// cubic curve requires 6*N control points, but only 4*N+0 or 4*N+1 are used
// here: all (or all but one) of the piecewise cubic curve's tangents are
// implicitly horizontal or vertical.
//
// swap is whether that implicit horizontal / vertical constraint swaps as you
// move along the piecewise cubic curve. If swap is false, the constraints are
// either all horizontal or all vertical. If swap is true, it alternates.
//
// vertical is whether the first implicit constraint is vertical.
func t2CCurveto( *psInterpreter, ,  bool) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 4 {
		return errInvalidCFFTable
	}

	 := int32(0)
	switch .argStack.top & 3 {
	case 0:
		// No-op.
	case 1:
		if  {
			break
		}
		 = 1
		if  {
			.type2Charstrings.x += .argStack.a[0]
		} else {
			.type2Charstrings.y += .argStack.a[0]
		}
	default:
		return errInvalidCFFTable
	}

	for  != .argStack.top {
		 = t2CCurveto4(, , , )
		if  < 0 {
			return errInvalidCFFTable
		}
		if  {
			 = !
		}
	}
	return nil
}

func t2CCurveto4( *psInterpreter,  bool,  bool,  int32) ( int32) {
	if +4 > .argStack.top {
		return -1
	}
	 := .argStack.a[+0]
	 := int32(0)
	 := .argStack.a[+1]
	 := .argStack.a[+2]
	 := .argStack.a[+3]
	 := int32(0)
	 += 4

	if  {
		,  = , 
	}

	if  {
		if +1 == .argStack.top {
			 = .argStack.a[]
			++
		}
	}

	if  !=  {
		,  = , 
	}

	.type2Charstrings.cubeTo(, , , , , )
	return 
}

func t2CRrcurveto( *psInterpreter) error {
	if !.type2Charstrings.seenWidth || .argStack.top < 6 || .argStack.top%6 != 0 {
		return errInvalidCFFTable
	}
	for  := int32(0);  != .argStack.top;  += 6 {
		.type2Charstrings.cubeTo(
			.argStack.a[+0],
			.argStack.a[+1],
			.argStack.a[+2],
			.argStack.a[+3],
			.argStack.a[+4],
			.argStack.a[+5],
		)
	}
	return nil
}

// For the flex operators, we ignore the flex depth and always produce cubic
// segments, not linear segments. It's not obvious why the Type 2 Charstring
// format cares about switching behavior based on a metric in pixels, not in
// ideal font units. The Go vector rasterizer has no problems with almost
// linear cubic segments.

func t2CHflex( *psInterpreter) error {
	.type2Charstrings.cubeTo(
		.argStack.a[0], 0,
		.argStack.a[1], +.argStack.a[2],
		.argStack.a[3], 0,
	)
	.type2Charstrings.cubeTo(
		.argStack.a[4], 0,
		.argStack.a[5], -.argStack.a[2],
		.argStack.a[6], 0,
	)
	return nil
}

func t2CHflex1( *psInterpreter) error {
	 := .argStack.a[1]
	 := .argStack.a[3]
	 := .argStack.a[7]
	 := - -  - 
	.type2Charstrings.cubeTo(
		.argStack.a[0], ,
		.argStack.a[2], ,
		.argStack.a[4], 0,
	)
	.type2Charstrings.cubeTo(
		.argStack.a[5], 0,
		.argStack.a[6], ,
		.argStack.a[8], ,
	)
	return nil
}

// subrBias returns the subroutine index bias as per 5177.Type2.pdf section 4.7
// "Subroutine Operators".
func subrBias( int) int32 {
	if  < 1240 {
		return 107
	}
	if  < 33900 {
		return 1131
	}
	return 32768
}

func t2CCallgsubr( *psInterpreter) error {
	return t2CCall(, .type2Charstrings.f.cached.glyphData.gsubrs)
}

func t2CCallsubr( *psInterpreter) error {
	 := &.type2Charstrings
	 := &.f.cached.glyphData
	 := .singleSubrs
	if .multiSubrs != nil {
		if .fdSelectIndexPlusOne == 0 {
			,  := .fdSelect.lookup(.f, .b, .glyphIndex)
			if  != nil {
				return 
			}
			if  < 0 || len(.multiSubrs) <=  {
				return errInvalidCFFTable
			}
			.fdSelectIndexPlusOne = int32( + 1)
		}
		 = .multiSubrs[.fdSelectIndexPlusOne-1]
	}
	return t2CCall(, )
}

func t2CCall( *psInterpreter,  []uint32) error {
	if .callStack.top == psCallStackSize || len() == 0 {
		return errInvalidCFFTable
	}
	 := uint32(len(.instructions))
	.callStack.a[.callStack.top] = psCallStackEntry{
		offset: .instrOffset + .instrLength - ,
		length: ,
	}
	.callStack.top++

	 := .argStack.a[.argStack.top-1] + subrBias(len()-1)
	if  < 0 || int32(len()-1) <=  {
		return errInvalidCFFTable
	}
	 := [+0]
	 := [+1]
	if  <  {
		return errInvalidCFFTable
	}
	if - > maxGlyphDataLength {
		return errUnsupportedGlyphDataLength
	}
	,  := .type2Charstrings.b.view(&.type2Charstrings.f.src, int(), int(-))
	if  != nil {
		return 
	}

	.instructions = 
	.instrOffset = 
	.instrLength =  - 
	return nil
}

func t2CReturn( *psInterpreter) error {
	if .callStack.top <= 0 {
		return errInvalidCFFTable
	}
	.callStack.top--
	 := .callStack.a[.callStack.top].offset
	 := .callStack.a[.callStack.top].length
	,  := .type2Charstrings.b.view(&.type2Charstrings.f.src, int(), int())
	if  != nil {
		return 
	}

	.instructions = 
	.instrOffset = 
	.instrLength = 
	return nil
}

func t2CEndchar( *psInterpreter) error {
	t2CReadWidth(, 0)
	if .argStack.top != 0 || .hasMoreInstructions() {
		if .argStack.top == 4 {
			// TODO: process the implicit "seac" command as per 5177.Type2.pdf
			// Appendix C "Compatibility and Deprecated Operators".
			return errUnsupportedType2Charstring
		}
		return errInvalidCFFTable
	}
	.type2Charstrings.closePath()
	.type2Charstrings.ended = true
	return nil
}