freetype/truetype: run compound glyph hinting instructions.

Also add API for vertical metrics.

R=bsiegert
CC=golang-dev, remyoudompheng
https://codereview.appspot.com/21330043
This commit is contained in:
Nigel Tao 2013-11-05 09:58:40 +11:00
parent 1f81822fe1
commit cdaff3c716
3 changed files with 107 additions and 34 deletions

View file

@ -34,8 +34,12 @@ type GlyphBuf struct {
hinter *Hinter
scale int32
// pp1x is the X co-ordinate of the first phantom point.
pp1x int32
pp1x int32
// metricsSet is whether the glyph's metrics have been set yet. For a
// compound glyph, a sub-glyph may override the outer glyph's metrics.
metricsSet bool
// tmp is a scratch buffer.
tmp []Point
}
// Flags for decoding a glyph's contours. These flags are documented at
@ -127,31 +131,14 @@ func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error)
return UnsupportedError("negative number of contours")
}
pp1x = g.font.scale(g.scale * (b.XMin - uhm.LeftSideBearing))
if err := g.loadCompound(recursion, glyf, useMyMetrics); err != nil {
if err := g.loadCompound(recursion, b, uhm, i, glyf, useMyMetrics); err != nil {
return err
}
} else {
np0, ne0 := len(g.Point), len(g.End)
program := g.loadSimple(glyf, ne)
// Set the four phantom points. Freetype-Go uses only the first two,
// but the hinting bytecode may expect four.
g.Point = append(g.Point,
Point{X: b.XMin - uhm.LeftSideBearing},
Point{X: b.XMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
Point{},
Point{},
)
// Scale and hint the glyph.
g.addPhantomsAndScale(b, uhm, i, np0, g.hinter != nil)
if g.hinter != nil {
g.InFontUnits = append(g.InFontUnits, g.Point[np0:]...)
}
for i := np0; i < len(g.Point); i++ {
p := &g.Point[i]
p.X = g.font.scale(g.scale * p.X)
p.Y = g.font.scale(g.scale * p.Y)
}
if g.hinter != nil {
g.Unhinted = append(g.Unhinted, g.Point[np0:]...)
if len(program) != 0 {
err := g.hinter.run(
program,
@ -204,7 +191,6 @@ const loadOffset = 10
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
offset := loadOffset
for i := 0; i < ne; i++ {
g.End = append(g.End, 1+int(u16(glyf, offset)))
offset += 2
@ -274,7 +260,9 @@ func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
return program
}
func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool) error {
func (g *GlyphBuf) loadCompound(recursion int32, b Bounds, uhm HMetric, i Index,
glyf []byte, useMyMetrics bool) error {
// Flags for decoding a compound glyph. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
@ -290,7 +278,9 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
flagUseMyMetrics
flagOverlapCompound
)
for offset := loadOffset; ; {
np0, ne0 := len(g.Point), len(g.End)
offset := loadOffset
for {
flags := u16(glyf, offset)
component := Index(u16(glyf, offset+2))
dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
@ -331,8 +321,8 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
return err
}
if hasTransform {
for i := np0; i < len(g.Point); i++ {
p := &g.Point[i]
for j := np0; j < len(g.Point); j++ {
p := &g.Point[j]
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) +
@ -346,8 +336,8 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
dx = (dx + 32) &^ 63
dy = (dy + 32) &^ 63
}
for i := np0; i < len(g.Point); i++ {
p := &g.Point[i]
for j := np0; j < len(g.Point); j++ {
p := &g.Point[j]
p.X += dx
p.Y += dy
}
@ -356,8 +346,55 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
break
}
}
// TODO: hint the compound glyph.
return nil
// Hint the compound glyph.
if g.hinter == nil || offset+2 > len(glyf) {
return nil
}
instrLen := int(u16(glyf, offset))
offset += 2
if instrLen == 0 {
return nil
}
program := glyf[offset : offset+instrLen]
g.addPhantomsAndScale(b, uhm, i, len(g.Point), false)
points := g.Point[np0:]
g.Point = g.Point[:len(g.Point)-4]
for j := range points {
points[j].Flags &^= flagTouchedX | flagTouchedY
}
// Hinting instructions of a composite glyph completely refer to the
// (already) hinted subglyphs.
g.tmp = append(g.tmp[:0], points...)
return g.hinter.run(program, points, g.tmp, g.tmp, g.End[ne0:])
}
func (g *GlyphBuf) addPhantomsAndScale(b Bounds, uhm HMetric, i Index, np0 int, appendOther bool) {
// Add the four phantom points.
uvm := g.font.unscaledVMetric(i)
g.Point = append(g.Point,
Point{X: b.XMin - uhm.LeftSideBearing},
Point{X: b.XMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
Point{Y: b.YMax + uvm.TopSideBearing},
Point{Y: b.YMax + uvm.TopSideBearing - uvm.AdvanceHeight},
)
// Scale the points.
if appendOther {
g.InFontUnits = append(g.InFontUnits, g.Point[np0:]...)
}
for i := np0; i < len(g.Point); i++ {
p := &g.Point[i]
p.X = g.font.scale(g.scale * p.X)
p.Y = g.font.scale(g.scale * p.Y)
}
if appendOther {
g.Unhinted = append(g.Unhinted, g.Point[np0:]...)
}
// Round the 2nd and 4th phantom point to the grid.
p := &g.Point[len(g.Point)-3]
p.X = (p.X + 32) &^ 63
p = &g.Point[len(g.Point)-1]
p.Y = (p.Y + 32) &^ 63
}
// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable.

View file

@ -33,8 +33,12 @@ type Bounds struct {
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
AdvanceWidth int32
LeftSideBearing int32
AdvanceWidth, LeftSideBearing int32
}
// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
AdvanceHeight, TopSideBearing int32
}
// A FormatError reports that the input is not a valid TrueType font.
@ -94,7 +98,7 @@ type cm struct {
type Font struct {
// Tables sliced from the TTF data. The different tables are documented
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
cmap, cvt, fpgm, glyf, head, hhea, hmtx, kern, loca, maxp, prep []byte
cmap, cvt, fpgm, glyf, head, hhea, hmtx, kern, loca, maxp, prep, vmtx []byte
cmapIndexes []byte
@ -344,7 +348,7 @@ func (f *Font) Index(x rune) Index {
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
j := int(i)
if j >= f.nGlyph {
if j < 0 || f.nGlyph <= j {
return HMetric{}
}
if j >= f.nHMetric {
@ -368,6 +372,33 @@ func (f *Font) HMetric(scale int32, i Index) HMetric {
return h
}
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index.
func (f *Font) unscaledVMetric(i Index) (v VMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return VMetric{}
}
if 4*j+4 <= len(f.vmtx) {
return VMetric{
AdvanceHeight: int32(u16(f.vmtx, 4*j)),
TopSideBearing: int32(int16(u16(f.vmtx, 4*j+2))),
}
}
return VMetric{
AdvanceHeight: f.fUnitsPerEm,
TopSideBearing: 0,
}
}
// VMetric returns the vertical metrics for the glyph with the given index.
func (f *Font) VMetric(scale int32, i Index) VMetric {
v := f.unscaledVMetric(i)
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
return v
}
// Kerning returns the kerning for the given glyph pair.
func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
if f.nKern == 0 {
@ -471,6 +502,8 @@ func parse(ttf []byte, offset int) (font *Font, err error) {
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
case "prep":
f.prep, err = readTable(ttf, ttf[x+8:x+16])
case "vmtx":
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
}
if err != nil {
return

View file

@ -53,6 +53,9 @@ func TestParse(t *testing.T) {
if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
t.Errorf("HMetric: got %v, want %v", got, want)
}
if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
t.Errorf("VMetric: got %v, want %v", got, want)
}
if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
t.Errorf("Kerning: got %v, want %v", got, want)
}
@ -253,7 +256,7 @@ var scalingTestCases = []struct {
}{
{"luxisr", 12, -1},
{"x-arial-bold", 11, 0},
{"x-deja-vu-sans-oblique", 17, 513},
{"x-deja-vu-sans-oblique", 17, 2077},
{"x-droid-sans-japanese", 9, 0},
{"x-times-new-roman", 13, 0},
}