font/sfnt: support TrueType glyph transformations.

Change-Id: Iea2387b5e30dd0fff53e2808b25599c3be5b1cdb
Reviewed-on: https://go-review.googlesource.com/38210
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/proprietary_test.go b/font/sfnt/proprietary_test.go
index 4c6a633..9137fea 100644
--- a/font/sfnt/proprietary_test.go
+++ b/font/sfnt/proprietary_test.go
@@ -88,7 +88,7 @@
 }
 
 func TestProprietaryMicrosoftArial(t *testing.T) {
-	testProprietary(t, "microsoft", "Arial.ttf", 1200, 599)
+	testProprietary(t, "microsoft", "Arial.ttf", 1200, -1)
 }
 
 func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
@@ -96,7 +96,7 @@
 }
 
 func TestProprietaryMicrosoftTimesNewRoman(t *testing.T) {
-	testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, 423)
+	testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, -1)
 }
 
 func TestProprietaryMicrosoftWebdings(t *testing.T) {
@@ -534,6 +534,42 @@
 			translate(339, 650, lineTo(371, 1194)),
 			translate(339, 650, lineTo(222, 1194)),
 		},
+
+		'﴾': { // U+FD3E ORNATE LEFT PARENTHESIS.
+			// - contour #0
+			moveTo(560, -384),
+			lineTo(516, -429),
+			quadTo(412, -304, 361, -226),
+			quadTo(258, -68, 201, 106),
+			quadTo(127, 334, 127, 595),
+			quadTo(127, 845, 201, 1069),
+			quadTo(259, 1246, 361, 1404),
+			quadTo(414, 1487, 514, 1608),
+			lineTo(560, 1566),
+			quadTo(452, 1328, 396, 1094),
+			quadTo(336, 845, 336, 603),
+			quadTo(336, 359, 370, 165),
+			quadTo(398, 8, 454, -142),
+			quadTo(482, -217, 560, -384),
+		},
+
+		'﴿': { // U+FD3F ORNATE RIGHT PARENTHESIS
+			// - contour #0
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, moveTo(560, -384)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(516, -429)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(412, -304, 361, -226)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(258, -68, 201, 106)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 334, 127, 595)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 845, 201, 1069)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(259, 1246, 361, 1404)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(414, 1487, 514, 1608)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(560, 1566)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(452, 1328, 396, 1094)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 845, 336, 603)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 359, 370, 165)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(398, 8, 454, -142)),
+			transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(482, -217, 560, -384)),
+		},
 	},
 }
 
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 71aae52..e7c6670 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -1025,8 +1025,13 @@
 	segments []Segment
 	// compoundStack holds the components of a TrueType compound glyph.
 	compoundStack [maxCompoundStackSize]struct {
-		glyphIndex GlyphIndex
-		dx, dy     int16
+		glyphIndex   GlyphIndex
+		dx, dy       int16
+		hasTransform bool
+		transformXX  int16
+		transformXY  int16
+		transformYX  int16
+		transformYY  int16
 	}
 	// psi is a PostScript interpreter for when the Font is an OpenType/CFF
 	// font.
@@ -1061,12 +1066,31 @@
 	SegmentOpCubeTo
 )
 
-func translate(dx, dy fixed.Int26_6, s Segment) Segment {
-	s.Args[0] += dx
-	s.Args[1] += dy
-	s.Args[2] += dx
-	s.Args[3] += dy
-	s.Args[4] += dx
-	s.Args[5] += dy
-	return s
+// translateArgs applies a translation to args.
+func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) {
+	args[0] += dx
+	args[1] += dy
+	args[2] += dx
+	args[3] += dy
+	args[4] += dx
+	args[5] += dy
+}
+
+// transformArgs applies an affine transformation to args. The t?? arguments
+// are 2.14 fixed point values.
+func transformArgs(args *[6]fixed.Int26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
+	args[0], args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[0], args[1])
+	args[2], args[3] = tform(txx, txy, tyx, tyy, dx, dy, args[2], args[3])
+	args[4], args[5] = tform(txx, txy, tyx, tyy, dx, dy, args[4], args[5])
+}
+
+func tform(txx, txy, tyx, tyy int16, dx, dy, x, y fixed.Int26_6) (newX, newY fixed.Int26_6) {
+	const half = 1 << 13
+	newX = dx +
+		fixed.Int26_6((int64(x)*int64(txx)+half)>>14) +
+		fixed.Int26_6((int64(y)*int64(txy)+half)>>14)
+	newY = dy +
+		fixed.Int26_6((int64(x)*int64(tyx)+half)>>14) +
+		fixed.Int26_6((int64(y)*int64(tyy)+half)>>14)
+	return newX, newY
 }
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index 85d96a9..4f10e84 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -43,6 +43,16 @@
 	}
 }
 
+func translate(dx, dy fixed.Int26_6, s Segment) Segment {
+	translateArgs(&s.Args, dx, dy)
+	return s
+}
+
+func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segment {
+	transformArgs(&s.Args, txx, txy, tyx, tyy, dx, dy)
+	return s
+}
+
 func checkSegmentsEqual(got, want []Segment) error {
 	if len(got) != len(want) {
 		return fmt.Errorf("got %d elements, want %d\noverall:\ngot  %v\nwant %v",
diff --git a/font/sfnt/truetype.go b/font/sfnt/truetype.go
index 3c0f880..553302f 100644
--- a/font/sfnt/truetype.go
+++ b/font/sfnt/truetype.go
@@ -255,10 +255,42 @@
 		if flags&flagArgsAreXYValues == 0 {
 			return errUnsupportedCompoundGlyph
 		}
-		// TODO: read the other elem.transform elements.
-		if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 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:]
+				// TODO: find a font that does this, so we can verify that
+				// we've got the xy vs yx ordering right.
+				return errUnsupportedCompoundGlyph
+			}
 		}
+
 		if flags&flagMoreComponents == 0 {
 			break
 		}
@@ -276,8 +308,18 @@
 		}
 		dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy)
 		segs := b.segments[base:]
-		for j := range segs {
-			segs[j] = translate(dx, dy, segs[j])
+		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)
+			}
 		}
 	}