From 14bb08c1df8db9ec6c8a05520d4eee67971235d9 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 27 Sep 2018 20:03:23 +0200 Subject: mod tidy --- vendor/golang.org/x/image/font/sfnt/postscript.go | 1414 --------------------- 1 file changed, 1414 deletions(-) delete mode 100644 vendor/golang.org/x/image/font/sfnt/postscript.go (limited to 'vendor/golang.org/x/image/font/sfnt/postscript.go') diff --git a/vendor/golang.org/x/image/font/sfnt/postscript.go b/vendor/golang.org/x/image/font/sfnt/postscript.go deleted file mode 100644 index b686e60..0000000 --- a/vendor/golang.org/x/image/font/sfnt/postscript.go +++ /dev/null @@ -1,1414 +0,0 @@ -// 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 ( - "fmt" - "math" - "strconv" - - "golang.org/x/image/math/fixed" -) - -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(b []byte) uint32 { - switch len(b) { - case 1: - return uint32(b[0]) - case 2: - return uint32(b[0])<<8 | uint32(b[1]) - case 3: - return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]) - case 4: - return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) - } - panic("unreachable") -} - -// fdSelect holds a CFF font's Font Dict Select data. -type fdSelect struct { - format uint8 - numRanges uint16 - offset int32 -} - -func (t *fdSelect) lookup(f *Font, b *Buffer, x GlyphIndex) (int, error) { - switch t.format { - case 0: - buf, err := b.view(&f.src, int(t.offset)+int(x), 1) - if err != nil { - return 0, err - } - return int(buf[0]), nil - case 3: - lo, hi := 0, int(t.numRanges) - for lo < hi { - i := (lo + hi) / 2 - buf, err := b.view(&f.src, int(t.offset)+3*i, 3+2) - if err != nil { - return 0, err - } - // buf holds the range [xlo, xhi). - if xlo := GlyphIndex(u16(buf[0:])); x < xlo { - hi = i - continue - } - if xhi := GlyphIndex(u16(buf[3:])); xhi <= x { - lo = i + 1 - continue - } - return int(buf[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 (p *cffParser) parse(numGlyphs int32) (ret glyphData, err error) { - // Parse the header. - { - if !p.read(4) { - return glyphData{}, p.err - } - if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 { - return glyphData{}, errUnsupportedCFFVersion - } - } - - // Parse the Name INDEX. - { - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.err - } - // https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The - // Name INDEX in the CFF must contain only one entry". - if count != 1 { - return glyphData{}, errInvalidCFFTable - } - if !p.parseIndexLocations(p.locBuf[:2], count, offSize) { - return glyphData{}, p.err - } - p.offset = int(p.locBuf[1]) - } - - // Parse the Top DICT INDEX. - p.psi.topDict.initialize() - { - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.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 count != 1 { - return glyphData{}, errInvalidCFFTable - } - if !p.parseIndexLocations(p.locBuf[:2], count, offSize) { - return glyphData{}, p.err - } - if !p.read(int(p.locBuf[1] - p.locBuf[0])) { - return glyphData{}, p.err - } - if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil { - return glyphData{}, p.err - } - } - - // Skip the String INDEX. - { - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.err - } - if count != 0 { - // Read the last location. Locations are off by 1 byte. See the - // comment in parseIndexLocations. - if !p.skip(int(count * offSize)) { - return glyphData{}, p.err - } - if !p.read(int(offSize)) { - return glyphData{}, p.err - } - loc := bigEndian(p.buf) - 1 - // Check that locations are in bounds. - if uint32(p.end-p.offset) < loc { - return glyphData{}, errInvalidCFFTable - } - // Skip the index data. - if !p.skip(int(loc)) { - return glyphData{}, p.err - } - } - } - - // Parse the Global Subrs [Subroutines] INDEX. - { - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.err - } - if count != 0 { - if count > maxNumSubroutines { - return glyphData{}, errUnsupportedNumberOfSubroutines - } - ret.gsubrs = make([]uint32, count+1) - if !p.parseIndexLocations(ret.gsubrs, count, offSize) { - return glyphData{}, p.err - } - } - } - - // Parse the CharStrings INDEX, whose location was found in the Top DICT. - { - if !p.seekFromBase(p.psi.topDict.charStringsOffset) { - return glyphData{}, errInvalidCFFTable - } - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.err - } - if count == 0 || int32(count) != numGlyphs { - return glyphData{}, errInvalidCFFTable - } - ret.locations = make([]uint32, count+1) - if !p.parseIndexLocations(ret.locations, count, offSize) { - return glyphData{}, p.err - } - } - - if !p.psi.topDict.isCIDFont { - // Parse the Private DICT, whose location was found in the Top DICT. - ret.singleSubrs, err = p.parsePrivateDICT( - p.psi.topDict.privateDictOffset, - p.psi.topDict.privateDictLength, - ) - if err != nil { - return glyphData{}, err - } - - } else { - // Parse the Font Dict Select data, whose location was found in the Top - // DICT. - ret.fdSelect, err = p.parseFDSelect(p.psi.topDict.fdSelect, numGlyphs) - if err != nil { - return glyphData{}, err - } - - // Parse the Font Dicts. Each one contains its own Private DICT. - if !p.seekFromBase(p.psi.topDict.fdArray) { - return glyphData{}, errInvalidCFFTable - } - - count, offSize, ok := p.parseIndexHeader() - if !ok { - return glyphData{}, p.err - } - if count > maxNumFontDicts { - return glyphData{}, errUnsupportedNumberOfFontDicts - } - - fdLocations := make([]uint32, count+1) - if !p.parseIndexLocations(fdLocations, count, offSize) { - return glyphData{}, p.err - } - - privateDicts := make([]struct { - offset, length int32 - }, count) - - for i := range privateDicts { - length := fdLocations[i+1] - fdLocations[i] - if !p.read(int(length)) { - return glyphData{}, errInvalidCFFTable - } - p.psi.topDict.initialize() - if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil { - return glyphData{}, p.err - } - privateDicts[i].offset = p.psi.topDict.privateDictOffset - privateDicts[i].length = p.psi.topDict.privateDictLength - } - - ret.multiSubrs = make([][]uint32, count) - for i, pd := range privateDicts { - ret.multiSubrs[i], err = p.parsePrivateDICT(pd.offset, pd.length) - if err != nil { - return glyphData{}, err - } - } - } - - return ret, err -} - -// parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section -// 19 "FDSelect". -func (p *cffParser) parseFDSelect(offset int32, numGlyphs int32) (ret fdSelect, err error) { - if !p.seekFromBase(p.psi.topDict.fdSelect) { - return fdSelect{}, errInvalidCFFTable - } - if !p.read(1) { - return fdSelect{}, p.err - } - ret.format = p.buf[0] - switch ret.format { - case 0: - if p.end-p.offset < int(numGlyphs) { - return fdSelect{}, errInvalidCFFTable - } - ret.offset = int32(p.offset) - return ret, nil - case 3: - if !p.read(2) { - return fdSelect{}, p.err - } - ret.numRanges = u16(p.buf) - if p.end-p.offset < 3*int(ret.numRanges)+2 { - return fdSelect{}, errInvalidCFFTable - } - ret.offset = int32(p.offset) - return ret, nil - } - return fdSelect{}, errUnsupportedCFFFDSelectTable -} - -func (p *cffParser) parsePrivateDICT(offset, length int32) (subrs []uint32, err error) { - p.psi.privateDict.initialize() - if length != 0 { - fullLength := int32(p.end - p.base) - if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 { - return nil, errInvalidCFFTable - } - p.offset = p.base + int(offset) - if !p.read(int(length)) { - return nil, p.err - } - if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil { - return nil, p.err - } - } - - // Parse the Local Subrs [Subroutines] INDEX, whose location was found in - // the Private DICT. - if p.psi.privateDict.subrsOffset != 0 { - if !p.seekFromBase(offset + p.psi.privateDict.subrsOffset) { - return nil, errInvalidCFFTable - } - count, offSize, ok := p.parseIndexHeader() - if !ok { - return nil, p.err - } - if count != 0 { - if count > maxNumSubroutines { - return nil, errUnsupportedNumberOfSubroutines - } - subrs = make([]uint32, count+1) - if !p.parseIndexLocations(subrs, count, offSize) { - return nil, p.err - } - } - } - - return subrs, err -} - -// 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 (p *cffParser) read(n int) (ok bool) { - if n < 0 || p.end-p.offset < n { - p.err = errInvalidCFFTable - return false - } - p.buf, p.err = p.src.view(p.buf, p.offset, n) - // TODO: if p.err == io.EOF, change that to a different error?? - p.offset += n - return p.err == nil -} - -func (p *cffParser) skip(n int) (ok bool) { - if p.end-p.offset < n { - p.err = errInvalidCFFTable - return false - } - p.offset += n - return true -} - -func (p *cffParser) seekFromBase(offset int32) (ok bool) { - if offset < 0 || int32(p.end-p.base) < offset { - return false - } - p.offset = p.base + int(offset) - return true -} - -func (p *cffParser) parseIndexHeader() (count, offSize int32, ok bool) { - if !p.read(2) { - return 0, 0, false - } - count = int32(u16(p.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 count == 0 { - return count, 0, true - } - if !p.read(1) { - return 0, 0, false - } - offSize = int32(p.buf[0]) - if offSize < 1 || 4 < offSize { - p.err = errInvalidCFFTable - return 0, 0, false - } - return count, offSize, true -} - -func (p *cffParser) parseIndexLocations(dst []uint32, count, offSize int32) (ok bool) { - if count == 0 { - return true - } - if len(dst) != int(count+1) { - panic("unreachable") - } - if !p.read(len(dst) * int(offSize)) { - return false - } - - buf, prev := p.buf, uint32(0) - for i := range dst { - loc := bigEndian(buf[:offSize]) - buf = buf[offSize:] - - // 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 loc == 0 { - p.err = errInvalidCFFTable - return false - } - loc-- - - // In the same paragraph, "Therefore the first element of the offset - // array is always 1" before correcting for the off-by-1. - if i == 0 { - if loc != 0 { - p.err = errInvalidCFFTable - break - } - } else if loc <= prev { // Check that locations are increasing. - p.err = errInvalidCFFTable - break - } - - // Check that locations are in bounds. - if uint32(p.end-p.offset) < loc { - p.err = errInvalidCFFTable - break - } - - dst[i] = uint32(p.offset) + loc - prev = loc - } - return p.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 (d *psTopDictData) initialize() { - *d = psTopDictData{} -} - -// psPrivateDictData contains fields specific to the Private DICT context. -type psPrivateDictData struct { - subrsOffset int32 -} - -func (d *psPrivateDictData) initialize() { - *d = 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 (d *psType2CharstringsData) initialize(f *Font, b *Buffer, glyphIndex GlyphIndex) { - *d = psType2CharstringsData{ - f: f, - b: b, - glyphIndex: glyphIndex, - } -} - -func (d *psType2CharstringsData) closePath() { - if d.x != d.firstX || d.y != d.firstY { - d.b.segments = append(d.b.segments, Segment{ - Op: SegmentOpLineTo, - Args: [3]fixed.Point26_6{{ - X: fixed.Int26_6(d.firstX), - Y: fixed.Int26_6(d.firstY), - }}, - }) - } -} - -func (d *psType2CharstringsData) moveTo(dx, dy int32) { - d.closePath() - d.x += dx - d.y += dy - d.b.segments = append(d.b.segments, Segment{ - Op: SegmentOpMoveTo, - Args: [3]fixed.Point26_6{{ - X: fixed.Int26_6(d.x), - Y: fixed.Int26_6(d.y), - }}, - }) - d.firstX = d.x - d.firstY = d.y -} - -func (d *psType2CharstringsData) lineTo(dx, dy int32) { - d.x += dx - d.y += dy - d.b.segments = append(d.b.segments, Segment{ - Op: SegmentOpLineTo, - Args: [3]fixed.Point26_6{{ - X: fixed.Int26_6(d.x), - Y: fixed.Int26_6(d.y), - }}, - }) -} - -func (d *psType2CharstringsData) cubeTo(dxa, dya, dxb, dyb, dxc, dyc int32) { - d.x += dxa - d.y += dya - xa := fixed.Int26_6(d.x) - ya := fixed.Int26_6(d.y) - d.x += dxb - d.y += dyb - xb := fixed.Int26_6(d.x) - yb := fixed.Int26_6(d.y) - d.x += dxc - d.y += dyc - xc := fixed.Int26_6(d.x) - yc := fixed.Int26_6(d.y) - d.b.segments = append(d.b.segments, Segment{ - Op: SegmentOpCubeTo, - Args: [3]fixed.Point26_6{{X: xa, Y: ya}, {X: xb, Y: yb}, {X: xc, Y: yc}}, - }) -} - -// 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 (p *psInterpreter) hasMoreInstructions() bool { - if len(p.instructions) != 0 { - return true - } - for i := int32(0); i < p.callStack.top; i++ { - if p.callStack.a[i].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 (p *psInterpreter) run(ctx psContext, instructions []byte, offset, length uint32) error { - p.ctx = ctx - p.instructions = instructions - p.instrOffset = offset - p.instrLength = length - p.argStack.top = 0 - p.callStack.top = 0 - -loop: - for len(p.instructions) > 0 { - // Push a numeric operand on the stack, if applicable. - if hasResult, err := p.parseNumber(); hasResult { - if err != nil { - return err - } - continue - } - - // Otherwise, execute an operator. - b := p.instructions[0] - p.instructions = p.instructions[1:] - - for escaped, ops := false, psOperators[ctx][0]; ; { - if b == escapeByte && !escaped { - if len(p.instructions) <= 0 { - return errInvalidCFFTable - } - b = p.instructions[0] - p.instructions = p.instructions[1:] - escaped = true - ops = psOperators[ctx][1] - continue - } - - if int(b) < len(ops) { - if op := ops[b]; op.name != "" { - if p.argStack.top < op.numPop { - return errInvalidCFFTable - } - if op.run != nil { - if err := op.run(p); err != nil { - return err - } - } - if op.numPop < 0 { - p.argStack.top = 0 - } else { - p.argStack.top -= op.numPop - } - continue loop - } - } - - if escaped { - return fmt.Errorf("sfnt: unrecognized CFF 2-byte operator (12 %d)", b) - } else { - return fmt.Errorf("sfnt: unrecognized CFF 1-byte operator (%d)", b) - } - } - } - return nil -} - -// See 5176.CFF.pdf section 4 "DICT Data". -func (p *psInterpreter) parseNumber() (hasResult bool, err error) { - number := int32(0) - switch b := p.instructions[0]; { - case b == 28: - if len(p.instructions) < 3 { - return true, errInvalidCFFTable - } - number, hasResult = int32(int16(u16(p.instructions[1:]))), true - p.instructions = p.instructions[3:] - - case b == 29 && p.ctx != psContextType2Charstring: - if len(p.instructions) < 5 { - return true, errInvalidCFFTable - } - number, hasResult = int32(u32(p.instructions[1:])), true - p.instructions = p.instructions[5:] - - case b == 30 && p.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." - - s := p.parseNumberBuf[:0] - p.instructions = p.instructions[1:] - loop: - for { - if len(p.instructions) == 0 { - return true, errInvalidCFFTable - } - b := p.instructions[0] - p.instructions = p.instructions[1:] - // Process b's two nibbles, high then low. - for i := 0; i < 2; i++ { - nib := b >> 4 - b = b << 4 - if nib == 0x0f { - f, err := strconv.ParseFloat(string(s), 32) - if err != nil { - return true, errInvalidCFFTable - } - number, hasResult = int32(math.Float32bits(float32(f))), true - break loop - } - if nib == 0x0d { - return true, errInvalidCFFTable - } - if len(s)+maxNibbleDefsLength > len(p.parseNumberBuf) { - return true, errUnsupportedRealNumberEncoding - } - s = append(s, nibbleDefs[nib]...) - } - } - - case b < 32: - // No-op. - - case b < 247: - p.instructions = p.instructions[1:] - number, hasResult = int32(b)-139, true - - case b < 251: - if len(p.instructions) < 2 { - return true, errInvalidCFFTable - } - b1 := p.instructions[1] - p.instructions = p.instructions[2:] - number, hasResult = +int32(b-247)*256+int32(b1)+108, true - - case b < 255: - if len(p.instructions) < 2 { - return true, errInvalidCFFTable - } - b1 := p.instructions[1] - p.instructions = p.instructions[2:] - number, hasResult = -int32(b-251)*256-int32(b1)-108, true - - case b == 255 && p.ctx == psContextType2Charstring: - if len(p.instructions) < 5 { - return true, errInvalidCFFTable - } - number, hasResult = int32(u32(p.instructions[1:])), true - p.instructions = p.instructions[5:] - } - - if hasResult { - if p.argStack.top == psArgStackSize { - return true, errInvalidCFFTable - } - p.argStack.a[p.argStack.top] = number - p.argStack.top++ - } - return hasResult, 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(p *psInterpreter) error { - p.topDict.charStringsOffset = p.argStack.a[p.argStack.top-1] - return nil - }}, - 18: {+2, "Private", func(p *psInterpreter) error { - p.topDict.privateDictLength = p.argStack.a[p.argStack.top-2] - p.topDict.privateDictOffset = p.argStack.a[p.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(p *psInterpreter) error { - p.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(p *psInterpreter) error { - p.topDict.fdArray = p.argStack.a[p.argStack.top-1] - return nil - }}, - 37: {+1, "FDSelect", func(p *psInterpreter) error { - p.topDict.fdSelect = p.argStack.a[p.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(p *psInterpreter) error { - p.privateDict.subrsOffset = p.argStack.a[p.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(p *psInterpreter, nArgs int32) { - if p.type2Charstrings.seenWidth { - return - } - p.type2Charstrings.seenWidth = true - if nArgs >= 0 { - if p.argStack.top != nArgs+1 { - return - } - } else if p.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(p.argStack.a[:p.argStack.top-1], p.argStack.a[1:p.argStack.top]) - p.argStack.top-- -} - -func t2CStem(p *psInterpreter) error { - t2CReadWidth(p, -1) - if p.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. - p.type2Charstrings.hintBits += p.argStack.top / 2 - if p.type2Charstrings.hintBits > maxHintBits { - return errUnsupportedNumberOfHints - } - return nil -} - -func t2CMask(p *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 p.argStack.top != 0 { - if err := t2CStem(p); err != nil { - return err - } - } else if !p.type2Charstrings.seenWidth { - p.type2Charstrings.seenWidth = true - } - - hintBytes := (p.type2Charstrings.hintBits + 7) / 8 - if len(p.instructions) < int(hintBytes) { - return errInvalidCFFTable - } - p.instructions = p.instructions[hintBytes:] - return nil -} - -func t2CHmoveto(p *psInterpreter) error { - t2CReadWidth(p, 1) - if p.argStack.top != 1 { - return errInvalidCFFTable - } - p.type2Charstrings.moveTo(p.argStack.a[0], 0) - return nil -} - -func t2CVmoveto(p *psInterpreter) error { - t2CReadWidth(p, 1) - if p.argStack.top != 1 { - return errInvalidCFFTable - } - p.type2Charstrings.moveTo(0, p.argStack.a[0]) - return nil -} - -func t2CRmoveto(p *psInterpreter) error { - t2CReadWidth(p, 2) - if p.argStack.top != 2 { - return errInvalidCFFTable - } - p.type2Charstrings.moveTo(p.argStack.a[0], p.argStack.a[1]) - return nil -} - -func t2CHlineto(p *psInterpreter) error { return t2CLineto(p, false) } -func t2CVlineto(p *psInterpreter) error { return t2CLineto(p, true) } - -func t2CLineto(p *psInterpreter, vertical bool) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 1 { - return errInvalidCFFTable - } - for i := int32(0); i < p.argStack.top; i, vertical = i+1, !vertical { - dx, dy := p.argStack.a[i], int32(0) - if vertical { - dx, dy = dy, dx - } - p.type2Charstrings.lineTo(dx, dy) - } - return nil -} - -func t2CRlineto(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 2 || p.argStack.top%2 != 0 { - return errInvalidCFFTable - } - for i := int32(0); i < p.argStack.top; i += 2 { - p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+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(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 8 || p.argStack.top%6 != 2 { - return errInvalidCFFTable - } - i := int32(0) - for iMax := p.argStack.top - 2; i < iMax; i += 6 { - p.type2Charstrings.cubeTo( - p.argStack.a[i+0], - p.argStack.a[i+1], - p.argStack.a[i+2], - p.argStack.a[i+3], - p.argStack.a[i+4], - p.argStack.a[i+5], - ) - } - p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1]) - return nil -} - -func t2CRlinecurve(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 8 || p.argStack.top%2 != 0 { - return errInvalidCFFTable - } - i := int32(0) - for iMax := p.argStack.top - 6; i < iMax; i += 2 { - p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1]) - } - p.type2Charstrings.cubeTo( - p.argStack.a[i+0], - p.argStack.a[i+1], - p.argStack.a[i+2], - p.argStack.a[i+3], - p.argStack.a[i+4], - p.argStack.a[i+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(p *psInterpreter) error { return t2CCurveto(p, false, false) } -func t2CVvcurveto(p *psInterpreter) error { return t2CCurveto(p, false, true) } -func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, true, false) } -func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, 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(p *psInterpreter, swap, vertical bool) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 4 { - return errInvalidCFFTable - } - - i := int32(0) - switch p.argStack.top & 3 { - case 0: - // No-op. - case 1: - if swap { - break - } - i = 1 - if vertical { - p.type2Charstrings.x += p.argStack.a[0] - } else { - p.type2Charstrings.y += p.argStack.a[0] - } - default: - return errInvalidCFFTable - } - - for i != p.argStack.top { - i = t2CCurveto4(p, swap, vertical, i) - if i < 0 { - return errInvalidCFFTable - } - if swap { - vertical = !vertical - } - } - return nil -} - -func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) { - if i+4 > p.argStack.top { - return -1 - } - dxa := p.argStack.a[i+0] - dya := int32(0) - dxb := p.argStack.a[i+1] - dyb := p.argStack.a[i+2] - dxc := p.argStack.a[i+3] - dyc := int32(0) - i += 4 - - if vertical { - dxa, dya = dya, dxa - } - - if swap { - if i+1 == p.argStack.top { - dyc = p.argStack.a[i] - i++ - } - } - - if swap != vertical { - dxc, dyc = dyc, dxc - } - - p.type2Charstrings.cubeTo(dxa, dya, dxb, dyb, dxc, dyc) - return i -} - -func t2CRrcurveto(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth || p.argStack.top < 6 || p.argStack.top%6 != 0 { - return errInvalidCFFTable - } - for i := int32(0); i != p.argStack.top; i += 6 { - p.type2Charstrings.cubeTo( - p.argStack.a[i+0], - p.argStack.a[i+1], - p.argStack.a[i+2], - p.argStack.a[i+3], - p.argStack.a[i+4], - p.argStack.a[i+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(p *psInterpreter) error { - p.type2Charstrings.cubeTo( - p.argStack.a[0], 0, - p.argStack.a[1], +p.argStack.a[2], - p.argStack.a[3], 0, - ) - p.type2Charstrings.cubeTo( - p.argStack.a[4], 0, - p.argStack.a[5], -p.argStack.a[2], - p.argStack.a[6], 0, - ) - return nil -} - -func t2CHflex1(p *psInterpreter) error { - dy1 := p.argStack.a[1] - dy2 := p.argStack.a[3] - dy5 := p.argStack.a[7] - dy6 := -dy1 - dy2 - dy5 - p.type2Charstrings.cubeTo( - p.argStack.a[0], dy1, - p.argStack.a[2], dy2, - p.argStack.a[4], 0, - ) - p.type2Charstrings.cubeTo( - p.argStack.a[5], 0, - p.argStack.a[6], dy5, - p.argStack.a[8], dy6, - ) - return nil -} - -// subrBias returns the subroutine index bias as per 5177.Type2.pdf section 4.7 -// "Subroutine Operators". -func subrBias(numSubroutines int) int32 { - if numSubroutines < 1240 { - return 107 - } - if numSubroutines < 33900 { - return 1131 - } - return 32768 -} - -func t2CCallgsubr(p *psInterpreter) error { - return t2CCall(p, p.type2Charstrings.f.cached.glyphData.gsubrs) -} - -func t2CCallsubr(p *psInterpreter) error { - t := &p.type2Charstrings - d := &t.f.cached.glyphData - subrs := d.singleSubrs - if d.multiSubrs != nil { - if t.fdSelectIndexPlusOne == 0 { - index, err := d.fdSelect.lookup(t.f, t.b, t.glyphIndex) - if err != nil { - return err - } - if index < 0 || len(d.multiSubrs) <= index { - return errInvalidCFFTable - } - t.fdSelectIndexPlusOne = int32(index + 1) - } - subrs = d.multiSubrs[t.fdSelectIndexPlusOne-1] - } - return t2CCall(p, subrs) -} - -func t2CCall(p *psInterpreter, subrs []uint32) error { - if p.callStack.top == psCallStackSize || len(subrs) == 0 { - return errInvalidCFFTable - } - length := uint32(len(p.instructions)) - p.callStack.a[p.callStack.top] = psCallStackEntry{ - offset: p.instrOffset + p.instrLength - length, - length: length, - } - p.callStack.top++ - - subrIndex := p.argStack.a[p.argStack.top-1] + subrBias(len(subrs)-1) - if subrIndex < 0 || int32(len(subrs)-1) <= subrIndex { - return errInvalidCFFTable - } - i := subrs[subrIndex+0] - j := subrs[subrIndex+1] - if j < i { - return errInvalidCFFTable - } - if j-i > maxGlyphDataLength { - return errUnsupportedGlyphDataLength - } - buf, err := p.type2Charstrings.b.view(&p.type2Charstrings.f.src, int(i), int(j-i)) - if err != nil { - return err - } - - p.instructions = buf - p.instrOffset = i - p.instrLength = j - i - return nil -} - -func t2CReturn(p *psInterpreter) error { - if p.callStack.top <= 0 { - return errInvalidCFFTable - } - p.callStack.top-- - o := p.callStack.a[p.callStack.top].offset - n := p.callStack.a[p.callStack.top].length - buf, err := p.type2Charstrings.b.view(&p.type2Charstrings.f.src, int(o), int(n)) - if err != nil { - return err - } - - p.instructions = buf - p.instrOffset = o - p.instrLength = n - return nil -} - -func t2CEndchar(p *psInterpreter) error { - t2CReadWidth(p, 0) - if p.argStack.top != 0 || p.hasMoreInstructions() { - if p.argStack.top == 4 { - // TODO: process the implicit "seac" command as per 5177.Type2.pdf - // Appendix C "Compatibility and Deprecated Operators". - return errUnsupportedType2Charstring - } - return errInvalidCFFTable - } - p.type2Charstrings.closePath() - p.type2Charstrings.ended = true - return nil -} -- cgit v1.2.3