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 }