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>
This commit is contained in:
Nigel Tao 2017-03-23 12:44:43 +11:00
parent ecc1a9abb6
commit 59d151bf14
2 changed files with 180 additions and 20 deletions

View File

@ -762,7 +762,8 @@ var psOperators = [...][2][]psOperator{
const escapeByte = 12 const escapeByte = 12
// t2CReadWidth reads the optional width adjustment. If present, it is on the // 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, // 5177.Type2.pdf page 16 Note 4 says: "The first stack-clearing operator,
// which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask, // which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask,
@ -773,19 +774,12 @@ func t2CReadWidth(p *psInterpreter, nArgs int32) {
return return
} }
p.type2Charstrings.seenWidth = true p.type2Charstrings.seenWidth = true
switch nArgs { if nArgs >= 0 {
case 0: if p.argStack.top != nArgs+1 {
if p.argStack.top != 1 {
return
}
case 1:
if p.argStack.top <= 1 {
return
}
default:
if p.argStack.top%nArgs != 1 {
return return
} }
} else if p.argStack.top&1 == 0 {
return
} }
// When parsing a standalone CFF, we'd save the value of p.argStack.a[0] // 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, // here as it defines the glyph's width (horizontal advance). Specifically,
@ -803,7 +797,7 @@ func t2CReadWidth(p *psInterpreter, nArgs int32) {
} }
func t2CStem(p *psInterpreter) error { func t2CStem(p *psInterpreter) error {
t2CReadWidth(p, 2) t2CReadWidth(p, -1)
if p.argStack.top%2 != 0 { if p.argStack.top%2 != 0 {
return errInvalidCFFTable return errInvalidCFFTable
} }
@ -818,8 +812,27 @@ func t2CStem(p *psInterpreter) error {
} }
func t2CMask(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 hintBytes := (p.type2Charstrings.hintBits + 7) / 8
t2CReadWidth(p, hintBytes)
if len(p.instructions) < int(hintBytes) { if len(p.instructions) < int(hintBytes) {
return errInvalidCFFTable return errInvalidCFFTable
} }

View File

@ -80,7 +80,7 @@ var (
) )
func TestProprietaryAdobeSourceCodeProOTF(t *testing.T) { 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) { func TestProprietaryAdobeSourceCodeProTTF(t *testing.T) {
@ -92,7 +92,7 @@ func TestProprietaryAdobeSourceHanSansSC(t *testing.T) {
} }
func TestProprietaryAdobeSourceSansProOTF(t *testing.T) { 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) { func TestProprietaryAdobeSourceSansProTTF(t *testing.T) {
@ -112,11 +112,11 @@ func TestProprietaryAppleGeezaPro1(t *testing.T) {
} }
func TestProprietaryAppleHiragino0(t *testing.T) { 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) { 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) { func TestProprietaryMicrosoftArial(t *testing.T) {
@ -442,13 +442,14 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
// proprietaryGlyphTestCases hold a sample of each font's glyph vectors. The // proprietaryGlyphTestCases hold a sample of each font's glyph vectors. The
// numerical values can be verified by running the ttx tool, remembering that: // numerical values can be verified by running the ttx tool, remembering that:
// - for PostScript glyphs, ttx coordinates are relative, and hstem / vstem // - for PostScript glyphs, ttx coordinates are relative.
// operators are hinting-related and can be ignored.
// - for TrueType glyphs, ttx coordinates are absolute, and consecutive // - for TrueType glyphs, ttx coordinates are absolute, and consecutive
// off-curve points implies an on-curve point at the midpoint. // off-curve points implies an on-curve point at the midpoint.
var proprietaryGlyphTestCases = map[string]map[rune][]Segment{ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
"adobe/SourceSansPro-Regular.otf": { "adobe/SourceSansPro-Regular.otf": {
',': { ',': {
// -309 -1 115 hstem
// 137 61 vstem
// 67 -170 rmoveto // 67 -170 rmoveto
moveTo(67, -170), moveTo(67, -170),
// 81 34 50 67 86 vvcurveto // 81 34 50 67 86 vvcurveto
@ -464,6 +465,8 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
}, },
'Q': { 'Q': {
// 106 -165 70 87 65 538 73 hstem
// 52 86 388 87 vstem
// 332 57 rmoveto // 332 57 rmoveto
moveTo(332, 57), moveTo(332, 57),
// -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto // -117 -77 106 168 163 77 101 117 117 77 -101 -163 -168 -77 -106 -117 hvcurveto
@ -492,6 +495,77 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
// endchar // 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 'ī': { // U+012B LATIN SMALL LETTER I WITH MACRON
// 92 callgsubr # 92 + bias = 199. // 92 callgsubr # 92 + bias = 199.
// : # Arg stack is []. // : # Arg stack is [].
@ -587,6 +661,8 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
}, },
'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA 'Λ': { // U+039B GREEK CAPITAL LETTER LAMDA
// -43 21 -21 572 84 hstem
// 0 515 vstem
// 0 vmoveto // 0 vmoveto
moveTo(0, 0), moveTo(0, 0),
// 85 hlineto // 85 hlineto
@ -607,6 +683,77 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
lineTo(209, 656), lineTo(209, 656),
// endchar // 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": { "microsoft/Arial.ttf": {