font/sfnt: add Metrics to Font
Change-Id: I4bfcf264e5ee7e4f3ddf89e289d730f230095401 Reviewed-on: https://go-review.googlesource.com/67330 Reviewed-by: Nigel Tao <nigeltao@golang.org>
This commit is contained in:
parent
334384d9e1
commit
f7e31b4ea2
|
@ -564,14 +564,17 @@ type Font struct {
|
||||||
kern table
|
kern table
|
||||||
|
|
||||||
cached struct {
|
cached struct {
|
||||||
|
ascent int32
|
||||||
glyphData glyphData
|
glyphData glyphData
|
||||||
glyphIndex glyphIndexFunc
|
glyphIndex glyphIndexFunc
|
||||||
bounds [4]int16
|
bounds [4]int16
|
||||||
|
descent int32
|
||||||
indexToLocFormat bool // false means short, true means long.
|
indexToLocFormat bool // false means short, true means long.
|
||||||
isColorBitmap bool
|
isColorBitmap bool
|
||||||
isPostScript bool
|
isPostScript bool
|
||||||
kernNumPairs int32
|
kernNumPairs int32
|
||||||
kernOffset int32
|
kernOffset int32
|
||||||
|
lineGap int32
|
||||||
numHMetrics int32
|
numHMetrics int32
|
||||||
postTableVersion uint32
|
postTableVersion uint32
|
||||||
unitsPerEm Units
|
unitsPerEm Units
|
||||||
|
@ -621,7 +624,7 @@ func (f *Font) initialize(offset int, isDfont bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf, numHMetrics, err := f.parseHhea(buf, numGlyphs)
|
buf, ascent, descent, lineGap, numHMetrics, err := f.parseHhea(buf, numGlyphs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -634,14 +637,17 @@ func (f *Font) initialize(offset int, isDfont bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.cached.ascent = ascent
|
||||||
f.cached.glyphData = glyphData
|
f.cached.glyphData = glyphData
|
||||||
f.cached.glyphIndex = glyphIndex
|
f.cached.glyphIndex = glyphIndex
|
||||||
f.cached.bounds = bounds
|
f.cached.bounds = bounds
|
||||||
|
f.cached.descent = descent
|
||||||
f.cached.indexToLocFormat = indexToLocFormat
|
f.cached.indexToLocFormat = indexToLocFormat
|
||||||
f.cached.isColorBitmap = isColorBitmap
|
f.cached.isColorBitmap = isColorBitmap
|
||||||
f.cached.isPostScript = isPostScript
|
f.cached.isPostScript = isPostScript
|
||||||
f.cached.kernNumPairs = kernNumPairs
|
f.cached.kernNumPairs = kernNumPairs
|
||||||
f.cached.kernOffset = kernOffset
|
f.cached.kernOffset = kernOffset
|
||||||
|
f.cached.lineGap = lineGap
|
||||||
f.cached.numHMetrics = numHMetrics
|
f.cached.numHMetrics = numHMetrics
|
||||||
f.cached.postTableVersion = postTableVersion
|
f.cached.postTableVersion = postTableVersion
|
||||||
f.cached.unitsPerEm = unitsPerEm
|
f.cached.unitsPerEm = unitsPerEm
|
||||||
|
@ -838,20 +844,32 @@ func (f *Font) parseHead(buf []byte) (buf1 []byte, bounds [4]int16, indexToLocFo
|
||||||
return buf, bounds, indexToLocFormat, unitsPerEm, nil
|
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
|
// https://www.microsoft.com/typography/OTSPEC/hhea.htm
|
||||||
|
|
||||||
if f.hhea.length != 36 {
|
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)
|
u, err := f.src.u16(buf, f.hhea, 34)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, 0, 0, 0, err
|
||||||
}
|
}
|
||||||
if int32(u) > numGlyphs || u == 0 {
|
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) {
|
func (f *Font) parseHmtx(buf []byte, numGlyphs, numHMetrics int32) (buf1 []byte, err error) {
|
||||||
|
@ -1336,6 +1354,23 @@ func (f *Font) Kern(b *Buffer, x0, x1 GlyphIndex, ppem fixed.Int26_6, h font.Hin
|
||||||
return 0, nil
|
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.
|
// Name returns the name value keyed by the given NameID.
|
||||||
//
|
//
|
||||||
// It returns ErrNotFound if there is no value for that key.
|
// It returns ErrNotFound if there is no value for that key.
|
||||||
|
|
|
@ -214,6 +214,40 @@ func TestBounds(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestGlyphAdvance(t *testing.T) {
|
||||||
testCases := map[string][]struct {
|
testCases := map[string][]struct {
|
||||||
r rune
|
r rune
|
||||||
|
|
Loading…
Reference in New Issue
Block a user