From 991ec62608f3c0da01d400756917825d1e2fd528 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 19 Sep 2018 13:48:13 +0200 Subject: [PATCH] image/font: expose caret slope Change-Id: I775224dd3fc7e5b6c2fc5c4a7d3db83bb36d047d Reviewed-on: https://go-review.googlesource.com/136255 Run-TryBot: Elias Naur TryBot-Result: Gobot Gobot Reviewed-by: Nigel Tao --- font/basicfont/basicfont.go | 11 +++++---- font/basicfont/basicfont_test.go | 3 ++- font/font.go | 4 ++++ font/opentype/face_test.go | 4 +++- font/plan9font/plan9font.go | 22 ++++++++++-------- font/plan9font/plan9font_test.go | 4 +++- font/sfnt/sfnt.go | 40 +++++++++++++++++++++----------- font/sfnt/sfnt_test.go | 7 ++++-- 8 files changed, 61 insertions(+), 34 deletions(-) diff --git a/font/basicfont/basicfont.go b/font/basicfont/basicfont.go index acd1179..1550381 100644 --- a/font/basicfont/basicfont.go +++ b/font/basicfont/basicfont.go @@ -77,11 +77,12 @@ func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } func (f *Face) Metrics() font.Metrics { return 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), + 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), + CaretSlope: image.Point{X: 0, Y: 1}, } } diff --git a/font/basicfont/basicfont_test.go b/font/basicfont/basicfont_test.go index f3409d7..43cf635 100644 --- a/font/basicfont/basicfont_test.go +++ b/font/basicfont/basicfont_test.go @@ -5,13 +5,14 @@ package basicfont import ( + "image" "testing" "golang.org/x/image/font" ) func TestMetrics(t *testing.T) { - want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704} + want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704, CaretSlope: image.Point{X: 0, Y: 1}} if got := Face7x13.Metrics(); got != want { t.Errorf("Face7x13: Metrics: got %v want %v", got, want) } diff --git a/font/font.go b/font/font.go index 56a3310..4d9d63c 100644 --- a/font/font.go +++ b/font/font.go @@ -94,6 +94,10 @@ type Metrics struct { // CapHeight is the distance from the top of uppercase letters to the // baseline. CapHeight fixed.Int26_6 + + // CaretSlope is the slope of a caret as a vector with the Y axis pointing up. + // The slope {0, 1} is the vertical caret. + CaretSlope image.Point } // Drawer draws text on a destination image. diff --git a/font/opentype/face_test.go b/font/opentype/face_test.go index 8389bd8..c8b7054 100644 --- a/font/opentype/face_test.go +++ b/font/opentype/face_test.go @@ -5,6 +5,7 @@ package opentype import ( + "image" "testing" "golang.org/x/image/font" @@ -82,7 +83,8 @@ func TestFaceKern(t *testing.T) { } func TestFaceMetrics(t *testing.T) { - want := font.Metrics{Height: 888, Ascent: 726, Descent: 162, XHeight: 407, CapHeight: 555} + want := font.Metrics{Height: 888, Ascent: 726, Descent: 162, XHeight: 407, CapHeight: 555, + CaretSlope: image.Point{X: 0, Y: 1}} got := regular.Metrics() if got != want { t.Fatalf("metrics failed. got=%#v. want=%#v", got, want) diff --git a/font/plan9font/plan9font.go b/font/plan9font/plan9font.go index 82cd882..96d6944 100644 --- a/font/plan9font/plan9font.go +++ b/font/plan9font/plan9font.go @@ -67,11 +67,12 @@ func (f *subface) Metrics() font.Metrics { // 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, + 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, + CaretSlope: image.Point{X: 0, Y: 1}, } } @@ -153,11 +154,12 @@ 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, + 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, + CaretSlope: image.Point{X: 0, Y: 1}, } } diff --git a/font/plan9font/plan9font_test.go b/font/plan9font/plan9font_test.go index 04a701d..573f63b 100644 --- a/font/plan9font/plan9font_test.go +++ b/font/plan9font/plan9font_test.go @@ -5,6 +5,7 @@ package plan9font import ( + "image" "io/ioutil" "path" "path/filepath" @@ -25,7 +26,8 @@ func TestMetrics(t *testing.T) { if err != nil { t.Fatal(err) } - want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704} + want := font.Metrics{Height: 832, Ascent: 704, Descent: 128, XHeight: 704, CapHeight: 704, + CaretSlope: image.Point{X: 0, Y: 1}} if got := face.Metrics(); got != want { t.Errorf("unicode.7x13.font: Metrics: got %v, want %v", got, want) } diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go index 3f952e9..0b46d0a 100644 --- a/font/sfnt/sfnt.go +++ b/font/sfnt/sfnt.go @@ -21,6 +21,7 @@ package sfnt // import "golang.org/x/image/font/sfnt" import ( "errors" + "image" "io" "golang.org/x/image/font" @@ -579,6 +580,7 @@ type Font struct { lineGap int32 numHMetrics int32 postTableVersion uint32 + slope [2]int32 unitsPerEm Units xHeight int32 } @@ -627,7 +629,7 @@ func (f *Font) initialize(offset int, isDfont bool) error { if err != nil { return err } - buf, ascent, descent, lineGap, numHMetrics, err := f.parseHhea(buf, numGlyphs) + buf, ascent, descent, lineGap, run, rise, numHMetrics, err := f.parseHhea(buf, numGlyphs) if err != nil { return err } @@ -658,6 +660,7 @@ func (f *Font) initialize(offset int, isDfont bool) error { f.cached.lineGap = lineGap f.cached.numHMetrics = numHMetrics f.cached.postTableVersion = postTableVersion + f.cached.slope = [2]int32{run, rise} f.cached.unitsPerEm = unitsPerEm f.cached.xHeight = xHeight @@ -853,32 +856,40 @@ func (f *Font) parseHead(buf []byte) (buf1 []byte, bounds [4]int16, indexToLocFo return buf, bounds, indexToLocFormat, unitsPerEm, nil } -func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, ascent, descent, lineGap, numHMetrics int32, err error) { +func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, ascent, descent, lineGap, run, rise, numHMetrics int32, err error) { // https://www.microsoft.com/typography/OTSPEC/hhea.htm if f.hhea.length != 36 { - return nil, 0, 0, 0, 0, errInvalidHheaTable + return nil, 0, 0, 0, 0, 0, 0, errInvalidHheaTable } u, err := f.src.u16(buf, f.hhea, 34) if err != nil { - return nil, 0, 0, 0, 0, err + return nil, 0, 0, 0, 0, 0, 0, err } if int32(u) > numGlyphs || u == 0 { - return nil, 0, 0, 0, 0, errInvalidHheaTable + return nil, 0, 0, 0, 0, 0, 0, errInvalidHheaTable } a, err := f.src.u16(buf, f.hhea, 4) if err != nil { - return nil, 0, 0, 0, 0, err + return nil, 0, 0, 0, 0, 0, 0, err } d, err := f.src.u16(buf, f.hhea, 6) if err != nil { - return nil, 0, 0, 0, 0, err + return nil, 0, 0, 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 nil, 0, 0, 0, 0, 0, 0, err } - return buf, int32(int16(a)), int32(int16(d)), int32(int16(l)), int32(u), nil + ru, err := f.src.u16(buf, f.hhea, 20) + if err != nil { + return nil, 0, 0, 0, 0, 0, 0, err + } + ri, err := f.src.u16(buf, f.hhea, 18) + if err != nil { + return nil, 0, 0, 0, 0, 0, 0, err + } + return buf, int32(int16(a)), int32(int16(d)), int32(int16(l)), int32(int16(ru)), int32(int16(ri)), int32(u), nil } func (f *Font) parseHmtx(buf []byte, numGlyphs, numHMetrics int32) (buf1 []byte, err error) { @@ -1384,11 +1395,12 @@ func (f *Font) Kern(b *Buffer, x0, x1 GlyphIndex, ppem fixed.Int26_6, h font.Hin // 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{ - 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), + 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), + CaretSlope: image.Point{X: int(f.cached.slope[0]), Y: int(f.cached.slope[1])}, } if h == font.HintingFull { // Quantize up to a whole pixel. diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go index f5ddd2b..f91f3a1 100644 --- a/font/sfnt/sfnt_test.go +++ b/font/sfnt/sfnt_test.go @@ -7,6 +7,7 @@ package sfnt import ( "bytes" "fmt" + "image" "io/ioutil" "path/filepath" "testing" @@ -223,9 +224,11 @@ func TestMetrics(t *testing.T) { font []byte want font.Metrics }{ - "goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432, XHeight: 1086, CapHeight: 1480}}, + "goregular": {goregular.TTF, font.Metrics{Height: 2367, Ascent: 1935, Descent: 432, XHeight: 1086, CapHeight: 1480, + CaretSlope: image.Point{X: 0, Y: 1}}}, // cmapTest.ttf has a non-zero lineGap. - "cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0, XHeight: 800, CapHeight: 800}}, + "cmapTest": {cmapFont, font.Metrics{Height: 1549, Ascent: 1365, Descent: 0, XHeight: 800, CapHeight: 800, + CaretSlope: image.Point{X: 20, Y: 100}}}, } var b Buffer for name, tc := range testCases {