123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // Copyright 2015 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 font defines an interface for font faces, for drawing text on an
- // image.
- //
- // Other packages provide font face implementations. For example, a truetype
- // package would provide one based on .ttf font files.
- package font // import "golang.org/x/image/font"
- import (
- "image"
- "image/draw"
- "io"
- "unicode/utf8"
- "golang.org/x/image/math/fixed"
- )
- // TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
- // The Drawer or the Face?
- // Face is a font face. Its glyphs are often derived from a font file, such as
- // "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
- // hinting. For example, the 12pt and 18pt versions of Comic Sans are two
- // different faces, even if derived from the same font file.
- //
- // A Face is not safe for concurrent use by multiple goroutines, as its methods
- // may re-use implementation-specific caches and mask image buffers.
- //
- // To create a Face, look to other packages that implement specific font file
- // formats.
- type Face interface {
- io.Closer
- // Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
- // glyph at the sub-pixel destination location dot, and that glyph's
- // advance width.
- //
- // 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
- // after the next Glyph call. Callers that want to cache the mask must make
- // a copy.
- Glyph(dot fixed.Point26_6, r rune) (
- dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, 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 are equal to -bounds.Min.Y and
- // +bounds.Max.Y. The glyph's left-side and right-side bearings are equal
- // to bounds.Min.X and advance-bounds.Max.X. A visual depiction of what
- // these metrics are is at
- // https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_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
- // positive kern means to move the glyphs further apart.
- Kern(r0, r1 rune) fixed.Int26_6
- // Metrics returns the metrics for this Face.
- Metrics() Metrics
- // TODO: ColoredGlyph for various emoji?
- // TODO: Ligatures? Shaping?
- }
- // Metrics holds the metrics for a Face. A visual depiction is at
- // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
- type Metrics struct {
- // Height is the recommended amount of vertical space between two lines of
- // text.
- Height fixed.Int26_6
- // Ascent is the distance from the top of a line to its baseline.
- Ascent fixed.Int26_6
- // Descent is the distance from the bottom of a line to its baseline. The
- // 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
- // 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.
- //
- // A Drawer is not safe for concurrent use by multiple goroutines, since its
- // Face is not.
- type Drawer struct {
- // Dst is the destination image.
- Dst draw.Image
- // Src is the source image.
- Src image.Image
- // Face provides the glyph mask images.
- Face Face
- // Dot is the baseline location to draw the next glyph. The majority of the
- // affected pixels will be above and to the right of the dot, but some may
- // be below or to the left. For example, drawing a 'j' in an italic face
- // may affect pixels below and to the left of the dot.
- Dot fixed.Point26_6
- // TODO: Clip image.Image?
- // TODO: SrcP image.Point for Src images other than *image.Uniform? How
- // does it get updated during DrawString?
- }
- // TODO: should DrawString return the last rune drawn, so the next DrawString
- // call can kern beforehand? Or should that be the responsibility of the caller
- // if they really want to do that, since they have to explicitly shift d.Dot
- // anyway? What if ligatures span more than two runes? What if grapheme
- // clusters span multiple runes?
- //
- // TODO: do we assume that the input is in any particular Unicode Normalization
- // Form?
- //
- // TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take
- // io.RuneReader, we can't assume that we can rewind the stream.
- //
- // TODO: how does this work with line breaking: drawing text up until a
- // vertical line? Should DrawString return the number of runes drawn?
- // DrawBytes draws s at the dot and advances the dot's location.
- //
- // It is equivalent to DrawString(string(s)) but may be more efficient.
- func (d *Drawer) DrawBytes(s []byte) {
- prevC := rune(-1)
- for len(s) > 0 {
- c, size := utf8.DecodeRune(s)
- s = s[size:]
- if prevC >= 0 {
- d.Dot.X += d.Face.Kern(prevC, c)
- }
- dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, 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
- }
- draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
- d.Dot.X += advance
- prevC = c
- }
- }
- // DrawString draws s at the dot and advances the dot's location.
- func (d *Drawer) DrawString(s string) {
- prevC := rune(-1)
- for _, c := range s {
- if prevC >= 0 {
- d.Dot.X += d.Face.Kern(prevC, c)
- }
- dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, 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
- }
- draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
- d.Dot.X += advance
- prevC = c
- }
- }
- // BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as
- // the advance.
- //
- // It is equivalent to BoundBytes(string(s)) but may be more efficient.
- func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
- bounds, advance = BoundBytes(d.Face, s)
- bounds.Min = bounds.Min.Add(d.Dot)
- bounds.Max = bounds.Max.Add(d.Dot)
- return
- }
- // BoundString returns the bounding box of s, drawn at the drawer dot, as well
- // as the advance.
- func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
- bounds, advance = BoundString(d.Face, s)
- bounds.Min = bounds.Min.Add(d.Dot)
- bounds.Max = bounds.Max.Add(d.Dot)
- return
- }
- // MeasureBytes returns how far dot would advance by drawing s.
- //
- // It is equivalent to MeasureString(string(s)) but may be more efficient.
- func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) {
- return MeasureBytes(d.Face, s)
- }
- // MeasureString returns how far dot would advance by drawing s.
- func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
- return MeasureString(d.Face, s)
- }
- // BoundBytes returns the bounding box of s with f, drawn at a dot equal to the
- // origin, as well as the advance.
- //
- // It is equivalent to BoundString(string(s)) but may be more efficient.
- func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
- prevC := rune(-1)
- for len(s) > 0 {
- c, size := utf8.DecodeRune(s)
- s = s[size:]
- if prevC >= 0 {
- advance += f.Kern(prevC, c)
- }
- b, a, ok := f.GlyphBounds(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
- }
- b.Min.X += advance
- b.Max.X += advance
- bounds = bounds.Union(b)
- advance += a
- prevC = c
- }
- return
- }
- // BoundString returns the bounding box of s with f, drawn at a dot equal to the
- // origin, as well as the advance.
- func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
- prevC := rune(-1)
- for _, c := range s {
- if prevC >= 0 {
- advance += f.Kern(prevC, c)
- }
- b, a, ok := f.GlyphBounds(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
- }
- b.Min.X += advance
- b.Max.X += advance
- bounds = bounds.Union(b)
- advance += a
- prevC = c
- }
- return
- }
- // MeasureBytes returns how far dot would advance by drawing s with f.
- //
- // It is equivalent to MeasureString(string(s)) but may be more efficient.
- func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) {
- prevC := rune(-1)
- for len(s) > 0 {
- c, size := utf8.DecodeRune(s)
- s = s[size:]
- if prevC >= 0 {
- advance += f.Kern(prevC, c)
- }
- a, ok := f.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
- }
- // MeasureString returns how far dot would advance by drawing s with f.
- func MeasureString(f Face, s string) (advance fixed.Int26_6) {
- prevC := rune(-1)
- for _, c := range s {
- if prevC >= 0 {
- advance += f.Kern(prevC, c)
- }
- a, ok := f.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.
- //
- // Not all fonts support hinting.
- type Hinting int
- const (
- HintingNone Hinting = iota
- HintingVertical
- HintingFull
- )
- // Stretch selects a normal, condensed, or expanded face.
- //
- // Not all fonts support stretches.
- type Stretch int
- const (
- StretchUltraCondensed Stretch = -4
- StretchExtraCondensed Stretch = -3
- StretchCondensed Stretch = -2
- StretchSemiCondensed Stretch = -1
- StretchNormal Stretch = +0
- StretchSemiExpanded Stretch = +1
- StretchExpanded Stretch = +2
- StretchExtraExpanded Stretch = +3
- StretchUltraExpanded Stretch = +4
- )
- // Style selects a normal, italic, or oblique face.
- //
- // Not all fonts support styles.
- type Style int
- const (
- StyleNormal Style = iota
- StyleItalic
- StyleOblique
- )
- // Weight selects a normal, light or bold face.
- //
- // Not all fonts support weights.
- //
- // The named Weight constants (e.g. WeightBold) correspond to CSS' common
- // weight names (e.g. "Bold"), but the numerical values differ, so that in Go,
- // the zero value means to use a normal weight. For the CSS names and values,
- // see https://developer.mozilla.org/en/docs/Web/CSS/font-weight
- type Weight int
- const (
- WeightThin Weight = -3 // CSS font-weight value 100.
- WeightExtraLight Weight = -2 // CSS font-weight value 200.
- WeightLight Weight = -1 // CSS font-weight value 300.
- WeightNormal Weight = +0 // CSS font-weight value 400.
- WeightMedium Weight = +1 // CSS font-weight value 500.
- WeightSemiBold Weight = +2 // CSS font-weight value 600.
- WeightBold Weight = +3 // CSS font-weight value 700.
- WeightExtraBold Weight = +4 // CSS font-weight value 800.
- WeightBlack Weight = +5 // CSS font-weight value 900.
- )
|