From 701d1ab638b23c24877fc41824add66232446676 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 2 Feb 2017 09:32:00 -0500 Subject: Updating server dependancies (#5249) --- vendor/golang.org/x/image/font/sfnt/postscript.go | 865 ++++++++++++++++++++++ vendor/golang.org/x/image/font/sfnt/sfnt.go | 621 ++++++++++++++++ vendor/golang.org/x/image/font/sfnt/sfnt_test.go | 272 +++++++ vendor/golang.org/x/image/font/sfnt/truetype.go | 489 ++++++++++++ 4 files changed, 2247 insertions(+) create mode 100644 vendor/golang.org/x/image/font/sfnt/postscript.go create mode 100644 vendor/golang.org/x/image/font/sfnt/sfnt.go create mode 100644 vendor/golang.org/x/image/font/sfnt/sfnt_test.go create mode 100644 vendor/golang.org/x/image/font/sfnt/truetype.go (limited to 'vendor/golang.org/x/image/font/sfnt') 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 +} -- cgit v1.2.3-1-g7c22