From 54d3d47daf9190275bbdaf8703b84969a4593451 Mon Sep 17 00:00:00 2001 From: Corey Hulen Date: Fri, 24 Mar 2017 23:31:34 -0700 Subject: PLT-6076 Adding viper libs for config file changes (#5871) * Adding viper libs for config file changes * Removing the old fsnotify lib * updating some missing libs --- vendor/golang.org/x/image/font/sfnt/postscript.go | 522 ++++++++++++++++----- .../x/image/font/sfnt/proprietary_test.go | 394 +++++++++++++++- vendor/golang.org/x/image/font/sfnt/sfnt.go | 292 +++++++++--- vendor/golang.org/x/image/font/sfnt/sfnt_test.go | 74 +++ vendor/golang.org/x/image/font/sfnt/truetype.go | 142 +++++- .../golang.org/x/image/font/testdata/glyfTest.sfd | 183 ++++++-- .../golang.org/x/image/font/testdata/glyfTest.ttf | Bin 2000 -> 2136 bytes 7 files changed, 1355 insertions(+), 252 deletions(-) (limited to 'vendor/golang.org/x/image') diff --git a/vendor/golang.org/x/image/font/sfnt/postscript.go b/vendor/golang.org/x/image/font/sfnt/postscript.go index ca1b8318c..1a00b827f 100644 --- a/vendor/golang.org/x/image/font/sfnt/postscript.go +++ b/vendor/golang.org/x/image/font/sfnt/postscript.go @@ -56,11 +56,14 @@ import ( ) const ( - // psStackSize is the 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". Similarly, 5177.Type2.pdf Appendix B "Type 2 - // Charstring Implementation Limits" says that "Argument stack 48". - psStackSize = 48 + // 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 { @@ -91,74 +94,162 @@ type cffParser struct { psi psInterpreter } -func (p *cffParser) parse() (locations []uint32, err error) { - // Parse header. +func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) { + // Parse the header. { if !p.read(4) { - return nil, p.err + return nil, nil, nil, p.err } if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 { - return nil, errUnsupportedCFFVersion + return nil, nil, nil, errUnsupportedCFFVersion } } - // Parse Name INDEX. + // Parse the Name INDEX. { count, offSize, ok := p.parseIndexHeader() if !ok { - return nil, p.err + return nil, nil, nil, 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 nil, errInvalidCFFTable + return nil, nil, nil, errInvalidCFFTable } if !p.parseIndexLocations(p.locBuf[:2], count, offSize) { - return nil, p.err + return nil, nil, nil, p.err } p.offset = int(p.locBuf[1]) } - // Parse Top DICT INDEX. + // Parse the Top DICT INDEX. + p.psi.topDict.initialize() { count, offSize, ok := p.parseIndexHeader() if !ok { - return nil, p.err + return nil, nil, nil, 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 nil, errInvalidCFFTable + return nil, nil, nil, errInvalidCFFTable } if !p.parseIndexLocations(p.locBuf[:2], count, offSize) { - return nil, p.err + return nil, nil, nil, p.err } if !p.read(int(p.locBuf[1] - p.locBuf[0])) { - return nil, p.err + return nil, nil, nil, p.err } - p.psi.topDict.initialize() - if p.err = p.psi.run(psContextTopDict, p.buf); p.err != nil { - return nil, p.err + if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil { + return nil, nil, nil, p.err } } - // Parse the CharStrings INDEX, whose location was found in the Top DICT. - if p.psi.topDict.charStrings <= 0 || int32(p.end-p.base) < p.psi.topDict.charStrings { - return nil, errInvalidCFFTable + // Skip the String INDEX. + { + count, offSize, ok := p.parseIndexHeader() + if !ok { + return nil, nil, nil, 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 nil, nil, nil, p.err + } + if !p.read(int(offSize)) { + return nil, nil, nil, p.err + } + loc := bigEndian(p.buf) - 1 + // Check that locations are in bounds. + if uint32(p.end-p.offset) < loc { + return nil, nil, nil, errInvalidCFFTable + } + // Skip the index data. + if !p.skip(int(loc)) { + return nil, nil, nil, p.err + } + } + } + + // Parse the Global Subrs [Subroutines] INDEX. + { + count, offSize, ok := p.parseIndexHeader() + if !ok { + return nil, nil, nil, p.err + } + if count != 0 { + if count > maxNumSubroutines { + return nil, nil, nil, errUnsupportedNumberOfSubroutines + } + gsubrs = make([]uint32, count+1) + if !p.parseIndexLocations(gsubrs, count, offSize) { + return nil, nil, nil, p.err + } + } } - p.offset = p.base + int(p.psi.topDict.charStrings) - count, offSize, ok := p.parseIndexHeader() - if !ok { - return nil, p.err + + // Parse the CharStrings INDEX, whose location was found in the Top DICT. + { + if p.psi.topDict.charStrings <= 0 || int32(p.end-p.base) < p.psi.topDict.charStrings { + return nil, nil, nil, errInvalidCFFTable + } + p.offset = p.base + int(p.psi.topDict.charStrings) + count, offSize, ok := p.parseIndexHeader() + if !ok { + return nil, nil, nil, p.err + } + if count == 0 { + return nil, nil, nil, errInvalidCFFTable + } + locations = make([]uint32, count+1) + if !p.parseIndexLocations(locations, count, offSize) { + return nil, nil, nil, p.err + } } - if count == 0 { - return nil, errInvalidCFFTable + + // Parse the Private DICT, whose location was found in the Top DICT. + p.psi.privateDict.initialize() + if p.psi.topDict.privateDictLength != 0 { + offset := p.psi.topDict.privateDictOffset + length := p.psi.topDict.privateDictLength + fullLength := int32(p.end - p.base) + if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 { + return nil, nil, nil, errInvalidCFFTable + } + p.offset = p.base + int(offset) + if !p.read(int(length)) { + return nil, nil, nil, p.err + } + if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil { + return nil, nil, nil, p.err + } } - locations = make([]uint32, count+1) - if !p.parseIndexLocations(locations, count, offSize) { - return nil, p.err + + // Parse the Local Subrs [Subroutines] INDEX, whose location was found in + // the Private DICT. + if p.psi.privateDict.subrs != 0 { + offset := p.psi.topDict.privateDictOffset + p.psi.privateDict.subrs + if offset <= 0 || int32(p.end-p.base) < offset { + return nil, nil, nil, errInvalidCFFTable + } + p.offset = p.base + int(offset) + count, offSize, ok := p.parseIndexHeader() + if !ok { + return nil, nil, nil, p.err + } + if count != 0 { + if count > maxNumSubroutines { + return nil, nil, nil, errUnsupportedNumberOfSubroutines + } + subrs = make([]uint32, count+1) + if !p.parseIndexLocations(subrs, count, offSize) { + return nil, nil, nil, p.err + } + } } - return locations, nil + + return locations, gsubrs, subrs, nil } // read sets p.buf to view the n bytes from p.offset to p.offset+n. It also @@ -181,6 +272,15 @@ func (p *cffParser) read(n int) (ok bool) { 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) parseIndexHeader() (count, offSize int32, ok bool) { if !p.read(2) { return 0, 0, false @@ -253,34 +353,53 @@ func (p *cffParser) parseIndexLocations(dst []uint32, count, offSize int32) (ok 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 { - charStrings int32 + charStrings int32 + privateDictOffset int32 + privateDictLength int32 } func (d *psTopDictData) initialize() { *d = psTopDictData{} } +// psPrivateDictData contains fields specific to the Private DICT context. +type psPrivateDictData struct { + subrs int32 +} + +func (d *psPrivateDictData) initialize() { + *d = psPrivateDictData{} +} + // psType2CharstringsData contains fields specific to the Type 2 Charstrings // context. type psType2CharstringsData struct { - segments []Segment + f *Font + b *Buffer x, y int32 hintBits int32 seenWidth bool + ended bool } -func (d *psType2CharstringsData) initialize(segments []Segment) { +func (d *psType2CharstringsData) initialize(f *Font, b *Buffer) { *d = psType2CharstringsData{ - segments: segments, + f: f, + b: b, } } @@ -288,19 +407,45 @@ func (d *psType2CharstringsData) initialize(segments []Segment) { type psInterpreter struct { ctx psContext instructions []byte - stack struct { - a [psStackSize]int32 + instrOffset uint32 + instrLength uint32 + argStack struct { + a [psArgStackSize]int32 + top int32 + } + callStack struct { + a [psCallStackSize]psCallStackEntry top int32 } - parseNumberBuf [maxRealNumberStrLen]byte + parseNumberBuf [maxRealNumberStrLen]byte + topDict psTopDictData + privateDict psPrivateDictData type2Charstrings psType2CharstringsData } -func (p *psInterpreter) run(ctx psContext, instructions []byte) error { +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.stack.top = 0 + p.instrOffset = offset + p.instrLength = length + p.argStack.top = 0 + p.callStack.top = 0 loop: for len(p.instructions) > 0 { @@ -330,7 +475,7 @@ loop: if int(b) < len(ops) { if op := ops[b]; op.name != "" { - if p.stack.top < op.numPop { + if p.argStack.top < op.numPop { return errInvalidCFFTable } if op.run != nil { @@ -339,9 +484,9 @@ loop: } } if op.numPop < 0 { - p.stack.top = 0 + p.argStack.top = 0 } else { - p.stack.top -= op.numPop + p.argStack.top -= op.numPop } continue loop } @@ -375,7 +520,7 @@ func (p *psInterpreter) parseNumber() (hasResult bool, err error) { number, hasResult = int32(u32(p.instructions[1:])), true p.instructions = p.instructions[5:] - case b == 30 && p.ctx == psContextTopDict: + 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 @@ -445,11 +590,11 @@ func (p *psInterpreter) parseNumber() (hasResult bool, err error) { } if hasResult { - if p.stack.top == psStackSize { + if p.argStack.top == psArgStackSize { return true, errInvalidCFFTable } - p.stack.a[p.stack.top] = number - p.stack.top++ + p.argStack.a[p.argStack.top] = number + p.argStack.top++ } return hasResult, nil } @@ -506,10 +651,14 @@ var psOperators = [...][2][]psOperator{ 15: {+1, "charset", nil}, 16: {+1, "Encoding", nil}, 17: {+1, "CharStrings", func(p *psInterpreter) error { - p.topDict.charStrings = p.stack.a[p.stack.top-1] + p.topDict.charStrings = 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 }}, - 18: {+2, "Private", nil}, }, { // 2-byte operators. The first byte is the escape byte. 0: {+1, "Copyright", nil}, @@ -536,6 +685,35 @@ var psOperators = [...][2][]psOperator{ 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.subrs = 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: {{ @@ -550,8 +728,8 @@ var psOperators = [...][2][]psOperator{ 7: {-1, "vlineto", t2CVlineto}, 8: {-1, "rrcurveto", t2CRrcurveto}, 9: {}, // Reserved. - 10: {}, // callsubr. - 11: {}, // return. + 10: {+1, "callsubr", t2CCallsubr}, + 11: {+0, "return", t2CReturn}, 12: {}, // escape. 13: {}, // Reserved. 14: {-1, "endchar", t2CEndchar}, @@ -564,12 +742,12 @@ var psOperators = [...][2][]psOperator{ 21: {-1, "rmoveto", t2CRmoveto}, 22: {-1, "hmoveto", t2CHmoveto}, 23: {-1, "vstemhm", t2CStem}, - 24: {}, // rcurveline. - 25: {}, // rlinecurve. + 24: {-1, "rcurveline", t2CRcurveline}, + 25: {-1, "rlinecurve", t2CRlinecurve}, 26: {-1, "vvcurveto", t2CVvcurveto}, 27: {-1, "hhcurveto", t2CHhcurveto}, 28: {}, // shortint. - 29: {}, // callgsubr. + 29: {+1, "callgsubr", t2CCallgsubr}, 30: {-1, "vhcurveto", t2CVhcurveto}, 31: {-1, "hvcurveto", t2CHvcurveto}, }, { @@ -597,42 +775,42 @@ func t2CReadWidth(p *psInterpreter, nArgs int32) { p.type2Charstrings.seenWidth = true switch nArgs { case 0: - if p.stack.top != 1 { + if p.argStack.top != 1 { return } case 1: - if p.stack.top <= 1 { + if p.argStack.top <= 1 { return } default: - if p.stack.top%nArgs != 1 { + if p.argStack.top%nArgs != 1 { return } } - // When parsing a standalone CFF, we'd save the value of p.stack.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 + // 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 stack. - copy(p.stack.a[:p.stack.top-1], p.stack.a[1:p.stack.top]) - p.stack.top-- + // 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, 2) - if p.stack.top%2 != 0 { + 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.stack.top / 2 + p.type2Charstrings.hintBits += p.argStack.top / 2 if p.type2Charstrings.hintBits > maxHintBits { return errUnsupportedNumberOfHints } @@ -650,7 +828,7 @@ func t2CMask(p *psInterpreter) error { } func t2CAppendMoveto(p *psInterpreter) { - p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{ + p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{ Op: SegmentOpMoveTo, Args: [6]fixed.Int26_6{ 0: fixed.Int26_6(p.type2Charstrings.x), @@ -660,7 +838,7 @@ func t2CAppendMoveto(p *psInterpreter) { } func t2CAppendLineto(p *psInterpreter) { - p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{ + p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{ Op: SegmentOpLineTo, Args: [6]fixed.Int26_6{ 0: fixed.Int26_6(p.type2Charstrings.x), @@ -682,7 +860,7 @@ func t2CAppendCubeto(p *psInterpreter, dxa, dya, dxb, dyb, dxc, dyc int32) { p.type2Charstrings.y += dyc xc := p.type2Charstrings.x yc := p.type2Charstrings.y - p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{ + p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{ Op: SegmentOpCubeTo, Args: [6]fixed.Int26_6{ 0: fixed.Int26_6(xa), @@ -697,11 +875,11 @@ func t2CAppendCubeto(p *psInterpreter, dxa, dya, dxb, dyb, dxc, dyc int32) { func t2CHmoveto(p *psInterpreter) error { t2CReadWidth(p, 1) - if p.stack.top < 1 { + if p.argStack.top < 1 { return errInvalidCFFTable } - for i := int32(0); i < p.stack.top; i++ { - p.type2Charstrings.x += p.stack.a[i] + for i := int32(0); i < p.argStack.top; i++ { + p.type2Charstrings.x += p.argStack.a[i] } t2CAppendMoveto(p) return nil @@ -709,11 +887,11 @@ func t2CHmoveto(p *psInterpreter) error { func t2CVmoveto(p *psInterpreter) error { t2CReadWidth(p, 1) - if p.stack.top < 1 { + if p.argStack.top < 1 { return errInvalidCFFTable } - for i := int32(0); i < p.stack.top; i++ { - p.type2Charstrings.y += p.stack.a[i] + for i := int32(0); i < p.argStack.top; i++ { + p.type2Charstrings.y += p.argStack.a[i] } t2CAppendMoveto(p) return nil @@ -721,12 +899,12 @@ func t2CVmoveto(p *psInterpreter) error { func t2CRmoveto(p *psInterpreter) error { t2CReadWidth(p, 2) - if p.stack.top < 2 || p.stack.top%2 != 0 { + if p.argStack.top < 2 || p.argStack.top%2 != 0 { return errInvalidCFFTable } - for i := int32(0); i < p.stack.top; i += 2 { - p.type2Charstrings.x += p.stack.a[i+0] - p.type2Charstrings.y += p.stack.a[i+1] + for i := int32(0); i < p.argStack.top; i += 2 { + p.type2Charstrings.x += p.argStack.a[i+0] + p.type2Charstrings.y += p.argStack.a[i+1] } t2CAppendMoveto(p) return nil @@ -736,17 +914,14 @@ 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 { - return errInvalidCFFTable - } - if p.stack.top < 1 { + if !p.type2Charstrings.seenWidth || p.argStack.top < 1 { return errInvalidCFFTable } - for i := int32(0); i < p.stack.top; i, vertical = i+1, !vertical { + for i := int32(0); i < p.argStack.top; i, vertical = i+1, !vertical { if vertical { - p.type2Charstrings.y += p.stack.a[i] + p.type2Charstrings.y += p.argStack.a[i] } else { - p.type2Charstrings.x += p.stack.a[i] + p.type2Charstrings.x += p.argStack.a[i] } t2CAppendLineto(p) } @@ -754,17 +929,64 @@ func t2CLineto(p *psInterpreter, vertical bool) error { } func t2CRlineto(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth { + if !p.type2Charstrings.seenWidth || p.argStack.top < 2 || p.argStack.top%2 != 0 { return errInvalidCFFTable } - if p.stack.top < 2 || p.stack.top%2 != 0 { + for i := int32(0); i < p.argStack.top; i += 2 { + p.type2Charstrings.x += p.argStack.a[i+0] + p.type2Charstrings.y += p.argStack.a[i+1] + t2CAppendLineto(p) + } + 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 { + t2CAppendCubeto(p, + 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.x += p.argStack.a[i+0] + p.type2Charstrings.y += p.argStack.a[i+1] + t2CAppendLineto(p) + return nil +} + +func t2CRlinecurve(p *psInterpreter) error { + if !p.type2Charstrings.seenWidth || p.argStack.top < 8 || p.argStack.top%2 != 0 { return errInvalidCFFTable } - for i := int32(0); i < p.stack.top; i += 2 { - p.type2Charstrings.x += p.stack.a[i+0] - p.type2Charstrings.y += p.stack.a[i+1] + i := int32(0) + for iMax := p.argStack.top - 6; i < iMax; i += 2 { + p.type2Charstrings.x += p.argStack.a[i+0] + p.type2Charstrings.y += p.argStack.a[i+1] t2CAppendLineto(p) } + t2CAppendCubeto(p, + 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 } @@ -800,12 +1022,12 @@ func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true, true) } // // vertical is whether the first implicit constraint is vertical. func t2CCurveto(p *psInterpreter, swap, vertical bool) error { - if !p.type2Charstrings.seenWidth || p.stack.top < 4 { + if !p.type2Charstrings.seenWidth || p.argStack.top < 4 { return errInvalidCFFTable } i := int32(0) - switch p.stack.top & 3 { + switch p.argStack.top & 3 { case 0: // No-op. case 1: @@ -814,15 +1036,15 @@ func t2CCurveto(p *psInterpreter, swap, vertical bool) error { } i = 1 if vertical { - p.type2Charstrings.x += p.stack.a[0] + p.type2Charstrings.x += p.argStack.a[0] } else { - p.type2Charstrings.y += p.stack.a[0] + p.type2Charstrings.y += p.argStack.a[0] } default: return errInvalidCFFTable } - for i != p.stack.top { + for i != p.argStack.top { i = t2CCurveto4(p, swap, vertical, i) if i < 0 { return errInvalidCFFTable @@ -835,14 +1057,14 @@ func t2CCurveto(p *psInterpreter, swap, vertical bool) error { } func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) { - if i+4 > p.stack.top { + if i+4 > p.argStack.top { return -1 } - dxa := p.stack.a[i+0] + dxa := p.argStack.a[i+0] dya := int32(0) - dxb := p.stack.a[i+1] - dyb := p.stack.a[i+2] - dxc := p.stack.a[i+3] + dxb := p.argStack.a[i+1] + dyb := p.argStack.a[i+2] + dxc := p.argStack.a[i+3] dyc := int32(0) i += 4 @@ -851,8 +1073,8 @@ func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) } if swap { - if i+1 == p.stack.top { - dyc = p.stack.a[i] + if i+1 == p.argStack.top { + dyc = p.argStack.a[i] i++ } } @@ -866,31 +1088,99 @@ func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) } func t2CRrcurveto(p *psInterpreter) error { - if !p.type2Charstrings.seenWidth || p.stack.top < 6 || p.stack.top%6 != 0 { + if !p.type2Charstrings.seenWidth || p.argStack.top < 6 || p.argStack.top%6 != 0 { return errInvalidCFFTable } - for i := int32(0); i != p.stack.top; i += 6 { + for i := int32(0); i != p.argStack.top; i += 6 { t2CAppendCubeto(p, - p.stack.a[i+0], - p.stack.a[i+1], - p.stack.a[i+2], - p.stack.a[i+3], - p.stack.a[i+4], - p.stack.a[i+5], + 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 } +// 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.gsubrs) } +func t2CCallsubr(p *psInterpreter) error { return t2CCall(p, p.type2Charstrings.f.cached.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.stack.top != 0 || len(p.instructions) != 0 { - if p.stack.top == 4 { + 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.ended = true return nil } diff --git a/vendor/golang.org/x/image/font/sfnt/proprietary_test.go b/vendor/golang.org/x/image/font/sfnt/proprietary_test.go index b105ee593..ccf013d1d 100644 --- a/vendor/golang.org/x/image/font/sfnt/proprietary_test.go +++ b/vendor/golang.org/x/image/font/sfnt/proprietary_test.go @@ -19,14 +19,14 @@ End User License Agreement (EULA) and a CAB format decoder. These tests assume that such fonts have already been installed. You may need to specify the directories for these fonts: -go test golang.org/x/image/font/sfnt -args -proprietary -adobeDir=/foo/bar/aFonts -microsoftDir=/foo/bar/mFonts +go test golang.org/x/image/font/sfnt -args -proprietary -adobeDir=$HOME/fonts/adobe -appleDir=$HOME/fonts/apple -microsoftDir=$HOME/fonts/microsoft To only run those tests for the Microsoft fonts: -go test golang.org/x/image/font/sfnt -test.run=ProprietaryMicrosoft -args -proprietary +go test golang.org/x/image/font/sfnt -test.run=ProprietaryMicrosoft -args -proprietary etc */ -// TODO: add Apple system fonts? Google fonts (Droid? Noto?)? Emoji fonts? +// TODO: add Google fonts (Droid? Noto?)? Emoji fonts? // TODO: enable Apple/Microsoft tests by default on Darwin/Windows? @@ -35,6 +35,8 @@ import ( "flag" "io/ioutil" "path/filepath" + "strconv" + "strings" "testing" "golang.org/x/image/font" @@ -60,6 +62,16 @@ var ( "directory name for the Adobe proprietary fonts", ) + appleDir = flag.String( + "appleDir", + // This needs to be set explicitly. These fonts come with macOS, which + // is widely available but not freely available. + // + // On a Mac, set this to "/System/Library/Fonts/". + "", + "directory name for the Apple proprietary fonts", + ) + microsoftDir = flag.String( "microsoftDir", "/usr/share/fonts/truetype/msttcorefonts", @@ -68,11 +80,11 @@ var ( ) func TestProprietaryAdobeSourceCodeProOTF(t *testing.T) { - testProprietary(t, "adobe", "SourceCodePro-Regular.otf", 1500, 2) + testProprietary(t, "adobe", "SourceCodePro-Regular.otf", 1500, 34) } func TestProprietaryAdobeSourceCodeProTTF(t *testing.T) { - testProprietary(t, "adobe", "SourceCodePro-Regular.ttf", 1500, 36) + testProprietary(t, "adobe", "SourceCodePro-Regular.ttf", 1500, -1) } func TestProprietaryAdobeSourceHanSansSC(t *testing.T) { @@ -80,23 +92,47 @@ func TestProprietaryAdobeSourceHanSansSC(t *testing.T) { } func TestProprietaryAdobeSourceSansProOTF(t *testing.T) { - testProprietary(t, "adobe", "SourceSansPro-Regular.otf", 1800, 2) + testProprietary(t, "adobe", "SourceSansPro-Regular.otf", 1800, 34) } func TestProprietaryAdobeSourceSansProTTF(t *testing.T) { - testProprietary(t, "adobe", "SourceSansPro-Regular.ttf", 1800, 54) + testProprietary(t, "adobe", "SourceSansPro-Regular.ttf", 1800, -1) +} + +func TestProprietaryAppleAppleSymbols(t *testing.T) { + testProprietary(t, "apple", "Apple Symbols.ttf", 4600, -1) +} + +func TestProprietaryAppleGeezaPro0(t *testing.T) { + testProprietary(t, "apple", "GeezaPro.ttc?0", 1700, -1) +} + +func TestProprietaryAppleGeezaPro1(t *testing.T) { + testProprietary(t, "apple", "GeezaPro.ttc?1", 1700, -1) +} + +func TestProprietaryAppleHiragino0(t *testing.T) { + testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?0", 9000, 6) +} + +func TestProprietaryAppleHiragino1(t *testing.T) { + testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?1", 9000, 6) } func TestProprietaryMicrosoftArial(t *testing.T) { - testProprietary(t, "microsoft", "Arial.ttf", 1200, 98) + testProprietary(t, "microsoft", "Arial.ttf", 1200, -1) +} + +func TestProprietaryMicrosoftArialAsACollection(t *testing.T) { + testProprietary(t, "microsoft", "Arial.ttf?0", 1200, -1) } func TestProprietaryMicrosoftComicSansMS(t *testing.T) { - testProprietary(t, "microsoft", "Comic_Sans_MS.ttf", 550, 98) + testProprietary(t, "microsoft", "Comic_Sans_MS.ttf", 550, -1) } func TestProprietaryMicrosoftTimesNewRoman(t *testing.T) { - testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, 98) + testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, -1) } func TestProprietaryMicrosoftWebdings(t *testing.T) { @@ -117,27 +153,55 @@ func testProprietary(t *testing.T, proprietor, filename string, minNumGlyphs, fi t.Skip("skipping proprietary font test") } - file, err := []byte(nil), error(nil) - switch proprietor { - case "adobe": - file, err = ioutil.ReadFile(filepath.Join(*adobeDir, filename)) + basename, fontIndex, err := filename, -1, error(nil) + if i := strings.IndexByte(filename, '?'); i >= 0 { + fontIndex, err = strconv.Atoi(filename[i+1:]) if err != nil { - t.Fatalf("%v\nPerhaps you need to set the -adobeDir=%v flag?", err, *adobeDir) + t.Fatalf("could not parse collection font index from filename %q", filename) } + basename = filename[:i] + } + + dir := "" + switch proprietor { + case "adobe": + dir = *adobeDir + case "apple": + dir = *appleDir case "microsoft": - file, err = ioutil.ReadFile(filepath.Join(*microsoftDir, filename)) - if err != nil { - t.Fatalf("%v\nPerhaps you need to set the -microsoftDir=%v flag?", err, *microsoftDir) - } + dir = *microsoftDir default: panic("unreachable") } - f, err := Parse(file) + file, err := ioutil.ReadFile(filepath.Join(dir, basename)) if err != nil { - t.Fatalf("Parse: %v", err) + t.Fatalf("%v\nPerhaps you need to set the -%sDir flag?", err, proprietor) } - ppem := fixed.Int26_6(f.UnitsPerEm()) qualifiedFilename := proprietor + "/" + filename + + f := (*Font)(nil) + if fontIndex >= 0 { + c, err := ParseCollection(file) + if err != nil { + t.Fatalf("ParseCollection: %v", err) + } + if want, ok := proprietaryNumFonts[qualifiedFilename]; ok { + if got := c.NumFonts(); got != want { + t.Fatalf("NumFonts: got %d, want %d", got, want) + } + } + f, err = c.Font(fontIndex) + if err != nil { + t.Fatalf("Font: %v", err) + } + } else { + f, err = Parse(file) + if err != nil { + t.Fatalf("Parse: %v", err) + } + } + + ppem := fixed.Int26_6(f.UnitsPerEm()) var buf Buffer // Some of the tests below, such as which glyph index a particular rune @@ -147,7 +211,7 @@ func testProprietary(t *testing.T, proprietor, filename string, minNumGlyphs, fi // message, but don't automatically fail (i.e. dont' call t.Fatalf). gotVersion, err := f.Name(&buf, NameIDVersion) if err != nil { - t.Fatalf("Name: %v", err) + t.Fatalf("Name(Version): %v", err) } wantVersion := proprietaryVersions[qualifiedFilename] if gotVersion != wantVersion { @@ -155,6 +219,15 @@ func testProprietary(t *testing.T, proprietor, filename string, minNumGlyphs, fi "\ngot %q\nwant %q", gotVersion, wantVersion) } + gotFull, err := f.Name(&buf, NameIDFull) + if err != nil { + t.Fatalf("Name(Full): %v", err) + } + wantFull := proprietaryFullNames[qualifiedFilename] + if gotFull != wantFull { + t.Fatalf("Name(Full):\ngot %q\nwant %q", gotFull, wantFull) + } + numGlyphs := f.NumGlyphs() if numGlyphs < minNumGlyphs { t.Fatalf("NumGlyphs: got %d, want at least %d", numGlyphs, minNumGlyphs) @@ -231,6 +304,15 @@ kernLoop: } } +// proprietaryNumFonts holds the expected number of fonts in each collection, +// or 1 for a single font. It is not necessarily an exhaustive list of all +// proprietary fonts tested. +var proprietaryNumFonts = map[string]int{ + "apple/ヒラギノ角ゴシック W0.ttc?0": 2, + "apple/ヒラギノ角ゴシック W0.ttc?1": 2, + "microsoft/Arial.ttf?0": 1, +} + // proprietaryVersions holds the expected version string of each proprietary // font tested. If third parties such as Adobe or Microsoft update their fonts, // and the tests subsequently fail, these versions should be updated too. @@ -245,12 +327,41 @@ var proprietaryVersions = map[string]string{ "adobe/SourceSansPro-Regular.otf": "Version 2.020;PS 2.0;hotconv 1.0.86;makeotf.lib2.5.63406", "adobe/SourceSansPro-Regular.ttf": "Version 2.020;PS 2.000;hotconv 1.0.86;makeotf.lib2.5.63406", + "apple/Apple Symbols.ttf": "12.0d3e10", + "apple/GeezaPro.ttc?0": "12.0d1e3", + "apple/GeezaPro.ttc?1": "12.0d1e3", + "apple/ヒラギノ角ゴシック W0.ttc?0": "11.0d7e1", + "apple/ヒラギノ角ゴシック W0.ttc?1": "11.0d7e1", + "microsoft/Arial.ttf": "Version 2.82", + "microsoft/Arial.ttf?0": "Version 2.82", "microsoft/Comic_Sans_MS.ttf": "Version 2.10", "microsoft/Times_New_Roman.ttf": "Version 2.82", "microsoft/Webdings.ttf": "Version 1.03", } +// proprietaryFullNames holds the expected full name of each proprietary font +// tested. +var proprietaryFullNames = map[string]string{ + "adobe/SourceCodePro-Regular.otf": "Source Code Pro", + "adobe/SourceCodePro-Regular.ttf": "Source Code Pro", + "adobe/SourceHanSansSC-Regular.otf": "Source Han Sans SC Regular", + "adobe/SourceSansPro-Regular.otf": "Source Sans Pro", + "adobe/SourceSansPro-Regular.ttf": "Source Sans Pro", + + "apple/Apple Symbols.ttf": "Apple Symbols", + "apple/GeezaPro.ttc?0": "Geeza Pro Regular", + "apple/GeezaPro.ttc?1": "Geeza Pro Bold", + "apple/ヒラギノ角ゴシック W0.ttc?0": "Hiragino Sans W0", + "apple/ヒラギノ角ゴシック W0.ttc?1": ".Hiragino Kaku Gothic Interface W0", + + "microsoft/Arial.ttf": "Arial", + "microsoft/Arial.ttf?0": "Arial", + "microsoft/Comic_Sans_MS.ttf": "Comic Sans MS", + "microsoft/Times_New_Roman.ttf": "Times New Roman", + "microsoft/Webdings.ttf": "Webdings", +} + // proprietaryGlyphIndexTestCases hold a sample of each font's rune to glyph // index cmap. The numerical values can be verified by running the ttx tool. var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{ @@ -338,7 +449,6 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ "adobe/SourceSansPro-Regular.otf": { ',': { - // - contour #0 // 67 -170 rmoveto moveTo(67, -170), // 81 34 50 67 86 vvcurveto @@ -350,9 +460,10 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ cubeTo(130, -1, 134, -1, 137, 0), // 1 -53 -34 -44 -57 -25 rrcurveto cubeTo(138, -53, 104, -97, 47, -122), + // endchar }, + 'Q': { - // - contour #0 // 332 57 rmoveto moveTo(332, 57), // -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto @@ -360,7 +471,6 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ cubeTo(138, 494, 215, 595, 332, 595), cubeTo(449, 595, 526, 494, 526, 331), cubeTo(526, 163, 449, 57, 332, 57), - // - contour #1 // 201 -222 rmoveto moveTo(533, -165), // 39 35 7 8 20 hvcurveto @@ -379,6 +489,123 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ cubeTo(52, 138, 148, 11, 291, -9), // -90 38 83 -66 121 hhcurveto cubeTo(329, -99, 412, -165, 533, -165), + // endchar + }, + + 'ī': { // U+012B LATIN SMALL LETTER I WITH MACRON + // 92 callgsubr # 92 + bias = 199. + // : # Arg stack is []. + // : -312 21 85 callgsubr # 85 + bias = 192. + // : : # Arg stack is [-312 21]. + // : : -21 486 -20 return + // : : # Arg stack is [-312 21 -21 486 -20]. + // : return + // : # Arg stack is [-312 21 -21 486 -20]. + // 135 57 112 callgsubr # 112 + bias = 219 + // : # Arg stack is [-312 21 -21 486 -20 135 57]. + // : hstem + // : 82 82 vstem + // : 134 callsubr # 134 + bias = 241 + // : : # Arg stack is []. + // : : 82 hmoveto + moveTo(82, 0), + // : : 82 127 callsubr # 127 + bias = 234 + // : : : # Arg stack is [82]. + // : : : 486 -82 hlineto + lineTo(164, 0), + lineTo(164, 486), + lineTo(82, 486), + // : : : return + // : : : # Arg stack is []. + // : : return + // : : # Arg stack is []. + // : return + // : # Arg stack is []. + // -92 115 -60 callgsubr # -60 + bias = 47 + // : # Arg stack is [-92 115]. + // : rmoveto + moveTo(-10, 601), + // : 266 57 -266 hlineto + lineTo(256, 601), + lineTo(256, 658), + lineTo(-10, 658), + // : endchar + }, + + 'ĭ': { // U+012D LATIN SMALL LETTER I WITH BREVE + // 92 callgsubr # 92 + bias = 199. + // : # Arg stack is []. + // : -312 21 85 callgsubr # 85 + bias = 192. + // : : # Arg stack is [-312 21]. + // : : -21 486 -20 return + // : : # Arg stack is [-312 21 -21 486 -20]. + // : return + // : # Arg stack is [-312 21 -21 486 -20]. + // 105 55 96 -20 hstem + // -32 51 63 82 65 51 vstem + // 134 callsubr # 134 + bias = 241 + // : # Arg stack is []. + // : 82 hmoveto + moveTo(82, 0), + // : 82 127 callsubr # 127 + bias = 234 + // : : # Arg stack is [82]. + // : : 486 -82 hlineto + lineTo(164, 0), + lineTo(164, 486), + lineTo(82, 486), + // : : return + // : : # Arg stack is []. + // : return + // : # Arg stack is []. + // 42 85 143 callsubr # 143 + bias = 250 + // : # Arg stack is [42 85]. + // : rmoveto + moveTo(124, 571), + // : -84 callsubr # -84 + bias = 23 + // : : # Arg stack is []. + // : : 107 44 77 74 5 hvcurveto + cubeTo(231, 571, 275, 648, 280, 722), + // : : -51 8 rlineto + lineTo(229, 730), + // : : -51 -8 -32 -53 -65 hhcurveto + cubeTo(221, 679, 189, 626, 124, 626), + // : : -65 -32 53 51 -8 hvcurveto + cubeTo(59, 626, 27, 679, 19, 730), + // : : -51 -22 callsubr # -22 + bias = 85 + // : : : # Arg stack is [-51]. + // : : : -8 rlineto + lineTo(-32, 722), + // : : : -74 5 44 -77 107 hhcurveto + cubeTo(-27, 648, 17, 571, 124, 571), + // : : : return + // : : : # Arg stack is []. + // : : return + // : : # Arg stack is []. + // : return + // : # Arg stack is []. + // endchar + }, + + 'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA + // 0 vmoveto + moveTo(0, 0), + // 85 hlineto + lineTo(85, 0), + // 105 355 23 77 16 63 24 77 rlinecurve + lineTo(190, 355), + cubeTo(213, 432, 229, 495, 253, 572), + // 4 hlineto + lineTo(257, 572), + // 25 -77 16 -63 23 -77 106 -355 rcurveline + cubeTo(282, 495, 298, 432, 321, 355), + lineTo(427, 0), + // 88 hlineto + lineTo(515, 0), + // -210 656 rlineto + lineTo(305, 656), + // -96 hlineto + lineTo(209, 656), + // endchar }, }, @@ -396,6 +623,7 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ quadTo(281, -91, 284, 0), lineTo(182, 0), }, + 'i': { // - contour #0 moveTo(136, 1259), @@ -410,6 +638,7 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ lineTo(316, 0), lineTo(136, 0), }, + 'o': { // - contour #0 moveTo(68, 531), @@ -433,6 +662,119 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ quadTo(431, 937, 342, 836), quadTo(253, 735, 253, 531), }, + + 'í': { // U+00ED LATIN SMALL LETTER I WITH ACUTE + // - contour #0 + translate(0, 0, moveTo(198, 0)), + translate(0, 0, lineTo(198, 1062)), + translate(0, 0, lineTo(378, 1062)), + translate(0, 0, lineTo(378, 0)), + translate(0, 0, lineTo(198, 0)), + // - contour #1 + translate(-33, 0, moveTo(222, 1194)), + translate(-33, 0, lineTo(355, 1474)), + translate(-33, 0, lineTo(591, 1474)), + translate(-33, 0, lineTo(371, 1194)), + translate(-33, 0, lineTo(222, 1194)), + }, + + 'Ī': { // U+012A LATIN CAPITAL LETTER I WITH MACRON + // - contour #0 + translate(0, 0, moveTo(191, 0)), + translate(0, 0, lineTo(191, 1466)), + translate(0, 0, lineTo(385, 1466)), + translate(0, 0, lineTo(385, 0)), + translate(0, 0, lineTo(191, 0)), + // - contour #1 + translate(-57, 336, moveTo(29, 1227)), + translate(-57, 336, lineTo(29, 1375)), + translate(-57, 336, lineTo(653, 1375)), + translate(-57, 336, lineTo(653, 1227)), + translate(-57, 336, lineTo(29, 1227)), + }, + + // Ǻ is a compound glyph whose elements are also compound glyphs. + 'Ǻ': { // U+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE + // - contour #0 + translate(0, 0, moveTo(-3, 0)), + translate(0, 0, lineTo(560, 1466)), + translate(0, 0, lineTo(769, 1466)), + translate(0, 0, lineTo(1369, 0)), + translate(0, 0, lineTo(1148, 0)), + translate(0, 0, lineTo(977, 444)), + translate(0, 0, lineTo(364, 444)), + translate(0, 0, lineTo(203, 0)), + translate(0, 0, lineTo(-3, 0)), + // - contour #1 + translate(0, 0, moveTo(420, 602)), + translate(0, 0, lineTo(917, 602)), + translate(0, 0, lineTo(764, 1008)), + translate(0, 0, quadTo(694, 1193, 660, 1312)), + translate(0, 0, quadTo(632, 1171, 581, 1032)), + translate(0, 0, lineTo(420, 602)), + // - contour #2 + translate(319, 263, moveTo(162, 1338)), + translate(319, 263, quadTo(162, 1411, 215, 1464)), + translate(319, 263, quadTo(269, 1517, 342, 1517)), + translate(319, 263, quadTo(416, 1517, 469, 1463)), + translate(319, 263, quadTo(522, 1410, 522, 1334)), + translate(319, 263, quadTo(522, 1257, 469, 1204)), + translate(319, 263, quadTo(416, 1151, 343, 1151)), + translate(319, 263, quadTo(268, 1151, 215, 1204)), + translate(319, 263, quadTo(162, 1258, 162, 1338)), + // - contour #3 + translate(319, 263, moveTo(238, 1337)), + translate(319, 263, quadTo(238, 1290, 269, 1258)), + translate(319, 263, quadTo(301, 1226, 344, 1226)), + translate(319, 263, quadTo(387, 1226, 418, 1258)), + translate(319, 263, quadTo(450, 1290, 450, 1335)), + translate(319, 263, quadTo(450, 1380, 419, 1412)), + translate(319, 263, quadTo(388, 1444, 344, 1444)), + translate(319, 263, quadTo(301, 1444, 269, 1412)), + translate(319, 263, quadTo(238, 1381, 238, 1337)), + // - contour #4 + translate(339, 650, moveTo(222, 1194)), + translate(339, 650, lineTo(355, 1474)), + translate(339, 650, lineTo(591, 1474)), + translate(339, 650, lineTo(371, 1194)), + translate(339, 650, lineTo(222, 1194)), + }, + + '﴾': { // U+FD3E ORNATE LEFT PARENTHESIS. + // - contour #0 + moveTo(560, -384), + lineTo(516, -429), + quadTo(412, -304, 361, -226), + quadTo(258, -68, 201, 106), + quadTo(127, 334, 127, 595), + quadTo(127, 845, 201, 1069), + quadTo(259, 1246, 361, 1404), + quadTo(414, 1487, 514, 1608), + lineTo(560, 1566), + quadTo(452, 1328, 396, 1094), + quadTo(336, 845, 336, 603), + quadTo(336, 359, 370, 165), + quadTo(398, 8, 454, -142), + quadTo(482, -217, 560, -384), + }, + + '﴿': { // U+FD3F ORNATE RIGHT PARENTHESIS + // - contour #0 + transform(-1<<14, 0, 0, +1<<14, 653, 0, moveTo(560, -384)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(516, -429)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(412, -304, 361, -226)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(258, -68, 201, 106)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 334, 127, 595)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 845, 201, 1069)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(259, 1246, 361, 1404)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(414, 1487, 514, 1608)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(560, 1566)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(452, 1328, 396, 1094)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 845, 336, 603)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 359, 370, 165)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(398, 8, 454, -142)), + transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(482, -217, 560, -384)), + }, }, } diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt.go b/vendor/golang.org/x/image/font/sfnt/sfnt.go index 02efd5f3a..356841db1 100644 --- a/vendor/golang.org/x/image/font/sfnt/sfnt.go +++ b/vendor/golang.org/x/image/font/sfnt/sfnt.go @@ -45,10 +45,17 @@ const ( // safe to call concurrently, as long as each call has a different *Buffer. maxCmapSegments = 20000 - maxGlyphDataLength = 64 * 1024 - maxHintBits = 256 - maxNumTables = 256 - maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation. + // TODO: similarly, load subroutine locations lazily. Adobe's + // SourceHanSansSC-Regular.otf has up to 30000 subroutines. + maxNumSubroutines = 40000 + + maxCompoundRecursionDepth = 8 + maxCompoundStackSize = 64 + maxGlyphDataLength = 64 * 1024 + maxHintBits = 256 + maxNumFonts = 256 + maxNumTables = 256 + maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation. // (maxTableOffset + maxTableLength) will not overflow an int32. maxTableLength = 1 << 29 @@ -59,22 +66,25 @@ var ( // ErrNotFound indicates that the requested value was not found. ErrNotFound = errors.New("sfnt: not found") - errInvalidBounds = errors.New("sfnt: invalid bounds") - errInvalidCFFTable = errors.New("sfnt: invalid CFF table") - errInvalidCmapTable = errors.New("sfnt: invalid cmap table") - errInvalidGlyphData = errors.New("sfnt: invalid glyph data") - errInvalidHeadTable = errors.New("sfnt: invalid head table") - errInvalidKernTable = errors.New("sfnt: invalid kern table") - errInvalidLocaTable = errors.New("sfnt: invalid loca table") - errInvalidLocationData = errors.New("sfnt: invalid location data") - errInvalidMaxpTable = errors.New("sfnt: invalid maxp table") - errInvalidNameTable = errors.New("sfnt: invalid name table") - errInvalidPostTable = errors.New("sfnt: invalid post table") - errInvalidSourceData = errors.New("sfnt: invalid source data") - errInvalidTableOffset = errors.New("sfnt: invalid table offset") - errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order") - errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string") - errInvalidVersion = errors.New("sfnt: invalid version") + errInvalidBounds = errors.New("sfnt: invalid bounds") + errInvalidCFFTable = errors.New("sfnt: invalid CFF table") + errInvalidCmapTable = errors.New("sfnt: invalid cmap table") + errInvalidFont = errors.New("sfnt: invalid font") + errInvalidFontCollection = errors.New("sfnt: invalid font collection") + errInvalidGlyphData = errors.New("sfnt: invalid glyph data") + errInvalidGlyphDataLength = errors.New("sfnt: invalid glyph data length") + errInvalidHeadTable = errors.New("sfnt: invalid head table") + errInvalidKernTable = errors.New("sfnt: invalid kern table") + errInvalidLocaTable = errors.New("sfnt: invalid loca table") + errInvalidLocationData = errors.New("sfnt: invalid location data") + errInvalidMaxpTable = errors.New("sfnt: invalid maxp table") + errInvalidNameTable = errors.New("sfnt: invalid name table") + errInvalidPostTable = errors.New("sfnt: invalid post table") + errInvalidSingleFont = errors.New("sfnt: invalid single font (data is a font collection)") + errInvalidSourceData = errors.New("sfnt: invalid source data") + errInvalidTableOffset = errors.New("sfnt: invalid table offset") + errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order") + errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string") errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version") errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings") @@ -83,7 +93,9 @@ var ( errUnsupportedKernTable = errors.New("sfnt: unsupported kern table") errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding") errUnsupportedNumberOfCmapSegments = errors.New("sfnt: unsupported number of cmap segments") + errUnsupportedNumberOfFonts = errors.New("sfnt: unsupported number of fonts") errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints") + errUnsupportedNumberOfSubroutines = errors.New("sfnt: unsupported number of subroutines") errUnsupportedNumberOfTables = errors.New("sfnt: unsupported number of tables") errUnsupportedPlatformEncoding = errors.New("sfnt: unsupported platform encoding") errUnsupportedPostTable = errors.New("sfnt: unsupported post table") @@ -254,19 +266,105 @@ type table struct { offset, length uint32 } -// Parse parses an SFNT font from a []byte data source. +// ParseCollection parses an SFNT font collection, such as TTC or OTC data, +// from a []byte data source. +// +// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it +// will return a collection containing 1 font. +func ParseCollection(src []byte) (*Collection, error) { + c := &Collection{src: source{b: src}} + if err := c.initialize(); err != nil { + return nil, err + } + return c, nil +} + +// ParseCollectionReaderAt parses an SFNT collection, such as TTC or OTC data, +// from an io.ReaderAt data source. +// +// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it +// will return a collection containing 1 font. +func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) { + c := &Collection{src: source{r: src}} + if err := c.initialize(); err != nil { + return nil, err + } + return c, nil +} + +// Collection is a collection of one or more fonts. +// +// All of the Collection methods are safe to call concurrently. +type Collection struct { + src source + offsets []uint32 +} + +// NumFonts returns the number of fonts in the collection. +func (c *Collection) NumFonts() int { return len(c.offsets) } + +func (c *Collection) initialize() error { + // The https://www.microsoft.com/typography/otspec/otff.htm "Font + // Collections" section describes the TTC Header. + buf, err := c.src.view(nil, 0, 12) + if err != nil { + return err + } + // These cases match the switch statement in Font.initializeTables. + switch u32(buf) { + default: + return errInvalidFontCollection + case 0x00010000, 0x4f54544f: + // Try parsing it as a single font instead of a collection. + c.offsets = []uint32{0} + case 0x74746366: // "ttcf". + numFonts := u32(buf[8:]) + if numFonts == 0 || numFonts > maxNumFonts { + return errUnsupportedNumberOfFonts + } + buf, err = c.src.view(nil, 12, int(4*numFonts)) + if err != nil { + return err + } + c.offsets = make([]uint32, numFonts) + for i := range c.offsets { + o := u32(buf[4*i:]) + if o > maxTableOffset { + return errUnsupportedTableOffsetLength + } + c.offsets[i] = o + } + } + return nil +} + +// Font returns the i'th font in the collection. +func (c *Collection) Font(i int) (*Font, error) { + if i < 0 || len(c.offsets) <= i { + return nil, ErrNotFound + } + f := &Font{src: c.src} + if err := f.initialize(int(c.offsets[i])); err != nil { + return nil, err + } + return f, nil +} + +// Parse parses an SFNT font, such as TTF or OTF data, from a []byte data +// source. func Parse(src []byte) (*Font, error) { f := &Font{src: source{b: src}} - if err := f.initialize(); err != nil { + if err := f.initialize(0); err != nil { return nil, err } return f, nil } -// ParseReaderAt parses an SFNT font from an io.ReaderAt data source. +// ParseReaderAt parses an SFNT font, such as TTF or OTF data, from an +// io.ReaderAt data source. func ParseReaderAt(src io.ReaderAt) (*Font, error) { f := &Font{src: source{r: src}} - if err := f.initialize(); err != nil { + if err := f.initialize(0); err != nil { return nil, err } return f, nil @@ -348,9 +446,17 @@ type Font struct { postTableVersion uint32 unitsPerEm Units - // The glyph data for the glyph index i is in + // The glyph data for the i'th glyph index is in // src[locations[i+0]:locations[i+1]]. + // + // The slice length equals 1 plus the number of glyphs. locations []uint32 + + // For PostScript fonts, the bytecode for the i'th global or local + // subroutine is in src[x[i+0]:x[i+1]]. + // + // The slice length equals 1 plus the number of subroutines + gsubrs, subrs []uint32 } } @@ -360,11 +466,11 @@ func (f *Font) NumGlyphs() int { return len(f.cached.locations) - 1 } // UnitsPerEm returns the number of units per em for f. func (f *Font) UnitsPerEm() Units { return f.cached.unitsPerEm } -func (f *Font) initialize() error { +func (f *Font) initialize(offset int) error { if !f.src.valid() { return errInvalidSourceData } - buf, isPostScript, err := f.initializeTables(nil) + buf, isPostScript, err := f.initializeTables(offset) if err != nil { return err } @@ -381,7 +487,7 @@ func (f *Font) initialize() error { if err != nil { return err } - buf, numGlyphs, locations, err := f.parseMaxp(buf, indexToLocFormat, isPostScript) + buf, numGlyphs, locations, gsubrs, subrs, err := f.parseMaxp(buf, indexToLocFormat, isPostScript) if err != nil { return err } @@ -406,25 +512,31 @@ func (f *Font) initialize() error { f.cached.postTableVersion = postTableVersion f.cached.unitsPerEm = unitsPerEm f.cached.locations = locations + f.cached.gsubrs = gsubrs + f.cached.subrs = subrs return nil } -func (f *Font) initializeTables(buf []byte) (buf1 []byte, isPostScript bool, err error) { +func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err error) { // https://www.microsoft.com/typography/otspec/otff.htm "Organization of an // OpenType Font" says that "The OpenType font starts with the Offset // Table", which is 12 bytes. - buf, err = f.src.view(buf, 0, 12) + buf, err := f.src.view(nil, offset, 12) if err != nil { return nil, false, err } + // When updating the cases in this switch statement, also update the + // Collection.initialize method. switch u32(buf) { default: - return nil, false, errInvalidVersion + return nil, false, errInvalidFont case 0x00010000: // No-op. case 0x4f54544f: // "OTTO". isPostScript = true + case 0x74746366: // "ttcf". + return nil, false, errInvalidSingleFont } numTables := int(u16(buf[4:])) if numTables > maxNumTables { @@ -433,7 +545,7 @@ func (f *Font) initializeTables(buf []byte) (buf1 []byte, isPostScript bool, err // "The Offset Table is followed immediately by the Table Record entries... // sorted in ascending order by tag", 16 bytes each. - buf, err = f.src.view(buf, 12, 16*numTables) + buf, err = f.src.view(buf, offset+12, 16*numTables) if err != nil { return nil, false, err } @@ -597,10 +709,20 @@ func (f *Font) parseKern(buf []byte) (buf1 []byte, kernNumPairs, kernOffset int3 } return f.parseKernVersion0(buf, offset, length) case 1: - // TODO: find such a (proprietary?) font, and support it. Both of - // https://www.microsoft.com/typography/otspec/kern.htm + if buf[2] != 0 || buf[3] != 0 { + return nil, 0, 0, errUnsupportedKernTable + } + // Microsoft's https://www.microsoft.com/typography/otspec/kern.htm + // says that "Apple has extended the definition of the 'kern' table to + // provide additional functionality. The Apple extensions are not + // supported on Windows." + // + // The format is relatively complicated, including encoding a state + // machine, but rarely seen. We follow Microsoft's and FreeType's + // behavior and simply ignore it. Theoretically, we could follow // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html - // say that such fonts work on Mac OS but not on Windows. + // but it doesn't seem worth the effort. + return buf, 0, 0, nil } return nil, 0, 0, errUnsupportedKernTable } @@ -633,7 +755,9 @@ func (f *Font) parseKernVersion0(buf []byte, offset, length int) (buf1 []byte, k case 0: return f.parseKernFormat0(buf, offset, subtableLength) case 2: - // TODO: find such a (proprietary?) font, and support it. + // If we could find such a font, we could write code to support it, but + // a comment in the equivalent FreeType code (sfnt/ttkern.c) says that + // they've never seen such a font. } return nil, 0, 0, errUnsupportedKernTable } @@ -654,21 +778,21 @@ func (f *Font) parseKernFormat0(buf []byte, offset, length int) (buf1 []byte, ke return buf, kernNumPairs, int32(offset) + headerSize, nil } -func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1 []byte, numGlyphs int, locations []uint32, err error) { +func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1 []byte, numGlyphs int, locations, gsubrs, subrs []uint32, err error) { // https://www.microsoft.com/typography/otspec/maxp.htm if isPostScript { if f.maxp.length != 6 { - return nil, 0, nil, errInvalidMaxpTable + return nil, 0, nil, nil, nil, errInvalidMaxpTable } } else { if f.maxp.length != 32 { - return nil, 0, nil, errInvalidMaxpTable + return nil, 0, nil, nil, nil, errInvalidMaxpTable } } u, err := f.src.u16(buf, f.maxp, 4) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, nil, err } numGlyphs = int(u) @@ -679,21 +803,21 @@ func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1 offset: int(f.cff.offset), end: int(f.cff.offset + f.cff.length), } - locations, err = p.parse() + locations, gsubrs, subrs, err = p.parse() if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, nil, err } } else { locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, nil, err } } if len(locations) != numGlyphs+1 { - return nil, 0, nil, errInvalidLocationData + return nil, 0, nil, nil, nil, errInvalidLocationData } - return buf, numGlyphs, locations, nil + return buf, numGlyphs, locations, gsubrs, subrs, nil } func (f *Font) parsePost(buf []byte, numGlyphs int) (buf1 []byte, postTableVersion uint32, err error) { @@ -737,17 +861,21 @@ func (f *Font) GlyphIndex(b *Buffer, r rune) (GlyphIndex, error) { return f.cached.glyphIndex(f, b, r) } -func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) ([]byte, error) { +func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) (buf []byte, offset, length uint32, err error) { xx := int(x) if f.NumGlyphs() <= xx { - return nil, ErrNotFound + return nil, 0, 0, ErrNotFound } i := f.cached.locations[xx+0] j := f.cached.locations[xx+1] + if j < i { + return nil, 0, 0, errInvalidGlyphDataLength + } if j-i > maxGlyphDataLength { - return nil, errUnsupportedGlyphDataLength + return nil, 0, 0, errUnsupportedGlyphDataLength } - return b.view(&f.src, int(i), int(j-i)) + buf, err = b.view(&f.src, int(i), int(j-i)) + return buf, i, j - i, err } // LoadGlyphOptions are the options to the Font.LoadGlyph method. @@ -766,24 +894,23 @@ func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *Load b = &Buffer{} } - buf, err := f.viewGlyphData(b, x) - if err != nil { - return nil, err - } - b.segments = b.segments[:0] if f.cached.isPostScript { - b.psi.type2Charstrings.initialize(b.segments) - if err := b.psi.run(psContextType2Charstring, buf); err != nil { + buf, offset, length, err := f.viewGlyphData(b, x) + if err != nil { + return nil, err + } + b.psi.type2Charstrings.initialize(f, b) + if err := b.psi.run(psContextType2Charstring, buf, offset, length); err != nil { return nil, err } - b.segments = b.psi.type2Charstrings.segments + if !b.psi.type2Charstrings.ended { + return nil, errInvalidCFFTable + } } else { - segments, err := appendGlyfSegments(b.segments, buf) - if err != nil { + if err := loadGlyf(f, b, x, 0, 0); err != nil { return nil, err } - b.segments = segments } // Scale the segments. If we want to support hinting, we'll have to push @@ -889,9 +1016,9 @@ func (f *Font) Kern(b *Buffer, x0, x1 GlyphIndex, ppem fixed.Int26_6, h font.Hin if n := f.NumGlyphs(); int(x0) >= n || int(x1) >= n { return 0, ErrNotFound } - // Not every font has a kern table. If it doesn't, there's no need to - // allocate a Buffer. - if f.kern.length == 0 { + // Not every font has a kern table. If it doesn't, or if that table is + // ignored, there's no need to allocate a Buffer. + if f.cached.kernNumPairs == 0 { return 0, nil } if b == nil { @@ -1024,6 +1151,16 @@ type Buffer struct { buf []byte // segments holds glyph vector path segments. segments []Segment + // compoundStack holds the components of a TrueType compound glyph. + compoundStack [maxCompoundStackSize]struct { + glyphIndex GlyphIndex + dx, dy int16 + hasTransform bool + transformXX int16 + transformXY int16 + transformYX int16 + transformYY int16 + } // psi is a PostScript interpreter for when the Font is an OpenType/CFF // font. psi psInterpreter @@ -1056,3 +1193,32 @@ const ( SegmentOpQuadTo SegmentOpCubeTo ) + +// translateArgs applies a translation to args. +func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) { + args[0] += dx + args[1] += dy + args[2] += dx + args[3] += dy + args[4] += dx + args[5] += dy +} + +// transformArgs applies an affine transformation to args. The t?? arguments +// are 2.14 fixed point values. +func transformArgs(args *[6]fixed.Int26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) { + args[0], args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[0], args[1]) + args[2], args[3] = tform(txx, txy, tyx, tyy, dx, dy, args[2], args[3]) + args[4], args[5] = tform(txx, txy, tyx, tyy, dx, dy, args[4], args[5]) +} + +func tform(txx, txy, tyx, tyy int16, dx, dy, x, y fixed.Int26_6) (newX, newY fixed.Int26_6) { + const half = 1 << 13 + newX = dx + + fixed.Int26_6((int64(x)*int64(txx)+half)>>14) + + fixed.Int26_6((int64(y)*int64(tyx)+half)>>14) + newY = dy + + fixed.Int26_6((int64(x)*int64(txy)+half)>>14) + + fixed.Int26_6((int64(y)*int64(tyy)+half)>>14) + return newX, newY +} diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt_test.go b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go index 85d96a96f..dc1c9c54f 100644 --- a/vendor/golang.org/x/image/font/sfnt/sfnt_test.go +++ b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go @@ -43,6 +43,16 @@ func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment { } } +func translate(dx, dy fixed.Int26_6, s Segment) Segment { + translateArgs(&s.Args, dx, dy) + return s +} + +func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segment { + transformArgs(&s.Args, txx, txy, tyx, tyy, dx, dy) + return s +} + func checkSegmentsEqual(got, want []Segment) error { if len(got) != len(want) { return fmt.Errorf("got %d elements, want %d\noverall:\ngot %v\nwant %v", @@ -378,6 +388,70 @@ func TestTrueTypeSegments(t *testing.T) { lineTo(614, 1638), lineTo(614, 0), lineTo(205, 0), + }, { + // five + // - contour #0 + moveTo(0, 0), + lineTo(0, 100), + lineTo(400, 100), + lineTo(400, 0), + lineTo(0, 0), + }, { + // six + // - contour #0 + moveTo(0, 0), + lineTo(0, 100), + lineTo(400, 100), + lineTo(400, 0), + lineTo(0, 0), + // - contour #1 + translate(111, 234, moveTo(205, 0)), + translate(111, 234, lineTo(205, 1638)), + translate(111, 234, lineTo(614, 1638)), + translate(111, 234, lineTo(614, 0)), + translate(111, 234, lineTo(205, 0)), + }, { + // seven + // - contour #0 + moveTo(0, 0), + lineTo(0, 100), + lineTo(400, 100), + lineTo(400, 0), + lineTo(0, 0), + // - contour #1 + transform(1<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)), + transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)), + transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)), + transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)), + transform(1<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)), + }, { + // eight + // - contour #0 + moveTo(0, 0), + lineTo(0, 100), + lineTo(400, 100), + lineTo(400, 0), + lineTo(0, 0), + // - contour #1 + transform(3<<13, 0, 0, 1<<13, 56, 117, moveTo(205, 0)), + transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 1638)), + transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 1638)), + transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(614, 0)), + transform(3<<13, 0, 0, 1<<13, 56, 117, lineTo(205, 0)), + }, { + // nine + // - contour #0 + moveTo(0, 0), + lineTo(0, 100), + lineTo(400, 100), + lineTo(400, 0), + lineTo(0, 0), + // - contour #1 + transform(22381, 8192, 5996, 14188, 237, 258, moveTo(205, 0)), + transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 1638)), + transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 1638)), + transform(22381, 8192, 5996, 14188, 237, 258, lineTo(614, 0)), + transform(22381, 8192, 5996, 14188, 237, 258, lineTo(205, 0)), }} testSegments(t, "glyfTest.ttf", wants) diff --git a/vendor/golang.org/x/image/font/sfnt/truetype.go b/vendor/golang.org/x/image/font/sfnt/truetype.go index c0eefba56..41819617d 100644 --- a/vendor/golang.org/x/image/font/sfnt/truetype.go +++ b/vendor/golang.org/x/image/font/sfnt/truetype.go @@ -84,13 +84,16 @@ func parseLoca(src *source, loca table, glyfOffset uint32, indexToLocFormat bool // glyph begins with the following [10 byte] header". const glyfHeaderLen = 10 -// appendGlyfSegments appends to dst the segments encoded in the glyf data. -func appendGlyfSegments(dst []Segment, data []byte) ([]Segment, error) { +func loadGlyf(f *Font, b *Buffer, x GlyphIndex, stackBottom, recursionDepth uint32) error { + data, _, _, err := f.viewGlyphData(b, x) + if err != nil { + return err + } if len(data) == 0 { - return dst, nil + return nil } if len(data) < glyfHeaderLen { - return nil, errInvalidGlyphData + return errInvalidGlyphData } index := glyfHeaderLen @@ -99,34 +102,33 @@ func appendGlyfSegments(dst []Segment, data []byte) ([]Segment, error) { case numContours == -1: // We have a compound glyph. No-op. case numContours == 0: - return dst, nil + return nil case numContours > 0: // We have a simple (non-compound) glyph. index += 2 * int(numContours) if index > len(data) { - return nil, errInvalidGlyphData + return errInvalidGlyphData } // The +1 for numPoints is because the value in the file format is // inclusive, but Go's slice[:index] semantics are exclusive. numPoints = 1 + int(u16(data[index-2:])) default: - return nil, errInvalidGlyphData + return errInvalidGlyphData } - // TODO: support compound glyphs. if numContours < 0 { - return nil, errUnsupportedCompoundGlyph + return loadCompoundGlyf(f, b, data[glyfHeaderLen:], stackBottom, recursionDepth) } // Skip the hinting instructions. index += 2 if index > len(data) { - return nil, errInvalidGlyphData + return errInvalidGlyphData } hintsLength := int(u16(data[index-2:])) index += hintsLength if index > len(data) { - return nil, errInvalidGlyphData + return errInvalidGlyphData } // For simple (non-compound) glyphs, the remainder of the glyf data @@ -140,7 +142,7 @@ func appendGlyfSegments(dst []Segment, data []byte) ([]Segment, error) { flagIndex := int32(index) xIndex, yIndex, ok := findXYIndexes(data, index, numPoints) if !ok { - return nil, errInvalidGlyphData + return errInvalidGlyphData } // The second pass decodes each (flags, x, y) tuple in row order. @@ -157,13 +159,10 @@ func appendGlyfSegments(dst []Segment, data []byte) ([]Segment, error) { } for g.nextContour() { for g.nextSegment() { - dst = append(dst, g.seg) + b.segments = append(b.segments, g.seg) } } - if g.err != nil { - return nil, g.err - } - return dst, nil + return g.err } func findXYIndexes(data []byte, index, numPoints int) (xIndex, yIndex int32, ok bool) { @@ -215,6 +214,115 @@ func findXYIndexes(data []byte, index, numPoints int) (xIndex, yIndex int32, ok return int32(index), int32(index + xDataLen), true } +func loadCompoundGlyf(f *Font, b *Buffer, data []byte, stackBottom, recursionDepth uint32) error { + if recursionDepth++; recursionDepth == maxCompoundRecursionDepth { + return errUnsupportedCompoundGlyph + } + + // Read and process the compound glyph's components. They are two separate + // for loops, since reading parses the elements of the data slice, and + // processing can overwrite the backing array. + + stackTop := stackBottom + for { + if stackTop >= maxCompoundStackSize { + return errUnsupportedCompoundGlyph + } + elem := &b.compoundStack[stackTop] + stackTop++ + + if len(data) < 4 { + return errInvalidGlyphData + } + flags := u16(data) + elem.glyphIndex = GlyphIndex(u16(data[2:])) + if flags&flagArg1And2AreWords == 0 { + if len(data) < 6 { + return errInvalidGlyphData + } + elem.dx = int16(int8(data[4])) + elem.dy = int16(int8(data[5])) + data = data[6:] + } else { + if len(data) < 8 { + return errInvalidGlyphData + } + elem.dx = int16(u16(data[4:])) + elem.dy = int16(u16(data[6:])) + data = data[8:] + } + + if flags&flagArgsAreXYValues == 0 { + return errUnsupportedCompoundGlyph + } + elem.hasTransform = flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 + if elem.hasTransform { + switch { + case flags&flagWeHaveAScale != 0: + if len(data) < 2 { + return errInvalidGlyphData + } + elem.transformXX = int16(u16(data)) + elem.transformXY = 0 + elem.transformYX = 0 + elem.transformYY = elem.transformXX + data = data[2:] + case flags&flagWeHaveAnXAndYScale != 0: + if len(data) < 4 { + return errInvalidGlyphData + } + elem.transformXX = int16(u16(data[0:])) + elem.transformXY = 0 + elem.transformYX = 0 + elem.transformYY = int16(u16(data[2:])) + data = data[4:] + case flags&flagWeHaveATwoByTwo != 0: + if len(data) < 8 { + return errInvalidGlyphData + } + elem.transformXX = int16(u16(data[0:])) + elem.transformXY = int16(u16(data[2:])) + elem.transformYX = int16(u16(data[4:])) + elem.transformYY = int16(u16(data[6:])) + data = data[8:] + } + } + + if flags&flagMoreComponents == 0 { + break + } + } + + // To support hinting, we'd have to save the remaining bytes in data here + // and interpret them after the for loop below, since that for loop's + // loadGlyf calls can overwrite the backing array. + + for i := stackBottom; i < stackTop; i++ { + elem := &b.compoundStack[i] + base := len(b.segments) + if err := loadGlyf(f, b, elem.glyphIndex, stackTop, recursionDepth); err != nil { + return err + } + dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy) + segs := b.segments[base:] + if elem.hasTransform { + txx := elem.transformXX + txy := elem.transformXY + tyx := elem.transformYX + tyy := elem.transformYY + for j := range segs { + transformArgs(&segs[j].Args, txx, txy, tyx, tyy, dx, dy) + } + } else { + for j := range segs { + translateArgs(&segs[j].Args, dx, dy) + } + } + } + + return nil +} + type glyfIter struct { data []byte err error diff --git a/vendor/golang.org/x/image/font/testdata/glyfTest.sfd b/vendor/golang.org/x/image/font/testdata/glyfTest.sfd index e61c54c86..6b57a5409 100644 --- a/vendor/golang.org/x/image/font/testdata/glyfTest.sfd +++ b/vendor/golang.org/x/image/font/testdata/glyfTest.sfd @@ -2,7 +2,7 @@ SplineFontDB: 3.0 FontName: glyfTest FullName: glyfTest FamilyName: glyfTest -Weight: Regular +Weight: Book Copyright: Copyright 2016 The Go Authors. All rights reserved.\nUse of this font is governed by a BSD-style license that can be found at https://golang.org/LICENSE. Version: 001.000 ItalicAngle: -11.25 @@ -10,38 +10,75 @@ UnderlinePosition: -204 UnderlineWidth: 102 Ascent: 1638 Descent: 410 +sfntRevision: 0x00010000 LayerCount: 2 Layer: 0 1 "Back" 1 Layer: 1 1 "Fore" 0 -XUID: [1021 367 888937226 7862908] +XUID: [1021 367 888937226 5879518] FSType: 8 -OS2Version: 0 +OS2Version: 4 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 1 CreationTime: 1484386143 -ModificationTime: 1484386143 +ModificationTime: 1489831626 PfmFamily: 17 TTFWeight: 400 TTFWidth: 5 LineGap: 184 VLineGap: 0 -OS2TypoAscent: 0 -OS2TypoAOffset: 1 -OS2TypoDescent: 0 -OS2TypoDOffset: 1 +Panose: 2 0 5 3 0 0 0 0 0 0 +OS2TypoAscent: 1638 +OS2TypoAOffset: 0 +OS2TypoDescent: -410 +OS2TypoDOffset: 0 OS2TypoLinegap: 184 -OS2WinAscent: 0 -OS2WinAOffset: 1 +OS2WinAscent: 1984 +OS2WinAOffset: 0 OS2WinDescent: 0 -OS2WinDOffset: 1 -HheadAscent: 0 -HheadAOffset: 1 +OS2WinDOffset: 0 +HheadAscent: 1984 +HheadAOffset: 0 HheadDescent: 0 -HheadDOffset: 1 +HheadDOffset: 0 +OS2SubXSize: 1331 +OS2SubYSize: 1433 +OS2SubXOff: 55 +OS2SubYOff: 286 +OS2SupXSize: 1331 +OS2SupYSize: 1433 +OS2SupXOff: -191 +OS2SupYOff: 983 +OS2StrikeYSize: 102 +OS2StrikeYPos: 530 OS2Vendor: 'PfEd' +OS2CodePages: 00000001.00000000 +OS2UnicodeRanges: 00000001.00000000.00000000.00000000 MarkAttachClasses: 1 DEI: 91125 -LangName: 1033 +ShortTable: cvt 2 + 68 + 1297 +EndShort +ShortTable: maxp 16 + 1 + 0 + 10 + 18 + 2 + 8 + 2 + 2 + 0 + 1 + 1 + 0 + 64 + 46 + 2 + 1 +EndShort +LangName: 1033 "" "" "Regular" "FontForge : glyfTest : 18-3-2017" "" "Version 001.000" +GaspTable: 1 65535 2 0 Encoding: UnicodeBmp UnicodeInterp: none NameList: Adobe Glyph List @@ -49,18 +86,47 @@ DisplaySize: -24 AntiAlias: 1 FitToEm: 1 WinInfo: 0 32 23 -BeginPrivate: 0 -EndPrivate -TeXData: 1 0 0 346030 173015 115343 0 -1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 -BeginChars: 65536 2 +BeginChars: 65539 10 + +StartChar: .notdef +Encoding: 65536 -1 0 +Width: 748 +Flags: W +LayerCount: 2 +Fore +SplineSet +68 0 m 1,0,-1 + 68 1365 l 1,1,-1 + 612 1365 l 1,2,-1 + 612 0 l 1,3,-1 + 68 0 l 1,0,-1 +136 68 m 1,4,-1 + 544 68 l 1,5,-1 + 544 1297 l 1,6,-1 + 136 1297 l 1,7,-1 + 136 68 l 1,4,-1 +EndSplineSet +Validated: 1 +EndChar + +StartChar: .null +Encoding: 65537 -1 1 +Width: 0 +Flags: W +LayerCount: 2 +EndChar + +StartChar: nonmarkingreturn +Encoding: 65538 -1 2 +Width: 682 +Flags: W +LayerCount: 2 +EndChar StartChar: zero -Encoding: 48 48 0 +Encoding: 48 48 3 Width: 1228 -VWidth: 0 Flags: W -HStem: 0 205<508 700> 1434 205<529 720> -VStem: 205 164<500 1088> 860 164<550 1139> LayerCount: 2 Fore SplineSet @@ -69,7 +135,7 @@ SplineSet 369 471 369 471 435 338 c 0,4,5 502 205 502 205 614 205 c 0,6,7 860 205 860 205 860 1024 c 0,8,9 - 860 1167 860 1167 793 1300 c 0,10,11 + 860 1167 860 1167 793 1300 c 1,10,11 727 1434 727 1434 614 1434 c 0,0,1 614 1638 m 0,12,13 1024 1638 1024 1638 1024 819 c 128,-1,14 @@ -81,22 +147,79 @@ Validated: 1 EndChar StartChar: one -Encoding: 49 49 1 +Encoding: 49 49 4 Width: 819 -VWidth: 0 Flags: W -HStem: 0 43G<205 614> -VStem: 205 410<0 1638> LayerCount: 2 Fore SplineSet -205 0 m 25,0,-1 +205 0 m 1,0,-1 205 1638 l 1,1,-1 614 1638 l 1,2,-1 614 0 l 1,3,-1 - 205 0 l 25,0,-1 + 205 0 l 1,0,-1 +EndSplineSet +Validated: 1 +EndChar + +StartChar: five +Encoding: 53 53 5 +Width: 400 +Flags: W +LayerCount: 2 +Fore +SplineSet +0 0 m 1,0,-1 + 0 100 l 1,1,-1 + 400 100 l 1,2,-1 + 400 0 l 1,3,-1 + 0 0 l 1,0,-1 EndSplineSet Validated: 1 EndChar + +StartChar: six +Encoding: 54 54 6 +Width: 400 +Flags: W +LayerCount: 2 +Fore +Refer: 5 53 N 1 0 0 1 0 0 2 +Refer: 4 49 N 1 0 0 1 111 234 2 +Validated: 1 +EndChar + +StartChar: seven +Encoding: 55 55 7 +Width: 400 +Flags: W +LayerCount: 2 +Fore +Refer: 5 53 N 1 0 0 1 0 0 2 +Refer: 4 49 N 0.5 0 0 0.5 56 117 2 +Validated: 1 +EndChar + +StartChar: eight +Encoding: 56 56 8 +Width: 400 +Flags: W +LayerCount: 2 +Fore +Refer: 5 53 N 1 0 0 1 0 0 2 +Refer: 4 49 N 1.5 0 0 0.5 56 117 2 +Validated: 1 +EndChar + +StartChar: nine +Encoding: 57 57 9 +Width: 400 +Flags: W +LayerCount: 2 +Fore +Refer: 5 53 N 1 0 0 1 0 0 2 +Refer: 4 49 N 1.36603 0.5 0.365967 0.865967 237 258 2 +Validated: 1 +EndChar EndChars EndSplineFont diff --git a/vendor/golang.org/x/image/font/testdata/glyfTest.ttf b/vendor/golang.org/x/image/font/testdata/glyfTest.ttf index 587b3fe0b..2ae24f8e2 100644 Binary files a/vendor/golang.org/x/image/font/testdata/glyfTest.ttf and b/vendor/golang.org/x/image/font/testdata/glyfTest.ttf differ -- cgit v1.2.3-1-g7c22