shiny/font: add per-glyph metrics.
Change-Id: Ie5c7e29b4eb7bd87b8e99de941f2f94b042e268f Reviewed-on: https://go-review.googlesource.com/13827 Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
ab08d42a8a
commit
627898392a
43
font/font.go
43
font/font.go
|
@ -37,8 +37,9 @@ type Face interface {
|
||||||
|
|
||||||
// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
|
// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
|
||||||
// glyph at the sub-pixel destination location dot. It also returns the new
|
// glyph at the sub-pixel destination location dot. It also returns the new
|
||||||
// dot after adding the glyph's advance width. It returns !ok if the face
|
// dot after adding the glyph's advance width.
|
||||||
// does not contain a glyph for r.
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
//
|
//
|
||||||
// The contents of the mask image returned by one Glyph call may change
|
// The contents of the mask image returned by one Glyph call may change
|
||||||
// after the next Glyph call. Callers that want to cache the mask must make
|
// after the next Glyph call. Callers that want to cache the mask must make
|
||||||
|
@ -46,11 +47,26 @@ type Face interface {
|
||||||
Glyph(dot fixed.Point26_6, r rune) (
|
Glyph(dot fixed.Point26_6, r rune) (
|
||||||
newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool)
|
newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool)
|
||||||
|
|
||||||
|
// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
|
||||||
|
// to the origin, and that glyph's advance width.
|
||||||
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
|
//
|
||||||
|
// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A
|
||||||
|
// visual depiction of what these metrics are is at
|
||||||
|
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool)
|
||||||
|
|
||||||
|
// GlyphAdvance returns the advance width of r's glyph.
|
||||||
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
|
GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
|
||||||
|
|
||||||
// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
|
// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
|
||||||
// positive kern means to move the glyphs further apart.
|
// positive kern means to move the glyphs further apart.
|
||||||
Kern(r0, r1 rune) fixed.Int26_6
|
Kern(r0, r1 rune) fixed.Int26_6
|
||||||
|
|
||||||
// TODO: per-font and per-glyph Metrics.
|
// TODO: per-font Metrics.
|
||||||
// TODO: ColoredGlyph for various emoji?
|
// TODO: ColoredGlyph for various emoji?
|
||||||
// TODO: Ligatures? Shaping?
|
// TODO: Ligatures? Shaping?
|
||||||
}
|
}
|
||||||
|
@ -102,6 +118,7 @@ func (d *Drawer) DrawString(s string) {
|
||||||
if !ok {
|
if !ok {
|
||||||
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
// the Drawer or the Face?
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
||||||
|
@ -109,6 +126,26 @@ func (d *Drawer) DrawString(s string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MeasureString returns how far dot would advance by drawing s.
|
||||||
|
func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
|
||||||
|
var prevC rune
|
||||||
|
for i, c := range s {
|
||||||
|
if i != 0 {
|
||||||
|
advance += d.Face.Kern(prevC, c)
|
||||||
|
}
|
||||||
|
a, ok := d.Face.GlyphAdvance(c)
|
||||||
|
if !ok {
|
||||||
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
advance += a
|
||||||
|
prevC = c
|
||||||
|
}
|
||||||
|
return advance
|
||||||
|
}
|
||||||
|
|
||||||
// Hinting selects how to quantize a vector font's glyph nodes.
|
// Hinting selects how to quantize a vector font's glyph nodes.
|
||||||
//
|
//
|
||||||
// Not all fonts support hinting.
|
// Not all fonts support hinting.
|
||||||
|
|
|
@ -94,6 +94,31 @@ func (f *subface) Glyph(dot fixed.Point26_6, r rune) (
|
||||||
return newDot, dr, f.img, image.Point{int(i.x), int(i.top)}, true
|
return newDot, dr, f.img, image.Point{int(i.x), int(i.top)}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *subface) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||||
|
r -= f.firstRune
|
||||||
|
if r < 0 || f.n <= int(r) {
|
||||||
|
return fixed.Rectangle26_6{}, 0, false
|
||||||
|
}
|
||||||
|
i := &f.fontchars[r+0]
|
||||||
|
j := &f.fontchars[r+1]
|
||||||
|
|
||||||
|
bounds = fixed.R(
|
||||||
|
int(i.left),
|
||||||
|
int(i.top)-f.ascent,
|
||||||
|
int(i.left)+int(j.x-i.x),
|
||||||
|
int(i.bottom)-f.ascent,
|
||||||
|
)
|
||||||
|
return bounds, fixed.Int26_6(i.width) << 6, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *subface) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||||
|
r -= f.firstRune
|
||||||
|
if r < 0 || f.n <= int(r) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return fixed.Int26_6(f.fontchars[r].width) << 6, true
|
||||||
|
}
|
||||||
|
|
||||||
// runeRange maps a single rune range [lo, hi] to a lazily loaded subface. Both
|
// runeRange maps a single rune range [lo, hi] to a lazily loaded subface. Both
|
||||||
// ends of the range are inclusive.
|
// ends of the range are inclusive.
|
||||||
type runeRange struct {
|
type runeRange struct {
|
||||||
|
@ -121,6 +146,27 @@ func (f *face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
|
||||||
func (f *face) Glyph(dot fixed.Point26_6, r rune) (
|
func (f *face) Glyph(dot fixed.Point26_6, r rune) (
|
||||||
newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool) {
|
newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool) {
|
||||||
|
|
||||||
|
if s, rr := f.subface(r); s != nil {
|
||||||
|
return s.Glyph(dot, rr)
|
||||||
|
}
|
||||||
|
return fixed.Point26_6{}, image.Rectangle{}, nil, image.Point{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||||
|
if s, rr := f.subface(r); s != nil {
|
||||||
|
return s.GlyphBounds(rr)
|
||||||
|
}
|
||||||
|
return fixed.Rectangle26_6{}, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||||
|
if s, rr := f.subface(r); s != nil {
|
||||||
|
return s.GlyphAdvance(rr)
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *face) subface(r rune) (*subface, rune) {
|
||||||
// Fall back on U+FFFD if we can't find r.
|
// Fall back on U+FFFD if we can't find r.
|
||||||
for _, rr := range [2]rune{r, '\ufffd'} {
|
for _, rr := range [2]rune{r, '\ufffd'} {
|
||||||
// We have to do linear, not binary search. plan9port's
|
// We have to do linear, not binary search. plan9port's
|
||||||
|
@ -148,10 +194,10 @@ func (f *face) Glyph(dot fixed.Point26_6, r rune) (
|
||||||
}
|
}
|
||||||
x.subface = sub.(*subface)
|
x.subface = sub.(*subface)
|
||||||
}
|
}
|
||||||
return x.subface.Glyph(dot, rr)
|
return x.subface, rr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fixed.Point26_6{}, image.Rectangle{}, nil, image.Point{}, false
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseFont parses a Plan 9 font file. data is the contents of that font file,
|
// ParseFont parses a Plan 9 font file. data is the contents of that font file,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user