font/sfnt: implement implicit vstem before hintmask.

Change-Id: I811bcf94b518dabcfbebd085ad3c6a47c17ef38e
Reviewed-on: https://go-review.googlesource.com/38288
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/postscript.go b/font/sfnt/postscript.go
index 60a8294..5506819 100644
--- a/font/sfnt/postscript.go
+++ b/font/sfnt/postscript.go
@@ -762,7 +762,8 @@
 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 +774,12 @@
 		return
 	}
 	p.type2Charstrings.seenWidth = true
-	switch nArgs {
-	case 0:
-		if p.argStack.top != 1 {
+	if nArgs >= 0 {
+		if p.argStack.top != nArgs+1 {
 			return
 		}
-	case 1:
-		if p.argStack.top <= 1 {
-			return
-		}
-	default:
-		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 +797,7 @@
 }
 
 func t2CStem(p *psInterpreter) error {
-	t2CReadWidth(p, 2)
+	t2CReadWidth(p, -1)
 	if p.argStack.top%2 != 0 {
 		return errInvalidCFFTable
 	}
@@ -818,8 +812,27 @@
 }
 
 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
 	}
diff --git a/font/sfnt/proprietary_test.go b/font/sfnt/proprietary_test.go
index ccf013d..bf26719 100644
--- a/font/sfnt/proprietary_test.go
+++ b/font/sfnt/proprietary_test.go
@@ -80,7 +80,7 @@
 )
 
 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) {
@@ -92,7 +92,7 @@
 }
 
 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) {
@@ -112,11 +112,11 @@
 }
 
 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) {
@@ -442,13 +442,14 @@
 
 // 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/SourceSansPro-Regular.otf": {
 		',': {
+			// -309 -1 115 hstem
+			// 137 61 vstem
 			// 67 -170 rmoveto
 			moveTo(67, -170),
 			// 81 34 50 67 86 vvcurveto
@@ -464,6 +465,8 @@
 		},
 
 		'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 +495,77 @@
 			// 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
+			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 [].
@@ -587,6 +661,8 @@
 		},
 
 		'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA
+			// -43 21 -21 572 84 hstem
+			// 0 515 vstem
 			// 0 vmoveto
 			moveTo(0, 0),
 			// 85 hlineto
@@ -607,6 +683,77 @@
 			lineTo(209, 656),
 			// endchar
 		},
+
+		'Ḫ': { // 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
+			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
+		},
 	},
 
 	"microsoft/Arial.ttf": {