font,font/sfnt: expose font x-Height and capHeight
Change-Id: I6e3e6e51c7e270e16413c23990f6df5e22cbfeb6 Reviewed-on: https://go-review.googlesource.com/135555 Run-TryBot: Elias Naur <elias.naur@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Nigel Tao <nigeltao@golang.org>
This commit is contained in:
parent
c73c2afc3b
commit
e1a1ede689
|
@ -80,6 +80,8 @@ func (f *Face) Metrics() font.Metrics {
|
|||
Height: fixed.I(f.Height),
|
||||
Ascent: fixed.I(f.Ascent),
|
||||
Descent: fixed.I(f.Descent),
|
||||
XHeight: fixed.I(f.Ascent),
|
||||
CapHeight: fixed.I(f.Ascent),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
font/basicfont/basicfont_test.go
Normal file
18
font/basicfont/basicfont_test.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package basicfont
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704}
|
||||
if got := Face7x13.Metrics(); got != want {
|
||||
t.Errorf("Face7x13: Metrics: got %v want %v", got, want)
|
||||
}
|
||||
}
|
|
@ -86,6 +86,14 @@ type Metrics struct {
|
|||
// value is typically positive, even though a descender goes below the
|
||||
// baseline.
|
||||
Descent fixed.Int26_6
|
||||
|
||||
// XHeight is the distance from the top of non-ascending lowercase letters
|
||||
// to the baseline.
|
||||
XHeight fixed.Int26_6
|
||||
|
||||
// CapHeight is the distance from the top of uppercase letters to the
|
||||
// baseline.
|
||||
CapHeight fixed.Int26_6
|
||||
}
|
||||
|
||||
// Drawer draws text on a destination image.
|
||||
|
|
|
@ -82,7 +82,7 @@ func TestFaceKern(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFaceMetrics(t *testing.T) {
|
||||
want := font.Metrics{Height: 888, Ascent: 726, Descent: 162}
|
||||
want := font.Metrics{Height: 888, Ascent: 726, Descent: 162, XHeight: 407, CapHeight: 555}
|
||||
got := regular.Metrics()
|
||||
if got != want {
|
||||
t.Fatalf("metrics failed. got=%#v. want=%#v", got, want)
|
||||
|
|
|
@ -62,10 +62,16 @@ func (f *subface) Close() error { return nil }
|
|||
func (f *subface) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
|
||||
|
||||
func (f *subface) Metrics() font.Metrics {
|
||||
// Approximate XHeight with the ascent of lowercase 'x'.
|
||||
xbounds, _, _ := f.GlyphBounds('x')
|
||||
// The same applies to CapHeight, using the uppercase 'H'.
|
||||
hbounds, _, _ := f.GlyphBounds('H')
|
||||
return font.Metrics{
|
||||
Height: fixed.I(f.height),
|
||||
Ascent: fixed.I(f.ascent),
|
||||
Descent: fixed.I(f.height - f.ascent),
|
||||
XHeight: -xbounds.Min.Y,
|
||||
CapHeight: -hbounds.Min.Y,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,10 +150,14 @@ func (f *face) Close() error { return nil }
|
|||
func (f *face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
|
||||
|
||||
func (f *face) Metrics() font.Metrics {
|
||||
xbounds, _, _ := f.GlyphBounds('x')
|
||||
hbounds, _, _ := f.GlyphBounds('H')
|
||||
return font.Metrics{
|
||||
Height: fixed.I(f.height),
|
||||
Ascent: fixed.I(f.ascent),
|
||||
Descent: fixed.I(f.height - f.ascent),
|
||||
XHeight: -xbounds.Min.Y,
|
||||
CapHeight: -hbounds.Min.Y,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,42 @@ package plan9font
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
readFile := func(name string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filepath.FromSlash(path.Join("../testdata/fixed", name)))
|
||||
}
|
||||
data, err := readFile("unicode.7x13.font")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
face, err := ParseFont(data, readFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704}
|
||||
if got := face.Metrics(); got != want {
|
||||
t.Errorf("unicode.7x13.font: Metrics: got %v, want %v", got, want)
|
||||
}
|
||||
subData, err := readFile("7x13.0000")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subFace, err := ParseSubfont(subData, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := subFace.Metrics(); got != want {
|
||||
t.Errorf("7x13.0000: Metrics: got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseSubfont(b *testing.B) {
|
||||
subfontData, err := ioutil.ReadFile(filepath.FromSlash("../testdata/fixed/7x13.0000"))
|
||||
if err != nil {
|
||||
|
|
|
@ -86,6 +86,7 @@ var (
|
|||
errInvalidLocationData = errors.New("sfnt: invalid location data")
|
||||
errInvalidMaxpTable = errors.New("sfnt: invalid maxp table")
|
||||
errInvalidNameTable = errors.New("sfnt: invalid name table")
|
||||
errInvalidOS2Table = errors.New("sfnt: invalid OS/2 table")
|
||||
errInvalidPostTable = errors.New("sfnt: invalid post table")
|
||||
errInvalidSingleFont = errors.New("sfnt: invalid single font (data is a font collection)")
|
||||
errInvalidSourceData = errors.New("sfnt: invalid source data")
|
||||
|
@ -565,6 +566,7 @@ type Font struct {
|
|||
|
||||
cached struct {
|
||||
ascent int32
|
||||
capHeight int32
|
||||
glyphData glyphData
|
||||
glyphIndex glyphIndexFunc
|
||||
bounds [4]int16
|
||||
|
@ -578,6 +580,7 @@ type Font struct {
|
|||
numHMetrics int32
|
||||
postTableVersion uint32
|
||||
unitsPerEm Units
|
||||
xHeight int32
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,12 +635,17 @@ func (f *Font) initialize(offset int, isDfont bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf, xHeight, capHeight, err := f.parseOS2(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf, postTableVersion, err := f.parsePost(buf, numGlyphs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.cached.ascent = ascent
|
||||
f.cached.capHeight = capHeight
|
||||
f.cached.glyphData = glyphData
|
||||
f.cached.glyphIndex = glyphIndex
|
||||
f.cached.bounds = bounds
|
||||
|
@ -651,6 +659,7 @@ func (f *Font) initialize(offset int, isDfont bool) error {
|
|||
f.cached.numHMetrics = numHMetrics
|
||||
f.cached.postTableVersion = postTableVersion
|
||||
f.cached.unitsPerEm = unitsPerEm
|
||||
f.cached.xHeight = xHeight
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1045,6 +1054,24 @@ func (f *Font) parseGlyphData(buf []byte, numGlyphs int32, indexToLocFormat, isP
|
|||
return buf, ret, isColorBitmap, nil
|
||||
}
|
||||
|
||||
func (f *Font) parseOS2(buf []byte) (buf1 []byte, xHeight, capHeight int32, err error) {
|
||||
// https://docs.microsoft.com/da-dk/typography/opentype/spec/os2
|
||||
|
||||
const headerSize = 96
|
||||
if f.os2.length < headerSize {
|
||||
return nil, 0, 0, errInvalidOS2Table
|
||||
}
|
||||
xh, err := f.src.u16(buf, f.os2, 86)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
ch, err := f.src.u16(buf, f.os2, 88)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return buf, int32(int16(xh)), int32(int16(ch)), nil
|
||||
}
|
||||
|
||||
func (f *Font) parsePost(buf []byte, numGlyphs int32) (buf1 []byte, postTableVersion uint32, err error) {
|
||||
// https://www.microsoft.com/typography/otspec/post.htm
|
||||
|
||||
|
@ -1360,12 +1387,16 @@ func (f *Font) Metrics(b *Buffer, ppem fixed.Int26_6, h font.Hinting) (font.Metr
|
|||
Height: scale(fixed.Int26_6(f.cached.ascent-f.cached.descent+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),
|
||||
XHeight: scale(fixed.Int26_6(f.cached.xHeight)*ppem, f.cached.unitsPerEm),
|
||||
CapHeight: scale(fixed.Int26_6(f.cached.capHeight)*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
|
||||
m.XHeight = (m.XHeight + 63) &^ 63
|
||||
m.CapHeight = (m.CapHeight + 63) &^ 63
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
|
|
@ -223,9 +223,9 @@ func TestMetrics(t *testing.T) {
|
|||
font []byte
|
||||
want font.Metrics
|
||||
}{
|
||||
"goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432}},
|
||||
"goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432, XHeight: 1086, CapHeight: 1480}},
|
||||
// cmapTest.ttf has a non-zero lineGap.
|
||||
"cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0}},
|
||||
"cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0, XHeight: 800, CapHeight: 800}},
|
||||
}
|
||||
var b Buffer
|
||||
for name, tc := range testCases {
|
||||
|
|
Loading…
Reference in New Issue
Block a user