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:
parent
1f81822fe1
commit
cdaff3c716
3 changed files with 107 additions and 34 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue