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.go865
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt.go621
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt_test.go272
-rw-r--r--vendor/golang.org/x/image/font/sfnt/truetype.go489
4 files changed, 2247 insertions, 0 deletions
diff --git a/vendor/golang.org/x/image/font/sfnt/postscript.go b/vendor/golang.org/x/image/font/sfnt/postscript.go
new file mode 100644
index 000000000..d5d6d9aed
--- /dev/null
+++ b/vendor/golang.org/x/image/font/sfnt/postscript.go
@@ -0,0 +1,865 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sfnt
+
+// Compact Font Format (CFF) fonts are written in PostScript, a stack-based
+// programming language.
+//
+// A fundamental concept is a DICT, or a key-value map, expressed in reverse
+// Polish notation. For example, this sequence of operations:
+// - push the number 379
+// - version operator
+// - push the number 392
+// - Notice operator
+// - etc
+// - push the number 100
+// - push the number 0
+// - push the number 500
+// - push the number 800
+// - FontBBox operator
+// - etc
+// defines a DICT that maps "version" to the String ID (SID) 379, "Notice" to
+// the SID 392, "FontBBox" to the four numbers [100, 0, 500, 800], etc.
+//
+// The first 391 String IDs (starting at 0) are predefined as per the CFF spec
+// Appendix A, in 5176.CFF.pdf referenced below. For example, 379 means
+// "001.000". String ID 392 is not predefined, and is mapped by a separate
+// structure, the "String INDEX", inside the CFF data. (String ID 391 is also
+// not predefined. Specifically for ../testdata/CFFTest.otf, 391 means
+// "uni4E2D", as this font contains a glyph for U+4E2D).
+//
+// The actual glyph vectors are similarly encoded (in PostScript), in a format
+// called Type 2 Charstrings. The wire encoding is similar to but not exactly
+// the same as CFF's. For example, the byte 0x05 means FontBBox for CFF DICTs,
+// but means rlineto (relative line-to) for Type 2 Charstrings. See
+// 5176.CFF.pdf Appendix H and 5177.Type2.pdf Appendix A in the PDF files
+// referenced below.
+//
+// CFF is a stand-alone format, but CFF as used in SFNT fonts have further
+// restrictions. For example, a stand-alone CFF can contain multiple fonts, but
+// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The Name
+// INDEX in the CFF must contain only one entry; that is, there must be only
+// one font in the CFF FontSet".
+//
+// The relevant specifications are:
+// - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
+// - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+
+ "golang.org/x/image/math/fixed"
+)
+
+const (
+ // 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
+)
+
+func bigEndian(b []byte) uint32 {
+ switch len(b) {
+ case 1:
+ return uint32(b[0])
+ case 2:
+ return uint32(b[0])<<8 | uint32(b[1])
+ case 3:
+ return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
+ case 4:
+ return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
+ }
+ panic("unreachable")
+}
+
+// cffParser parses the CFF table from an SFNT font.
+type cffParser struct {
+ src *source
+ base int
+ offset int
+ end int
+ err error
+
+ buf []byte
+ locBuf [2]uint32
+
+ psi psInterpreter
+}
+
+func (p *cffParser) parse() (locations []uint32, err error) {
+ // Parse header.
+ {
+ if !p.read(4) {
+ return nil, p.err
+ }
+ if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 {
+ return nil, errUnsupportedCFFVersion
+ }
+ }
+
+ // Parse Name INDEX.
+ {
+ count, offSize, ok := p.parseIndexHeader()
+ if !ok {
+ return 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
+ }
+ if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
+ return nil, p.err
+ }
+ p.offset = int(p.locBuf[1])
+ }
+
+ // Parse Top DICT INDEX.
+ {
+ count, offSize, ok := p.parseIndexHeader()
+ if !ok {
+ return 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
+ }
+ if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
+ return nil, p.err
+ }
+ if !p.read(int(p.locBuf[1] - p.locBuf[0])) {
+ return nil, p.err
+ }
+ p.psi.topDict.initialize()
+ if p.err = p.psi.run(psContextTopDict, p.buf); p.err != nil {
+ 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, errInvalidCFFTable
+ }
+ p.offset = p.base + int(p.psi.topDict.charStrings)
+ count, offSize, ok := p.parseIndexHeader()
+ if !ok {
+ return nil, p.err
+ }
+ if count == 0 {
+ return nil, errInvalidCFFTable
+ }
+ locations = make([]uint32, count+1)
+ if !p.parseIndexLocations(locations, count, offSize) {
+ return nil, p.err
+ }
+ return locations, nil
+}
+
+// read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
+// advances p.offset by n.
+//
+// As per the source.view method, the caller should not modify the contents of
+// p.buf after read returns, other than by calling read again.
+//
+// The caller should also avoid modifying the pointer / length / capacity of
+// the p.buf slice, not just avoid modifying the slice's contents, in order to
+// maximize the opportunity to re-use p.buf's allocated memory when viewing the
+// underlying source data for subsequent read calls.
+func (p *cffParser) read(n int) (ok bool) {
+ if p.end-p.offset < n {
+ p.err = errInvalidCFFTable
+ return false
+ }
+ p.buf, p.err = p.src.view(p.buf, p.offset, n)
+ p.offset += n
+ return p.err == nil
+}
+
+func (p *cffParser) parseIndexHeader() (count, offSize int32, ok bool) {
+ if !p.read(2) {
+ return 0, 0, false
+ }
+ count = int32(u16(p.buf[:2]))
+ // 5176.CFF.pdf section 5 "INDEX Data" says that "An empty INDEX is
+ // represented by a count field with a 0 value and no additional fields.
+ // Thus, the total size of an empty INDEX is 2 bytes".
+ if count == 0 {
+ return count, 0, true
+ }
+ if !p.read(1) {
+ return 0, 0, false
+ }
+ offSize = int32(p.buf[0])
+ if offSize < 1 || 4 < offSize {
+ p.err = errInvalidCFFTable
+ return 0, 0, false
+ }
+ return count, offSize, true
+}
+
+func (p *cffParser) parseIndexLocations(dst []uint32, count, offSize int32) (ok bool) {
+ if count == 0 {
+ return true
+ }
+ if len(dst) != int(count+1) {
+ panic("unreachable")
+ }
+ if !p.read(len(dst) * int(offSize)) {
+ return false
+ }
+
+ buf, prev := p.buf, uint32(0)
+ for i := range dst {
+ loc := bigEndian(buf[:offSize])
+ buf = buf[offSize:]
+
+ // Locations are off by 1 byte. 5176.CFF.pdf section 5 "INDEX Data"
+ // says that "Offsets in the offset array are relative to the byte that
+ // precedes the object data... This ensures that every object has a
+ // corresponding offset which is always nonzero".
+ if loc == 0 {
+ p.err = errInvalidCFFTable
+ return false
+ }
+ loc--
+
+ // In the same paragraph, "Therefore the first element of the offset
+ // array is always 1" before correcting for the off-by-1.
+ if i == 0 {
+ if loc != 0 {
+ p.err = errInvalidCFFTable
+ break
+ }
+ } else if loc <= prev { // Check that locations are increasing.
+ p.err = errInvalidCFFTable
+ break
+ }
+
+ // Check that locations are in bounds.
+ if uint32(p.end-p.offset) < loc {
+ p.err = errInvalidCFFTable
+ break
+ }
+
+ dst[i] = uint32(p.offset) + loc
+ prev = loc
+ }
+ return p.err == nil
+}
+
+type psContext uint32
+
+const (
+ psContextTopDict psContext = iota
+ psContextType2Charstring
+)
+
+// psTopDictData contains fields specific to the Top DICT context.
+type psTopDictData struct {
+ charStrings int32
+}
+
+func (d *psTopDictData) initialize() {
+ *d = psTopDictData{}
+}
+
+// psType2CharstringsData contains fields specific to the Type 2 Charstrings
+// context.
+type psType2CharstringsData struct {
+ segments []Segment
+ x, y int32
+ hintBits int32
+ seenWidth bool
+}
+
+func (d *psType2CharstringsData) initialize(segments []Segment) {
+ *d = psType2CharstringsData{
+ segments: segments,
+ }
+}
+
+// psInterpreter is a PostScript interpreter.
+type psInterpreter struct {
+ ctx psContext
+ instructions []byte
+ stack struct {
+ a [psStackSize]int32
+ top int32
+ }
+ parseNumberBuf [maxRealNumberStrLen]byte
+ topDict psTopDictData
+ type2Charstrings psType2CharstringsData
+}
+
+func (p *psInterpreter) run(ctx psContext, instructions []byte) error {
+ p.ctx = ctx
+ p.instructions = instructions
+ p.stack.top = 0
+
+loop:
+ for len(p.instructions) > 0 {
+ // Push a numeric operand on the stack, if applicable.
+ if hasResult, err := p.parseNumber(); hasResult {
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ // Otherwise, execute an operator.
+ b := p.instructions[0]
+ p.instructions = p.instructions[1:]
+
+ for escaped, ops := false, psOperators[ctx][0]; ; {
+ if b == escapeByte && !escaped {
+ if len(p.instructions) <= 0 {
+ return errInvalidCFFTable
+ }
+ b = p.instructions[0]
+ p.instructions = p.instructions[1:]
+ escaped = true
+ ops = psOperators[ctx][1]
+ continue
+ }
+
+ if int(b) < len(ops) {
+ if op := ops[b]; op.name != "" {
+ if p.stack.top < op.numPop {
+ return errInvalidCFFTable
+ }
+ if op.run != nil {
+ if err := op.run(p); err != nil {
+ return err
+ }
+ }
+ if op.numPop < 0 {
+ p.stack.top = 0
+ } else {
+ p.stack.top -= op.numPop
+ }
+ continue loop
+ }
+ }
+
+ if escaped {
+ return fmt.Errorf("sfnt: unrecognized CFF 2-byte operator (12 %d)", b)
+ } else {
+ return fmt.Errorf("sfnt: unrecognized CFF 1-byte operator (%d)", b)
+ }
+ }
+ }
+ return nil
+}
+
+// See 5176.CFF.pdf section 4 "DICT Data".
+func (p *psInterpreter) parseNumber() (hasResult bool, err error) {
+ number := int32(0)
+ switch b := p.instructions[0]; {
+ case b == 28:
+ if len(p.instructions) < 3 {
+ return true, errInvalidCFFTable
+ }
+ number, hasResult = int32(int16(u16(p.instructions[1:]))), true
+ p.instructions = p.instructions[3:]
+
+ case b == 29 && p.ctx == psContextTopDict:
+ if len(p.instructions) < 5 {
+ return true, errInvalidCFFTable
+ }
+ number, hasResult = int32(u32(p.instructions[1:])), true
+ p.instructions = p.instructions[5:]
+
+ case b == 30 && p.ctx == psContextTopDict:
+ // Parse a real number. This isn't listed in 5176.CFF.pdf Table 3
+ // "Operand Encoding" but that table lists integer encodings. Further
+ // down the page it says "A real number operand is provided in addition
+ // to integer operands. This operand begins with a byte value of 30
+ // followed by a variable-length sequence of bytes."
+
+ s := p.parseNumberBuf[:0]
+ p.instructions = p.instructions[1:]
+ loop:
+ for {
+ if len(p.instructions) == 0 {
+ return true, errInvalidCFFTable
+ }
+ b := p.instructions[0]
+ p.instructions = p.instructions[1:]
+ // Process b's two nibbles, high then low.
+ for i := 0; i < 2; i++ {
+ nib := b >> 4
+ b = b << 4
+ if nib == 0x0f {
+ f, err := strconv.ParseFloat(string(s), 32)
+ if err != nil {
+ return true, errInvalidCFFTable
+ }
+ number, hasResult = int32(math.Float32bits(float32(f))), true
+ break loop
+ }
+ if nib == 0x0d {
+ return true, errInvalidCFFTable
+ }
+ if len(s)+maxNibbleDefsLength > len(p.parseNumberBuf) {
+ return true, errUnsupportedRealNumberEncoding
+ }
+ s = append(s, nibbleDefs[nib]...)
+ }
+ }
+
+ case b < 32:
+ // No-op.
+
+ case b < 247:
+ p.instructions = p.instructions[1:]
+ number, hasResult = int32(b)-139, true
+
+ case b < 251:
+ if len(p.instructions) < 2 {
+ return true, errInvalidCFFTable
+ }
+ b1 := p.instructions[1]
+ p.instructions = p.instructions[2:]
+ number, hasResult = +int32(b-247)*256+int32(b1)+108, true
+
+ case b < 255:
+ if len(p.instructions) < 2 {
+ return true, errInvalidCFFTable
+ }
+ b1 := p.instructions[1]
+ p.instructions = p.instructions[2:]
+ number, hasResult = -int32(b-251)*256-int32(b1)-108, true
+
+ case b == 255 && p.ctx == psContextType2Charstring:
+ if len(p.instructions) < 5 {
+ return true, errInvalidCFFTable
+ }
+ number, hasResult = int32(u32(p.instructions[1:])), true
+ p.instructions = p.instructions[5:]
+ }
+
+ if hasResult {
+ if p.stack.top == psStackSize {
+ return true, errInvalidCFFTable
+ }
+ p.stack.a[p.stack.top] = number
+ p.stack.top++
+ }
+ return hasResult, nil
+}
+
+const maxNibbleDefsLength = len("E-")
+
+// nibbleDefs encodes 5176.CFF.pdf Table 5 "Nibble Definitions".
+var nibbleDefs = [16]string{
+ 0x00: "0",
+ 0x01: "1",
+ 0x02: "2",
+ 0x03: "3",
+ 0x04: "4",
+ 0x05: "5",
+ 0x06: "6",
+ 0x07: "7",
+ 0x08: "8",
+ 0x09: "9",
+ 0x0a: ".",
+ 0x0b: "E",
+ 0x0c: "E-",
+ 0x0d: "",
+ 0x0e: "-",
+ 0x0f: "",
+}
+
+type psOperator struct {
+ // numPop is the number of stack values to pop. -1 means "array" and -2
+ // means "delta" as per 5176.CFF.pdf Table 6 "Operand Types".
+ numPop int32
+ // name is the operator name. An empty name (i.e. the zero value for the
+ // struct overall) means an unrecognized 1-byte operator.
+ name string
+ // run is the function that implements the operator. Nil means that we
+ // ignore the operator, other than popping its arguments off the stack.
+ run func(*psInterpreter) error
+}
+
+// psOperators holds the 1-byte and 2-byte operators for PostScript interpreter
+// contexts.
+var psOperators = [...][2][]psOperator{
+ // The Top DICT operators are defined by 5176.CFF.pdf Table 9 "Top DICT
+ // Operator Entries" and Table 10 "CIDFont Operator Extensions".
+ psContextTopDict: {{
+ // 1-byte operators.
+ 0: {+1, "version", nil},
+ 1: {+1, "Notice", nil},
+ 2: {+1, "FullName", nil},
+ 3: {+1, "FamilyName", nil},
+ 4: {+1, "Weight", nil},
+ 5: {-1, "FontBBox", nil},
+ 13: {+1, "UniqueID", nil},
+ 14: {-1, "XUID", nil},
+ 15: {+1, "charset", nil},
+ 16: {+1, "Encoding", nil},
+ 17: {+1, "CharStrings", func(p *psInterpreter) error {
+ p.topDict.charStrings = p.stack.a[p.stack.top-1]
+ return nil
+ }},
+ 18: {+2, "Private", nil},
+ }, {
+ // 2-byte operators. The first byte is the escape byte.
+ 0: {+1, "Copyright", nil},
+ 1: {+1, "isFixedPitch", nil},
+ 2: {+1, "ItalicAngle", nil},
+ 3: {+1, "UnderlinePosition", nil},
+ 4: {+1, "UnderlineThickness", nil},
+ 5: {+1, "PaintType", nil},
+ 6: {+1, "CharstringType", nil},
+ 7: {-1, "FontMatrix", nil},
+ 8: {+1, "StrokeWidth", nil},
+ 20: {+1, "SyntheticBase", nil},
+ 21: {+1, "PostScript", nil},
+ 22: {+1, "BaseFontName", nil},
+ 23: {-2, "BaseFontBlend", nil},
+ 30: {+3, "ROS", nil},
+ 31: {+1, "CIDFontVersion", nil},
+ 32: {+1, "CIDFontRevision", nil},
+ 33: {+1, "CIDFontType", nil},
+ 34: {+1, "CIDCount", nil},
+ 35: {+1, "UIDBase", nil},
+ 36: {+1, "FDArray", nil},
+ 37: {+1, "FDSelect", nil},
+ 38: {+1, "FontName", nil},
+ }},
+
+ // The Type 2 Charstring operators are defined by 5177.Type2.pdf Appendix A
+ // "Type 2 Charstring Command Codes".
+ psContextType2Charstring: {{
+ // 1-byte operators.
+ 0: {}, // Reserved.
+ 1: {-1, "hstem", t2CStem},
+ 2: {}, // Reserved.
+ 3: {-1, "vstem", t2CStem},
+ 4: {-1, "vmoveto", t2CVmoveto},
+ 5: {-1, "rlineto", t2CRlineto},
+ 6: {-1, "hlineto", t2CHlineto},
+ 7: {-1, "vlineto", t2CVlineto},
+ 8: {-1, "rrcurveto", t2CRrcurveto},
+ 9: {}, // Reserved.
+ 10: {}, // callsubr.
+ 11: {}, // return.
+ 12: {}, // escape.
+ 13: {}, // Reserved.
+ 14: {-1, "endchar", t2CEndchar},
+ 15: {}, // Reserved.
+ 16: {}, // Reserved.
+ 17: {}, // Reserved.
+ 18: {-1, "hstemhm", t2CStem},
+ 19: {-1, "hintmask", t2CMask},
+ 20: {-1, "cntrmask", t2CMask},
+ 21: {-1, "rmoveto", t2CRmoveto},
+ 22: {-1, "hmoveto", t2CHmoveto},
+ 23: {-1, "vstemhm", t2CStem},
+ 24: {}, // rcurveline.
+ 25: {}, // rlinecurve.
+ 26: {}, // vvcurveto.
+ 27: {}, // hhcurveto.
+ 28: {}, // shortint.
+ 29: {}, // callgsubr.
+ 30: {-1, "vhcurveto", t2CVhcurveto},
+ 31: {-1, "hvcurveto", t2CHvcurveto},
+ }, {
+ // 2-byte operators. The first byte is the escape byte.
+ 0: {}, // Reserved.
+ // TODO: more operators.
+ }},
+}
+
+// 5176.CFF.pdf section 4 "DICT Data" says that "Two-byte operators have an
+// initial escape byte of 12".
+const escapeByte = 12
+
+// t2CReadWidth reads the optional width adjustment. If present, it is on the
+// bottom of the stack.
+//
+// 5177.Type2.pdf page 16 Note 4 says: "The first stack-clearing operator,
+// which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask,
+// hmoveto, vmoveto, rmoveto, or endchar, takes an additional argument — the
+// width... which may be expressed as zero or one numeric argument."
+func t2CReadWidth(p *psInterpreter, nArgs int32) {
+ if p.type2Charstrings.seenWidth {
+ return
+ }
+ p.type2Charstrings.seenWidth = true
+ switch nArgs {
+ case 0:
+ if p.stack.top != 1 {
+ return
+ }
+ case 1:
+ if p.stack.top <= 1 {
+ return
+ }
+ default:
+ if p.stack.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
+ // 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--
+}
+
+func t2CStem(p *psInterpreter) error {
+ t2CReadWidth(p, 2)
+ if p.stack.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
+ if p.type2Charstrings.hintBits > maxHintBits {
+ return errUnsupportedNumberOfHints
+ }
+ return nil
+}
+
+func t2CMask(p *psInterpreter) error {
+ hintBytes := (p.type2Charstrings.hintBits + 7) / 8
+ t2CReadWidth(p, hintBytes)
+ if len(p.instructions) < int(hintBytes) {
+ return errInvalidCFFTable
+ }
+ p.instructions = p.instructions[hintBytes:]
+ return nil
+}
+
+func t2CAppendMoveto(p *psInterpreter) {
+ p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
+ Op: SegmentOpMoveTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.Int26_6(p.type2Charstrings.x) << 6,
+ 1: fixed.Int26_6(p.type2Charstrings.y) << 6,
+ },
+ })
+}
+
+func t2CAppendLineto(p *psInterpreter) {
+ p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
+ Op: SegmentOpLineTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.Int26_6(p.type2Charstrings.x) << 6,
+ 1: fixed.Int26_6(p.type2Charstrings.y) << 6,
+ },
+ })
+}
+
+func t2CAppendCubeto(p *psInterpreter, dxa, dya, dxb, dyb, dxc, dyc int32) {
+ p.type2Charstrings.x += dxa
+ p.type2Charstrings.y += dya
+ xa := p.type2Charstrings.x
+ ya := p.type2Charstrings.y
+ p.type2Charstrings.x += dxb
+ p.type2Charstrings.y += dyb
+ xb := p.type2Charstrings.x
+ yb := p.type2Charstrings.y
+ p.type2Charstrings.x += dxc
+ p.type2Charstrings.y += dyc
+ xc := p.type2Charstrings.x
+ yc := p.type2Charstrings.y
+ p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
+ Op: SegmentOpCubeTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.Int26_6(xa) << 6,
+ 1: fixed.Int26_6(ya) << 6,
+ 2: fixed.Int26_6(xb) << 6,
+ 3: fixed.Int26_6(yb) << 6,
+ 4: fixed.Int26_6(xc) << 6,
+ 5: fixed.Int26_6(yc) << 6,
+ },
+ })
+}
+
+func t2CHmoveto(p *psInterpreter) error {
+ t2CReadWidth(p, 1)
+ if p.stack.top < 1 {
+ return errInvalidCFFTable
+ }
+ for i := int32(0); i < p.stack.top; i++ {
+ p.type2Charstrings.x += p.stack.a[i]
+ }
+ t2CAppendMoveto(p)
+ return nil
+}
+
+func t2CVmoveto(p *psInterpreter) error {
+ t2CReadWidth(p, 1)
+ if p.stack.top < 1 {
+ return errInvalidCFFTable
+ }
+ for i := int32(0); i < p.stack.top; i++ {
+ p.type2Charstrings.y += p.stack.a[i]
+ }
+ t2CAppendMoveto(p)
+ return nil
+}
+
+func t2CRmoveto(p *psInterpreter) error {
+ t2CReadWidth(p, 2)
+ if p.stack.top < 2 || p.stack.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]
+ }
+ t2CAppendMoveto(p)
+ return nil
+}
+
+func t2CHlineto(p *psInterpreter) error { return t2CLineto(p, false) }
+func t2CVlineto(p *psInterpreter) error { return t2CLineto(p, true) }
+
+func t2CLineto(p *psInterpreter, vertical bool) error {
+ if !p.type2Charstrings.seenWidth {
+ return errInvalidCFFTable
+ }
+ if p.stack.top < 1 {
+ return errInvalidCFFTable
+ }
+ for i := int32(0); i < p.stack.top; i, vertical = i+1, !vertical {
+ if vertical {
+ p.type2Charstrings.y += p.stack.a[i]
+ } else {
+ p.type2Charstrings.x += p.stack.a[i]
+ }
+ t2CAppendLineto(p)
+ }
+ return nil
+}
+
+func t2CRlineto(p *psInterpreter) error {
+ if !p.type2Charstrings.seenWidth {
+ return errInvalidCFFTable
+ }
+ if p.stack.top < 2 || p.stack.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]
+ t2CAppendLineto(p)
+ }
+ return nil
+}
+
+// As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
+//
+// hvcurveto is one of:
+// - dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
+// - {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+//
+// vhcurveto is one of:
+// - dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
+// - {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?
+
+func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, false) }
+func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true) }
+
+func t2CCurveto(p *psInterpreter, vertical bool) error {
+ if !p.type2Charstrings.seenWidth || p.stack.top < 4 {
+ return errInvalidCFFTable
+ }
+ for i := int32(0); i != p.stack.top; vertical = !vertical {
+ if vertical {
+ i = t2CVcurveto(p, i)
+ } else {
+ i = t2CHcurveto(p, i)
+ }
+ if i < 0 {
+ return errInvalidCFFTable
+ }
+ }
+ return nil
+}
+
+func t2CHcurveto(p *psInterpreter, i int32) (j int32) {
+ if i+4 > p.stack.top {
+ return -1
+ }
+ dxa := p.stack.a[i+0]
+ dxb := p.stack.a[i+1]
+ dyb := p.stack.a[i+2]
+ dyc := p.stack.a[i+3]
+ dxc := int32(0)
+ i += 4
+ if i+1 == p.stack.top {
+ dxc = p.stack.a[i]
+ i++
+ }
+ t2CAppendCubeto(p, dxa, 0, dxb, dyb, dxc, dyc)
+ return i
+}
+
+func t2CVcurveto(p *psInterpreter, i int32) (j int32) {
+ if i+4 > p.stack.top {
+ return -1
+ }
+ dya := p.stack.a[i+0]
+ dxb := p.stack.a[i+1]
+ dyb := p.stack.a[i+2]
+ dxc := p.stack.a[i+3]
+ dyc := int32(0)
+ i += 4
+ if i+1 == p.stack.top {
+ dyc = p.stack.a[i]
+ i++
+ }
+ t2CAppendCubeto(p, 0, dya, dxb, dyb, dxc, dyc)
+ return i
+}
+
+func t2CRrcurveto(p *psInterpreter) error {
+ if !p.type2Charstrings.seenWidth || p.stack.top < 6 || p.stack.top%6 != 0 {
+ return errInvalidCFFTable
+ }
+ for i := int32(0); i != p.stack.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],
+ )
+ }
+ return nil
+}
+
+func t2CEndchar(p *psInterpreter) error {
+ t2CReadWidth(p, 0)
+ if p.stack.top != 0 || len(p.instructions) != 0 {
+ if p.stack.top == 4 {
+ // TODO: process the implicit "seac" command as per 5177.Type2.pdf
+ // Appendix C "Compatibility and Deprecated Operators".
+ return errUnsupportedType2Charstring
+ }
+ return errInvalidCFFTable
+ }
+ return nil
+}
diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt.go b/vendor/golang.org/x/image/font/sfnt/sfnt.go
new file mode 100644
index 000000000..d4929a838
--- /dev/null
+++ b/vendor/golang.org/x/image/font/sfnt/sfnt.go
@@ -0,0 +1,621 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sfnt implements a decoder for SFNT font file formats, including
+// TrueType and OpenType.
+package sfnt // import "golang.org/x/image/font/sfnt"
+
+// This implementation was written primarily to the
+// https://www.microsoft.com/en-us/Typography/OpenTypeSpecification.aspx
+// specification. Additional documentation is at
+// http://developer.apple.com/fonts/TTRefMan/
+//
+// The pyftinspect tool from https://github.com/fonttools/fonttools is useful
+// for inspecting SFNT fonts.
+
+import (
+ "errors"
+ "io"
+
+ "golang.org/x/image/math/fixed"
+ "golang.org/x/text/encoding/charmap"
+)
+
+// These constants are not part of the specifications, but are limitations used
+// by this implementation.
+const (
+ maxGlyphDataLength = 64 * 1024
+ maxHintBits = 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
+ maxTableOffset = 1 << 29
+)
+
+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")
+ errInvalidGlyphData = errors.New("sfnt: invalid glyph data")
+ errInvalidHeadTable = errors.New("sfnt: invalid head 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")
+ 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")
+
+ errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version")
+ errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph")
+ errUnsupportedGlyphDataLength = errors.New("sfnt: unsupported glyph data length")
+ errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding")
+ errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints")
+ errUnsupportedNumberOfTables = errors.New("sfnt: unsupported number of tables")
+ errUnsupportedPlatformEncoding = errors.New("sfnt: unsupported platform encoding")
+ errUnsupportedTableOffsetLength = errors.New("sfnt: unsupported table offset or length")
+ errUnsupportedType2Charstring = errors.New("sfnt: unsupported Type 2 Charstring")
+)
+
+// GlyphIndex is a glyph index in a Font.
+type GlyphIndex uint16
+
+// NameID identifies a name table entry.
+//
+// See the "Name IDs" section of
+// https://www.microsoft.com/typography/otspec/name.htm
+type NameID uint16
+
+const (
+ NameIDCopyright NameID = 0
+ NameIDFamily = 1
+ NameIDSubfamily = 2
+ NameIDUniqueIdentifier = 3
+ NameIDFull = 4
+ NameIDVersion = 5
+ NameIDPostScript = 6
+ NameIDTrademark = 7
+ NameIDManufacturer = 8
+ NameIDDesigner = 9
+ NameIDDescription = 10
+ NameIDVendorURL = 11
+ NameIDDesignerURL = 12
+ NameIDLicense = 13
+ NameIDLicenseURL = 14
+ NameIDTypographicFamily = 16
+ NameIDTypographicSubfamily = 17
+ NameIDCompatibleFull = 18
+ NameIDSampleText = 19
+ NameIDPostScriptCID = 20
+ NameIDWWSFamily = 21
+ NameIDWWSSubfamily = 22
+ NameIDLightBackgroundPalette = 23
+ NameIDDarkBackgroundPalette = 24
+ NameIDVariationsPostScriptPrefix = 25
+)
+
+// Units are an integral number of abstract, scalable "font units". The em
+// square is typically 1000 or 2048 "font units". This would map to a certain
+// number (e.g. 30 pixels) of physical pixels, depending on things like the
+// display resolution (DPI) and font size (e.g. a 12 point font).
+type Units int32
+
+// Platform IDs and Platform Specific IDs as per
+// https://www.microsoft.com/typography/otspec/name.htm
+const (
+ pidMacintosh = 1
+ pidWindows = 3
+
+ psidMacintoshRoman = 0
+
+ psidWindowsUCS2 = 1
+)
+
+func u16(b []byte) uint16 {
+ _ = b[1] // Bounds check hint to compiler.
+ return uint16(b[0])<<8 | uint16(b[1])<<0
+}
+
+func u32(b []byte) uint32 {
+ _ = b[3] // Bounds check hint to compiler.
+ return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])<<0
+}
+
+// source is a source of byte data. Conceptually, it is like an io.ReaderAt,
+// except that a common source of SFNT font data is in-memory instead of
+// on-disk: a []byte containing the entire data, either as a global variable
+// (e.g. "goregular.TTF") or the result of an ioutil.ReadFile call. In such
+// cases, as an optimization, we skip the io.Reader / io.ReaderAt model of
+// copying from the source to a caller-supplied buffer, and instead provide
+// direct access to the underlying []byte data.
+type source struct {
+ b []byte
+ r io.ReaderAt
+
+ // TODO: add a caching layer, if we're using the io.ReaderAt? Note that
+ // this might make a source no longer safe to use concurrently.
+}
+
+// valid returns whether exactly one of s.b and s.r is nil.
+func (s *source) valid() bool {
+ return (s.b == nil) != (s.r == nil)
+}
+
+// viewBufferWritable returns whether the []byte returned by source.view can be
+// written to by the caller, including by passing it to the same method
+// (source.view) on other receivers (i.e. different sources).
+//
+// In other words, it returns whether the source's underlying data is an
+// io.ReaderAt, not a []byte.
+func (s *source) viewBufferWritable() bool {
+ return s.b == nil
+}
+
+// view returns the length bytes at the given offset. buf is an optional
+// scratch buffer to reduce allocations when calling view multiple times. A nil
+// buf is valid. The []byte returned may be a sub-slice of buf[:cap(buf)], or
+// it may be an unrelated slice. In any case, the caller should not modify the
+// contents of the returned []byte, other than passing that []byte back to this
+// method on the same source s.
+func (s *source) view(buf []byte, offset, length int) ([]byte, error) {
+ if 0 > offset || offset > offset+length {
+ return nil, errInvalidBounds
+ }
+
+ // Try reading from the []byte.
+ if s.b != nil {
+ if offset+length > len(s.b) {
+ return nil, errInvalidBounds
+ }
+ return s.b[offset : offset+length], nil
+ }
+
+ // Read from the io.ReaderAt.
+ if length <= cap(buf) {
+ buf = buf[:length]
+ } else {
+ // Round length up to the nearest KiB. The slack can lead to fewer
+ // allocations if the buffer is re-used for multiple source.view calls.
+ n := length
+ n += 1023
+ n &^= 1023
+ buf = make([]byte, length, n)
+ }
+ if n, err := s.r.ReadAt(buf, int64(offset)); n != length {
+ return nil, err
+ }
+ return buf, nil
+}
+
+// u16 returns the uint16 in the table t at the relative offset i.
+//
+// buf is an optional scratch buffer as per the source.view method.
+func (s *source) u16(buf []byte, t table, i int) (uint16, error) {
+ if i < 0 || uint(t.length) < uint(i+2) {
+ return 0, errInvalidBounds
+ }
+ buf, err := s.view(buf, int(t.offset)+i, 2)
+ if err != nil {
+ return 0, err
+ }
+ return u16(buf), nil
+}
+
+// table is a section of the font data.
+type table struct {
+ offset, length uint32
+}
+
+// Parse parses an SFNT font from a []byte data source.
+func Parse(src []byte) (*Font, error) {
+ f := &Font{src: source{b: src}}
+ if err := f.initialize(); err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// ParseReaderAt parses an SFNT font 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 {
+ return nil, err
+ }
+ return f, nil
+}
+
+// Font is an SFNT font.
+//
+// Many of its methods take a *Buffer argument, as re-using buffers can reduce
+// the total memory allocation of repeated Font method calls, such as measuring
+// and rasterizing every unique glyph in a string of text. If efficiency is not
+// a concern, passing a nil *Buffer is valid, and implies using a temporary
+// buffer for a single call.
+//
+// It is valid to re-use a *Buffer with multiple Font method calls, even with
+// different *Font receivers, as long as they are not concurrent calls.
+//
+// All of the Font methods are safe to call concurrently, as long as each call
+// has a different *Buffer (or nil).
+//
+// The Font methods that don't take a *Buffer argument are always safe to call
+// concurrently.
+type Font struct {
+ src source
+
+ // https://www.microsoft.com/typography/otspec/otff.htm#otttables
+ // "Required Tables".
+ cmap table
+ head table
+ hhea table
+ hmtx table
+ maxp table
+ name table
+ os2 table
+ post table
+
+ // https://www.microsoft.com/typography/otspec/otff.htm#otttables
+ // "Tables Related to TrueType Outlines".
+ //
+ // This implementation does not support hinting, so it does not read the
+ // cvt, fpgm gasp or prep tables.
+ glyf table
+ loca table
+
+ // https://www.microsoft.com/typography/otspec/otff.htm#otttables
+ // "Tables Related to PostScript Outlines".
+ //
+ // TODO: cff2, vorg?
+ cff table
+
+ // https://www.microsoft.com/typography/otspec/otff.htm#otttables
+ // "Advanced Typographic Tables".
+ //
+ // TODO: base, gdef, gpos, gsub, jstf, math?
+
+ // https://www.microsoft.com/typography/otspec/otff.htm#otttables
+ // "Other OpenType Tables".
+ //
+ // TODO: hdmx, kern, vmtx? Others?
+
+ cached struct {
+ indexToLocFormat bool // false means short, true means long.
+ isPostScript bool
+ unitsPerEm Units
+
+ // The glyph data for the glyph index i is in
+ // src[locations[i+0]:locations[i+1]].
+ locations []uint32
+ }
+}
+
+// NumGlyphs returns the number of glyphs in f.
+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 {
+ if !f.src.valid() {
+ return errInvalidSourceData
+ }
+ var buf []byte
+
+ // 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)
+ if err != nil {
+ return err
+ }
+ switch u32(buf) {
+ default:
+ return errInvalidVersion
+ case 0x00010000:
+ // No-op.
+ case 0x4f54544f: // "OTTO".
+ f.cached.isPostScript = true
+ }
+ numTables := int(u16(buf[4:]))
+ if numTables > maxNumTables {
+ return errUnsupportedNumberOfTables
+ }
+
+ // "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)
+ if err != nil {
+ return err
+ }
+ for b, first, prevTag := buf, true, uint32(0); len(b) > 0; b = b[16:] {
+ tag := u32(b)
+ if first {
+ first = false
+ } else if tag <= prevTag {
+ return errInvalidTableTagOrder
+ }
+ prevTag = tag
+
+ o, n := u32(b[8:12]), u32(b[12:16])
+ if o > maxTableOffset || n > maxTableLength {
+ return errUnsupportedTableOffsetLength
+ }
+ // We ignore the checksums, but "all tables must begin on four byte
+ // boundries [sic]".
+ if o&3 != 0 {
+ return errInvalidTableOffset
+ }
+
+ // Match the 4-byte tag as a uint32. For example, "OS/2" is 0x4f532f32.
+ switch tag {
+ case 0x43464620:
+ f.cff = table{o, n}
+ case 0x4f532f32:
+ f.os2 = table{o, n}
+ case 0x636d6170:
+ f.cmap = table{o, n}
+ case 0x676c7966:
+ f.glyf = table{o, n}
+ case 0x68656164:
+ f.head = table{o, n}
+ case 0x68686561:
+ f.hhea = table{o, n}
+ case 0x686d7478:
+ f.hmtx = table{o, n}
+ case 0x6c6f6361:
+ f.loca = table{o, n}
+ case 0x6d617870:
+ f.maxp = table{o, n}
+ case 0x6e616d65:
+ f.name = table{o, n}
+ case 0x706f7374:
+ f.post = table{o, n}
+ }
+ }
+
+ var u uint16
+
+ // https://www.microsoft.com/typography/otspec/head.htm
+ if f.head.length != 54 {
+ return errInvalidHeadTable
+ }
+ u, err = f.src.u16(buf, f.head, 18)
+ if err != nil {
+ return err
+ }
+ if u == 0 {
+ return errInvalidHeadTable
+ }
+ f.cached.unitsPerEm = Units(u)
+ u, err = f.src.u16(buf, f.head, 50)
+ if err != nil {
+ return err
+ }
+ f.cached.indexToLocFormat = u != 0
+
+ // https://www.microsoft.com/typography/otspec/maxp.htm
+ if f.cached.isPostScript {
+ if f.maxp.length != 6 {
+ return errInvalidMaxpTable
+ }
+ } else {
+ if f.maxp.length != 32 {
+ return errInvalidMaxpTable
+ }
+ }
+ u, err = f.src.u16(buf, f.maxp, 4)
+ if err != nil {
+ return err
+ }
+ numGlyphs := int(u)
+
+ if f.cached.isPostScript {
+ p := cffParser{
+ src: &f.src,
+ base: int(f.cff.offset),
+ offset: int(f.cff.offset),
+ end: int(f.cff.offset + f.cff.length),
+ }
+ f.cached.locations, err = p.parse()
+ if err != nil {
+ return err
+ }
+ } else {
+ f.cached.locations, err = parseLoca(
+ &f.src, f.loca, f.glyf.offset, f.cached.indexToLocFormat, numGlyphs)
+ if err != nil {
+ return err
+ }
+ }
+ if len(f.cached.locations) != numGlyphs+1 {
+ return errInvalidLocationData
+ }
+ return nil
+}
+
+// TODO: func (f *Font) GlyphIndex(r rune) (x GlyphIndex, ok bool)
+// This will require parsing the cmap table.
+
+func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) ([]byte, error) {
+ xx := int(x)
+ if f.NumGlyphs() <= xx {
+ return nil, ErrNotFound
+ }
+ i := f.cached.locations[xx+0]
+ j := f.cached.locations[xx+1]
+ if j-i > maxGlyphDataLength {
+ return nil, errUnsupportedGlyphDataLength
+ }
+ return b.view(&f.src, int(i), int(j-i))
+}
+
+// LoadGlyphOptions are the options to the Font.LoadGlyph method.
+type LoadGlyphOptions struct {
+ // TODO: scale / transform / hinting.
+}
+
+// LoadGlyph returns the vector segments for the x'th glyph.
+//
+// If b is non-nil, the segments become invalid to use once b is re-used.
+//
+// It returns ErrNotFound if the glyph index is out of range.
+func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, opts *LoadGlyphOptions) ([]Segment, error) {
+ if b == nil {
+ 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 {
+ return nil, err
+ }
+ b.segments = b.psi.type2Charstrings.segments
+ } else {
+ segments, err := appendGlyfSegments(b.segments, buf)
+ if err != nil {
+ return nil, err
+ }
+ b.segments = segments
+ }
+
+ // TODO: look at opts to scale / transform / hint the Buffer.segments.
+
+ return b.segments, nil
+}
+
+// Name returns the name value keyed by the given NameID.
+//
+// It returns ErrNotFound if there is no value for that key.
+func (f *Font) Name(b *Buffer, id NameID) (string, error) {
+ if b == nil {
+ b = &Buffer{}
+ }
+
+ const headerSize, entrySize = 6, 12
+ if f.name.length < headerSize {
+ return "", errInvalidNameTable
+ }
+ buf, err := b.view(&f.src, int(f.name.offset), headerSize)
+ if err != nil {
+ return "", err
+ }
+ nSubtables := u16(buf[2:])
+ if f.name.length < headerSize+entrySize*uint32(nSubtables) {
+ return "", errInvalidNameTable
+ }
+ stringOffset := u16(buf[4:])
+
+ seen := false
+ for i, n := 0, int(nSubtables); i < n; i++ {
+ buf, err := b.view(&f.src, int(f.name.offset)+headerSize+entrySize*i, entrySize)
+ if err != nil {
+ return "", err
+ }
+ if u16(buf[6:]) != uint16(id) {
+ continue
+ }
+ seen = true
+
+ var stringify func([]byte) (string, error)
+ switch u32(buf) {
+ default:
+ continue
+ case pidMacintosh<<16 | psidMacintoshRoman:
+ stringify = stringifyMacintosh
+ case pidWindows<<16 | psidWindowsUCS2:
+ stringify = stringifyUCS2
+ }
+
+ nameLength := u16(buf[8:])
+ nameOffset := u16(buf[10:])
+ buf, err = b.view(&f.src, int(f.name.offset)+int(nameOffset)+int(stringOffset), int(nameLength))
+ if err != nil {
+ return "", err
+ }
+ return stringify(buf)
+ }
+
+ if seen {
+ return "", errUnsupportedPlatformEncoding
+ }
+ return "", ErrNotFound
+}
+
+func stringifyMacintosh(b []byte) (string, error) {
+ for _, c := range b {
+ if c >= 0x80 {
+ // b contains some non-ASCII bytes.
+ s, _ := charmap.Macintosh.NewDecoder().Bytes(b)
+ return string(s), nil
+ }
+ }
+ // b contains only ASCII bytes.
+ return string(b), nil
+}
+
+func stringifyUCS2(b []byte) (string, error) {
+ if len(b)&1 != 0 {
+ return "", errInvalidUCS2String
+ }
+ r := make([]rune, len(b)/2)
+ for i := range r {
+ r[i] = rune(u16(b))
+ b = b[2:]
+ }
+ return string(r), nil
+}
+
+// Buffer holds re-usable buffers that can reduce the total memory allocation
+// of repeated Font method calls.
+//
+// See the Font type's documentation comment for more details.
+type Buffer struct {
+ // buf is a byte buffer for when a Font's source is an io.ReaderAt.
+ buf []byte
+ // segments holds glyph vector path segments.
+ segments []Segment
+ // psi is a PostScript interpreter for when the Font is an OpenType/CFF
+ // font.
+ psi psInterpreter
+}
+
+func (b *Buffer) view(src *source, offset, length int) ([]byte, error) {
+ buf, err := src.view(b.buf, offset, length)
+ if err != nil {
+ return nil, err
+ }
+ // Only update b.buf if it is safe to re-use buf.
+ if src.viewBufferWritable() {
+ b.buf = buf
+ }
+ return buf, nil
+}
+
+// Segment is a segment of a vector path.
+type Segment struct {
+ Op SegmentOp
+ Args [6]fixed.Int26_6
+}
+
+// SegmentOp is a vector path segment's operator.
+type SegmentOp uint32
+
+const (
+ SegmentOpMoveTo SegmentOp = iota
+ SegmentOpLineTo
+ SegmentOpQuadTo
+ SegmentOpCubeTo
+)
diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt_test.go b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go
new file mode 100644
index 000000000..4ffd72f40
--- /dev/null
+++ b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go
@@ -0,0 +1,272 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sfnt
+
+import (
+ "bytes"
+ "io/ioutil"
+ "path/filepath"
+ "testing"
+
+ "golang.org/x/image/font/gofont/goregular"
+ "golang.org/x/image/math/fixed"
+)
+
+func moveTo(xa, ya int) Segment {
+ return Segment{
+ Op: SegmentOpMoveTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.I(xa),
+ 1: fixed.I(ya),
+ },
+ }
+}
+
+func lineTo(xa, ya int) Segment {
+ return Segment{
+ Op: SegmentOpLineTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.I(xa),
+ 1: fixed.I(ya),
+ },
+ }
+}
+
+func quadTo(xa, ya, xb, yb int) Segment {
+ return Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.I(xa),
+ 1: fixed.I(ya),
+ 2: fixed.I(xb),
+ 3: fixed.I(yb),
+ },
+ }
+}
+
+func cubeTo(xa, ya, xb, yb, xc, yc int) Segment {
+ return Segment{
+ Op: SegmentOpCubeTo,
+ Args: [6]fixed.Int26_6{
+ 0: fixed.I(xa),
+ 1: fixed.I(ya),
+ 2: fixed.I(xb),
+ 3: fixed.I(yb),
+ 4: fixed.I(xc),
+ 5: fixed.I(yc),
+ },
+ }
+}
+
+func TestTrueTypeParse(t *testing.T) {
+ f, err := Parse(goregular.TTF)
+ if err != nil {
+ t.Fatalf("Parse: %v", err)
+ }
+ testTrueType(t, f)
+}
+
+func TestTrueTypeParseReaderAt(t *testing.T) {
+ f, err := ParseReaderAt(bytes.NewReader(goregular.TTF))
+ if err != nil {
+ t.Fatalf("ParseReaderAt: %v", err)
+ }
+ testTrueType(t, f)
+}
+
+func testTrueType(t *testing.T, f *Font) {
+ if got, want := f.UnitsPerEm(), Units(2048); got != want {
+ t.Errorf("UnitsPerEm: got %d, want %d", got, want)
+ }
+ // The exact number of glyphs in goregular.TTF can vary, and future
+ // versions may add more glyphs, but https://blog.golang.org/go-fonts says
+ // that "The WGL4 character set... [has] more than 650 characters in all.
+ if got, want := f.NumGlyphs(), 650; got <= want {
+ t.Errorf("NumGlyphs: got %d, want > %d", got, want)
+ }
+}
+
+func TestPostScriptSegments(t *testing.T) {
+ // wants' vectors correspond 1-to-1 to what's in the CFFTest.sfd file,
+ // although OpenType/CFF and FontForge's SFD have reversed orders.
+ // https://fontforge.github.io/validation.html says that "All paths must be
+ // drawn in a consistent direction. Clockwise for external paths,
+ // anti-clockwise for internal paths. (Actually PostScript requires the
+ // exact opposite, but FontForge reverses PostScript contours when it loads
+ // them so that everything is consistant internally -- and reverses them
+ // again when it saves them, of course)."
+ //
+ // The .notdef glyph isn't explicitly in the SFD file, but for some unknown
+ // reason, FontForge generates it in the OpenType/CFF file.
+ wants := [][]Segment{{
+ // .notdef
+ // - contour #0
+ moveTo(50, 0),
+ lineTo(450, 0),
+ lineTo(450, 533),
+ lineTo(50, 533),
+ // - contour #1
+ moveTo(100, 50),
+ lineTo(100, 483),
+ lineTo(400, 483),
+ lineTo(400, 50),
+ }, {
+ // zero
+ // - contour #0
+ moveTo(300, 700),
+ cubeTo(380, 700, 420, 580, 420, 500),
+ cubeTo(420, 350, 390, 100, 300, 100),
+ cubeTo(220, 100, 180, 220, 180, 300),
+ cubeTo(180, 450, 210, 700, 300, 700),
+ // - contour #1
+ moveTo(300, 800),
+ cubeTo(200, 800, 100, 580, 100, 400),
+ cubeTo(100, 220, 200, 0, 300, 0),
+ cubeTo(400, 0, 500, 220, 500, 400),
+ cubeTo(500, 580, 400, 800, 300, 800),
+ }, {
+ // one
+ // - contour #0
+ moveTo(100, 0),
+ lineTo(300, 0),
+ lineTo(300, 800),
+ lineTo(100, 800),
+ }, {
+ // Q
+ // - contour #0
+ moveTo(657, 237),
+ lineTo(289, 387),
+ lineTo(519, 615),
+ // - contour #1
+ moveTo(792, 169),
+ cubeTo(867, 263, 926, 502, 791, 665),
+ cubeTo(645, 840, 380, 831, 228, 673),
+ cubeTo(71, 509, 110, 231, 242, 93),
+ cubeTo(369, -39, 641, 18, 722, 93),
+ lineTo(802, 3),
+ lineTo(864, 83),
+ }, {
+ // uni4E2D
+ // - contour #0
+ moveTo(141, 520),
+ lineTo(137, 356),
+ lineTo(245, 400),
+ lineTo(331, 26),
+ lineTo(355, 414),
+ lineTo(463, 434),
+ lineTo(453, 620),
+ lineTo(341, 592),
+ lineTo(331, 758),
+ lineTo(243, 752),
+ lineTo(235, 562),
+ // TODO: explicitly (not implicitly) close these contours?
+ }}
+
+ testSegments(t, "CFFTest.otf", wants)
+}
+
+func TestTrueTypeSegments(t *testing.T) {
+ // wants' vectors correspond 1-to-1 to what's in the glyfTest.sfd file,
+ // although FontForge's SFD format stores quadratic Bézier curves as cubics
+ // with duplicated off-curve points. quadTo(bx, by, cx, cy) is stored as
+ // "bx by bx by cx cy".
+ //
+ // The .notdef, .null and nonmarkingreturn glyphs aren't explicitly in the
+ // SFD file, but for some unknown reason, FontForge generates them in the
+ // TrueType file.
+ wants := [][]Segment{{
+ // .notdef
+ // - contour #0
+ moveTo(68, 0),
+ lineTo(68, 1365),
+ lineTo(612, 1365),
+ lineTo(612, 0),
+ lineTo(68, 0),
+ // - contour #1
+ moveTo(136, 68),
+ lineTo(544, 68),
+ lineTo(544, 1297),
+ lineTo(136, 1297),
+ lineTo(136, 68),
+ }, {
+ // .null
+ // Empty glyph.
+ }, {
+ // nonmarkingreturn
+ // Empty glyph.
+ }, {
+ // zero
+ // - contour #0
+ moveTo(614, 1434),
+ quadTo(369, 1434, 369, 614),
+ quadTo(369, 471, 435, 338),
+ quadTo(502, 205, 614, 205),
+ quadTo(860, 205, 860, 1024),
+ quadTo(860, 1167, 793, 1300),
+ quadTo(727, 1434, 614, 1434),
+ // - contour #1
+ moveTo(614, 1638),
+ quadTo(1024, 1638, 1024, 819),
+ quadTo(1024, 0, 614, 0),
+ quadTo(205, 0, 205, 819),
+ quadTo(205, 1638, 614, 1638),
+ }, {
+ // one
+ // - contour #0
+ moveTo(205, 0),
+ lineTo(205, 1638),
+ lineTo(614, 1638),
+ lineTo(614, 0),
+ lineTo(205, 0),
+ }}
+
+ testSegments(t, "glyfTest.ttf", wants)
+}
+
+func testSegments(t *testing.T, filename string, wants [][]Segment) {
+ data, err := ioutil.ReadFile(filepath.Join("..", "testdata", filename))
+ if err != nil {
+ t.Fatal(err)
+ }
+ f, err := Parse(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if ng := f.NumGlyphs(); ng != len(wants) {
+ t.Fatalf("NumGlyphs: got %d, want %d", ng, len(wants))
+ }
+ var b Buffer
+loop:
+ for i, want := range wants {
+ got, err := f.LoadGlyph(&b, GlyphIndex(i), nil)
+ if err != nil {
+ t.Errorf("i=%d: LoadGlyph: %v", i, err)
+ continue
+ }
+ if len(got) != len(want) {
+ t.Errorf("i=%d: got %d elements, want %d\noverall:\ngot %v\nwant %v",
+ i, len(got), len(want), got, want)
+ continue
+ }
+ for j, g := range got {
+ if w := want[j]; g != w {
+ t.Errorf("i=%d: element %d:\ngot %v\nwant %v\noverall:\ngot %v\nwant %v",
+ i, j, g, w, got, want)
+ continue loop
+ }
+ }
+ }
+ if _, err := f.LoadGlyph(nil, 0xffff, nil); err != ErrNotFound {
+ t.Errorf("LoadGlyph(..., 0xffff, ...):\ngot %v\nwant %v", err, ErrNotFound)
+ }
+
+ name, err := f.Name(nil, NameIDFamily)
+ if err != nil {
+ t.Errorf("Name: %v", err)
+ } else if want := filename[:len(filename)-len(".ttf")]; name != want {
+ t.Errorf("Name:\ngot %q\nwant %q", name, want)
+ }
+}
diff --git a/vendor/golang.org/x/image/font/sfnt/truetype.go b/vendor/golang.org/x/image/font/sfnt/truetype.go
new file mode 100644
index 000000000..851904d10
--- /dev/null
+++ b/vendor/golang.org/x/image/font/sfnt/truetype.go
@@ -0,0 +1,489 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sfnt
+
+import (
+ "golang.org/x/image/math/fixed"
+)
+
+// Flags for simple (non-compound) glyphs.
+//
+// See https://www.microsoft.com/typography/OTSPEC/glyf.htm
+const (
+ flagOnCurve = 1 << 0 // 0x0001
+ flagXShortVector = 1 << 1 // 0x0002
+ flagYShortVector = 1 << 2 // 0x0004
+ flagRepeat = 1 << 3 // 0x0008
+
+ // The same flag bits are overloaded to have two meanings, dependent on the
+ // value of the flag{X,Y}ShortVector bits.
+ flagPositiveXShortVector = 1 << 4 // 0x0010
+ flagThisXIsSame = 1 << 4 // 0x0010
+ flagPositiveYShortVector = 1 << 5 // 0x0020
+ flagThisYIsSame = 1 << 5 // 0x0020
+)
+
+// Flags for compound glyphs.
+//
+// See https://www.microsoft.com/typography/OTSPEC/glyf.htm
+const (
+ flagArg1And2AreWords = 1 << 0 // 0x0001
+ flagArgsAreXYValues = 1 << 1 // 0x0002
+ flagRoundXYToGrid = 1 << 2 // 0x0004
+ flagWeHaveAScale = 1 << 3 // 0x0008
+ flagReserved4 = 1 << 4 // 0x0010
+ flagMoreComponents = 1 << 5 // 0x0020
+ flagWeHaveAnXAndYScale = 1 << 6 // 0x0040
+ flagWeHaveATwoByTwo = 1 << 7 // 0x0080
+ flagWeHaveInstructions = 1 << 8 // 0x0100
+ flagUseMyMetrics = 1 << 9 // 0x0200
+ flagOverlapCompound = 1 << 10 // 0x0400
+ flagScaledComponentOffset = 1 << 11 // 0x0800
+ flagUnscaledComponentOffset = 1 << 12 // 0x1000
+)
+
+func midPoint(p, q fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{
+ X: (p.X + q.X) / 2,
+ Y: (p.Y + q.Y) / 2,
+ }
+}
+
+func parseLoca(src *source, loca table, glyfOffset uint32, indexToLocFormat bool, numGlyphs int) (locations []uint32, err error) {
+ if indexToLocFormat {
+ if loca.length != 4*uint32(numGlyphs+1) {
+ return nil, errInvalidLocaTable
+ }
+ } else {
+ if loca.length != 2*uint32(numGlyphs+1) {
+ return nil, errInvalidLocaTable
+ }
+ }
+
+ locations = make([]uint32, numGlyphs+1)
+ buf, err := src.view(nil, int(loca.offset), int(loca.length))
+ if err != nil {
+ return nil, err
+ }
+
+ if indexToLocFormat {
+ for i := range locations {
+ locations[i] = 1*uint32(u32(buf[4*i:])) + glyfOffset
+ }
+ } else {
+ for i := range locations {
+ locations[i] = 2*uint32(u16(buf[2*i:])) + glyfOffset
+ }
+ }
+ return locations, err
+}
+
+// https://www.microsoft.com/typography/OTSPEC/glyf.htm says that "Each
+// 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) {
+ if len(data) == 0 {
+ return dst, nil
+ }
+ if len(data) < glyfHeaderLen {
+ return nil, errInvalidGlyphData
+ }
+ index := glyfHeaderLen
+
+ numContours, numPoints := int16(u16(data)), 0
+ switch {
+ case numContours == -1:
+ // We have a compound glyph. No-op.
+ case numContours == 0:
+ return dst, nil
+ case numContours > 0:
+ // We have a simple (non-compound) glyph.
+ index += 2 * int(numContours)
+ if index > len(data) {
+ return nil, 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
+ }
+
+ // Skip the hinting instructions.
+ index += 2
+ if index > len(data) {
+ return nil, errInvalidGlyphData
+ }
+ hintsLength := int(u16(data[index-2:]))
+ index += hintsLength
+ if index > len(data) {
+ return nil, errInvalidGlyphData
+ }
+
+ // TODO: support compound glyphs.
+ if numContours < 0 {
+ return nil, errUnsupportedCompoundGlyph
+ }
+
+ // For simple (non-compound) glyphs, the remainder of the glyf data
+ // consists of (flags, x, y) points: the Bézier curve segments. These are
+ // stored in columns (all the flags first, then all the x co-ordinates,
+ // then all the y co-ordinates), not rows, as it compresses better.
+ //
+ // Decoding those points in row order involves two passes. The first pass
+ // determines the indexes (relative to the data slice) of where the flags,
+ // the x co-ordinates and the y co-ordinates each start.
+ flagIndex := int32(index)
+ xIndex, yIndex, ok := findXYIndexes(data, index, numPoints)
+ if !ok {
+ return nil, errInvalidGlyphData
+ }
+
+ // The second pass decodes each (flags, x, y) tuple in row order.
+ g := glyfIter{
+ data: data,
+ flagIndex: flagIndex,
+ xIndex: xIndex,
+ yIndex: yIndex,
+ endIndex: glyfHeaderLen,
+ // The -1 is because the contour-end index in the file format is
+ // inclusive, but Go's slice[:index] semantics are exclusive.
+ prevEnd: -1,
+ numContours: int32(numContours),
+ }
+ for g.nextContour() {
+ for g.nextSegment() {
+ dst = append(dst, g.seg)
+ }
+ }
+ if g.err != nil {
+ return nil, g.err
+ }
+ return dst, nil
+}
+
+func findXYIndexes(data []byte, index, numPoints int) (xIndex, yIndex int32, ok bool) {
+ xDataLen := 0
+ yDataLen := 0
+ for i := 0; ; {
+ if i > numPoints {
+ return 0, 0, false
+ }
+ if i == numPoints {
+ break
+ }
+
+ repeatCount := 1
+ if index >= len(data) {
+ return 0, 0, false
+ }
+ flag := data[index]
+ index++
+ if flag&flagRepeat != 0 {
+ if index >= len(data) {
+ return 0, 0, false
+ }
+ repeatCount += int(data[index])
+ index++
+ }
+
+ xSize := 0
+ if flag&flagXShortVector != 0 {
+ xSize = 1
+ } else if flag&flagThisXIsSame == 0 {
+ xSize = 2
+ }
+ xDataLen += xSize * repeatCount
+
+ ySize := 0
+ if flag&flagYShortVector != 0 {
+ ySize = 1
+ } else if flag&flagThisYIsSame == 0 {
+ ySize = 2
+ }
+ yDataLen += ySize * repeatCount
+
+ i += repeatCount
+ }
+ if index+xDataLen+yDataLen > len(data) {
+ return 0, 0, false
+ }
+ return int32(index), int32(index + xDataLen), true
+}
+
+type glyfIter struct {
+ data []byte
+ err error
+
+ // Various indices into the data slice. See the "Decoding those points in
+ // row order" comment above.
+ flagIndex int32
+ xIndex int32
+ yIndex int32
+
+ // endIndex points to the uint16 that is the inclusive point index of the
+ // current contour's end. prevEnd is the previous contour's end.
+ endIndex int32
+ prevEnd int32
+
+ // c and p count the current contour and point, up to numContours and
+ // numPoints.
+ c, numContours int32
+ p, nPoints int32
+
+ // The next two groups of fields track points and segments. Points are what
+ // the underlying file format provides. Bézier curve segments are what the
+ // rasterizer consumes.
+ //
+ // Points are either on-curve or off-curve. Two consecutive on-curve points
+ // define a linear curve segment between them. N off-curve points between
+ // on-curve points define N quadratic curve segments. The TrueType glyf
+ // format does not use cubic curves. If N is greater than 1, some of these
+ // segment end points are implicit, the midpoint of two off-curve points.
+ // Given the points A, B1, B2, ..., BN, C, where A and C are on-curve and
+ // all the Bs are off-curve, the segments are:
+ //
+ // - A, B1, midpoint(B1, B2)
+ // - midpoint(B1, B2), B2, midpoint(B2, B3)
+ // - midpoint(B2, B3), B3, midpoint(B3, B4)
+ // - ...
+ // - midpoint(BN-1, BN), BN, C
+ //
+ // Note that the sequence of Bs may wrap around from the last point in the
+ // glyf data to the first. A and C may also be the same point (the only
+ // explicit on-curve point), or there may be no explicit on-curve points at
+ // all (but still implicit ones between explicit off-curve points).
+
+ // Points.
+ x, y int16
+ on bool
+ flag uint8
+ repeats uint8
+
+ // Segments.
+ closing bool
+ closed bool
+ firstOnCurveValid bool
+ firstOffCurveValid bool
+ lastOffCurveValid bool
+ firstOnCurve fixed.Point26_6
+ firstOffCurve fixed.Point26_6
+ lastOffCurve fixed.Point26_6
+ seg Segment
+}
+
+func (g *glyfIter) nextContour() (ok bool) {
+ if g.c == g.numContours {
+ return false
+ }
+ g.c++
+
+ end := int32(u16(g.data[g.endIndex:]))
+ g.endIndex += 2
+ if end <= g.prevEnd {
+ g.err = errInvalidGlyphData
+ return false
+ }
+ g.nPoints = end - g.prevEnd
+ g.p = 0
+ g.prevEnd = end
+
+ g.closing = false
+ g.closed = false
+ g.firstOnCurveValid = false
+ g.firstOffCurveValid = false
+ g.lastOffCurveValid = false
+
+ return true
+}
+
+func (g *glyfIter) close() {
+ switch {
+ case !g.firstOffCurveValid && !g.lastOffCurveValid:
+ g.closed = true
+ g.seg = Segment{
+ Op: SegmentOpLineTo,
+ Args: [6]fixed.Int26_6{
+ g.firstOnCurve.X,
+ g.firstOnCurve.Y,
+ },
+ }
+ case !g.firstOffCurveValid && g.lastOffCurveValid:
+ g.closed = true
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ g.lastOffCurve.X,
+ g.lastOffCurve.Y,
+ g.firstOnCurve.X,
+ g.firstOnCurve.Y,
+ },
+ }
+ case g.firstOffCurveValid && !g.lastOffCurveValid:
+ g.closed = true
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ g.firstOffCurve.X,
+ g.firstOffCurve.Y,
+ g.firstOnCurve.X,
+ g.firstOnCurve.Y,
+ },
+ }
+ case g.firstOffCurveValid && g.lastOffCurveValid:
+ mid := midPoint(g.lastOffCurve, g.firstOffCurve)
+ g.lastOffCurveValid = false
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ g.lastOffCurve.X,
+ g.lastOffCurve.Y,
+ mid.X,
+ mid.Y,
+ },
+ }
+ }
+}
+
+func (g *glyfIter) nextSegment() (ok bool) {
+ for !g.closed {
+ if g.closing || !g.nextPoint() {
+ g.closing = true
+ g.close()
+ return true
+ }
+
+ p := fixed.Point26_6{
+ X: fixed.Int26_6(g.x) << 6,
+ Y: fixed.Int26_6(g.y) << 6,
+ }
+
+ if !g.firstOnCurveValid {
+ if g.on {
+ g.firstOnCurve = p
+ g.firstOnCurveValid = true
+ g.seg = Segment{
+ Op: SegmentOpMoveTo,
+ Args: [6]fixed.Int26_6{
+ p.X,
+ p.Y,
+ },
+ }
+ return true
+ } else if !g.firstOffCurveValid {
+ g.firstOffCurve = p
+ g.firstOffCurveValid = true
+ continue
+ } else {
+ midp := midPoint(g.firstOffCurve, p)
+ g.firstOnCurve = midp
+ g.firstOnCurveValid = true
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ g.seg = Segment{
+ Op: SegmentOpMoveTo,
+ Args: [6]fixed.Int26_6{
+ midp.X,
+ midp.Y,
+ },
+ }
+ return true
+ }
+
+ } else if !g.lastOffCurveValid {
+ if !g.on {
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ continue
+ } else {
+ g.seg = Segment{
+ Op: SegmentOpLineTo,
+ Args: [6]fixed.Int26_6{
+ p.X,
+ p.Y,
+ },
+ }
+ return true
+ }
+
+ } else {
+ if !g.on {
+ midp := midPoint(g.lastOffCurve, p)
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ g.lastOffCurve.X,
+ g.lastOffCurve.Y,
+ midp.X,
+ midp.Y,
+ },
+ }
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ return true
+ } else {
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [6]fixed.Int26_6{
+ g.lastOffCurve.X,
+ g.lastOffCurve.Y,
+ p.X,
+ p.Y,
+ },
+ }
+ g.lastOffCurveValid = false
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (g *glyfIter) nextPoint() (ok bool) {
+ if g.p == g.nPoints {
+ return false
+ }
+ g.p++
+
+ if g.repeats > 0 {
+ g.repeats--
+ } else {
+ g.flag = g.data[g.flagIndex]
+ g.flagIndex++
+ if g.flag&flagRepeat != 0 {
+ g.repeats = g.data[g.flagIndex]
+ g.flagIndex++
+ }
+ }
+
+ if g.flag&flagXShortVector != 0 {
+ if g.flag&flagPositiveXShortVector != 0 {
+ g.x += int16(g.data[g.xIndex])
+ } else {
+ g.x -= int16(g.data[g.xIndex])
+ }
+ g.xIndex += 1
+ } else if g.flag&flagThisXIsSame == 0 {
+ g.x += int16(u16(g.data[g.xIndex:]))
+ g.xIndex += 2
+ }
+
+ if g.flag&flagYShortVector != 0 {
+ if g.flag&flagPositiveYShortVector != 0 {
+ g.y += int16(g.data[g.yIndex])
+ } else {
+ g.y -= int16(g.data[g.yIndex])
+ }
+ g.yIndex += 1
+ } else if g.flag&flagThisYIsSame == 0 {
+ g.y += int16(u16(g.data[g.yIndex:]))
+ g.yIndex += 2
+ }
+
+ g.on = g.flag&flagOnCurve != 0
+ return true
+}