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
|
@ -34,8 +34,12 @@ type GlyphBuf struct {
|
||||||
hinter *Hinter
|
hinter *Hinter
|
||||||
scale int32
|
scale int32
|
||||||
// pp1x is the X co-ordinate of the first phantom point.
|
// 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
|
metricsSet bool
|
||||||
|
// tmp is a scratch buffer.
|
||||||
|
tmp []Point
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags for decoding a glyph's contours. These flags are documented at
|
// 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")
|
return UnsupportedError("negative number of contours")
|
||||||
}
|
}
|
||||||
pp1x = g.font.scale(g.scale * (b.XMin - uhm.LeftSideBearing))
|
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
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
np0, ne0 := len(g.Point), len(g.End)
|
np0, ne0 := len(g.Point), len(g.End)
|
||||||
program := g.loadSimple(glyf, ne)
|
program := g.loadSimple(glyf, ne)
|
||||||
// Set the four phantom points. Freetype-Go uses only the first two,
|
g.addPhantomsAndScale(b, uhm, i, np0, g.hinter != nil)
|
||||||
// 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.
|
|
||||||
if 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 {
|
if len(program) != 0 {
|
||||||
err := g.hinter.run(
|
err := g.hinter.run(
|
||||||
program,
|
program,
|
||||||
|
@ -204,7 +191,6 @@ const loadOffset = 10
|
||||||
|
|
||||||
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
||||||
offset := loadOffset
|
offset := loadOffset
|
||||||
|
|
||||||
for i := 0; i < ne; i++ {
|
for i := 0; i < ne; i++ {
|
||||||
g.End = append(g.End, 1+int(u16(glyf, offset)))
|
g.End = append(g.End, 1+int(u16(glyf, offset)))
|
||||||
offset += 2
|
offset += 2
|
||||||
|
@ -274,7 +260,9 @@ func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
||||||
return program
|
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
|
// Flags for decoding a compound glyph. These flags are documented at
|
||||||
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
||||||
const (
|
const (
|
||||||
|
@ -290,7 +278,9 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
|
||||||
flagUseMyMetrics
|
flagUseMyMetrics
|
||||||
flagOverlapCompound
|
flagOverlapCompound
|
||||||
)
|
)
|
||||||
for offset := loadOffset; ; {
|
np0, ne0 := len(g.Point), len(g.End)
|
||||||
|
offset := loadOffset
|
||||||
|
for {
|
||||||
flags := u16(glyf, offset)
|
flags := u16(glyf, offset)
|
||||||
component := Index(u16(glyf, offset+2))
|
component := Index(u16(glyf, offset+2))
|
||||||
dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
|
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
|
return err
|
||||||
}
|
}
|
||||||
if hasTransform {
|
if hasTransform {
|
||||||
for i := np0; i < len(g.Point); i++ {
|
for j := np0; j < len(g.Point); j++ {
|
||||||
p := &g.Point[i]
|
p := &g.Point[j]
|
||||||
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
|
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
|
||||||
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
|
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
|
||||||
newY := int32((int64(p.X)*int64(transform[1])+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
|
dx = (dx + 32) &^ 63
|
||||||
dy = (dy + 32) &^ 63
|
dy = (dy + 32) &^ 63
|
||||||
}
|
}
|
||||||
for i := np0; i < len(g.Point); i++ {
|
for j := np0; j < len(g.Point); j++ {
|
||||||
p := &g.Point[i]
|
p := &g.Point[j]
|
||||||
p.X += dx
|
p.X += dx
|
||||||
p.Y += dy
|
p.Y += dy
|
||||||
}
|
}
|
||||||
|
@ -356,8 +346,55 @@ func (g *GlyphBuf) loadCompound(recursion int32, glyf []byte, useMyMetrics bool)
|
||||||
break
|
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.
|
// 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.
|
// An HMetric holds the horizontal metrics of a single glyph.
|
||||||
type HMetric struct {
|
type HMetric struct {
|
||||||
AdvanceWidth int32
|
AdvanceWidth, LeftSideBearing int32
|
||||||
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.
|
// A FormatError reports that the input is not a valid TrueType font.
|
||||||
|
@ -94,7 +98,7 @@ type cm struct {
|
||||||
type Font struct {
|
type Font struct {
|
||||||
// Tables sliced from the TTF data. The different tables are documented
|
// Tables sliced from the TTF data. The different tables are documented
|
||||||
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
|
// 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
|
cmapIndexes []byte
|
||||||
|
|
||||||
|
@ -344,7 +348,7 @@ func (f *Font) Index(x rune) Index {
|
||||||
// the given index.
|
// the given index.
|
||||||
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
|
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
|
||||||
j := int(i)
|
j := int(i)
|
||||||
if j >= f.nGlyph {
|
if j < 0 || f.nGlyph <= j {
|
||||||
return HMetric{}
|
return HMetric{}
|
||||||
}
|
}
|
||||||
if j >= f.nHMetric {
|
if j >= f.nHMetric {
|
||||||
|
@ -368,6 +372,33 @@ func (f *Font) HMetric(scale int32, i Index) HMetric {
|
||||||
return h
|
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.
|
// Kerning returns the kerning for the given glyph pair.
|
||||||
func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
|
func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
|
||||||
if f.nKern == 0 {
|
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])
|
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
case "prep":
|
case "prep":
|
||||||
f.prep, err = readTable(ttf, ttf[x+8:x+16])
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -53,6 +53,9 @@ func TestParse(t *testing.T) {
|
||||||
if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
|
if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
|
||||||
t.Errorf("HMetric: got %v, want %v", 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 {
|
if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
|
||||||
t.Errorf("Kerning: got %v, want %v", got, want)
|
t.Errorf("Kerning: got %v, want %v", got, want)
|
||||||
}
|
}
|
||||||
|
@ -253,7 +256,7 @@ var scalingTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
{"luxisr", 12, -1},
|
{"luxisr", 12, -1},
|
||||||
{"x-arial-bold", 11, 0},
|
{"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-droid-sans-japanese", 9, 0},
|
||||||
{"x-times-new-roman", 13, 0},
|
{"x-times-new-roman", 13, 0},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue