1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package font defines an interface for font faces, for drawing text on an 6 // image. 7 // 8 // Other packages provide font face implementations. For example, a truetype 9 // package would provide one based on .ttf font files. 10 package font // import "golang.org/x/image/font" 11 12 import ( 13 "image" 14 "image/draw" 15 "io" 16 "unicode/utf8" 17 18 "golang.org/x/image/math/fixed" 19 ) 20 21 // TODO: who is responsible for caches (glyph images, glyph indices, kerns)? 22 // The Drawer or the Face? 23 24 // Face is a font face. Its glyphs are often derived from a font file, such as 25 // "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and 26 // hinting. For example, the 12pt and 18pt versions of Comic Sans are two 27 // different faces, even if derived from the same font file. 28 // 29 // A Face is not safe for concurrent use by multiple goroutines, as its methods 30 // may re-use implementation-specific caches and mask image buffers. 31 // 32 // To create a Face, look to other packages that implement specific font file 33 // formats. 34 type Face interface { 35 io.Closer 36 37 // Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's 38 // glyph at the sub-pixel destination location dot, and that glyph's 39 // advance width. 40 // 41 // It returns !ok if the face does not contain a glyph for r. This includes 42 // returning !ok for a fallback glyph (such as substituting a U+FFFD glyph 43 // or OpenType's .notdef glyph), in which case the other return values may 44 // still be non-zero. 45 // 46 // The contents of the mask image returned by one Glyph call may change 47 // after the next Glyph call. Callers that want to cache the mask must make 48 // a copy. 49 Glyph(dot fixed.Point26_6, r rune) ( 50 dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) 51 52 // GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal 53 // to the origin, and that glyph's advance width. 54 // 55 // It returns !ok if the face does not contain a glyph for r. This includes 56 // returning !ok for a fallback glyph (such as substituting a U+FFFD glyph 57 // or OpenType's .notdef glyph), in which case the other return values may 58 // still be non-zero. 59 // 60 // The glyph's ascent and descent are equal to -bounds.Min.Y and 61 // +bounds.Max.Y. The glyph's left-side and right-side bearings are equal 62 // to bounds.Min.X and advance-bounds.Max.X. A visual depiction of what 63 // these metrics are is at 64 // https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png 65 GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) 66 67 // GlyphAdvance returns the advance width of r's glyph. 68 // 69 // It returns !ok if the face does not contain a glyph for r. This includes 70 // returning !ok for a fallback glyph (such as substituting a U+FFFD glyph 71 // or OpenType's .notdef glyph), in which case the other return values may 72 // still be non-zero. 73 GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) 74 75 // Kern returns the horizontal adjustment for the kerning pair (r0, r1). A 76 // positive kern means to move the glyphs further apart. 77 Kern(r0, r1 rune) fixed.Int26_6 78 79 // Metrics returns the metrics for this Face. 80 Metrics() Metrics 81 82 // TODO: ColoredGlyph for various emoji? 83 // TODO: Ligatures? Shaping? 84 } 85 86 // Metrics holds the metrics for a Face. A visual depiction is at 87 // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png 88 type Metrics struct { 89 // Height is the recommended amount of vertical space between two lines of 90 // text. 91 Height fixed.Int26_6 92 93 // Ascent is the distance from the top of a line to its baseline. 94 Ascent fixed.Int26_6 95 96 // Descent is the distance from the bottom of a line to its baseline. The 97 // value is typically positive, even though a descender goes below the 98 // baseline. 99 Descent fixed.Int26_6 100 101 // XHeight is the distance from the top of non-ascending lowercase letters 102 // to the baseline. 103 XHeight fixed.Int26_6 104 105 // CapHeight is the distance from the top of uppercase letters to the 106 // baseline. 107 CapHeight fixed.Int26_6 108 109 // CaretSlope is the slope of a caret as a vector with the Y axis pointing up. 110 // The slope {0, 1} is the vertical caret. 111 CaretSlope image.Point 112 } 113 114 // Drawer draws text on a destination image. 115 // 116 // A Drawer is not safe for concurrent use by multiple goroutines, since its 117 // Face is not. 118 type Drawer struct { 119 // Dst is the destination image. 120 Dst draw.Image 121 // Src is the source image. 122 Src image.Image 123 // Face provides the glyph mask images. 124 Face Face 125 // Dot is the baseline location to draw the next glyph. The majority of the 126 // affected pixels will be above and to the right of the dot, but some may 127 // be below or to the left. For example, drawing a 'j' in an italic face 128 // may affect pixels below and to the left of the dot. 129 Dot fixed.Point26_6 130 131 // TODO: Clip image.Image? 132 // TODO: SrcP image.Point for Src images other than *image.Uniform? How 133 // does it get updated during DrawString? 134 } 135 136 // TODO: should DrawString return the last rune drawn, so the next DrawString 137 // call can kern beforehand? Or should that be the responsibility of the caller 138 // if they really want to do that, since they have to explicitly shift d.Dot 139 // anyway? What if ligatures span more than two runes? What if grapheme 140 // clusters span multiple runes? 141 // 142 // TODO: do we assume that the input is in any particular Unicode Normalization 143 // Form? 144 // 145 // TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take 146 // io.RuneReader, we can't assume that we can rewind the stream. 147 // 148 // TODO: how does this work with line breaking: drawing text up until a 149 // vertical line? Should DrawString return the number of runes drawn? 150 151 // DrawBytes draws s at the dot and advances the dot's location. 152 // 153 // It is equivalent to DrawString(string(s)) but may be more efficient. 154 func (d *Drawer) DrawBytes(s []byte) { 155 prevC := rune(-1) 156 for len(s) > 0 { 157 c, size := utf8.DecodeRune(s) 158 s = s[size:] 159 if prevC >= 0 { 160 d.Dot.X += d.Face.Kern(prevC, c) 161 } 162 dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c) 163 if !dr.Empty() { 164 draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over) 165 } 166 d.Dot.X += advance 167 prevC = c 168 } 169 } 170 171 // DrawString draws s at the dot and advances the dot's location. 172 func (d *Drawer) DrawString(s string) { 173 prevC := rune(-1) 174 for _, c := range s { 175 if prevC >= 0 { 176 d.Dot.X += d.Face.Kern(prevC, c) 177 } 178 dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c) 179 if !dr.Empty() { 180 draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over) 181 } 182 d.Dot.X += advance 183 prevC = c 184 } 185 } 186 187 // BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as 188 // the advance. 189 // 190 // It is equivalent to BoundBytes(string(s)) but may be more efficient. 191 func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { 192 bounds, advance = BoundBytes(d.Face, s) 193 bounds.Min = bounds.Min.Add(d.Dot) 194 bounds.Max = bounds.Max.Add(d.Dot) 195 return 196 } 197 198 // BoundString returns the bounding box of s, drawn at the drawer dot, as well 199 // as the advance. 200 func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { 201 bounds, advance = BoundString(d.Face, s) 202 bounds.Min = bounds.Min.Add(d.Dot) 203 bounds.Max = bounds.Max.Add(d.Dot) 204 return 205 } 206 207 // MeasureBytes returns how far dot would advance by drawing s. 208 // 209 // It is equivalent to MeasureString(string(s)) but may be more efficient. 210 func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) { 211 return MeasureBytes(d.Face, s) 212 } 213 214 // MeasureString returns how far dot would advance by drawing s. 215 func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) { 216 return MeasureString(d.Face, s) 217 } 218 219 // BoundBytes returns the bounding box of s with f, drawn at a dot equal to the 220 // origin, as well as the advance. 221 // 222 // It is equivalent to BoundString(string(s)) but may be more efficient. 223 func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { 224 prevC := rune(-1) 225 for len(s) > 0 { 226 c, size := utf8.DecodeRune(s) 227 s = s[size:] 228 if prevC >= 0 { 229 advance += f.Kern(prevC, c) 230 } 231 b, a, _ := f.GlyphBounds(c) 232 if !b.Empty() { 233 b.Min.X += advance 234 b.Max.X += advance 235 bounds = bounds.Union(b) 236 } 237 advance += a 238 prevC = c 239 } 240 return 241 } 242 243 // BoundString returns the bounding box of s with f, drawn at a dot equal to the 244 // origin, as well as the advance. 245 func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { 246 prevC := rune(-1) 247 for _, c := range s { 248 if prevC >= 0 { 249 advance += f.Kern(prevC, c) 250 } 251 b, a, _ := f.GlyphBounds(c) 252 if !b.Empty() { 253 b.Min.X += advance 254 b.Max.X += advance 255 bounds = bounds.Union(b) 256 } 257 advance += a 258 prevC = c 259 } 260 return 261 } 262 263 // MeasureBytes returns how far dot would advance by drawing s with f. 264 // 265 // It is equivalent to MeasureString(string(s)) but may be more efficient. 266 func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) { 267 prevC := rune(-1) 268 for len(s) > 0 { 269 c, size := utf8.DecodeRune(s) 270 s = s[size:] 271 if prevC >= 0 { 272 advance += f.Kern(prevC, c) 273 } 274 a, _ := f.GlyphAdvance(c) 275 advance += a 276 prevC = c 277 } 278 return advance 279 } 280 281 // MeasureString returns how far dot would advance by drawing s with f. 282 func MeasureString(f Face, s string) (advance fixed.Int26_6) { 283 prevC := rune(-1) 284 for _, c := range s { 285 if prevC >= 0 { 286 advance += f.Kern(prevC, c) 287 } 288 a, _ := f.GlyphAdvance(c) 289 advance += a 290 prevC = c 291 } 292 return advance 293 } 294 295 // Hinting selects how to quantize a vector font's glyph nodes. 296 // 297 // Not all fonts support hinting. 298 type Hinting int 299 300 const ( 301 HintingNone Hinting = iota 302 HintingVertical 303 HintingFull 304 ) 305 306 // Stretch selects a normal, condensed, or expanded face. 307 // 308 // Not all fonts support stretches. 309 type Stretch int 310 311 const ( 312 StretchUltraCondensed Stretch = -4 313 StretchExtraCondensed Stretch = -3 314 StretchCondensed Stretch = -2 315 StretchSemiCondensed Stretch = -1 316 StretchNormal Stretch = +0 317 StretchSemiExpanded Stretch = +1 318 StretchExpanded Stretch = +2 319 StretchExtraExpanded Stretch = +3 320 StretchUltraExpanded Stretch = +4 321 ) 322 323 // Style selects a normal, italic, or oblique face. 324 // 325 // Not all fonts support styles. 326 type Style int 327 328 const ( 329 StyleNormal Style = iota 330 StyleItalic 331 StyleOblique 332 ) 333 334 // Weight selects a normal, light or bold face. 335 // 336 // Not all fonts support weights. 337 // 338 // The named Weight constants (e.g. WeightBold) correspond to CSS' common 339 // weight names (e.g. "Bold"), but the numerical values differ, so that in Go, 340 // the zero value means to use a normal weight. For the CSS names and values, 341 // see https://developer.mozilla.org/en/docs/Web/CSS/font-weight 342 type Weight int 343 344 const ( 345 WeightThin Weight = -3 // CSS font-weight value 100. 346 WeightExtraLight Weight = -2 // CSS font-weight value 200. 347 WeightLight Weight = -1 // CSS font-weight value 300. 348 WeightNormal Weight = +0 // CSS font-weight value 400. 349 WeightMedium Weight = +1 // CSS font-weight value 500. 350 WeightSemiBold Weight = +2 // CSS font-weight value 600. 351 WeightBold Weight = +3 // CSS font-weight value 700. 352 WeightExtraBold Weight = +4 // CSS font-weight value 800. 353 WeightBlack Weight = +5 // CSS font-weight value 900. 354 ) 355