aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/image/font/sfnt/truetype.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/image/font/sfnt/truetype.go')
-rw-r--r--vendor/golang.org/x/image/font/sfnt/truetype.go572
1 files changed, 572 insertions, 0 deletions
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 0000000..ab27f5b
--- /dev/null
+++ b/vendor/golang.org/x/image/font/sfnt/truetype.go
@@ -0,0 +1,572 @@
+// 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 int32) (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
+
+func loadGlyf(f *Font, b *Buffer, x GlyphIndex, stackBottom, recursionDepth uint32) error {
+ data, _, _, err := f.viewGlyphData(b, x)
+ if err != nil {
+ return err
+ }
+ if len(data) == 0 {
+ return nil
+ }
+ if len(data) < glyfHeaderLen {
+ return errInvalidGlyphData
+ }
+ index := glyfHeaderLen
+
+ numContours, numPoints := int16(u16(data)), 0
+ switch {
+ case numContours == -1:
+ // We have a compound glyph. No-op.
+ case numContours == 0:
+ return nil
+ case numContours > 0:
+ // We have a simple (non-compound) glyph.
+ index += 2 * int(numContours)
+ if index > len(data) {
+ return errInvalidGlyphData
+ }
+ // The +1 for numPoints is because the value in the file format is
+ // inclusive, but Go's slice[:index] semantics are exclusive.
+ numPoints = 1 + int(u16(data[index-2:]))
+ default:
+ return errInvalidGlyphData
+ }
+
+ if numContours < 0 {
+ return loadCompoundGlyf(f, b, data[glyfHeaderLen:], stackBottom, recursionDepth)
+ }
+
+ // Skip the hinting instructions.
+ index += 2
+ if index > len(data) {
+ return errInvalidGlyphData
+ }
+ hintsLength := int(u16(data[index-2:]))
+ index += hintsLength
+ if index > len(data) {
+ return errInvalidGlyphData
+ }
+
+ // 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 coordinates, then
+ // all the y coordinates), 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 coordinates and the y coordinates each start.
+ flagIndex := int32(index)
+ xIndex, yIndex, ok := findXYIndexes(data, index, numPoints)
+ if !ok {
+ return 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() {
+ b.segments = append(b.segments, g.seg)
+ }
+ }
+ return g.err
+}
+
+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
+}
+
+func loadCompoundGlyf(f *Font, b *Buffer, data []byte, stackBottom, recursionDepth uint32) error {
+ if recursionDepth++; recursionDepth == maxCompoundRecursionDepth {
+ return errUnsupportedCompoundGlyph
+ }
+
+ // Read and process the compound glyph's components. They are two separate
+ // for loops, since reading parses the elements of the data slice, and
+ // processing can overwrite the backing array.
+
+ stackTop := stackBottom
+ for {
+ if stackTop >= maxCompoundStackSize {
+ return errUnsupportedCompoundGlyph
+ }
+ elem := &b.compoundStack[stackTop]
+ stackTop++
+
+ if len(data) < 4 {
+ return errInvalidGlyphData
+ }
+ flags := u16(data)
+ elem.glyphIndex = GlyphIndex(u16(data[2:]))
+ if flags&flagArg1And2AreWords == 0 {
+ if len(data) < 6 {
+ return errInvalidGlyphData
+ }
+ elem.dx = int16(int8(data[4]))
+ elem.dy = int16(int8(data[5]))
+ data = data[6:]
+ } else {
+ if len(data) < 8 {
+ return errInvalidGlyphData
+ }
+ elem.dx = int16(u16(data[4:]))
+ elem.dy = int16(u16(data[6:]))
+ data = data[8:]
+ }
+
+ if flags&flagArgsAreXYValues == 0 {
+ return errUnsupportedCompoundGlyph
+ }
+ elem.hasTransform = flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0
+ if elem.hasTransform {
+ switch {
+ case flags&flagWeHaveAScale != 0:
+ if len(data) < 2 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data))
+ elem.transformXY = 0
+ elem.transformYX = 0
+ elem.transformYY = elem.transformXX
+ data = data[2:]
+ case flags&flagWeHaveAnXAndYScale != 0:
+ if len(data) < 4 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data[0:]))
+ elem.transformXY = 0
+ elem.transformYX = 0
+ elem.transformYY = int16(u16(data[2:]))
+ data = data[4:]
+ case flags&flagWeHaveATwoByTwo != 0:
+ if len(data) < 8 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data[0:]))
+ elem.transformXY = int16(u16(data[2:]))
+ elem.transformYX = int16(u16(data[4:]))
+ elem.transformYY = int16(u16(data[6:]))
+ data = data[8:]
+ }
+ }
+
+ if flags&flagMoreComponents == 0 {
+ break
+ }
+ }
+
+ // To support hinting, we'd have to save the remaining bytes in data here
+ // and interpret them after the for loop below, since that for loop's
+ // loadGlyf calls can overwrite the backing array.
+
+ for i := stackBottom; i < stackTop; i++ {
+ elem := &b.compoundStack[i]
+ base := len(b.segments)
+ if err := loadGlyf(f, b, elem.glyphIndex, stackTop, recursionDepth); err != nil {
+ return err
+ }
+ dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy)
+ segs := b.segments[base:]
+ if elem.hasTransform {
+ txx := elem.transformXX
+ txy := elem.transformXY
+ tyx := elem.transformYX
+ tyy := elem.transformYY
+ for j := range segs {
+ transformArgs(&segs[j].Args, txx, txy, tyx, tyy, dx, dy)
+ }
+ } else {
+ for j := range segs {
+ translateArgs(&segs[j].Args, dx, dy)
+ }
+ }
+ }
+
+ return nil
+}
+
+type glyfIter struct {
+ data []byte
+ err error
+
+ // 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: [3]fixed.Point26_6{g.firstOnCurve},
+ }
+ case !g.firstOffCurveValid && g.lastOffCurveValid:
+ g.closed = true
+ g.seg = Segment{
+ 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: [3]fixed.Point26_6{g.firstOffCurve, g.firstOnCurve},
+ }
+ case g.firstOffCurveValid && g.lastOffCurveValid:
+ g.lastOffCurveValid = false
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{
+ g.lastOffCurve,
+ midPoint(g.lastOffCurve, g.firstOffCurve),
+ },
+ }
+ }
+}
+
+func (g *glyfIter) nextSegment() (ok bool) {
+ for !g.closed {
+ if g.closing || !g.nextPoint() {
+ g.closing = true
+ g.close()
+ return true
+ }
+
+ // Convert the tuple (g.x, g.y) to a fixed.Point26_6, since the latter
+ // is what's held in a Segment. The input (g.x, g.y) is a pair of int16
+ // values, measured in font units, since that is what the underlying
+ // format provides. The output is a pair of fixed.Int26_6 values. A
+ // fixed.Int26_6 usually represents a 26.6 fixed number of pixels, but
+ // this here is just a straight numerical conversion, with no scaling
+ // factor. A later step scales the Segment.Args values by such a factor
+ // to convert e.g. 1792 font units to 10.5 pixels at 2048 font units
+ // per em and 12 ppem (pixels per em).
+ p := fixed.Point26_6{
+ X: fixed.Int26_6(g.x),
+ Y: fixed.Int26_6(g.y),
+ }
+
+ if !g.firstOnCurveValid {
+ if g.on {
+ g.firstOnCurve = p
+ g.firstOnCurveValid = true
+ g.seg = Segment{
+ Op: SegmentOpMoveTo,
+ Args: [3]fixed.Point26_6{p},
+ }
+ return true
+ } else if !g.firstOffCurveValid {
+ g.firstOffCurve = p
+ g.firstOffCurveValid = true
+ continue
+ } else {
+ g.firstOnCurve = midPoint(g.firstOffCurve, p)
+ g.firstOnCurveValid = true
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ g.seg = Segment{
+ Op: SegmentOpMoveTo,
+ Args: [3]fixed.Point26_6{g.firstOnCurve},
+ }
+ return true
+ }
+
+ } else if !g.lastOffCurveValid {
+ if !g.on {
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ continue
+ } else {
+ g.seg = Segment{
+ Op: SegmentOpLineTo,
+ Args: [3]fixed.Point26_6{p},
+ }
+ return true
+ }
+
+ } else {
+ if !g.on {
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{
+ g.lastOffCurve,
+ midPoint(g.lastOffCurve, p),
+ },
+ }
+ g.lastOffCurve = p
+ g.lastOffCurveValid = true
+ return true
+ } else {
+ g.seg = Segment{
+ Op: SegmentOpQuadTo,
+ Args: [3]fixed.Point26_6{g.lastOffCurve, p},
+ }
+ 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
+}