font/sfnt: add Metrics to Font
Change-Id: I4bfcf264e5ee7e4f3ddf89e289d730f230095401
Reviewed-on: https://go-review.googlesource.com/67330
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 53ac94b..cc4ceac 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -564,14 +564,17 @@
kern table
cached struct {
+ ascent int32
glyphData glyphData
glyphIndex glyphIndexFunc
bounds [4]int16
+ descent int32
indexToLocFormat bool // false means short, true means long.
isColorBitmap bool
isPostScript bool
kernNumPairs int32
kernOffset int32
+ lineGap int32
numHMetrics int32
postTableVersion uint32
unitsPerEm Units
@@ -621,7 +624,7 @@
if err != nil {
return err
}
- buf, numHMetrics, err := f.parseHhea(buf, numGlyphs)
+ buf, ascent, descent, lineGap, numHMetrics, err := f.parseHhea(buf, numGlyphs)
if err != nil {
return err
}
@@ -634,14 +637,17 @@
return err
}
+ f.cached.ascent = ascent
f.cached.glyphData = glyphData
f.cached.glyphIndex = glyphIndex
f.cached.bounds = bounds
+ f.cached.descent = descent
f.cached.indexToLocFormat = indexToLocFormat
f.cached.isColorBitmap = isColorBitmap
f.cached.isPostScript = isPostScript
f.cached.kernNumPairs = kernNumPairs
f.cached.kernOffset = kernOffset
+ f.cached.lineGap = lineGap
f.cached.numHMetrics = numHMetrics
f.cached.postTableVersion = postTableVersion
f.cached.unitsPerEm = unitsPerEm
@@ -838,20 +844,32 @@
return buf, bounds, indexToLocFormat, unitsPerEm, nil
}
-func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, numHMetrics int32, err error) {
+func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, ascent, descent, lineGap, numHMetrics int32, err error) {
// https://www.microsoft.com/typography/OTSPEC/hhea.htm
if f.hhea.length != 36 {
- return nil, 0, errInvalidHheaTable
+ return nil, 0, 0, 0, 0, errInvalidHheaTable
}
u, err := f.src.u16(buf, f.hhea, 34)
if err != nil {
- return nil, 0, err
+ return nil, 0, 0, 0, 0, err
}
if int32(u) > numGlyphs || u == 0 {
- return nil, 0, errInvalidHheaTable
+ return nil, 0, 0, 0, 0, errInvalidHheaTable
}
- return buf, int32(u), nil
+ a, err := f.src.u16(buf, f.hhea, 4)
+ if err != nil {
+ return nil, 0, 0, 0, 0, err
+ }
+ d, err := f.src.u16(buf, f.hhea, 6)
+ if err != nil {
+ return nil, 0, 0, 0, 0, err
+ }
+ l, err := f.src.u16(buf, f.hhea, 8)
+ if err != nil {
+ return nil, 0, 0, 0, 0, err
+ }
+ return buf, int32(int16(a)), int32(int16(d)), int32(int16(l)), int32(u), nil
}
func (f *Font) parseHmtx(buf []byte, numGlyphs, numHMetrics int32) (buf1 []byte, err error) {
@@ -1336,6 +1354,23 @@
return 0, nil
}
+// Metrics returns the metrics of this font.
+func (f *Font) Metrics(b *Buffer, ppem fixed.Int26_6, h font.Hinting) (font.Metrics, error) {
+ m := font.Metrics{
+ // TODO: is adding lineGap correct?
+ Height: ppem + scale(fixed.Int26_6(f.cached.lineGap)*ppem, f.cached.unitsPerEm),
+ Ascent: +scale(fixed.Int26_6(f.cached.ascent)*ppem, f.cached.unitsPerEm),
+ Descent: -scale(fixed.Int26_6(f.cached.descent)*ppem, f.cached.unitsPerEm),
+ }
+ if h == font.HintingFull {
+ // Quantize up to a whole pixel.
+ m.Height = (m.Height + 63) &^ 63
+ m.Ascent = (m.Ascent + 63) &^ 63
+ m.Descent = (m.Descent + 63) &^ 63
+ }
+ return m, nil
+}
+
// Name returns the name value keyed by the given NameID.
//
// It returns ErrNotFound if there is no value for that key.
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index 74de278..b9b66a7 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -214,6 +214,40 @@
}
}
+func TestMetrics(t *testing.T) {
+ cmapFont, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ testCases := map[string]struct {
+ font []byte
+ want font.Metrics
+ }{
+ "goregular": {goregular.TTF, font.Metrics{Height: 2048, Ascent: 1935, Descent: 432}},
+ // cmapTest.ttf has a non-zero lineGap.
+ "cmapTest": {cmapFont, font.Metrics{Height: 2232, Ascent: 1365, Descent: 0}},
+ }
+ var b Buffer
+ for name, tc := range testCases {
+ f, err := Parse(tc.font)
+ if err != nil {
+ t.Errorf("name=%q: Parse: %v", name, err)
+ continue
+ }
+ ppem := fixed.Int26_6(f.UnitsPerEm())
+
+ got, err := f.Metrics(&b, ppem, font.HintingNone)
+ if err != nil {
+ t.Errorf("name=%q: Metrics: %v", name, err)
+ continue
+ }
+ if got != tc.want {
+ t.Errorf("name=%q: Metrics: got %v, want %v", name, got, tc.want)
+ continue
+ }
+ }
+}
+
func TestGlyphAdvance(t *testing.T) {
testCases := map[string][]struct {
r rune