summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/image/font/sfnt
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/image/font/sfnt')
-rw-r--r--vendor/golang.org/x/image/font/sfnt/postscript.go522
-rw-r--r--vendor/golang.org/x/image/font/sfnt/proprietary_test.go394
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt.go292
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt_test.go74
-rw-r--r--vendor/golang.org/x/image/font/sfnt/truetype.go142
5 files changed, 1202 insertions, 222 deletions
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