font/sfnt: implement {hh,vv}curveto.
Change-Id: I873f8b273d2fe9f39df7d333c36976f1b45239a0 Reviewed-on: https://go-review.googlesource.com/37917 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
069db1da13
commit
793f3be7da
|
@ -38,6 +38,9 @@ func ExampleRasterizeGlyph() {
|
||||||
log.Fatalf("GlyphIndex: no glyph index found for the rune 'G'")
|
log.Fatalf("GlyphIndex: no glyph index found for the rune 'G'")
|
||||||
}
|
}
|
||||||
segments, err := f.LoadGlyph(&b, x, fixed.I(ppem), nil)
|
segments, err := f.LoadGlyph(&b, x, fixed.I(ppem), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("LoadGlyph: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
r := vector.NewRasterizer(width, height)
|
r := vector.NewRasterizer(width, height)
|
||||||
r.DrawOp = draw.Src
|
r.DrawOp = draw.Src
|
||||||
|
|
|
@ -566,8 +566,8 @@ var psOperators = [...][2][]psOperator{
|
||||||
23: {-1, "vstemhm", t2CStem},
|
23: {-1, "vstemhm", t2CStem},
|
||||||
24: {}, // rcurveline.
|
24: {}, // rcurveline.
|
||||||
25: {}, // rlinecurve.
|
25: {}, // rlinecurve.
|
||||||
26: {}, // vvcurveto.
|
26: {-1, "vvcurveto", t2CVvcurveto},
|
||||||
27: {}, // hhcurveto.
|
27: {-1, "hhcurveto", t2CHhcurveto},
|
||||||
28: {}, // shortint.
|
28: {}, // shortint.
|
||||||
29: {}, // callgsubr.
|
29: {}, // callgsubr.
|
||||||
30: {-1, "vhcurveto", t2CVhcurveto},
|
30: {-1, "vhcurveto", t2CVhcurveto},
|
||||||
|
@ -770,6 +770,12 @@ func t2CRlineto(p *psInterpreter) error {
|
||||||
|
|
||||||
// As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
|
// 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:
|
// hvcurveto is one of:
|
||||||
// - dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
|
// - dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
|
||||||
// - {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
|
// - {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
|
||||||
|
@ -778,59 +784,84 @@ func t2CRlineto(p *psInterpreter) error {
|
||||||
// - dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
|
// - dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
|
||||||
// - {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?
|
// - {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?
|
||||||
|
|
||||||
func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, false) }
|
func t2CHhcurveto(p *psInterpreter) error { return t2CCurveto(p, false, false) }
|
||||||
func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true) }
|
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 {
|
if !p.type2Charstrings.seenWidth || p.stack.top < 4 {
|
||||||
return errInvalidCFFTable
|
return errInvalidCFFTable
|
||||||
}
|
}
|
||||||
for i := int32(0); i != p.stack.top; vertical = !vertical {
|
|
||||||
if vertical {
|
i := int32(0)
|
||||||
i = t2CVcurveto(p, i)
|
switch p.stack.top & 3 {
|
||||||
} else {
|
case 0:
|
||||||
i = t2CHcurveto(p, i)
|
// 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 {
|
if i < 0 {
|
||||||
return errInvalidCFFTable
|
return errInvalidCFFTable
|
||||||
}
|
}
|
||||||
|
if swap {
|
||||||
|
vertical = !vertical
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
if i+4 > p.stack.top {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
dxa := p.stack.a[i+0]
|
dxa := p.stack.a[i+0]
|
||||||
dxb := p.stack.a[i+1]
|
dya := int32(0)
|
||||||
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]
|
|
||||||
dxb := p.stack.a[i+1]
|
dxb := p.stack.a[i+1]
|
||||||
dyb := p.stack.a[i+2]
|
dyb := p.stack.a[i+2]
|
||||||
dxc := p.stack.a[i+3]
|
dxc := p.stack.a[i+3]
|
||||||
dyc := int32(0)
|
dyc := int32(0)
|
||||||
i += 4
|
i += 4
|
||||||
if i+1 == p.stack.top {
|
|
||||||
dyc = p.stack.a[i]
|
if vertical {
|
||||||
i++
|
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
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,23 @@ func testProprietary(t *testing.T, proprietor, filename string, minNumGlyphs, fi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
kernLoop:
|
||||||
for _, tc := range proprietaryKernTestCases[qualifiedFilename] {
|
for _, tc := range proprietaryKernTestCases[qualifiedFilename] {
|
||||||
var indexes [2]GlyphIndex
|
var indexes [2]GlyphIndex
|
||||||
|
@ -312,6 +329,113 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
type kernTestCase struct {
|
||||||
ppem fixed.Int26_6
|
ppem fixed.Int26_6
|
||||||
hinting font.Hinting
|
hinting font.Hinting
|
||||||
|
|
Loading…
Reference in New Issue
Block a user