1 /** 2 * Font 3 */ 4 module d2d.sdl2.Font; 5 6 import std.algorithm; 7 import std.array; 8 import std.conv; 9 import std..string; 10 import d2d.sdl2; 11 12 /** 13 * The three types of encoding TTF can use 14 * LATIN1 is the set of ASCII characters; its glyphs are represented as chars 15 * UTF8 is the set of "Unicode" characters; it can handle all ASCII characters as well because ASCII is a subset of Unicode 16 * UNICODE encoding is determined by operating system; Windows handles UNICODE using UTF16, while UNIX systems use UTF8 17 * Note: UNICODE is identical to UTF8 in some cases but uses ushorts to represent glyphs instead of using chars 18 */ 19 enum Encoding { 20 LATIN1, 21 UTF8, 22 UNICODE 23 } 24 25 /** 26 * Fonts are contexts for displaying strings 27 * Fonts describe how each character in the string looks and how they are spaced 28 * Each font includes a collection of supported glyphs and general information 29 * TODO: allow for multiline printing 30 */ 31 class Font { 32 33 private TTF_Font* font; 34 35 /** 36 * Returns the raw SDL data of this object 37 */ 38 @property TTF_Font* handle() { 39 return this.font; 40 } 41 42 /** 43 * Sets the style (bold, italic, etc.) of this font 44 * Style should be inputted as a bitmask composed of: 45 * TTF_STYLE_BOLD 46 * TTF_STYLE_ITALIC 47 * TTF_STYLE_UNDERLINE 48 * TTF_STYLE_STRIKETHROUGH 49 * If the style is normal, use TTF_STYLE_NORMAL 50 * For multiple styles, use a bitwise OR operator (TTF_STYLE_BOLD|TTF_STYLE_ITALIC means both bold and italic, etc.) 51 */ 52 @property void style(int style) { 53 TTF_SetFontStyle(this.font, style); 54 } 55 56 /** 57 * Gets the style (bold, italic, etc.) of this font 58 * Style is returned as a bitmask composed of: 59 * TTF_STYLE_BOLD 60 * TTF_STYLE_ITALIC 61 * TTF_STYLE_UNDERLINE 62 * TTF_STYLE_STRIKETHROUGH 63 * If the style is normal, the value returned will be TTF_STYLE_NORMAL 64 * Otherwise, use bitwise and operations to get individual values (style&TTF_STYLE_BOLD returns whether the font is bold, etc.) 65 */ 66 @property int style() { 67 return TTF_GetFontStyle(this.font); 68 } 69 70 /** 71 * Sets the size of the font's outline 72 * Use outline = 0 to disable outlining 73 */ 74 @property void outline(int outline) { 75 TTF_SetFontOutline(this.font, outline); 76 } 77 78 /** 79 * Gets the size of the font's outline 80 * Outline is constant across glyphs in a font 81 */ 82 @property int outline() { 83 return TTF_GetFontOutline(this.font); 84 } 85 86 /** 87 * Sets the font's hinting type 88 * Type is taken as a value matching one of the following: 89 * TTF_HINTING_NORMAL 90 * TTF_HINTING_LIGHT 91 * TTF_HINTING_MONO 92 * TTF_HINTING_NONE 93 * Hinting type is how the font is programmed to map onto the pixels on a screen 94 * Note: the method flushes the internal cache of glyphs in the font, even if there is no change in hinting 95 * It may be useful to first check the font's hinting type 96 */ 97 @property void hinting(int hinting) { 98 TTF_SetFontHinting(this.font, hinting); 99 } 100 101 /** 102 * Gets the font's hinting type 103 * Type is returned as a value matching one of the following: 104 * TTF_HINTING_NORMAL 105 * TTF_HINTING_LIGHT 106 * TTF_HINTING_MONO 107 * TTF_HINTING_NONE 108 * Type defaults to TTF_HINTING_NORMAL if no type has been set 109 * Hinting type is how the font is programmed to map onto the pixels on a screen 110 */ 111 @property int hinting() { 112 return TTF_GetFontHinting(this.font); 113 } 114 115 /** 116 * Sets the font's kerning setting 117 * Default for newly created fonts is true 118 * Kerning setting determines whether the spacing between individual characters is adjusted for a more pleasing result 119 */ 120 @property void kerning(bool shouldKern) { 121 TTF_SetFontKerning(this.font, shouldKern ? 1 : 0); 122 } 123 124 /** 125 * Gets the font's kerning setting 126 * Default for a newly created fonts is true 127 * Kerning setting determines whether the spacing between individual characters is adjusted for a more pleasing result 128 */ 129 @property bool kerning() { 130 return TTF_GetFontKerning(this.font) != 0; 131 } 132 133 /** 134 * Gets the maximum pixel height of all glyphs in this font 135 * Useful for multiline printing 136 */ 137 @property int height() { 138 return TTF_FontHeight(this.font); 139 } 140 141 /** 142 * Gets the maximum pixel ascent of all glyphs in this font 143 * Ascent is the distance from the top of the glyph to the baseline 144 */ 145 @property int ascent() { 146 return TTF_FontAscent(this.font); 147 } 148 149 /** 150 * Gets the maximum pixel descent of all glyphs in this font 151 * Descent is the distance from the bottom of the glyph to the baseline 152 */ 153 @property int descent() { 154 return TTF_FontDescent(this.font); 155 } 156 157 /** 158 * Gets the recommended pixel height of a line of text in this font 159 * This represents the distance from the baseline to the top of the line 160 * Line skip should be larger than height in most cases 161 */ 162 @property int lineSkip() { 163 return TTF_FontLineSkip(this.font); 164 } 165 166 /** 167 * Gets the number of faces in this font 168 * Faces are sub-fonts that vary slightly from the main font 169 */ 170 @property long faces() { 171 return TTF_FontFaces(this.font); 172 } 173 174 /** 175 * Checks if the current font face of this font is fixed-width 176 * Fixed-width fonts are monospace - each character is the same length 177 * The pixel length of a string of fixed-width characters is the width of the characters times the amount of characters 178 */ 179 @property bool isFixedWidth() { 180 return TTF_FontFaceIsFixedWidth(this.font) > 0; 181 } 182 183 /** 184 * Gets the font face family name (Times, Courier, etc.) 185 * Returns null if not available 186 */ 187 @property string familyName() { 188 return TTF_FontFaceFamilyName(this.font).to!string; 189 } 190 191 /** 192 * Gets the font face style name (Sans, Serif, etc.) 193 * Returns null if not available 194 */ 195 @property string styleName() { 196 return TTF_FontFaceStyleName(this.font).to!string; 197 } 198 199 /** 200 * Constructs a font from a font file 201 */ 202 this(string file, int psize, int index = 0) { 203 loadLibTTF(); 204 this.font = ensureSafe(TTF_OpenFontIndex(file.toStringz, psize, index)); 205 } 206 207 /** 208 * Constructs a font from an already existing TTF_Font 209 */ 210 this(TTF_Font* alreadyExisting) { 211 this.font = alreadyExisting; 212 } 213 214 /** 215 * Ensures that SDL_TTF can dispose of this font 216 */ 217 ~this() { 218 TTF_CloseFont(this.font); 219 } 220 221 /** 222 * Checks if the font supports the given glyph 223 */ 224 bool isProvided(char glyph) { 225 return TTF_GlyphIsProvided(this.font, glyph) != 0; 226 } 227 228 /** 229 * Gets the minimum offset of the glyph 230 * Returns the bottom left corner of the rectangle in which the glyph is inscribed in Cartesian coordinates 231 */ 232 iVector minimumOffset(char glyph) { 233 iVector offset = new iVector(0, 0); 234 TTF_GlyphMetrics(this.font, glyph, &offset.x, &offset.y, null, null, null); 235 return offset; 236 } 237 238 /** 239 * Gets the maximum offset of the glyph 240 * Returns the top right corner of the rectangle in which the glyph is inscribed in Cartesian coordinates 241 */ 242 iVector maximumOffset(char glyph) { 243 iVector offset = new iVector(0, 0); 244 TTF_GlyphMetrics(this.font, glyph, null, null, &offset.x, &offset.y, null); 245 return offset; 246 } 247 248 /** 249 * Gets a rectangle describing the offset of the given glyph 250 * Width of the rectangle is width of the glyph 251 */ 252 iRectangle offset(char glyph) { 253 iVector minOffset = this.minimumOffset(glyph); 254 iVector maxOffset = this.maximumOffset(glyph); 255 return new iRectangle(minOffset.x, maxOffset.y, 256 maxOffset.x - minOffset.x, maxOffset.y - minOffset.y); 257 } 258 259 /** 260 * Gets the advance offset of the glyph 261 * The advance offset is the distance the pen must be shifted after drawing a glyph 262 * Controls spacing between glyphs on an individual basis 263 */ 264 int advanceOffset(char glyph) { 265 int offset; 266 TTF_GlyphMetrics(this.font, glyph, null, null, null, null, &offset); 267 return offset; 268 } 269 270 /** 271 * Renders the text on an 8-bit palettized surface with the given color 272 * Background is transparent 273 * Text is less smooth than other render options 274 * This is the fastest rendering speed, and color can be changed without having to render again 275 */ 276 Surface renderTextSolid(string text, Color color = Color(0, 0, 0), Encoding T = Encoding.UTF8) { 277 switch (T) { 278 case Encoding.LATIN1: 279 return new Surface(TTF_RenderText_Solid(this.font, 280 text.toStringz, *color.handle)); 281 case Encoding.UTF8: 282 return new Surface(TTF_RenderUTF8_Solid(this.font, 283 text.toStringz, *color.handle)); 284 case Encoding.UNICODE: 285 return new Surface(TTF_RenderUNICODE_Solid(this.font, 286 (text.dup.map!(a => a.to!ushort).array ~ '\0').ptr, *color.handle)); 287 default: 288 throw new Exception("No encoding given"); 289 } 290 } 291 292 /** 293 * Renders the text with a given color on an 8-bit palettized surface with a given background color 294 * Text is smooth but renders slowly 295 * Surface blits as fast as the Solid render method once it is made 296 */ 297 Surface renderTextShaded(string text, Color foreground, Color background, 298 Encoding T = Encoding.UTF8) { 299 switch (T) { 300 case Encoding.LATIN1: 301 return new Surface(TTF_RenderText_Shaded(this.font, 302 text.toStringz, *foreground.handle, *background.handle)); 303 case Encoding.UTF8: 304 return new Surface(TTF_RenderUTF8_Shaded(this.font, 305 text.toStringz, *foreground.handle, *background.handle)); 306 case Encoding.UNICODE: 307 return new Surface(TTF_RenderUNICODE_Shaded(this.font, 308 (text.dup.map!(a => a.to!ushort).array ~ '\0').ptr, 309 *foreground.handle, *background.handle)); 310 default: 311 throw new Exception("No encoding given"); 312 } 313 } 314 315 /** 316 * Renders the text in high quality on a 32-bit ARGB surface, using alpha blending to dither the font with the given color 317 * The surface has alpha transparency 318 * Renders about as slowly as the Shaded render method, but blits more slowly than Solid and Shaded 319 */ 320 Surface renderTextBlended(string text, Color color = Color(0, 0, 0), Encoding T = Encoding.UTF8) { 321 switch (T) { 322 case Encoding.LATIN1: 323 return new Surface(TTF_RenderText_Blended(this.font, 324 text.toStringz, *color.handle)); 325 case Encoding.UTF8: 326 return new Surface(TTF_RenderUTF8_Blended(this.font, 327 text.toStringz, *color.handle)); 328 case Encoding.UNICODE: 329 return new Surface(TTF_RenderUNICODE_Blended(this.font, 330 (text.dup.map!(a => a.to!ushort).array ~ '\0').ptr, *color.handle)); 331 default: 332 throw new Exception("No encoding given"); 333 } 334 } 335 336 /** 337 * Renders a glyph quickly 338 * See renderTextSolid 339 */ 340 Surface renderGlyphSolid(char glyph, Color color = Color(0, 0, 0)) { 341 return new Surface(TTF_RenderGlyph_Solid(this.font, glyph, *color.handle)); 342 } 343 344 /** 345 * Renders a glyph slowly but smoothly 346 * See renderTextShaded 347 */ 348 Surface renderGlyphShaded(char glyph, Color foreground, Color background) { 349 return new Surface(TTF_RenderGlyph_Shaded(this.font, glyph, 350 *foreground.handle, *background.handle)); 351 } 352 353 /** 354 * Renders a glyph very slowly but with very high quality 355 * See renderTextBlended 356 */ 357 Surface renderGlyphBlended(char glyph, Color color = Color(0, 0, 0)) { 358 return new Surface(TTF_RenderGlyph_Blended(this.font, glyph, *color.handle)); 359 } 360 361 }