image/font: expose caret slope

Change-Id: I775224dd3fc7e5b6c2fc5c4a7d3db83bb36d047d
Reviewed-on: https://go-review.googlesource.com/136255
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
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) 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 @@
 	// 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 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 @@
 	// 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 @@
 	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 @@
 	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 @@
 
 import (
 	"errors"
+	"image"
 	"io"
 
 	"golang.org/x/image/font"
@@ -579,6 +580,7 @@
 		lineGap          int32
 		numHMetrics      int32
 		postTableVersion uint32
+		slope            [2]int32
 		unitsPerEm       Units
 		xHeight          int32
 	}
@@ -627,7 +629,7 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 @@
 import (
 	"bytes"
 	"fmt"
+	"image"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
@@ -223,9 +224,11 @@
 		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 {