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/cmap.go16
-rw-r--r--vendor/golang.org/x/image/font/sfnt/example_test.go41
-rw-r--r--vendor/golang.org/x/image/font/sfnt/postscript.go507
-rw-r--r--vendor/golang.org/x/image/font/sfnt/proprietary_test.go415
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt.go399
-rw-r--r--vendor/golang.org/x/image/font/sfnt/sfnt_test.go233
-rw-r--r--vendor/golang.org/x/image/font/sfnt/truetype.go78
7 files changed, 1336 insertions, 353 deletions
diff --git a/vendor/golang.org/x/image/font/sfnt/cmap.go b/vendor/golang.org/x/image/font/sfnt/cmap.go
index 42338f1bc..797e9d190 100644
--- a/vendor/golang.org/x/image/font/sfnt/cmap.go
+++ b/vendor/golang.org/x/image/font/sfnt/cmap.go
@@ -5,8 +5,6 @@
package sfnt
import (
- "unicode/utf8"
-
"golang.org/x/text/encoding/charmap"
)
@@ -112,20 +110,12 @@ func (f *Font) makeCachedGlyphIndexFormat0(buf []byte, offset, length uint32) ([
var table [256]byte
copy(table[:], buf[6:])
return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
- // TODO: for this closure to be goroutine-safe, the
- // golang.org/x/text/encoding/charmap API needs to allocate a new
- // Encoder and new []byte buffers, for every call to this closure, even
- // though all we want to do is to encode one rune as one byte. We could
- // possibly add some fields in the Buffer struct to re-use these
- // allocations, but a better solution is to improve the charmap API.
- var dst, src [utf8.UTFMax]byte
- n := utf8.EncodeRune(src[:], r)
- _, _, err = charmap.Macintosh.NewEncoder().Transform(dst[:], src[:n], true)
- if err != nil {
+ x, ok := charmap.Macintosh.EncodeRune(r)
+ if !ok {
// The source rune r is not representable in the Macintosh-Roman encoding.
return 0, nil
}
- return GlyphIndex(table[dst[0]]), nil
+ return GlyphIndex(table[x]), nil
}, nil
}
diff --git a/vendor/golang.org/x/image/font/sfnt/example_test.go b/vendor/golang.org/x/image/font/sfnt/example_test.go
index 17431560f..baddcfec0 100644
--- a/vendor/golang.org/x/image/font/sfnt/example_test.go
+++ b/vendor/golang.org/x/image/font/sfnt/example_test.go
@@ -20,9 +20,9 @@ func ExampleRasterizeGlyph() {
const (
ppem = 32
width = 24
- height = 32
+ height = 36
originX = 0
- originY = 28
+ originY = 32
)
f, err := sfnt.Parse(goregular.TTF)
@@ -30,12 +30,12 @@ func ExampleRasterizeGlyph() {
log.Fatalf("Parse: %v", err)
}
var b sfnt.Buffer
- x, err := f.GlyphIndex(&b, 'G')
+ x, err := f.GlyphIndex(&b, 'Ġ')
if err != nil {
log.Fatalf("GlyphIndex: %v", err)
}
if x == 0 {
- log.Fatalf("GlyphIndex: no glyph index found for the rune 'G'")
+ log.Fatalf("GlyphIndex: no glyph index found for the rune 'Ġ'")
}
segments, err := f.LoadGlyph(&b, x, fixed.I(ppem), nil)
if err != nil {
@@ -50,33 +50,32 @@ func ExampleRasterizeGlyph() {
switch seg.Op {
case sfnt.SegmentOpMoveTo:
r.MoveTo(
- originX+float32(seg.Args[0])/64,
- originY-float32(seg.Args[1])/64,
+ originX+float32(seg.Args[0].X)/64,
+ originY+float32(seg.Args[0].Y)/64,
)
case sfnt.SegmentOpLineTo:
r.LineTo(
- originX+float32(seg.Args[0])/64,
- originY-float32(seg.Args[1])/64,
+ originX+float32(seg.Args[0].X)/64,
+ originY+float32(seg.Args[0].Y)/64,
)
case sfnt.SegmentOpQuadTo:
r.QuadTo(
- originX+float32(seg.Args[0])/64,
- originY-float32(seg.Args[1])/64,
- originX+float32(seg.Args[2])/64,
- originY-float32(seg.Args[3])/64,
+ originX+float32(seg.Args[0].X)/64,
+ originY+float32(seg.Args[0].Y)/64,
+ originX+float32(seg.Args[1].X)/64,
+ originY+float32(seg.Args[1].Y)/64,
)
case sfnt.SegmentOpCubeTo:
r.CubeTo(
- originX+float32(seg.Args[0])/64,
- originY-float32(seg.Args[1])/64,
- originX+float32(seg.Args[2])/64,
- originY-float32(seg.Args[3])/64,
- originX+float32(seg.Args[4])/64,
- originY-float32(seg.Args[5])/64,
+ originX+float32(seg.Args[0].X)/64,
+ originY+float32(seg.Args[0].Y)/64,
+ originX+float32(seg.Args[1].X)/64,
+ originY+float32(seg.Args[1].Y)/64,
+ originX+float32(seg.Args[2].X)/64,
+ originY+float32(seg.Args[2].Y)/64,
)
}
}
- // TODO: call ClosePath? Once overall or once per contour (i.e. MoveTo)?
dst := image.NewAlpha(image.Rect(0, 0, width, height))
r.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
@@ -96,6 +95,10 @@ func ExampleRasterizeGlyph() {
// ........................
// ........................
// ........................
+ // ............888.........
+ // ............888.........
+ // ............888.........
+ // ............+++.........
// ........................
// ..........+++++++++.....
// .......+8888888888888+..
diff --git a/vendor/golang.org/x/image/font/sfnt/postscript.go b/vendor/golang.org/x/image/font/sfnt/postscript.go
index 1a00b827f..7c5db789d 100644
--- a/vendor/golang.org/x/image/font/sfnt/postscript.go
+++ b/vendor/golang.org/x/image/font/sfnt/postscript.go
@@ -80,6 +80,44 @@ func bigEndian(b []byte) uint32 {
panic("unreachable")
}
+// fdSelect holds a CFF font's Font Dict Select data.
+type fdSelect struct {
+ format uint8
+ numRanges uint16
+ offset int32
+}
+
+func (t *fdSelect) lookup(f *Font, b *Buffer, x GlyphIndex) (int, error) {
+ switch t.format {
+ case 0:
+ buf, err := b.view(&f.src, int(t.offset)+int(x), 1)
+ if err != nil {
+ return 0, err
+ }
+ return int(buf[0]), nil
+ case 3:
+ lo, hi := 0, int(t.numRanges)
+ for lo < hi {
+ i := (lo + hi) / 2
+ buf, err := b.view(&f.src, int(t.offset)+3*i, 3+2)
+ if err != nil {
+ return 0, err
+ }
+ // buf holds the range [xlo, xhi).
+ if xlo := GlyphIndex(u16(buf[0:])); x < xlo {
+ hi = i
+ continue
+ }
+ if xhi := GlyphIndex(u16(buf[3:])); xhi <= x {
+ lo = i + 1
+ continue
+ }
+ return int(buf[2]), nil
+ }
+ }
+ return 0, ErrNotFound
+}
+
// cffParser parses the CFF table from an SFNT font.
type cffParser struct {
src *source
@@ -94,14 +132,14 @@ type cffParser struct {
psi psInterpreter
}
-func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
+func (p *cffParser) parse(numGlyphs int32) (ret glyphData, err error) {
// Parse the header.
{
if !p.read(4) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 {
- return nil, nil, nil, errUnsupportedCFFVersion
+ return glyphData{}, errUnsupportedCFFVersion
}
}
@@ -109,15 +147,15 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
{
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The
// Name INDEX in the CFF must contain only one entry".
if count != 1 {
- return nil, nil, nil, errInvalidCFFTable
+ return glyphData{}, errInvalidCFFTable
}
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
p.offset = int(p.locBuf[1])
}
@@ -127,21 +165,21 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
{
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
// 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
// should match the count of the Name INDEX, which is 1.
if count != 1 {
- return nil, nil, nil, errInvalidCFFTable
+ return glyphData{}, errInvalidCFFTable
}
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if !p.read(int(p.locBuf[1] - p.locBuf[0])) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
}
@@ -149,25 +187,25 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
{
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if count != 0 {
// Read the last location. Locations are off by 1 byte. See the
// comment in parseIndexLocations.
if !p.skip(int(count * offSize)) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if !p.read(int(offSize)) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
loc := bigEndian(p.buf) - 1
// Check that locations are in bounds.
if uint32(p.end-p.offset) < loc {
- return nil, nil, nil, errInvalidCFFTable
+ return glyphData{}, errInvalidCFFTable
}
// Skip the index data.
if !p.skip(int(loc)) {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
}
}
@@ -176,80 +214,171 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
{
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
}
if count != 0 {
if count > maxNumSubroutines {
- return nil, nil, nil, errUnsupportedNumberOfSubroutines
+ return glyphData{}, errUnsupportedNumberOfSubroutines
}
- gsubrs = make([]uint32, count+1)
- if !p.parseIndexLocations(gsubrs, count, offSize) {
- return nil, nil, nil, p.err
+ ret.gsubrs = make([]uint32, count+1)
+ if !p.parseIndexLocations(ret.gsubrs, count, offSize) {
+ return glyphData{}, p.err
}
}
}
// Parse the CharStrings INDEX, whose location was found in the Top DICT.
{
- if p.psi.topDict.charStrings <= 0 || int32(p.end-p.base) < p.psi.topDict.charStrings {
- return nil, nil, nil, errInvalidCFFTable
+ if !p.seekFromBase(p.psi.topDict.charStringsOffset) {
+ return glyphData{}, errInvalidCFFTable
}
- p.offset = p.base + int(p.psi.topDict.charStrings)
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return glyphData{}, p.err
+ }
+ if count == 0 || int32(count) != numGlyphs {
+ return glyphData{}, errInvalidCFFTable
+ }
+ ret.locations = make([]uint32, count+1)
+ if !p.parseIndexLocations(ret.locations, count, offSize) {
+ return glyphData{}, p.err
+ }
+ }
+
+ if !p.psi.topDict.isCIDFont {
+ // Parse the Private DICT, whose location was found in the Top DICT.
+ ret.singleSubrs, err = p.parsePrivateDICT(
+ p.psi.topDict.privateDictOffset,
+ p.psi.topDict.privateDictLength,
+ )
+ if err != nil {
+ return glyphData{}, err
+ }
+
+ } else {
+ // Parse the Font Dict Select data, whose location was found in the Top
+ // DICT.
+ ret.fdSelect, err = p.parseFDSelect(p.psi.topDict.fdSelect, numGlyphs)
+ if err != nil {
+ return glyphData{}, err
+ }
+
+ // Parse the Font Dicts. Each one contains its own Private DICT.
+ if !p.seekFromBase(p.psi.topDict.fdArray) {
+ return glyphData{}, errInvalidCFFTable
+ }
+
+ count, offSize, ok := p.parseIndexHeader()
+ if !ok {
+ return glyphData{}, p.err
+ }
+ if count > maxNumFontDicts {
+ return glyphData{}, errUnsupportedNumberOfFontDicts
}
- if count == 0 {
- return nil, nil, nil, errInvalidCFFTable
+
+ fdLocations := make([]uint32, count+1)
+ if !p.parseIndexLocations(fdLocations, count, offSize) {
+ return glyphData{}, p.err
+ }
+
+ privateDicts := make([]struct {
+ offset, length int32
+ }, count)
+
+ for i := range privateDicts {
+ length := fdLocations[i+1] - fdLocations[i]
+ if !p.read(int(length)) {
+ return glyphData{}, errInvalidCFFTable
+ }
+ p.psi.topDict.initialize()
+ if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
+ return glyphData{}, p.err
+ }
+ privateDicts[i].offset = p.psi.topDict.privateDictOffset
+ privateDicts[i].length = p.psi.topDict.privateDictLength
+ }
+
+ ret.multiSubrs = make([][]uint32, count)
+ for i, pd := range privateDicts {
+ ret.multiSubrs[i], err = p.parsePrivateDICT(pd.offset, pd.length)
+ if err != nil {
+ return glyphData{}, err
+ }
+ }
+ }
+
+ return ret, err
+}
+
+// parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section
+// 19 "FDSelect".
+func (p *cffParser) parseFDSelect(offset int32, numGlyphs int32) (ret fdSelect, err error) {
+ if !p.seekFromBase(p.psi.topDict.fdSelect) {
+ return fdSelect{}, errInvalidCFFTable
+ }
+ if !p.read(1) {
+ return fdSelect{}, p.err
+ }
+ ret.format = p.buf[0]
+ switch ret.format {
+ case 0:
+ if p.end-p.offset < int(numGlyphs) {
+ return fdSelect{}, errInvalidCFFTable
}
- locations = make([]uint32, count+1)
- if !p.parseIndexLocations(locations, count, offSize) {
- return nil, nil, nil, p.err
+ ret.offset = int32(p.offset)
+ return ret, nil
+ case 3:
+ if !p.read(2) {
+ return fdSelect{}, p.err
+ }
+ ret.numRanges = u16(p.buf)
+ if p.end-p.offset < 3*int(ret.numRanges)+2 {
+ return fdSelect{}, errInvalidCFFTable
}
+ ret.offset = int32(p.offset)
+ return ret, nil
}
+ return fdSelect{}, errUnsupportedCFFFDSelectTable
+}
- // Parse the Private DICT, whose location was found in the Top DICT.
+func (p *cffParser) parsePrivateDICT(offset, length int32) (subrs []uint32, err error) {
p.psi.privateDict.initialize()
- if p.psi.topDict.privateDictLength != 0 {
- offset := p.psi.topDict.privateDictOffset
- length := p.psi.topDict.privateDictLength
+ if length != 0 {
fullLength := int32(p.end - p.base)
if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 {
- return nil, nil, nil, errInvalidCFFTable
+ return nil, errInvalidCFFTable
}
p.offset = p.base + int(offset)
if !p.read(int(length)) {
- return nil, nil, nil, p.err
+ return nil, p.err
}
if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil {
- return nil, nil, nil, p.err
+ return nil, p.err
}
}
// Parse the Local Subrs [Subroutines] INDEX, whose location was found in
// the Private DICT.
- if p.psi.privateDict.subrs != 0 {
- offset := p.psi.topDict.privateDictOffset + p.psi.privateDict.subrs
- if offset <= 0 || int32(p.end-p.base) < offset {
- return nil, nil, nil, errInvalidCFFTable
+ if p.psi.privateDict.subrsOffset != 0 {
+ if !p.seekFromBase(offset + p.psi.privateDict.subrsOffset) {
+ return nil, errInvalidCFFTable
}
- p.offset = p.base + int(offset)
count, offSize, ok := p.parseIndexHeader()
if !ok {
- return nil, nil, nil, p.err
+ return nil, p.err
}
if count != 0 {
if count > maxNumSubroutines {
- return nil, nil, nil, errUnsupportedNumberOfSubroutines
+ return nil, errUnsupportedNumberOfSubroutines
}
subrs = make([]uint32, count+1)
if !p.parseIndexLocations(subrs, count, offSize) {
- return nil, nil, nil, p.err
+ return nil, p.err
}
}
}
- return locations, gsubrs, subrs, nil
+ return subrs, err
}
// read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
@@ -263,11 +392,12 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
// 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 {
+ if n < 0 || p.end-p.offset < n {
p.err = errInvalidCFFTable
return false
}
p.buf, p.err = p.src.view(p.buf, p.offset, n)
+ // TODO: if p.err == io.EOF, change that to a different error??
p.offset += n
return p.err == nil
}
@@ -281,6 +411,14 @@ func (p *cffParser) skip(n int) (ok bool) {
return true
}
+func (p *cffParser) seekFromBase(offset int32) (ok bool) {
+ if offset < 0 || int32(p.end-p.base) < offset {
+ return false
+ }
+ p.offset = p.base + int(offset)
+ return true
+}
+
func (p *cffParser) parseIndexHeader() (count, offSize int32, ok bool) {
if !p.read(2) {
return 0, 0, false
@@ -367,7 +505,10 @@ const (
// psTopDictData contains fields specific to the Top DICT context.
type psTopDictData struct {
- charStrings int32
+ charStringsOffset int32
+ fdArray int32
+ fdSelect int32
+ isCIDFont bool
privateDictOffset int32
privateDictLength int32
}
@@ -378,7 +519,7 @@ func (d *psTopDictData) initialize() {
// psPrivateDictData contains fields specific to the Private DICT context.
type psPrivateDictData struct {
- subrs int32
+ subrsOffset int32
}
func (d *psPrivateDictData) initialize() {
@@ -388,21 +529,88 @@ func (d *psPrivateDictData) initialize() {
// psType2CharstringsData contains fields specific to the Type 2 Charstrings
// context.
type psType2CharstringsData struct {
- f *Font
- b *Buffer
- x, y int32
- hintBits int32
- seenWidth bool
- ended bool
+ f *Font
+ b *Buffer
+ x int32
+ y int32
+ firstX int32
+ firstY int32
+ hintBits int32
+ seenWidth bool
+ ended bool
+ glyphIndex GlyphIndex
+ // fdSelectIndexPlusOne is the result of the Font Dict Select lookup, plus
+ // one. That plus one lets us use the zero value to denote either unused
+ // (for CFF fonts with a single Font Dict) or lazily evaluated.
+ fdSelectIndexPlusOne int32
}
-func (d *psType2CharstringsData) initialize(f *Font, b *Buffer) {
+func (d *psType2CharstringsData) initialize(f *Font, b *Buffer, glyphIndex GlyphIndex) {
*d = psType2CharstringsData{
- f: f,
- b: b,
+ f: f,
+ b: b,
+ glyphIndex: glyphIndex,
+ }
+}
+
+func (d *psType2CharstringsData) closePath() {
+ if d.x != d.firstX || d.y != d.firstY {
+ d.b.segments = append(d.b.segments, Segment{
+ Op: SegmentOpLineTo,
+ Args: [3]fixed.Point26_6{{
+ X: fixed.Int26_6(d.firstX),
+ Y: fixed.Int26_6(d.firstY),
+ }},
+ })
}
}
+func (d *psType2CharstringsData) moveTo(dx, dy int32) {
+ d.closePath()
+ d.x += dx
+ d.y += dy
+ d.b.segments = append(d.b.segments, Segment{
+ Op: SegmentOpMoveTo,
+ Args: [3]fixed.Point26_6{{
+ X: fixed.Int26_6(d.x),
+ Y: fixed.Int26_6(d.y),
+ }},
+ })
+ d.firstX = d.x
+ d.firstY = d.y
+}
+
+func (d *psType2CharstringsData) lineTo(dx, dy int32) {
+ d.x += dx
+ d.y += dy
+ d.b.segments = append(d.b.segments, Segment{
+ Op: SegmentOpLineTo,
+ Args: [3]fixed.Point26_6{{
+ X: fixed.Int26_6(d.x),
+ Y: fixed.Int26_6(d.y),
+ }},
+ })
+}
+
+func (d *psType2CharstringsData) cubeTo(dxa, dya, dxb, dyb, dxc, dyc int32) {
+ d.x += dxa
+ d.y += dya
+ xa := fixed.Int26_6(d.x)
+ ya := fixed.Int26_6(d.y)
+ d.x += dxb
+ d.y += dyb
+ xb := fixed.Int26_6(d.x)
+ yb := fixed.Int26_6(d.y)
+ d.x += dxc
+ d.y += dyc
+ xc := fixed.Int26_6(d.x)
+ yc := fixed.Int26_6(d.y)
+ d.b.segments = append(d.b.segments, Segment{
+ Op: SegmentOpCubeTo,
+ Args: [3]fixed.Point26_6{{X: xa, Y: ya}, {X: xb, Y: yb}, {X: xc, Y: yc}},
+ })
+}
+
// psInterpreter is a PostScript interpreter.
type psInterpreter struct {
ctx psContext
@@ -513,7 +721,7 @@ func (p *psInterpreter) parseNumber() (hasResult bool, err error) {
number, hasResult = int32(int16(u16(p.instructions[1:]))), true
p.instructions = p.instructions[3:]
- case b == 29 && p.ctx == psContextTopDict:
+ case b == 29 && p.ctx != psContextType2Charstring:
if len(p.instructions) < 5 {
return true, errInvalidCFFTable
}
@@ -651,7 +859,7 @@ var psOperators = [...][2][]psOperator{
15: {+1, "charset", nil},
16: {+1, "Encoding", nil},
17: {+1, "CharStrings", func(p *psInterpreter) error {
- p.topDict.charStrings = p.argStack.a[p.argStack.top-1]
+ p.topDict.charStringsOffset = p.argStack.a[p.argStack.top-1]
return nil
}},
18: {+2, "Private", func(p *psInterpreter) error {
@@ -674,14 +882,23 @@ var psOperators = [...][2][]psOperator{
21: {+1, "PostScript", nil},
22: {+1, "BaseFontName", nil},
23: {-2, "BaseFontBlend", nil},
- 30: {+3, "ROS", nil},
+ 30: {+3, "ROS", func(p *psInterpreter) error {
+ p.topDict.isCIDFont = true
+ return nil
+ }},
31: {+1, "CIDFontVersion", nil},
32: {+1, "CIDFontRevision", nil},
33: {+1, "CIDFontType", nil},
34: {+1, "CIDCount", nil},
35: {+1, "UIDBase", nil},
- 36: {+1, "FDArray", nil},
- 37: {+1, "FDSelect", nil},
+ 36: {+1, "FDArray", func(p *psInterpreter) error {
+ p.topDict.fdArray = p.argStack.a[p.argStack.top-1]
+ return nil
+ }},
+ 37: {+1, "FDSelect", func(p *psInterpreter) error {
+ p.topDict.fdSelect = p.argStack.a[p.argStack.top-1]
+ return nil
+ }},
38: {+1, "FontName", nil},
}},
@@ -696,7 +913,7 @@ var psOperators = [...][2][]psOperator{
10: {+1, "StdHW", nil},
11: {+1, "StdVW", nil},
19: {+1, "Subrs", func(p *psInterpreter) error {
- p.privateDict.subrs = p.argStack.a[p.argStack.top-1]
+ p.privateDict.subrsOffset = p.argStack.a[p.argStack.top-1]
return nil
}},
20: {+1, "defaultWidthX", nil},
@@ -762,7 +979,8 @@ var psOperators = [...][2][]psOperator{
const escapeByte = 12
// t2CReadWidth reads the optional width adjustment. If present, it is on the
-// bottom of the stack.
+// bottom of the arg stack. nArgs is the expected number of arguments on the
+// stack. A negative nArgs means a multiple of 2.
//
// 5177.Type2.pdf page 16 Note 4 says: "The first stack-clearing operator,
// which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask,
@@ -773,19 +991,12 @@ func t2CReadWidth(p *psInterpreter, nArgs int32) {
return
}
p.type2Charstrings.seenWidth = true
- switch nArgs {
- case 0:
- if p.argStack.top != 1 {
- return
- }
- case 1:
- if p.argStack.top <= 1 {
- return
- }
- default:
- if p.argStack.top%nArgs != 1 {
+ if nArgs >= 0 {
+ if p.argStack.top != nArgs+1 {
return
}
+ } else if p.argStack.top&1 == 0 {
+ return
}
// When parsing a standalone CFF, we'd save the value of p.argStack.a[0]
// here as it defines the glyph's width (horizontal advance). Specifically,
@@ -803,7 +1014,7 @@ func t2CReadWidth(p *psInterpreter, nArgs int32) {
}
func t2CStem(p *psInterpreter) error {
- t2CReadWidth(p, 2)
+ t2CReadWidth(p, -1)
if p.argStack.top%2 != 0 {
return errInvalidCFFTable
}
@@ -818,8 +1029,27 @@ func t2CStem(p *psInterpreter) error {
}
func t2CMask(p *psInterpreter) error {
+ // 5176.CFF.pdf section 4.3 "Hint Operators" says that "If hstem and vstem
+ // hints are both declared at the beginning of a charstring, and this
+ // sequence is followed directly by the hintmask or cntrmask operators, the
+ // vstem hint operator need not be included."
+ //
+ // What we implement here is more permissive (but the same as what the
+ // FreeType implementation does, and simpler than tracking the previous
+ // operator and other hinting state): if a hintmask is given any arguments
+ // (i.e. the argStack is non-empty), we run an implicit vstem operator.
+ //
+ // Note that the vstem operator consumes from p.argStack, but the hintmask
+ // or cntrmask operators consume from p.instructions.
+ if p.argStack.top != 0 {
+ if err := t2CStem(p); err != nil {
+ return err
+ }
+ } else if !p.type2Charstrings.seenWidth {
+ p.type2Charstrings.seenWidth = true
+ }
+
hintBytes := (p.type2Charstrings.hintBits + 7) / 8
- t2CReadWidth(p, hintBytes)
if len(p.instructions) < int(hintBytes) {
return errInvalidCFFTable
}
@@ -827,86 +1057,30 @@ func t2CMask(p *psInterpreter) error {
return nil
}
-func t2CAppendMoveto(p *psInterpreter) {
- p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{
- Op: SegmentOpMoveTo,
- Args: [6]fixed.Int26_6{
- 0: fixed.Int26_6(p.type2Charstrings.x),
- 1: fixed.Int26_6(p.type2Charstrings.y),
- },
- })
-}
-
-func t2CAppendLineto(p *psInterpreter) {
- p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{
- Op: SegmentOpLineTo,
- Args: [6]fixed.Int26_6{
- 0: fixed.Int26_6(p.type2Charstrings.x),
- 1: fixed.Int26_6(p.type2Charstrings.y),
- },
- })
-}
-
-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.b.segments = append(p.type2Charstrings.b.segments, Segment{
- Op: SegmentOpCubeTo,
- Args: [6]fixed.Int26_6{
- 0: fixed.Int26_6(xa),
- 1: fixed.Int26_6(ya),
- 2: fixed.Int26_6(xb),
- 3: fixed.Int26_6(yb),
- 4: fixed.Int26_6(xc),
- 5: fixed.Int26_6(yc),
- },
- })
-}
-
func t2CHmoveto(p *psInterpreter) error {
t2CReadWidth(p, 1)
- if p.argStack.top < 1 {
+ if p.argStack.top != 1 {
return errInvalidCFFTable
}
- for i := int32(0); i < p.argStack.top; i++ {
- p.type2Charstrings.x += p.argStack.a[i]
- }
- t2CAppendMoveto(p)
+ p.type2Charstrings.moveTo(p.argStack.a[0], 0)
return nil
}
func t2CVmoveto(p *psInterpreter) error {
t2CReadWidth(p, 1)
- if p.argStack.top < 1 {
+ if p.argStack.top != 1 {
return errInvalidCFFTable
}
- for i := int32(0); i < p.argStack.top; i++ {
- p.type2Charstrings.y += p.argStack.a[i]
- }
- t2CAppendMoveto(p)
+ p.type2Charstrings.moveTo(0, p.argStack.a[0])
return nil
}
func t2CRmoveto(p *psInterpreter) error {
t2CReadWidth(p, 2)
- if p.argStack.top < 2 || p.argStack.top%2 != 0 {
+ if p.argStack.top != 2 {
return errInvalidCFFTable
}
- for i := int32(0); i < p.argStack.top; i += 2 {
- p.type2Charstrings.x += p.argStack.a[i+0]
- p.type2Charstrings.y += p.argStack.a[i+1]
- }
- t2CAppendMoveto(p)
+ p.type2Charstrings.moveTo(p.argStack.a[0], p.argStack.a[1])
return nil
}
@@ -918,12 +1092,11 @@ func t2CLineto(p *psInterpreter, vertical bool) error {
return errInvalidCFFTable
}
for i := int32(0); i < p.argStack.top; i, vertical = i+1, !vertical {
+ dx, dy := p.argStack.a[i], int32(0)
if vertical {
- p.type2Charstrings.y += p.argStack.a[i]
- } else {
- p.type2Charstrings.x += p.argStack.a[i]
+ dx, dy = dy, dx
}
- t2CAppendLineto(p)
+ p.type2Charstrings.lineTo(dx, dy)
}
return nil
}
@@ -933,9 +1106,7 @@ func t2CRlineto(p *psInterpreter) error {
return errInvalidCFFTable
}
for i := int32(0); i < p.argStack.top; i += 2 {
- p.type2Charstrings.x += p.argStack.a[i+0]
- p.type2Charstrings.y += p.argStack.a[i+1]
- t2CAppendLineto(p)
+ p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
}
return nil
}
@@ -954,7 +1125,7 @@ func t2CRcurveline(p *psInterpreter) error {
}
i := int32(0)
for iMax := p.argStack.top - 2; i < iMax; i += 6 {
- t2CAppendCubeto(p,
+ p.type2Charstrings.cubeTo(
p.argStack.a[i+0],
p.argStack.a[i+1],
p.argStack.a[i+2],
@@ -963,9 +1134,7 @@ func t2CRcurveline(p *psInterpreter) error {
p.argStack.a[i+5],
)
}
- p.type2Charstrings.x += p.argStack.a[i+0]
- p.type2Charstrings.y += p.argStack.a[i+1]
- t2CAppendLineto(p)
+ p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
return nil
}
@@ -975,11 +1144,9 @@ func t2CRlinecurve(p *psInterpreter) error {
}
i := int32(0)
for iMax := p.argStack.top - 6; i < iMax; i += 2 {
- p.type2Charstrings.x += p.argStack.a[i+0]
- p.type2Charstrings.y += p.argStack.a[i+1]
- t2CAppendLineto(p)
+ p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
}
- t2CAppendCubeto(p,
+ p.type2Charstrings.cubeTo(
p.argStack.a[i+0],
p.argStack.a[i+1],
p.argStack.a[i+2],
@@ -1083,7 +1250,7 @@ func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32)
dxc, dyc = dyc, dxc
}
- t2CAppendCubeto(p, dxa, dya, dxb, dyb, dxc, dyc)
+ p.type2Charstrings.cubeTo(dxa, dya, dxb, dyb, dxc, dyc)
return i
}
@@ -1092,7 +1259,7 @@ func t2CRrcurveto(p *psInterpreter) error {
return errInvalidCFFTable
}
for i := int32(0); i != p.argStack.top; i += 6 {
- t2CAppendCubeto(p,
+ p.type2Charstrings.cubeTo(
p.argStack.a[i+0],
p.argStack.a[i+1],
p.argStack.a[i+2],
@@ -1116,8 +1283,29 @@ func subrBias(numSubroutines int) int32 {
return 32768
}
-func t2CCallgsubr(p *psInterpreter) error { return t2CCall(p, p.type2Charstrings.f.cached.gsubrs) }
-func t2CCallsubr(p *psInterpreter) error { return t2CCall(p, p.type2Charstrings.f.cached.subrs) }
+func t2CCallgsubr(p *psInterpreter) error {
+ return t2CCall(p, p.type2Charstrings.f.cached.glyphData.gsubrs)
+}
+
+func t2CCallsubr(p *psInterpreter) error {
+ t := &p.type2Charstrings
+ d := &t.f.cached.glyphData
+ subrs := d.singleSubrs
+ if d.multiSubrs != nil {
+ if t.fdSelectIndexPlusOne == 0 {
+ index, err := d.fdSelect.lookup(t.f, t.b, t.glyphIndex)
+ if err != nil {
+ return err
+ }
+ if index < 0 || len(d.multiSubrs) <= index {
+ return errInvalidCFFTable
+ }
+ t.fdSelectIndexPlusOne = int32(index + 1)
+ }
+ subrs = d.multiSubrs[t.fdSelectIndexPlusOne-1]
+ }
+ return t2CCall(p, subrs)
+}
func t2CCall(p *psInterpreter, subrs []uint32) error {
if p.callStack.top == psCallStackSize || len(subrs) == 0 {
@@ -1181,6 +1369,7 @@ func t2CEndchar(p *psInterpreter) error {
}
return errInvalidCFFTable
}
+ p.type2Charstrings.closePath()
p.type2Charstrings.ended = true
return nil
}
diff --git a/vendor/golang.org/x/image/font/sfnt/proprietary_test.go b/vendor/golang.org/x/image/font/sfnt/proprietary_test.go
index ccf013d1d..2df6f6ce6 100644
--- a/vendor/golang.org/x/image/font/sfnt/proprietary_test.go
+++ b/vendor/golang.org/x/image/font/sfnt/proprietary_test.go
@@ -80,7 +80,7 @@ var (
)
func TestProprietaryAdobeSourceCodeProOTF(t *testing.T) {
- testProprietary(t, "adobe", "SourceCodePro-Regular.otf", 1500, 34)
+ testProprietary(t, "adobe", "SourceCodePro-Regular.otf", 1500, -1)
}
func TestProprietaryAdobeSourceCodeProTTF(t *testing.T) {
@@ -88,11 +88,11 @@ func TestProprietaryAdobeSourceCodeProTTF(t *testing.T) {
}
func TestProprietaryAdobeSourceHanSansSC(t *testing.T) {
- testProprietary(t, "adobe", "SourceHanSansSC-Regular.otf", 65535, 2)
+ testProprietary(t, "adobe", "SourceHanSansSC-Regular.otf", 65535, -1)
}
func TestProprietaryAdobeSourceSansProOTF(t *testing.T) {
- testProprietary(t, "adobe", "SourceSansPro-Regular.otf", 1800, 34)
+ testProprietary(t, "adobe", "SourceSansPro-Regular.otf", 1800, -1)
}
func TestProprietaryAdobeSourceSansProTTF(t *testing.T) {
@@ -111,12 +111,36 @@ func TestProprietaryAppleGeezaPro1(t *testing.T) {
testProprietary(t, "apple", "GeezaPro.ttc?1", 1700, -1)
}
+func TestProprietaryAppleHelvetica0(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?0", 2100, -1)
+}
+
+func TestProprietaryAppleHelvetica1(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?1", 2100, -1)
+}
+
+func TestProprietaryAppleHelvetica2(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?2", 2100, -1)
+}
+
+func TestProprietaryAppleHelvetica3(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?3", 2100, -1)
+}
+
+func TestProprietaryAppleHelvetica4(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?4", 1300, -1)
+}
+
+func TestProprietaryAppleHelvetica5(t *testing.T) {
+ testProprietary(t, "apple", "Helvetica.dfont?5", 1300, -1)
+}
+
func TestProprietaryAppleHiragino0(t *testing.T) {
- testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?0", 9000, 6)
+ testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?0", 9000, -1)
}
func TestProprietaryAppleHiragino1(t *testing.T) {
- testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?1", 9000, 6)
+ testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?1", 9000, -1)
}
func TestProprietaryMicrosoftArial(t *testing.T) {
@@ -302,14 +326,26 @@ kernLoop:
continue
}
}
+
+ for x, want := range proprietaryFDSelectTestCases[qualifiedFilename] {
+ got, err := f.cached.glyphData.fdSelect.lookup(f, &buf, x)
+ if err != nil {
+ t.Errorf("fdSelect.lookup(%d): %v", x, err)
+ continue
+ }
+ if got != want {
+ t.Errorf("fdSelect.lookup(%d): got %d, want %d", x, got, want)
+ continue
+ }
+ }
}
// proprietaryNumFonts holds the expected number of fonts in each collection,
// or 1 for a single font. It is not necessarily an exhaustive list of all
// proprietary fonts tested.
var proprietaryNumFonts = map[string]int{
+ "apple/Helvetica.dfont?0": 6,
"apple/ヒラギノ角ゴシック W0.ttc?0": 2,
- "apple/ヒラギノ角ゴシック W0.ttc?1": 2,
"microsoft/Arial.ttf?0": 1,
}
@@ -330,6 +366,12 @@ var proprietaryVersions = map[string]string{
"apple/Apple Symbols.ttf": "12.0d3e10",
"apple/GeezaPro.ttc?0": "12.0d1e3",
"apple/GeezaPro.ttc?1": "12.0d1e3",
+ "apple/Helvetica.dfont?0": "12.0d1e3",
+ "apple/Helvetica.dfont?1": "12.0d1e3",
+ "apple/Helvetica.dfont?2": "12.0d1e3",
+ "apple/Helvetica.dfont?3": "12.0d1e3",
+ "apple/Helvetica.dfont?4": "12.0d1e3",
+ "apple/Helvetica.dfont?5": "12.0d1e3",
"apple/ヒラギノ角ゴシック W0.ttc?0": "11.0d7e1",
"apple/ヒラギノ角ゴシック W0.ttc?1": "11.0d7e1",
@@ -352,6 +394,12 @@ var proprietaryFullNames = map[string]string{
"apple/Apple Symbols.ttf": "Apple Symbols",
"apple/GeezaPro.ttc?0": "Geeza Pro Regular",
"apple/GeezaPro.ttc?1": "Geeza Pro Bold",
+ "apple/Helvetica.dfont?0": "Helvetica",
+ "apple/Helvetica.dfont?1": "Helvetica Bold",
+ "apple/Helvetica.dfont?2": "Helvetica Oblique",
+ "apple/Helvetica.dfont?3": "Helvetica Bold Oblique",
+ "apple/Helvetica.dfont?4": "Helvetica Light",
+ "apple/Helvetica.dfont?5": "Helvetica Light Oblique",
"apple/ヒラギノ角ゴシック W0.ttc?0": "Hiragino Sans W0",
"apple/ヒラギノ角ゴシック W0.ttc?1": ".Hiragino Kaku Gothic Interface W0",
@@ -410,6 +458,17 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
'\u2030': 1728, // U+2030 PER MILLE SIGN
},
+ "apple/Helvetica.dfont?0": {
+ '\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
+ '\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
+ '\u0401': 473, // U+0401 CYRILLIC CAPITAL LETTER IO
+ '\u200d': 611, // U+200D ZERO WIDTH JOINER
+ '\u20ab': 1743, // U+20AB DONG SIGN
+ '\u2229': 0, // U+2229 INTERSECTION
+ '\u04e9': 1208, // U+04E9 CYRILLIC SMALL LETTER BARRED O
+ '\U0001f100': 0, // U+0001F100 DIGIT ZERO FULL STOP
+ },
+
"microsoft/Arial.ttf": {
'\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
'\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
@@ -442,13 +501,88 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
// proprietaryGlyphTestCases hold a sample of each font's glyph vectors. The
// numerical values can be verified by running the ttx tool, remembering that:
-// - for PostScript glyphs, ttx coordinates are relative, and hstem / vstem
-// operators are hinting-related and can be ignored.
+// - for PostScript glyphs, ttx coordinates are relative.
// - for TrueType glyphs, ttx coordinates are absolute, and consecutive
// off-curve points implies an on-curve point at the midpoint.
var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
+ "adobe/SourceHanSansSC-Regular.otf": {
+ '!': {
+ // -312 123 callsubr # 123 + bias = 230
+ // : # Arg stack is [-312].
+ // : -13 140 -119 -21 return
+ // : # Arg stack is [-312 -13 140 -119 -21].
+ // 120 callsubr # 120 + bias = 227
+ // : # Arg stack is [-312 -13 140 -119 -21].
+ // : hstemhm
+ // : 95 132 -103 75 return
+ // : # Arg stack is [95 132 -103 75].
+ // hintmask 01010000
+ // 8 callsubr # 8 + bias = 115
+ // : # Arg stack is [].
+ // : 130 221 rmoveto
+ moveTo(130, 221),
+ // : 63 hlineto
+ lineTo(193, 221),
+ // : 12 424 3 -735 callgsubr # -735 + bias = 396
+ // : : # Arg stack is [12 424 3].
+ // : : 104 rlineto
+ lineTo(205, 645),
+ lineTo(208, 749),
+ // : : -93 hlineto
+ lineTo(115, 749),
+ // : : 3 -104 rlineto
+ lineTo(118, 645),
+ // : : return
+ // : : # Arg stack is [].
+ // : return
+ // : # Arg stack is [].
+ // hintmask 01100000
+ // 106 callsubr # 106 + bias = 213
+ // : # Arg stack is [].
+ // : 43 -658 rmoveto
+ lineTo(130, 221),
+ moveTo(161, -13),
+ // : 37 29 28 41 return
+ // : # Arg stack is [37 29 28 41].
+ // hvcurveto
+ cubeTo(198, -13, 227, 15, 227, 56),
+ // hintmask 10100000
+ // 41 -29 30 -37 -36 -30 -30 -41 vhcurveto
+ cubeTo(227, 97, 198, 127, 161, 127),
+ cubeTo(125, 127, 95, 97, 95, 56),
+ // hintmask 01100000
+ // 111 callsubr # 111 + bias = 218
+ // : # Arg stack is [].
+ // : -41 30 -28 36 vhcurveto
+ cubeTo(95, 15, 125, -13, 161, -13),
+ // : endchar
+ },
+
+ '二': { // U+4E8C <CJK Ideograph> "two; twice"
+ // 23 81 510 79 hstem
+ // 60 881 cntrmask 11000000
+ // 144 693 rmoveto
+ moveTo(144, 693),
+ // -79 713 79 vlineto
+ lineTo(144, 614),
+ lineTo(857, 614),
+ lineTo(857, 693),
+ // -797 -589 rmoveto
+ lineTo(144, 693),
+ moveTo(60, 104),
+ // -81 881 81 vlineto
+ lineTo(60, 23),
+ lineTo(941, 23),
+ lineTo(941, 104),
+ // endchar
+ lineTo(60, 104),
+ },
+ },
+
"adobe/SourceSansPro-Regular.otf": {
',': {
+ // -309 -1 115 hstem
+ // 137 61 vstem
// 67 -170 rmoveto
moveTo(67, -170),
// 81 34 50 67 86 vvcurveto
@@ -461,9 +595,12 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// 1 -53 -34 -44 -57 -25 rrcurveto
cubeTo(138, -53, 104, -97, 47, -122),
// endchar
+ lineTo(67, -170),
},
'Q': {
+ // 106 -165 70 87 65 538 73 hstem
+ // 52 86 388 87 vstem
// 332 57 rmoveto
moveTo(332, 57),
// -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto
@@ -492,6 +629,78 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// endchar
},
+ 'ĩ': { // U+0129 LATIN SMALL LETTER I WITH TILDE
+ // 92 callgsubr # 92 + bias = 199.
+ // : # Arg stack is [].
+ // : -312 21 85 callgsubr # 85 + bias = 192.
+ // : : # Arg stack is [-312 21].
+ // : : -21 486 -20 return
+ // : : # Arg stack is [-312 21 -21 486 -20].
+ // : return
+ // : # Arg stack is [-312 21 -21 486 -20].
+ // 111 45 callsubr # 45 + bias = 152
+ // : # Arg stack is [-312 21 -21 486 -20 111].
+ // : 60 24 60 -9 216 callgsubr # 216 + bias = 323
+ // : : # Arg stack is [-312 21 -21 486 -20 111 60 24 60 -9].
+ // : : -20 24 -20 hstemhm
+ // : : return
+ // : : # Arg stack is [].
+ // : return
+ // : # Arg stack is [].
+ // -50 55 77 82 77 55 hintmask 1101000100000000
+ // 134 callsubr # 134 + bias = 241
+ // : # Arg stack is [].
+ // : 82 hmoveto
+ moveTo(82, 0),
+ // : 82 127 callsubr # 127 + bias = 234
+ // : : # Arg stack is [82].
+ // : : 486 -82 hlineto
+ lineTo(164, 0),
+ lineTo(164, 486),
+ lineTo(82, 486),
+ // : : return
+ // : : # Arg stack is [].
+ // : return
+ // : # Arg stack is [].
+ // hintmask 1110100110000000
+ // 113 91 15 callgsubr # 15 + bias = 122
+ // : # Arg stack is [113 91].
+ // : rmoveto
+ lineTo(82, 0),
+ moveTo(195, 577),
+ // : 69 29 58 77 3 hvcurveto
+ cubeTo(264, 577, 293, 635, 296, 712),
+ // : return
+ // : # Arg stack is [].
+ // hintmask 1110010110000000
+ // -58 callsubr # -58 + bias = 49
+ // : # Arg stack is [].
+ // : -55 4 rlineto
+ lineTo(241, 716),
+ // : -46 -3 -14 -33 -29 -47 -26 84 -71 hhcurveto
+ cubeTo(238, 670, 224, 637, 195, 637),
+ cubeTo(148, 637, 122, 721, 51, 721),
+ // : return
+ // : # Arg stack is [].
+ // hintmask 1101001100000000
+ // -70 callgsubr # -70 + bias = 37
+ // : # Arg stack is [].
+ // : -69 -29 -58 -78 -3 hvcurveto
+ cubeTo(-18, 721, -47, 663, -50, 585),
+ // : 55 -3 rlineto
+ lineTo(5, 582),
+ // : 47 3 14 32 30 hhcurveto
+ cubeTo(8, 629, 22, 661, 52, 661),
+ // : return
+ // : # Arg stack is [].
+ // hintmask 1110100110000000
+ // 51 callsubr # 51 + bias = 158
+ // : # Arg stack is [].
+ // : 46 26 -84 71 hhcurveto
+ cubeTo(98, 661, 124, 577, 195, 577),
+ // : endchar
+ },
+
'ī': { // U+012B LATIN SMALL LETTER I WITH MACRON
// 92 callgsubr # 92 + bias = 199.
// : # Arg stack is [].
@@ -524,12 +733,14 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// -92 115 -60 callgsubr # -60 + bias = 47
// : # Arg stack is [-92 115].
// : rmoveto
+ lineTo(82, 0),
moveTo(-10, 601),
// : 266 57 -266 hlineto
lineTo(256, 601),
lineTo(256, 658),
lineTo(-10, 658),
// : endchar
+ lineTo(-10, 601),
},
'ĭ': { // U+012D LATIN SMALL LETTER I WITH BREVE
@@ -560,6 +771,7 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// 42 85 143 callsubr # 143 + bias = 250
// : # Arg stack is [42 85].
// : rmoveto
+ lineTo(82, 0),
moveTo(124, 571),
// : -84 callsubr # -84 + bias = 23
// : : # Arg stack is [].
@@ -587,6 +799,8 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
},
'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA
+ // -43 21 -21 572 84 hstem
+ // 0 515 vstem
// 0 vmoveto
moveTo(0, 0),
// 85 hlineto
@@ -606,6 +820,113 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// -96 hlineto
lineTo(209, 656),
// endchar
+ lineTo(0, 0),
+ },
+
+ 'Ḫ': { // U+1E2A LATIN CAPITAL LETTER H WITH BREVE BELOW
+ // 94 -231 55 197 157 callgsubr # 157 + bias = 264
+ // : # Arg stack is [94 -231 55 197].
+ // : -21 309 72 return
+ // : # Arg stack is [94 -231 55 197 -21 309 72].
+ // 275 254 callgsubr # 254 + bias = 361
+ // : # Arg stack is [94 -231 55 197 -21 309 72 275].
+ // : -20 hstemhm
+ // : 90 83 return
+ // : # Arg stack is [90 83].
+ // -4 352 callsubr # 352 + bias = 459
+ // : # Arg stack is [90 83 -4].
+ // : 51 210 51 return
+ // : # Arg stack is [90 83 -4 51 210 51].
+ // -3 84 hintmask 11111001
+ // 90 -40 callsubr # -40 + bias = 67
+ // : # Arg stack is [90].
+ // : -27 callgsubr # -27 + bias = 80
+ // : : # Arg stack is [90].
+ // : : hmoveto
+ moveTo(90, 0),
+ // : : 83 309 305 -309 84 return
+ // : : # Arg stack is [83 309 305 -309 84].
+ // : -41 callgsubr # -41 + bias = 66
+ // : : # Arg stack is [83 309 305 -309 84].
+ // : : 656 -84 -275 -305 275 -83 return
+ // : : # Arg stack is [83 309 305 -309 84 656 -84 -275 -305 275 -83].
+ // : hlineto
+ lineTo(173, 0),
+ lineTo(173, 309),
+ lineTo(478, 309),
+ lineTo(478, 0),
+ lineTo(562, 0),
+ lineTo(562, 656),
+ lineTo(478, 656),
+ lineTo(478, 381),
+ lineTo(173, 381),
+ lineTo(173, 656),
+ lineTo(90, 656),
+ // : return
+ // : # Arg stack is [].
+ // hintmask 11110110
+ // 235 -887 143 callsubr # 143 + bias = 250
+ // : # Arg stack is [235 -887].
+ // : rmoveto
+ lineTo(90, 0),
+ moveTo(325, -231),
+ // : -84 callsubr # -84 + bias = 23
+ // : : # Arg stack is [].
+ // : : 107 44 77 74 5 hvcurveto
+ cubeTo(432, -231, 476, -154, 481, -80),
+ // : : -51 8 rlineto
+ lineTo(430, -72),
+ // : : -51 -8 -32 -53 -65 hhcurveto
+ cubeTo(422, -123, 390, -176, 325, -176),
+ // : : -65 -32 53 51 -8 hvcurveto
+ cubeTo(260, -176, 228, -123, 220, -72),
+ // : : -51 -22 callsubr # -22 + bias = 85
+ // : : : # Arg stack is [-51].
+ // : : : -8 rlineto
+ lineTo(169, -80),
+ // : : : -74 5 44 -77 107 hhcurveto
+ cubeTo(174, -154, 218, -231, 325, -231),
+ // : : : return
+ // : : : # Arg stack is [].
+ // : : return
+ // : : # Arg stack is [].
+ // : return
+ // : # Arg stack is [].
+ // endchar
+ },
+ },
+
+ "apple/Helvetica.dfont?0": {
+ 'i': {
+ // - contour #0
+ moveTo(132, 1066),
+ lineTo(315, 1066),
+ lineTo(315, 0),
+ lineTo(132, 0),
+ lineTo(132, 1066),
+ // - contour #1
+ moveTo(132, 1469),
+ lineTo(315, 1469),
+ lineTo(315, 1265),
+ lineTo(132, 1265),
+ lineTo(132, 1469),
+ },
+ },
+
+ "apple/Helvetica.dfont?1": {
+ 'i': {
+ // - contour #0
+ moveTo(426, 1220),
+ lineTo(137, 1220),
+ lineTo(137, 1483),
+ lineTo(426, 1483),
+ lineTo(426, 1220),
+ // - contour #1
+ moveTo(137, 1090),
+ lineTo(426, 1090),
+ lineTo(426, 0),
+ lineTo(137, 0),
+ lineTo(137, 1090),
},
},
@@ -819,3 +1140,81 @@ var proprietaryKernTestCases = map[string][]kernTestCase{
{2048, font.HintingNone, [2]rune{'\uf041', '\uf042'}, 0},
},
}
+
+// proprietaryFDSelectTestCases hold a sample of each font's Font Dict Select
+// (FDSelect) map. The numerical values can be verified by grepping the output
+// of the ttx tool:
+//
+// grep CharString.*fdSelectIndex SourceHanSansSC-Regular.ttx
+//
+// will print lines like this:
+//
+// <CharString name="cid00100" fdSelectIndex="15">
+// <CharString name="cid00101" fdSelectIndex="15">
+// <CharString name="cid00102" fdSelectIndex="3">
+// <CharString name="cid00103" fdSelectIndex="15">
+//
+// As for what the values like 3 or 15 actually mean, grepping that ttx file
+// for "FontName" gives this list:
+//
+// 0: <FontName value="SourceHanSansSC-Regular-Alphabetic"/>
+// 1: <FontName value="SourceHanSansSC-Regular-AlphabeticDigits"/>
+// 2: <FontName value="SourceHanSansSC-Regular-Bopomofo"/>
+// 3: <FontName value="SourceHanSansSC-Regular-Dingbats"/>
+// 4: <FontName value="SourceHanSansSC-Regular-DingbatsDigits"/>
+// 5: <FontName value="SourceHanSansSC-Regular-Generic"/>
+// 6: <FontName value="SourceHanSansSC-Regular-HDingbats"/>
+// 7: <FontName value="SourceHanSansSC-Regular-HHangul"/>
+// 8: <FontName value="SourceHanSansSC-Regular-HKana"/>
+// 9: <FontName value="SourceHanSansSC-Regular-HWidth"/>
+// 10: <FontName value="SourceHanSansSC-Regular-HWidthCJK"/>
+// 11: <FontName value="SourceHanSansSC-Regular-HWidthDigits"/>
+// 12: <FontName value="SourceHanSansSC-Regular-Hangul"/>
+// 13: <FontName value="SourceHanSansSC-Regular-Ideographs"/>
+// 14: <FontName value="SourceHanSansSC-Regular-Kana"/>
+// 15: <FontName value="SourceHanSansSC-Regular-Proportional"/>
+// 16: <FontName value="SourceHanSansSC-Regular-ProportionalCJK"/>
+// 17: <FontName value="SourceHanSansSC-Regular-ProportionalDigits"/>
+// 18: <FontName value="SourceHanSansSC-Regular-VKana"/>
+//
+// As a sanity check, the cmap table maps U+3127 BOPOMOFO LETTER I to the glyph
+// named "cid65353", proprietaryFDSelectTestCases here maps 65353 to Font Dict
+// 2, and the list immediately above maps 2 to "Bopomofo".
+var proprietaryFDSelectTestCases = map[string]map[GlyphIndex]int{
+ "adobe/SourceHanSansSC-Regular.otf": {
+ 0: 5,
+ 1: 15,
+ 2: 15,
+ 16: 15,
+ 17: 17,
+ 26: 17,
+ 27: 15,
+ 100: 15,
+ 101: 15,
+ 102: 3,
+ 103: 15,
+ 777: 4,
+ 1000: 3,
+ 2000: 3,
+ 3000: 13,
+ 4000: 13,
+ 20000: 13,
+ 48000: 12,
+ 59007: 1,
+ 59024: 0,
+ 59087: 8,
+ 59200: 7,
+ 59211: 6,
+ 60000: 13,
+ 63000: 16,
+ 63039: 9,
+ 63060: 11,
+ 63137: 10,
+ 65353: 2,
+ 65486: 14,
+ 65505: 18,
+ 65506: 5,
+ 65533: 5,
+ 65534: 5,
+ },
+}
diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt.go b/vendor/golang.org/x/image/font/sfnt/sfnt.go
index 356841db1..5cb1225ef 100644
--- a/vendor/golang.org/x/image/font/sfnt/sfnt.go
+++ b/vendor/golang.org/x/image/font/sfnt/sfnt.go
@@ -53,6 +53,7 @@ const (
maxCompoundStackSize = 64
maxGlyphDataLength = 64 * 1024
maxHintBits = 256
+ maxNumFontDicts = 256
maxNumFonts = 256
maxNumTables = 256
maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation.
@@ -69,11 +70,14 @@ var (
errInvalidBounds = errors.New("sfnt: invalid bounds")
errInvalidCFFTable = errors.New("sfnt: invalid CFF table")
errInvalidCmapTable = errors.New("sfnt: invalid cmap table")
+ errInvalidDfont = errors.New("sfnt: invalid dfont")
errInvalidFont = errors.New("sfnt: invalid font")
errInvalidFontCollection = errors.New("sfnt: invalid font collection")
errInvalidGlyphData = errors.New("sfnt: invalid glyph data")
errInvalidGlyphDataLength = errors.New("sfnt: invalid glyph data length")
errInvalidHeadTable = errors.New("sfnt: invalid head table")
+ errInvalidHheaTable = errors.New("sfnt: invalid hhea table")
+ errInvalidHmtxTable = errors.New("sfnt: invalid hmtx table")
errInvalidKernTable = errors.New("sfnt: invalid kern table")
errInvalidLocaTable = errors.New("sfnt: invalid loca table")
errInvalidLocationData = errors.New("sfnt: invalid location data")
@@ -86,6 +90,7 @@ var (
errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order")
errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string")
+ errUnsupportedCFFFDSelectTable = errors.New("sfnt: unsupported CFF FDSelect table")
errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version")
errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings")
errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph")
@@ -93,6 +98,7 @@ var (
errUnsupportedKernTable = errors.New("sfnt: unsupported kern table")
errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding")
errUnsupportedNumberOfCmapSegments = errors.New("sfnt: unsupported number of cmap segments")
+ errUnsupportedNumberOfFontDicts = errors.New("sfnt: unsupported number of font dicts")
errUnsupportedNumberOfFonts = errors.New("sfnt: unsupported number of fonts")
errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints")
errUnsupportedNumberOfSubroutines = errors.New("sfnt: unsupported number of subroutines")
@@ -298,6 +304,7 @@ func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) {
type Collection struct {
src source
offsets []uint32
+ isDfont bool
}
// NumFonts returns the number of fonts in the collection.
@@ -305,8 +312,13 @@ func (c *Collection) NumFonts() int { return len(c.offsets) }
func (c *Collection) initialize() error {
// The https://www.microsoft.com/typography/otspec/otff.htm "Font
- // Collections" section describes the TTC Header.
- buf, err := c.src.view(nil, 0, 12)
+ // Collections" section describes the TTC header.
+ //
+ // https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
+ // describes the dfont header.
+ //
+ // 16 is the maximum of sizeof(TTCHeader) and sizeof(DfontHeader).
+ buf, err := c.src.view(nil, 0, 16)
if err != nil {
return err
}
@@ -314,6 +326,8 @@ func (c *Collection) initialize() error {
switch u32(buf) {
default:
return errInvalidFontCollection
+ case dfontResourceDataOffset:
+ return c.parseDfont(buf, u32(buf[4:]), u32(buf[12:]))
case 0x00010000, 0x4f54544f:
// Try parsing it as a single font instead of a collection.
c.offsets = []uint32{0}
@@ -338,13 +352,116 @@ func (c *Collection) initialize() error {
return nil
}
+// dfontResourceDataOffset is the assumed value of a dfont file's resource data
+// offset.
+//
+// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
+// says that "A Mac OS resource file... [starts with an] offset from start of
+// file to start of resource data section... [usually] 0x0100". In theory,
+// 0x00000100 isn't always a magic number for identifying dfont files. In
+// practice, it seems to work.
+const dfontResourceDataOffset = 0x00000100
+
+// parseDfont parses a dfont resource map, as per
+// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
+//
+// That unofficial wiki page lists all of its fields as *signed* integers,
+// which looks unusual. The actual file format might use *unsigned* integers in
+// various places, but until we have either an official specification or an
+// actual dfont file where this matters, we'll use signed integers and treat
+// negative values as invalid.
+func (c *Collection) parseDfont(buf []byte, resourceMapOffset, resourceMapLength uint32) error {
+ if resourceMapOffset > maxTableOffset || resourceMapLength > maxTableLength {
+ return errUnsupportedTableOffsetLength
+ }
+
+ const headerSize = 28
+ if resourceMapLength < headerSize {
+ return errInvalidDfont
+ }
+ buf, err := c.src.view(buf, int(resourceMapOffset+24), 2)
+ if err != nil {
+ return err
+ }
+ typeListOffset := int(int16(u16(buf)))
+
+ if typeListOffset < headerSize || resourceMapLength < uint32(typeListOffset)+2 {
+ return errInvalidDfont
+ }
+ buf, err = c.src.view(buf, int(resourceMapOffset)+typeListOffset, 2)
+ if err != nil {
+ return err
+ }
+ typeCount := int(int16(u16(buf)))
+
+ const tSize = 8
+ if typeCount < 0 || tSize*uint32(typeCount) > resourceMapLength-uint32(typeListOffset)-2 {
+ return errInvalidDfont
+ }
+ buf, err = c.src.view(buf, int(resourceMapOffset)+typeListOffset+2, tSize*typeCount)
+ if err != nil {
+ return err
+ }
+ resourceCount, resourceListOffset := 0, 0
+ for i := 0; i < typeCount; i++ {
+ if u32(buf[tSize*i:]) != 0x73666e74 { // "sfnt".
+ continue
+ }
+
+ resourceCount = int(int16(u16(buf[tSize*i+4:])))
+ if resourceCount < 0 {
+ return errInvalidDfont
+ }
+ // https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
+ // says that the value in the wire format is "the number of
+ // resources of this type, minus one."
+ resourceCount++
+
+ resourceListOffset = int(int16(u16(buf[tSize*i+6:])))
+ if resourceListOffset < 0 {
+ return errInvalidDfont
+ }
+ break
+ }
+ if resourceCount == 0 {
+ return errInvalidDfont
+ }
+ if resourceCount > maxNumFonts {
+ return errUnsupportedNumberOfFonts
+ }
+
+ const rSize = 12
+ if o, n := uint32(typeListOffset+resourceListOffset), rSize*uint32(resourceCount); o > resourceMapLength || n > resourceMapLength-o {
+ return errInvalidDfont
+ } else {
+ buf, err = c.src.view(buf, int(resourceMapOffset+o), int(n))
+ if err != nil {
+ return err
+ }
+ }
+ c.offsets = make([]uint32, resourceCount)
+ for i := range c.offsets {
+ o := 0xffffff & u32(buf[rSize*i+4:])
+ // Offsets are relative to the resource data start, not the file start.
+ // A particular resource's data also starts with a 4-byte length, which
+ // we skip.
+ o += dfontResourceDataOffset + 4
+ if o > maxTableOffset {
+ return errUnsupportedTableOffsetLength
+ }
+ c.offsets[i] = o
+ }
+ c.isDfont = true
+ return nil
+}
+
// Font returns the i'th font in the collection.
func (c *Collection) Font(i int) (*Font, error) {
if i < 0 || len(c.offsets) <= i {
return nil, ErrNotFound
}
f := &Font{src: c.src}
- if err := f.initialize(int(c.offsets[i])); err != nil {
+ if err := f.initialize(int(c.offsets[i]), c.isDfont); err != nil {
return nil, err
}
return f, nil
@@ -354,7 +471,7 @@ func (c *Collection) Font(i int) (*Font, error) {
// source.
func Parse(src []byte) (*Font, error) {
f := &Font{src: source{b: src}}
- if err := f.initialize(0); err != nil {
+ if err := f.initialize(0, false); err != nil {
return nil, err
}
return f, nil
@@ -364,7 +481,7 @@ func Parse(src []byte) (*Font, error) {
// io.ReaderAt data source.
func ParseReaderAt(src io.ReaderAt) (*Font, error) {
f := &Font{src: source{r: src}}
- if err := f.initialize(0); err != nil {
+ if err := f.initialize(0, false); err != nil {
return nil, err
}
return f, nil
@@ -438,39 +555,30 @@ type Font struct {
kern table
cached struct {
+ glyphData glyphData
glyphIndex glyphIndexFunc
+ bounds [4]int16
indexToLocFormat bool // false means short, true means long.
isPostScript bool
kernNumPairs int32
kernOffset int32
+ numHMetrics int32
postTableVersion uint32
unitsPerEm Units
-
- // The glyph data for the i'th glyph index is in
- // src[locations[i+0]:locations[i+1]].
- //
- // The slice length equals 1 plus the number of glyphs.
- locations []uint32
-
- // For PostScript fonts, the bytecode for the i'th global or local
- // subroutine is in src[x[i+0]:x[i+1]].
- //
- // The slice length equals 1 plus the number of subroutines
- gsubrs, subrs []uint32
}
}
// NumGlyphs returns the number of glyphs in f.
-func (f *Font) NumGlyphs() int { return len(f.cached.locations) - 1 }
+func (f *Font) NumGlyphs() int { return len(f.cached.glyphData.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(offset int) error {
+func (f *Font) initialize(offset int, isDfont bool) error {
if !f.src.valid() {
return errInvalidSourceData
}
- buf, isPostScript, err := f.initializeTables(offset)
+ buf, isPostScript, err := f.initializeTables(offset, isDfont)
if err != nil {
return err
}
@@ -483,11 +591,15 @@ func (f *Font) initialize(offset int) error {
// When implementing new parseXxx methods, take care not to call methods
// such as Font.NumGlyphs that implicitly depend on f.cached fields.
- buf, indexToLocFormat, unitsPerEm, err := f.parseHead(buf)
+ buf, bounds, indexToLocFormat, unitsPerEm, err := f.parseHead(buf)
if err != nil {
return err
}
- buf, numGlyphs, locations, gsubrs, subrs, err := f.parseMaxp(buf, indexToLocFormat, isPostScript)
+ buf, numGlyphs, err := f.parseMaxp(buf, isPostScript)
+ if err != nil {
+ return err
+ }
+ buf, glyphData, err := f.parseGlyphData(buf, numGlyphs, indexToLocFormat, isPostScript)
if err != nil {
return err
}
@@ -499,26 +611,34 @@ func (f *Font) initialize(offset int) error {
if err != nil {
return err
}
+ buf, numHMetrics, err := f.parseHhea(buf, numGlyphs)
+ if err != nil {
+ return err
+ }
+ buf, err = f.parseHmtx(buf, numGlyphs, numHMetrics)
+ if err != nil {
+ return err
+ }
buf, postTableVersion, err := f.parsePost(buf, numGlyphs)
if err != nil {
return err
}
+ f.cached.glyphData = glyphData
f.cached.glyphIndex = glyphIndex
+ f.cached.bounds = bounds
f.cached.indexToLocFormat = indexToLocFormat
f.cached.isPostScript = isPostScript
f.cached.kernNumPairs = kernNumPairs
f.cached.kernOffset = kernOffset
+ f.cached.numHMetrics = numHMetrics
f.cached.postTableVersion = postTableVersion
f.cached.unitsPerEm = unitsPerEm
- f.cached.locations = locations
- f.cached.gsubrs = gsubrs
- f.cached.subrs = subrs
return nil
}
-func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err error) {
+func (f *Font) initializeTables(offset int, isDfont bool) (buf1 []byte, isPostScript bool, err error) {
// https://www.microsoft.com/typography/otspec/otff.htm "Organization of an
// OpenType Font" says that "The OpenType font starts with the Offset
// Table", which is 12 bytes.
@@ -531,6 +651,8 @@ func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err
switch u32(buf) {
default:
return nil, false, errInvalidFont
+ case dfontResourceDataOffset:
+ return nil, false, errInvalidSingleFont
case 0x00010000:
// No-op.
case 0x4f54544f: // "OTTO".
@@ -559,6 +681,15 @@ func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err
prevTag = tag
o, n := u32(b[8:12]), u32(b[12:16])
+ // For dfont files, the offset is relative to the resource, not the
+ // file.
+ if isDfont {
+ origO := o
+ o += uint32(offset)
+ if o < origO {
+ return nil, false, errUnsupportedTableOffsetLength
+ }
+ }
if o > maxTableOffset || n > maxTableLength {
return nil, false, errUnsupportedTableOffsetLength
}
@@ -662,26 +793,61 @@ func (f *Font) parseCmap(buf []byte) (buf1 []byte, glyphIndex glyphIndexFunc, er
return f.makeCachedGlyphIndex(buf, bestOffset, bestLength, bestFormat)
}
-func (f *Font) parseHead(buf []byte) (buf1 []byte, indexToLocFormat bool, unitsPerEm Units, err error) {
+func (f *Font) parseHead(buf []byte) (buf1 []byte, bounds [4]int16, indexToLocFormat bool, unitsPerEm Units, err error) {
// https://www.microsoft.com/typography/otspec/head.htm
if f.head.length != 54 {
- return nil, false, 0, errInvalidHeadTable
+ return nil, [4]int16{}, false, 0, errInvalidHeadTable
}
+
u, err := f.src.u16(buf, f.head, 18)
if err != nil {
- return nil, false, 0, err
+ return nil, [4]int16{}, false, 0, err
}
if u == 0 {
- return nil, false, 0, errInvalidHeadTable
+ return nil, [4]int16{}, false, 0, errInvalidHeadTable
}
unitsPerEm = Units(u)
+
+ for i := range bounds {
+ u, err := f.src.u16(buf, f.head, 36+2*i)
+ if err != nil {
+ return nil, [4]int16{}, false, 0, err
+ }
+ bounds[i] = int16(u)
+ }
+
u, err = f.src.u16(buf, f.head, 50)
if err != nil {
- return nil, false, 0, err
+ return nil, [4]int16{}, false, 0, err
}
indexToLocFormat = u != 0
- return buf, indexToLocFormat, unitsPerEm, nil
+ return buf, bounds, indexToLocFormat, unitsPerEm, nil
+}
+
+func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, numHMetrics int32, err error) {
+ // https://www.microsoft.com/typography/OTSPEC/hhea.htm
+
+ if f.hhea.length != 36 {
+ return nil, 0, errInvalidHheaTable
+ }
+ u, err := f.src.u16(buf, f.hhea, 34)
+ if err != nil {
+ return nil, 0, err
+ }
+ if int32(u) > numGlyphs || u == 0 {
+ return nil, 0, errInvalidHheaTable
+ }
+ return buf, int32(u), nil
+}
+
+func (f *Font) parseHmtx(buf []byte, numGlyphs, numHMetrics int32) (buf1 []byte, err error) {
+ // https://www.microsoft.com/typography/OTSPEC/hmtx.htm
+
+ if f.hmtx.length != uint32(2*numGlyphs+2*numHMetrics) {
+ return nil, errInvalidHmtxTable
+ }
+ return buf, nil
}
func (f *Font) parseKern(buf []byte) (buf1 []byte, kernNumPairs, kernOffset int32, err error) {
@@ -778,24 +944,44 @@ func (f *Font) parseKernFormat0(buf []byte, offset, length int) (buf1 []byte, ke
return buf, kernNumPairs, int32(offset) + headerSize, nil
}
-func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1 []byte, numGlyphs int, locations, gsubrs, subrs []uint32, err error) {
+func (f *Font) parseMaxp(buf []byte, isPostScript bool) (buf1 []byte, numGlyphs int32, err error) {
// https://www.microsoft.com/typography/otspec/maxp.htm
if isPostScript {
if f.maxp.length != 6 {
- return nil, 0, nil, nil, nil, errInvalidMaxpTable
+ return nil, 0, errInvalidMaxpTable
}
} else {
if f.maxp.length != 32 {
- return nil, 0, nil, nil, nil, errInvalidMaxpTable
+ return nil, 0, errInvalidMaxpTable
}
}
u, err := f.src.u16(buf, f.maxp, 4)
if err != nil {
- return nil, 0, nil, nil, nil, err
+ return nil, 0, err
}
- numGlyphs = int(u)
+ return buf, int32(u), nil
+}
+
+type glyphData struct {
+ // The glyph data for the i'th glyph index is in
+ // src[locations[i+0]:locations[i+1]].
+ //
+ // The slice length equals 1 plus the number of glyphs.
+ locations []uint32
+
+ // For PostScript fonts, the bytecode for the i'th global or local
+ // subroutine is in src[x[i+0]:x[i+1]].
+ //
+ // The []uint32 slice length equals 1 plus the number of subroutines
+ gsubrs []uint32
+ singleSubrs []uint32
+ multiSubrs [][]uint32
+
+ fdSelect fdSelect
+}
+func (f *Font) parseGlyphData(buf []byte, numGlyphs int32, indexToLocFormat, isPostScript bool) (buf1 []byte, ret glyphData, err error) {
if isPostScript {
p := cffParser{
src: &f.src,
@@ -803,24 +989,24 @@ func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1
offset: int(f.cff.offset),
end: int(f.cff.offset + f.cff.length),
}
- locations, gsubrs, subrs, err = p.parse()
+ ret, err = p.parse(numGlyphs)
if err != nil {
- return nil, 0, nil, nil, nil, err
+ return nil, glyphData{}, err
}
} else {
- locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs)
+ ret.locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs)
if err != nil {
- return nil, 0, nil, nil, nil, err
+ return nil, glyphData{}, err
}
}
- if len(locations) != numGlyphs+1 {
- return nil, 0, nil, nil, nil, errInvalidLocationData
+ if len(ret.locations) != int(numGlyphs+1) {
+ return nil, glyphData{}, errInvalidLocationData
}
- return buf, numGlyphs, locations, gsubrs, subrs, nil
+ return buf, ret, nil
}
-func (f *Font) parsePost(buf []byte, numGlyphs int) (buf1 []byte, postTableVersion uint32, err error) {
+func (f *Font) parsePost(buf []byte, numGlyphs int32) (buf1 []byte, postTableVersion uint32, err error) {
// https://www.microsoft.com/typography/otspec/post.htm
const headerSize = 32
@@ -844,6 +1030,33 @@ func (f *Font) parsePost(buf []byte, numGlyphs int) (buf1 []byte, postTableVersi
return buf, u, nil
}
+// Bounds returns the union of a Font's glyphs' bounds.
+//
+// In the returned Rectangle26_6's (x, y) coordinates, the Y axis increases
+// down.
+func (f *Font) Bounds(b *Buffer, ppem fixed.Int26_6, h font.Hinting) (fixed.Rectangle26_6, error) {
+ // The 0, 3, 2, 1 indices are to flip the Y coordinates. OpenType's Y axis
+ // increases up. Go's standard graphics libraries' Y axis increases down.
+ r := fixed.Rectangle26_6{
+ Min: fixed.Point26_6{
+ X: +scale(fixed.Int26_6(f.cached.bounds[0])*ppem, f.cached.unitsPerEm),
+ Y: -scale(fixed.Int26_6(f.cached.bounds[3])*ppem, f.cached.unitsPerEm),
+ },
+ Max: fixed.Point26_6{
+ X: +scale(fixed.Int26_6(f.cached.bounds[2])*ppem, f.cached.unitsPerEm),
+ Y: -scale(fixed.Int26_6(f.cached.bounds[1])*ppem, f.cached.unitsPerEm),
+ },
+ }
+ if h == font.HintingFull {
+ // Quantize the Min down and Max up to a whole pixel.
+ r.Min.X = (r.Min.X + 0) &^ 63
+ r.Min.Y = (r.Min.Y + 0) &^ 63
+ r.Max.X = (r.Max.X + 63) &^ 63
+ r.Max.Y = (r.Max.Y + 63) &^ 63
+ }
+ return r, nil
+}
+
// TODO: API for looking up glyph variants?? For example, some fonts may
// provide both slashed and dotted zero glyphs ('0'), or regular and 'old
// style' numerals, and users can direct software to choose a variant.
@@ -866,8 +1079,8 @@ func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) (buf []byte, offset, lengt
if f.NumGlyphs() <= xx {
return nil, 0, 0, ErrNotFound
}
- i := f.cached.locations[xx+0]
- j := f.cached.locations[xx+1]
+ i := f.cached.glyphData.locations[xx+0]
+ j := f.cached.glyphData.locations[xx+1]
if j < i {
return nil, 0, 0, errInvalidGlyphDataLength
}
@@ -888,6 +1101,8 @@ type LoadGlyphOptions struct {
//
// If b is non-nil, the segments become invalid to use once b is re-used.
//
+// In the returned Segments' (x, y) coordinates, the Y axis increases down.
+//
// It returns ErrNotFound if the glyph index is out of range.
func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *LoadGlyphOptions) ([]Segment, error) {
if b == nil {
@@ -900,7 +1115,7 @@ func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *Load
if err != nil {
return nil, err
}
- b.psi.type2Charstrings.initialize(f, b)
+ b.psi.type2Charstrings.initialize(f, b, x)
if err := b.psi.run(psContextType2Charstring, buf, offset, length); err != nil {
return nil, err
}
@@ -918,10 +1133,14 @@ func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *Load
// loading code, such as the appendGlyfSegments body, since TrueType
// hinting bytecode works on the scaled glyph vectors. For now, though,
// it's simpler to scale as a post-processing step.
+ //
+ // We also flip the Y coordinates. OpenType's Y axis increases up. Go's
+ // standard graphics libraries' Y axis increases down.
for i := range b.segments {
- s := &b.segments[i]
- for j := range s.Args {
- s.Args[j] = scale(s.Args[j]*ppem, f.cached.unitsPerEm)
+ a := &b.segments[i].Args
+ for j := range a {
+ a[j].X = +scale(a[j].X*ppem, f.cached.unitsPerEm)
+ a[j].Y = -scale(a[j].Y*ppem, f.cached.unitsPerEm)
}
}
@@ -1002,6 +1221,39 @@ func (f *Font) GlyphName(b *Buffer, x GlyphIndex) (string, error) {
}
}
+// GlyphAdvance returns the advance width for the x'th glyph. ppem is the
+// number of pixels in 1 em.
+//
+// It returns ErrNotFound if the glyph index is out of range.
+func (f *Font) GlyphAdvance(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, h font.Hinting) (fixed.Int26_6, error) {
+ if int(x) >= f.NumGlyphs() {
+ return 0, ErrNotFound
+ }
+ if b == nil {
+ b = &Buffer{}
+ }
+
+ // https://www.microsoft.com/typography/OTSPEC/hmtx.htm says that "As an
+ // optimization, the number of records can be less than the number of
+ // glyphs, in which case the advance width value of the last record applies
+ // to all remaining glyph IDs."
+ if n := GlyphIndex(f.cached.numHMetrics - 1); x > n {
+ x = n
+ }
+
+ buf, err := b.view(&f.src, int(f.hmtx.offset)+int(4*x), 2)
+ if err != nil {
+ return 0, err
+ }
+ adv := fixed.Int26_6(u16(buf))
+ adv = scale(adv*ppem, f.cached.unitsPerEm)
+ if h == font.HintingFull {
+ // Quantize the fixed.Int26_6 value to the nearest pixel.
+ adv = (adv + 32) &^ 63
+ }
+ return adv, nil
+}
+
// Kern returns the horizontal adjustment for the kerning pair (x0, x1). A
// positive kern means to move the glyphs further apart. ppem is the number of
// pixels in 1 em.
@@ -1180,8 +1432,10 @@ func (b *Buffer) view(src *source, offset, length int) ([]byte, error) {
// Segment is a segment of a vector path.
type Segment struct {
- Op SegmentOp
- Args [6]fixed.Int26_6
+ // Op is the operator.
+ Op SegmentOp
+ // Args is up to three (x, y) coordinates. The Y axis increases down.
+ Args [3]fixed.Point26_6
}
// SegmentOp is a vector path segment's operator.
@@ -1195,30 +1449,31 @@ const (
)
// translateArgs applies a translation to args.
-func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) {
- args[0] += dx
- args[1] += dy
- args[2] += dx
- args[3] += dy
- args[4] += dx
- args[5] += dy
+func translateArgs(args *[3]fixed.Point26_6, dx, dy fixed.Int26_6) {
+ args[0].X += dx
+ args[0].Y += dy
+ args[1].X += dx
+ args[1].Y += dy
+ args[2].X += dx
+ args[2].Y += dy
}
// transformArgs applies an affine transformation to args. The t?? arguments
// are 2.14 fixed point values.
-func transformArgs(args *[6]fixed.Int26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
- args[0], args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[0], args[1])
- args[2], args[3] = tform(txx, txy, tyx, tyy, dx, dy, args[2], args[3])
- args[4], args[5] = tform(txx, txy, tyx, tyy, dx, dy, args[4], args[5])
+func transformArgs(args *[3]fixed.Point26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
+ args[0] = tform(txx, txy, tyx, tyy, dx, dy, args[0])
+ args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[1])
+ args[2] = tform(txx, txy, tyx, tyy, dx, dy, args[2])
}
-func tform(txx, txy, tyx, tyy int16, dx, dy, x, y fixed.Int26_6) (newX, newY fixed.Int26_6) {
+func tform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, p fixed.Point26_6) fixed.Point26_6 {
const half = 1 << 13
- newX = dx +
- fixed.Int26_6((int64(x)*int64(txx)+half)>>14) +
- fixed.Int26_6((int64(y)*int64(tyx)+half)>>14)
- newY = dy +
- fixed.Int26_6((int64(x)*int64(txy)+half)>>14) +
- fixed.Int26_6((int64(y)*int64(tyy)+half)>>14)
- return newX, newY
+ return fixed.Point26_6{
+ X: dx +
+ fixed.Int26_6((int64(p.X)*int64(txx)+half)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(tyx)+half)>>14),
+ Y: dy +
+ fixed.Int26_6((int64(p.X)*int64(txy)+half)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(tyy)+half)>>14),
+ }
}
diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt_test.go b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go
index dc1c9c54f..74de278dd 100644
--- a/vendor/golang.org/x/image/font/sfnt/sfnt_test.go
+++ b/vendor/golang.org/x/image/font/sfnt/sfnt_test.go
@@ -11,35 +11,42 @@ import (
"path/filepath"
"testing"
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/gofont/gobold"
+ "golang.org/x/image/font/gofont/gomono"
"golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed"
)
+func pt(x, y fixed.Int26_6) fixed.Point26_6 {
+ return fixed.Point26_6{X: x, Y: y}
+}
+
func moveTo(xa, ya fixed.Int26_6) Segment {
return Segment{
Op: SegmentOpMoveTo,
- Args: [6]fixed.Int26_6{xa, ya},
+ Args: [3]fixed.Point26_6{pt(xa, ya)},
}
}
func lineTo(xa, ya fixed.Int26_6) Segment {
return Segment{
Op: SegmentOpLineTo,
- Args: [6]fixed.Int26_6{xa, ya},
+ Args: [3]fixed.Point26_6{pt(xa, ya)},
}
}
func quadTo(xa, ya, xb, yb fixed.Int26_6) Segment {
return Segment{
Op: SegmentOpQuadTo,
- Args: [6]fixed.Int26_6{xa, ya, xb, yb},
+ Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb)},
}
}
func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment {
return Segment{
Op: SegmentOpCubeTo,
- Args: [6]fixed.Int26_6{xa, ya, xb, yb, xc, yc},
+ Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb), pt(xc, yc)},
}
}
@@ -54,6 +61,16 @@ func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segmen
}
func checkSegmentsEqual(got, want []Segment) error {
+ // Flip got's Y axis. The test cases' coordinates are given with the Y axis
+ // increasing up, as that is what the ttx tool gives, and is the model for
+ // the underlying font format. The Go API returns coordinates with the Y
+ // axis increasing down, the same as the standard graphics libraries.
+ for i := range got {
+ for j := range got[i].Args {
+ got[i].Args[j].Y *= -1
+ }
+ }
+
if len(got) != len(want) {
return fmt.Errorf("got %d elements, want %d\noverall:\ngot %v\nwant %v",
len(got), len(want), got, want)
@@ -64,7 +81,42 @@ func checkSegmentsEqual(got, want []Segment) error {
i, g, w, got, want)
}
}
- return nil
+
+ // Check that every contour is closed.
+ if len(got) == 0 {
+ return nil
+ }
+ if got[0].Op != SegmentOpMoveTo {
+ return fmt.Errorf("segments do not start with a moveTo")
+ }
+ var (
+ first, last fixed.Point26_6
+ firstI int
+ )
+ checkClosed := func(lastI int) error {
+ if first != last {
+ return fmt.Errorf("segments[%d:%d] not closed:\nfirst %v\nlast %v", firstI, lastI, first, last)
+ }
+ return nil
+ }
+ for i, g := range got {
+ switch g.Op {
+ case SegmentOpMoveTo:
+ if i != 0 {
+ if err := checkClosed(i); err != nil {
+ return err
+ }
+ }
+ firstI, first, last = i, g.Args[0], g.Args[0]
+ case SegmentOpLineTo:
+ last = g.Args[0]
+ case SegmentOpQuadTo:
+ last = g.Args[1]
+ case SegmentOpCubeTo:
+ last = g.Args[2]
+ }
+ }
+ return checkClosed(len(got))
}
func TestTrueTypeParse(t *testing.T) {
@@ -95,6 +147,132 @@ func testTrueType(t *testing.T, f *Font) {
}
}
+func fontData(name string) []byte {
+ switch name {
+ case "gobold":
+ return gobold.TTF
+ case "gomono":
+ return gomono.TTF
+ case "goregular":
+ return goregular.TTF
+ }
+ panic("unreachable")
+}
+
+func TestBounds(t *testing.T) {
+ testCases := map[string]fixed.Rectangle26_6{
+ "gobold": {
+ Min: fixed.Point26_6{
+ X: -452,
+ Y: -2193,
+ },
+ Max: fixed.Point26_6{
+ X: 2190,
+ Y: 432,
+ },
+ },
+ "gomono": {
+ Min: fixed.Point26_6{
+ X: 0,
+ Y: -2227,
+ },
+ Max: fixed.Point26_6{
+ X: 1229,
+ Y: 432,
+ },
+ },
+ "goregular": {
+ Min: fixed.Point26_6{
+ X: -440,
+ Y: -2118,
+ },
+ Max: fixed.Point26_6{
+ X: 2160,
+ Y: 543,
+ },
+ },
+ }
+
+ var b Buffer
+ for name, want := range testCases {
+ f, err := Parse(fontData(name))
+ if err != nil {
+ t.Errorf("Parse(%q): %v", name, err)
+ continue
+ }
+ ppem := fixed.Int26_6(f.UnitsPerEm())
+
+ got, err := f.Bounds(&b, ppem, font.HintingNone)
+ if err != nil {
+ t.Errorf("name=%q: Bounds: %v", name, err)
+ continue
+ }
+ if got != want {
+ t.Errorf("name=%q: Bounds: got %v, want %v", name, got, want)
+ continue
+ }
+ }
+}
+
+func TestGlyphAdvance(t *testing.T) {
+ testCases := map[string][]struct {
+ r rune
+ want fixed.Int26_6
+ }{
+ "gobold": {
+ {' ', 569},
+ {'A', 1479},
+ {'Á', 1479},
+ {'Æ', 2048},
+ {'i', 592},
+ {'x', 1139},
+ },
+ "gomono": {
+ {' ', 1229},
+ {'A', 1229},
+ {'Á', 1229},
+ {'Æ', 1229},
+ {'i', 1229},
+ {'x', 1229},
+ },
+ "goregular": {
+ {' ', 569},
+ {'A', 1366},
+ {'Á', 1366},
+ {'Æ', 2048},
+ {'i', 505},
+ {'x', 1024},
+ },
+ }
+
+ var b Buffer
+ for name, testCases1 := range testCases {
+ f, err := Parse(fontData(name))
+ if err != nil {
+ t.Errorf("Parse(%q): %v", name, err)
+ continue
+ }
+ ppem := fixed.Int26_6(f.UnitsPerEm())
+
+ for _, tc := range testCases1 {
+ x, err := f.GlyphIndex(&b, tc.r)
+ if err != nil {
+ t.Errorf("name=%q, r=%q: GlyphIndex: %v", name, tc.r, err)
+ continue
+ }
+ got, err := f.GlyphAdvance(&b, x, ppem, font.HintingNone)
+ if err != nil {
+ t.Errorf("name=%q, r=%q: GlyphAdvance: %v", name, tc.r, err)
+ continue
+ }
+ if got != tc.want {
+ t.Errorf("name=%q, r=%q: GlyphAdvance: got %d, want %d", name, tc.r, got, tc.want)
+ continue
+ }
+ }
+ }
+}
+
func TestGoRegularGlyphIndex(t *testing.T) {
f, err := Parse(goregular.TTF)
if err != nil {
@@ -116,25 +294,23 @@ func TestGoRegularGlyphIndex(t *testing.T) {
// The actual values are ad hoc, and result from whatever tools the
// Bigelow & Holmes type foundry used and the order in which they
// crafted the glyphs. They may change over time as newer versions of
- // the font are released. In practice, though, running this test with
- // coverage analysis suggests that it covers both the zero and non-zero
- // cmapEntry16.offset cases for a format-4 cmap table.
-
- {'\u0020', 3}, // U+0020 SPACE
- {'\u0021', 4}, // U+0021 EXCLAMATION MARK
- {'\u0022', 5}, // U+0022 QUOTATION MARK
- {'\u0023', 6}, // U+0023 NUMBER SIGN
- {'\u0024', 223}, // U+0024 DOLLAR SIGN
- {'\u0025', 7}, // U+0025 PERCENT SIGN
- {'\u0026', 8}, // U+0026 AMPERSAND
- {'\u0027', 9}, // U+0027 APOSTROPHE
-
- {'\u03bd', 423}, // U+03BD GREEK SMALL LETTER NU
- {'\u03be', 424}, // U+03BE GREEK SMALL LETTER XI
- {'\u03bf', 438}, // U+03BF GREEK SMALL LETTER OMICRON
- {'\u03c0', 208}, // U+03C0 GREEK SMALL LETTER PI
- {'\u03c1', 425}, // U+03C1 GREEK SMALL LETTER RHO
- {'\u03c2', 426}, // U+03C2 GREEK SMALL LETTER FINAL SIGMA
+ // the font are released.
+
+ {'\u0020', 3}, // U+0020 SPACE
+ {'\u0021', 4}, // U+0021 EXCLAMATION MARK
+ {'\u0022', 5}, // U+0022 QUOTATION MARK
+ {'\u0023', 6}, // U+0023 NUMBER SIGN
+ {'\u0024', 7}, // U+0024 DOLLAR SIGN
+ {'\u0025', 8}, // U+0025 PERCENT SIGN
+ {'\u0026', 9}, // U+0026 AMPERSAND
+ {'\u0027', 10}, // U+0027 APOSTROPHE
+
+ {'\u03bd', 396}, // U+03BD GREEK SMALL LETTER NU
+ {'\u03be', 397}, // U+03BE GREEK SMALL LETTER XI
+ {'\u03bf', 398}, // U+03BF GREEK SMALL LETTER OMICRON
+ {'\u03c0', 399}, // U+03C0 GREEK SMALL LETTER PI
+ {'\u03c1', 400}, // U+03C1 GREEK SMALL LETTER RHO
+ {'\u03c2', 401}, // U+03C2 GREEK SMALL LETTER FINAL SIGMA
}
var b Buffer
@@ -275,11 +451,13 @@ func TestPostScriptSegments(t *testing.T) {
lineTo(450, 0),
lineTo(450, 533),
lineTo(50, 533),
+ lineTo(50, 0),
// - contour #1
moveTo(100, 50),
lineTo(100, 483),
lineTo(400, 483),
lineTo(400, 50),
+ lineTo(100, 50),
}, {
// zero
// - contour #0
@@ -301,12 +479,14 @@ func TestPostScriptSegments(t *testing.T) {
lineTo(300, 0),
lineTo(300, 800),
lineTo(100, 800),
+ lineTo(100, 0),
}, {
// Q
// - contour #0
moveTo(657, 237),
lineTo(289, 387),
lineTo(519, 615),
+ lineTo(657, 237),
// - contour #1
moveTo(792, 169),
cubeTo(867, 263, 926, 502, 791, 665),
@@ -315,6 +495,7 @@ func TestPostScriptSegments(t *testing.T) {
cubeTo(369, -39, 641, 18, 722, 93),
lineTo(802, 3),
lineTo(864, 83),
+ lineTo(792, 169),
}, {
// uni4E2D
// - contour #0
@@ -329,7 +510,7 @@ func TestPostScriptSegments(t *testing.T) {
lineTo(331, 758),
lineTo(243, 752),
lineTo(235, 562),
- // TODO: explicitly (not implicitly) close these contours?
+ lineTo(141, 520),
}}
testSegments(t, "CFFTest.otf", wants)
@@ -559,7 +740,7 @@ func TestGlyphName(t *testing.T) {
r rune
want string
}{
- {'\x00', "NULL"},
+ {'\x00', "uni0000"},
{'!', "exclam"},
{'A', "A"},
{'{', "braceleft"},
diff --git a/vendor/golang.org/x/image/font/sfnt/truetype.go b/vendor/golang.org/x/image/font/sfnt/truetype.go
index 41819617d..ab27f5b2c 100644
--- a/vendor/golang.org/x/image/font/sfnt/truetype.go
+++ b/vendor/golang.org/x/image/font/sfnt/truetype.go
@@ -51,7 +51,7 @@ func midPoint(p, q fixed.Point26_6) fixed.Point26_6 {
}
}
-func parseLoca(src *source, loca table, glyfOffset uint32, indexToLocFormat bool, numGlyphs int) (locations []uint32, err error) {
+func parseLoca(src *source, loca table, glyfOffset uint32, indexToLocFormat bool, numGlyphs int32) (locations []uint32, err error) {
if indexToLocFormat {
if loca.length != 4*uint32(numGlyphs+1) {
return nil, errInvalidLocaTable
@@ -414,44 +414,28 @@ func (g *glyfIter) close() {
case !g.firstOffCurveValid && !g.lastOffCurveValid:
g.closed = true
g.seg = Segment{
- Op: SegmentOpLineTo,
- Args: [6]fixed.Int26_6{
- g.firstOnCurve.X,
- g.firstOnCurve.Y,
- },
+ Op: SegmentOpLineTo,
+ Args: [3]fixed.Point26_6{g.firstOnCurve},
}
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,
- },
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{g.lastOffCurve, g.firstOnCurve},
}
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,
- },
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{g.firstOffCurve, g.firstOnCurve},
}
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,
+ Args: [3]fixed.Point26_6{
+ g.lastOffCurve,
+ midPoint(g.lastOffCurve, g.firstOffCurve),
},
}
}
@@ -484,11 +468,8 @@ func (g *glyfIter) nextSegment() (ok bool) {
g.firstOnCurve = p
g.firstOnCurveValid = true
g.seg = Segment{
- Op: SegmentOpMoveTo,
- Args: [6]fixed.Int26_6{
- p.X,
- p.Y,
- },
+ Op: SegmentOpMoveTo,
+ Args: [3]fixed.Point26_6{p},
}
return true
} else if !g.firstOffCurveValid {
@@ -496,17 +477,13 @@ func (g *glyfIter) nextSegment() (ok bool) {
g.firstOffCurveValid = true
continue
} else {
- midp := midPoint(g.firstOffCurve, p)
- g.firstOnCurve = midp
+ g.firstOnCurve = midPoint(g.firstOffCurve, p)
g.firstOnCurveValid = true
g.lastOffCurve = p
g.lastOffCurveValid = true
g.seg = Segment{
- Op: SegmentOpMoveTo,
- Args: [6]fixed.Int26_6{
- midp.X,
- midp.Y,
- },
+ Op: SegmentOpMoveTo,
+ Args: [3]fixed.Point26_6{g.firstOnCurve},
}
return true
}
@@ -518,25 +495,19 @@ func (g *glyfIter) nextSegment() (ok bool) {
continue
} else {
g.seg = Segment{
- Op: SegmentOpLineTo,
- Args: [6]fixed.Int26_6{
- p.X,
- p.Y,
- },
+ Op: SegmentOpLineTo,
+ Args: [3]fixed.Point26_6{p},
}
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,
+ Args: [3]fixed.Point26_6{
+ g.lastOffCurve,
+ midPoint(g.lastOffCurve, p),
},
}
g.lastOffCurve = p
@@ -544,13 +515,8 @@ func (g *glyfIter) nextSegment() (ok bool) {
return true
} else {
g.seg = Segment{
- Op: SegmentOpQuadTo,
- Args: [6]fixed.Int26_6{
- g.lastOffCurve.X,
- g.lastOffCurve.Y,
- p.X,
- p.Y,
- },
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{g.lastOffCurve, p},
}
g.lastOffCurveValid = false
return true