font/sfnt: implement {hh,vv}curveto.

Change-Id: I873f8b273d2fe9f39df7d333c36976f1b45239a0
Reviewed-on: https://go-review.googlesource.com/37917
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/example_test.go b/font/sfnt/example_test.go
index f9a6697..1743156 100644
--- a/font/sfnt/example_test.go
+++ b/font/sfnt/example_test.go
@@ -38,6 +38,9 @@
 		log.Fatalf("GlyphIndex: no glyph index found for the rune 'G'")
 	}
 	segments, err := f.LoadGlyph(&b, x, fixed.I(ppem), nil)
+	if err != nil {
+		log.Fatalf("LoadGlyph: %v", err)
+	}
 
 	r := vector.NewRasterizer(width, height)
 	r.DrawOp = draw.Src
diff --git a/font/sfnt/postscript.go b/font/sfnt/postscript.go
index 8142bdc..ca1b831 100644
--- a/font/sfnt/postscript.go
+++ b/font/sfnt/postscript.go
@@ -566,8 +566,8 @@
 		23: {-1, "vstemhm", t2CStem},
 		24: {}, // rcurveline.
 		25: {}, // rlinecurve.
-		26: {}, // vvcurveto.
-		27: {}, // hhcurveto.
+		26: {-1, "vvcurveto", t2CVvcurveto},
+		27: {-1, "hhcurveto", t2CHhcurveto},
 		28: {}, // shortint.
 		29: {}, // callgsubr.
 		30: {-1, "vhcurveto", t2CVhcurveto},
@@ -770,6 +770,12 @@
 
 // As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
 //
+// hhcurveto is:
+//	- dy1 {dxa dxb dyb dxc}+
+//
+// vvcurveto is:
+//	- dx1 {dya dxb dyb dyc}+
+//
 // hvcurveto is one of:
 //	- dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
 //	- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
@@ -778,59 +784,84 @@
 //	- dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
 //	- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?
 
-func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, false) }
-func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true) }
+func t2CHhcurveto(p *psInterpreter) error { return t2CCurveto(p, false, false) }
+func t2CVvcurveto(p *psInterpreter) error { return t2CCurveto(p, false, true) }
+func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, true, false) }
+func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true, true) }
 
-func t2CCurveto(p *psInterpreter, vertical bool) error {
+// t2CCurveto implements the hh / vv / hv / vh xxcurveto operators. N relative
+// cubic curve requires 6*N control points, but only 4*N+0 or 4*N+1 are used
+// here: all (or all but one) of the piecewise cubic curve's tangents are
+// implicitly horizontal or vertical.
+//
+// swap is whether that implicit horizontal / vertical constraint swaps as you
+// move along the piecewise cubic curve. If swap is false, the constraints are
+// either all horizontal or all vertical. If swap is true, it alternates.
+//
+// vertical is whether the first implicit constraint is vertical.
+func t2CCurveto(p *psInterpreter, swap, vertical bool) error {
 	if !p.type2Charstrings.seenWidth || p.stack.top < 4 {
 		return errInvalidCFFTable
 	}
-	for i := int32(0); i != p.stack.top; vertical = !vertical {
-		if vertical {
-			i = t2CVcurveto(p, i)
-		} else {
-			i = t2CHcurveto(p, i)
+
+	i := int32(0)
+	switch p.stack.top & 3 {
+	case 0:
+		// No-op.
+	case 1:
+		if swap {
+			break
 		}
+		i = 1
+		if vertical {
+			p.type2Charstrings.x += p.stack.a[0]
+		} else {
+			p.type2Charstrings.y += p.stack.a[0]
+		}
+	default:
+		return errInvalidCFFTable
+	}
+
+	for i != p.stack.top {
+		i = t2CCurveto4(p, swap, vertical, i)
 		if i < 0 {
 			return errInvalidCFFTable
 		}
+		if swap {
+			vertical = !vertical
+		}
 	}
 	return nil
 }
 
-func t2CHcurveto(p *psInterpreter, i int32) (j int32) {
+func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) {
 	if i+4 > p.stack.top {
 		return -1
 	}
 	dxa := p.stack.a[i+0]
-	dxb := p.stack.a[i+1]
-	dyb := p.stack.a[i+2]
-	dyc := p.stack.a[i+3]
-	dxc := int32(0)
-	i += 4
-	if i+1 == p.stack.top {
-		dxc = p.stack.a[i]
-		i++
-	}
-	t2CAppendCubeto(p, dxa, 0, dxb, dyb, dxc, dyc)
-	return i
-}
-
-func t2CVcurveto(p *psInterpreter, i int32) (j int32) {
-	if i+4 > p.stack.top {
-		return -1
-	}
-	dya := p.stack.a[i+0]
+	dya := int32(0)
 	dxb := p.stack.a[i+1]
 	dyb := p.stack.a[i+2]
 	dxc := p.stack.a[i+3]
 	dyc := int32(0)
 	i += 4
-	if i+1 == p.stack.top {
-		dyc = p.stack.a[i]
-		i++
+
+	if vertical {
+		dxa, dya = dya, dxa
 	}
-	t2CAppendCubeto(p, 0, dya, dxb, dyb, dxc, dyc)
+
+	if swap {
+		if i+1 == p.stack.top {
+			dyc = p.stack.a[i]
+			i++
+		}
+	}
+
+	if swap != vertical {
+		dxc, dyc = dyc, dxc
+	}
+
+	t2CAppendCubeto(p, dxa, dya, dxb, dyb, dxc, dyc)
 	return i
 }
 
diff --git a/font/sfnt/proprietary_test.go b/font/sfnt/proprietary_test.go
index b534a5b..b105ee5 100644
--- a/font/sfnt/proprietary_test.go
+++ b/font/sfnt/proprietary_test.go
@@ -186,6 +186,23 @@
 		}
 	}
 
+	for r, want := range proprietaryGlyphTestCases[qualifiedFilename] {
+		x, err := f.GlyphIndex(&buf, r)
+		if err != nil {
+			t.Errorf("GlyphIndex(%q): %v", r, err)
+			continue
+		}
+		got, err := f.LoadGlyph(&buf, x, ppem, nil)
+		if err != nil {
+			t.Errorf("LoadGlyph(%q): %v", r, err)
+			continue
+		}
+		if err := checkSegmentsEqual(got, want); err != nil {
+			t.Errorf("LoadGlyph(%q): %v", r, err)
+			continue
+		}
+	}
+
 kernLoop:
 	for _, tc := range proprietaryKernTestCases[qualifiedFilename] {
 		var indexes [2]GlyphIndex
@@ -312,6 +329,113 @@
 	},
 }
 
+// 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 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/SourceSansPro-Regular.otf": {
+		',': {
+			// - contour #0
+			// 67 -170 rmoveto
+			moveTo(67, -170),
+			// 81 34 50 67 86 vvcurveto
+			cubeTo(148, -136, 198, -69, 198, 17),
+			// 60 -26 37 -43 -33 -28 -22 -36 -37 27 -20 32 3 4 0 1 3 vhcurveto
+			cubeTo(198, 77, 172, 114, 129, 114),
+			cubeTo(96, 114, 68, 92, 68, 56),
+			cubeTo(68, 19, 95, -1, 127, -1),
+			cubeTo(130, -1, 134, -1, 137, 0),
+			// 1 -53 -34 -44 -57 -25 rrcurveto
+			cubeTo(138, -53, 104, -97, 47, -122),
+		},
+		'Q': {
+			// - contour #0
+			// 332 57 rmoveto
+			moveTo(332, 57),
+			// -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto
+			cubeTo(215, 57, 138, 163, 138, 331),
+			cubeTo(138, 494, 215, 595, 332, 595),
+			cubeTo(449, 595, 526, 494, 526, 331),
+			cubeTo(526, 163, 449, 57, 332, 57),
+			// - contour #1
+			// 201 -222 rmoveto
+			moveTo(533, -165),
+			// 39 35 7 8 20 hvcurveto
+			cubeTo(572, -165, 607, -158, 627, -150),
+			// -16 64 rlineto
+			lineTo(611, -86),
+			// -5 -18 -22 -4 -29 hhcurveto
+			cubeTo(593, -91, 571, -95, 542, -95),
+			// -71 -60 29 58 -30 hvcurveto
+			cubeTo(471, -95, 411, -66, 381, -8),
+			// 139 24 93 126 189 vvcurveto
+			cubeTo(520, 16, 613, 142, 613, 331),
+			// 209 -116 128 -165 -165 -115 -127 -210 -193 96 -127 143 -20 vhcurveto
+			cubeTo(613, 540, 497, 668, 332, 668),
+			cubeTo(167, 668, 52, 541, 52, 331),
+			cubeTo(52, 138, 148, 11, 291, -9),
+			// -90 38 83 -66 121 hhcurveto
+			cubeTo(329, -99, 412, -165, 533, -165),
+		},
+	},
+
+	"microsoft/Arial.ttf": {
+		',': {
+			// - contour #0
+			moveTo(182, 0),
+			lineTo(182, 205),
+			lineTo(387, 205),
+			lineTo(387, 0),
+			quadTo(387, -113, 347, -182),
+			quadTo(307, -252, 220, -290),
+			lineTo(170, -213),
+			quadTo(227, -188, 254, -139),
+			quadTo(281, -91, 284, 0),
+			lineTo(182, 0),
+		},
+		'i': {
+			// - contour #0
+			moveTo(136, 1259),
+			lineTo(136, 1466),
+			lineTo(316, 1466),
+			lineTo(316, 1259),
+			lineTo(136, 1259),
+			// - contour #1
+			moveTo(136, 0),
+			lineTo(136, 1062),
+			lineTo(316, 1062),
+			lineTo(316, 0),
+			lineTo(136, 0),
+		},
+		'o': {
+			// - contour #0
+			moveTo(68, 531),
+			quadTo(68, 826, 232, 968),
+			quadTo(369, 1086, 566, 1086),
+			quadTo(785, 1086, 924, 942),
+			quadTo(1063, 799, 1063, 546),
+			quadTo(1063, 341, 1001, 223),
+			quadTo(940, 106, 822, 41),
+			quadTo(705, -24, 566, -24),
+			quadTo(343, -24, 205, 119),
+			quadTo(68, 262, 68, 531),
+			// - contour #1
+			moveTo(253, 531),
+			quadTo(253, 327, 342, 225),
+			quadTo(431, 124, 566, 124),
+			quadTo(700, 124, 789, 226),
+			quadTo(878, 328, 878, 537),
+			quadTo(878, 734, 788, 835),
+			quadTo(699, 937, 566, 937),
+			quadTo(431, 937, 342, 836),
+			quadTo(253, 735, 253, 531),
+		},
+	},
+}
+
 type kernTestCase struct {
 	ppem    fixed.Int26_6
 	hinting font.Hinting