1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  *
27  * Fonts can be loaded from a file, from memory or from a custom stream, and
28  * supports the most common types of fonts. See the `loadFromFile` function for
29  * the complete list of supported formats.
30  *
31  * Once it is loaded, a $(U Font) instance provides three types of information
32  * about the font:
33  * $(UL
34  * $(LI Global metrics, such as the line spacing)
35  * $(LI Per-glyph metrics, such as bounding box or kerning)
36  * $(LI Pixel representation of glyphs))
37  *
38  * $(PARA
39  * Fonts alone are not very useful: they hold the font data but cannot make
40  * anything useful of it. To do so you need to use the $(TEXT_LINK) class, which
41  * is able to properly output text with several options such as character size,
42  * style, color, position, rotation, etc.
43  * This separation allows more flexibility and better performances: indeed a
44  & $(U Font) is a heavy resource, and any operation on it is slow (often too
45  * slow for real-time applications). On the other side, a $(TEXT_LINK) is a
46  * lightweight object which can combine the glyphs data and metrics of a
47  * $(U Font) to display any text on a render target.
48  * Note that it is also possible to bind several $(TEXT_LINK) instances to the
49  * same $(U Font).
50  *
51  * It is important to note that the $(TEXT_LINK) instance doesn't copy the font
52  * that it uses, it only keeps a reference to it. Thus, a $(U Font) must not be
53  * destructed while it is used by a $(TEXT_LINK).)
54  *
55  * Example:
56  * ---
57  * // Declare a new font
58  * auto font = new Font();
59  *
60  * // Load it from a file
61  * if (!font.loadFromFile("arial.ttf"))
62  * {
63  *     // error...
64  * }
65  *
66  * // Create a text which uses our font
67  * auto text1 = new Text();
68  * text1.setFont(font);
69  * text1.setCharacterSize(30);
70  * text1.setStyle(Text.Style.Regular);
71  *
72  * // Create another text using the same font, but with different parameters
73  * auto text2 = new Text();
74  * text2.setFont(font);
75  * text2.setCharacterSize(50);
76  * text2.setStyle(Text.Style.Italic);
77  * ---
78  *
79  * $(PARA Apart from loading font files, and passing them to instances of
80  * $(TEXT_LINK), you should normally not have to deal directly with this class.
81  * However, it may be useful to access the font metrics or rasterized glyphs for
82  * advanced usage.
83  *
84  * Note that if the font is a bitmap font, it is not scalable, thus not all
85  * requested sizes will be available to use. This needs to be taken into
86  * consideration when using $(TEXT_LINK).
87  * If you need to display text of a certain size, make sure the corresponding
88  * bitmap font that supports that size is used.)
89  *
90  * See_Also:
91  * $(TEXT_LINK)
92  */
93 module dsfml.graphics.font;
94 
95 import dsfml.graphics.texture;
96 import dsfml.graphics.glyph;
97 import dsfml.system.inputstream;
98 import dsfml.system.err;
99 
100 /**
101  * Class for loading and manipulating character fonts.
102  */
103 class Font
104 {
105 
106     /// Holds various information about a font.
107     struct Info
108     {
109         /// The font family.
110         const(char)[] family;
111     }
112 
113     package sfFont* sfPtr;
114     private Info m_info;
115     private Texture fontTexture;
116     //keeps an instance of the C++ stream stored if used
117     private fontStream m_stream;
118 
119     /**
120      * Default constructor.
121      *
122      * Defines an empty font.
123      */
124     this()
125     {
126         sfPtr = sfFont_construct();
127         fontTexture = new Texture(sfFont_getTexturePtr(sfPtr));
128     }
129 
130     package this(sfFont* newFont)
131     {
132         sfPtr = newFont;
133         fontTexture = new Texture(sfFont_getTexturePtr(sfPtr));
134     }
135 
136     /// Destructor.
137     ~this()
138     {
139         import dsfml.system.config;
140         mixin(destructorOutput);
141         sfFont_destroy(sfPtr);
142     }
143 
144     /**
145      * Load the font from a file.
146      *
147      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
148      * X11 PCF, Windows FNT, BDF, PFR and Type 42. Note that this function know
149      * nothing about the standard fonts installed on the user's system, thus you
150      * can't load them directly.
151      *
152      * DSFML cannot preload all the font data in this function, so the file has
153      * to remain accessible until the Font object loads a new font or is
154      * destroyed.
155      *
156      * Params:
157      * 		filename	= Path of the font file to load
158      *
159      * Returns: true if loading succeeded, false if it failed.
160      */
161     bool loadFromFile(const(char)[] filename)
162     {
163         import dsfml.system..string;
164 
165         bool ret = sfFont_loadFromFile(sfPtr, filename.ptr, filename.length);
166         if(!ret)
167         {
168             err.write(dsfml.system..string.toString(sfErr_getOutput()));
169         }
170 
171         return ret;
172     }
173 
174     /**
175      * Load the font from a file in memory.
176      *
177      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
178      * X11 PCF, Windows FNT, BDF, PFR and Type 42.
179      *
180      * DSFML cannot preload all the font data in this function, so the buffer
181      * pointed by data has to remain valid until the Font object loads a new
182      * font or is destroyed.
183      *
184      * Params:
185      * 		data	= data holding the font file
186      *
187      * Returns: true if loading succeeded, false if it failed.
188      */
189     bool loadFromMemory(const(void)[] data)
190     {
191         import dsfml.system..string;
192 
193         bool ret = sfFont_loadFromMemory(sfPtr, data.ptr, data.length);
194         if(!ret)
195         {
196             err.write(dsfml.system..string.toString(sfErr_getOutput()));
197         }
198 
199         return ret;
200     }
201 
202     /**
203      * Load the font from a custom stream.
204      *
205      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
206      * X11 PCF, Windows FNT, BDF, PFR and Type 42.
207      *
208      * DSFML cannot preload all the font data in this function, so the contents
209      * of stream have to remain valid as long as the font is used.
210      *
211      * Params:
212      * 		stream	= Source stream to read from
213      *
214      * Returns: true if loading succeeded, false if it failed.
215      */
216     bool loadFromStream(InputStream stream)
217     {
218         import dsfml.system..string;
219 
220         m_stream = new fontStream(stream);
221 
222         bool ret = sfFont_loadFromStream(sfPtr, m_stream);
223 
224         if(!ret)
225         {
226             err.write(dsfml.system..string.toString(sfErr_getOutput()));
227         }
228 
229         return ret;
230     }
231 
232     ref const(Info) getInfo()
233     {
234         return m_info;
235     }
236 
237     /**
238      * Retrieve a glyph of the font.
239      *
240      * Params:
241      * 		codePoint		= Unicode code point of the character ot get
242      * 		characterSize	= Reference character size
243      * 		bold			= Retrieve the bold version or the regular one?
244      *
245      * Returns: The glyph corresponding to codePoint and characterSize.
246      */
247     Glyph getGlyph(dchar codePoint, uint characterSize, bool bold) const
248     {
249         Glyph temp;
250 
251         sfFont_getGlyph(sfPtr, cast(uint)codePoint, characterSize,bold,&temp.advance,&temp.bounds.left,&temp.bounds.top,&temp.bounds.width,&temp.bounds.height,&temp.textureRect.left,&temp.textureRect.top,&temp.textureRect.width,&temp.textureRect.height);
252 
253         return temp;
254     }
255 
256     /**
257      * Get the kerning offset of two glyphs.
258      *
259      * The kerning is an extra offset (negative) to apply between two glyphs
260      * when rendering them, to make the pair look more "natural". For example,
261      * the pair "AV" have a special kerning to make them closer than other
262      * characters. Most of the glyphs pairs have a kerning offset of zero,
263      * though.
264      *
265      * Params:
266      * 		first			= Unicode code point of the first character
267      * 		second			= Unicode code point of the second character
268      * 		characterSize	= Reference character size
269      *
270      * Returns: Kerning value for first and second, in pixels.
271      */
272     float getKerning (dchar first, dchar second, uint characterSize) const
273     {
274         return sfFont_getKerning(sfPtr, cast(uint)first, cast(uint)second, characterSize);
275     }
276 
277     /**
278      * Get the line spacing.
279      *
280      * The spacing is the vertical offset to apply between consecutive lines of
281      * text.
282      *
283      * Params:
284      * 		characterSize	= Reference character size
285      *
286      * Returns: Line spacing, in pixels.
287      */
288     float getLineSpacing (uint characterSize) const
289     {
290         return sfFont_getLineSpacing(sfPtr, characterSize);
291     }
292 
293     /**
294      * Get the position of the underline.
295      *
296      * Underline position is the vertical offset to apply between the baseline
297      * and the underline.
298      *
299      * Params:
300      * 		characterSize	= Reference character size
301      *
302      * Returns: Underline position, in pixels.
303      */
304     float getUnderlinePosition (uint characterSize) const
305     {
306         return sfFont_getUnderlinePosition(sfPtr, characterSize);
307     }
308 
309     /**
310      * Get the thickness of the underline.
311      *
312      * Underline thickness is the vertical size of the underline.
313      *
314      * Params:
315      * 		characterSize	= Reference character size
316      *
317      * Returns: Underline thickness, in pixels.
318      */
319     float getUnderlineThickness (uint characterSize) const
320     {
321         return sfFont_getUnderlineThickness(sfPtr, characterSize);
322     }
323 
324     /**
325      * Retrieve the texture containing the loaded glyphs of a certain size.
326      *
327      * The contents of the returned texture changes as more glyphs are
328      * requested, thus it is not very relevant. It is mainly used internally by
329      * Text.
330      *
331      * Params:
332      * 		characterSize	= Reference character size
333      *
334      * Returns: Texture containing the glyphs of the requested size.
335      */
336     const(Texture) getTexture (uint characterSize) const
337     {
338         //ToDo: cache texture somehow?
339 
340         import std.stdio;
341 
342         sfFont_updateTexture(sfPtr, characterSize);
343 
344         return fontTexture;
345     }
346 
347     /**
348      * Performs a deep copy on the font.
349      *
350      * Returns: The duplicated font.
351      */
352     @property
353     Font dup() const
354     {
355         return new Font(sfFont_copy(sfPtr));
356     }
357 }
358 
359 unittest
360 {
361     version(DSFML_Unittest_Graphics)
362     {
363         import std.stdio;
364 
365         import dsfml.graphics.text;
366 
367         writeln("Unitest for Font");
368 
369         auto font = new Font();
370         assert(font.loadFromFile("res/Warenhaus-Standard.ttf"));
371 
372         Text text;
373         text = new Text("Sample String", font);
374 
375 
376         //draw text or something
377 
378         writeln();
379     }
380 }
381 
382 
383 private:
384 private extern(C++) interface fontInputStream
385 {
386     long read(void* data, long size);
387 
388     long seek(long position);
389 
390     long tell();
391 
392     long getSize();
393 }
394 
395 
396 private class fontStream:fontInputStream
397 {
398     private InputStream myStream;
399 
400     this(InputStream stream)
401     {
402         myStream = stream;
403     }
404 
405     extern(C++)long read(void* data, long size)
406     {
407         return myStream.read(data[0..cast(size_t)size]);
408     }
409 
410     extern(C++)long seek(long position)
411     {
412         return myStream.seek(position);
413     }
414 
415     extern(C++)long tell()
416     {
417         return myStream.tell();
418     }
419 
420     extern(C++)long getSize()
421     {
422         return myStream.getSize();
423     }
424 }
425 
426 
427 
428 package extern(C) struct sfFont;
429 
430 private extern(C):
431 
432 sfFont* sfFont_construct();
433 
434 //Create a new font from a file
435 bool sfFont_loadFromFile(sfFont* font, const(char)* filename, size_t length);
436 
437 
438 //Create a new image font a file in memory
439 bool sfFont_loadFromMemory(sfFont* font, const(void)* data, size_t sizeInBytes);
440 
441 
442 //Create a new image font a custom stream
443 bool sfFont_loadFromStream(sfFont* font, fontInputStream stream);
444 
445 
446 // Copy an existing font
447 sfFont* sfFont_copy(const sfFont* font);
448 
449 
450 //Destroy an existing font
451 void sfFont_destroy(sfFont* font);
452 
453 
454 //Get a glyph in a font
455 void sfFont_getGlyph(const(sfFont)* font, uint codePoint, int characterSize, bool bold, float* glyphAdvance, float* glyphBoundsLeft, float* glyphBoundsTop, float* glyphBoundsWidth, float* glyphBoundsHeight, int* glyphTextRectLeft, int* glyphTextRectTop, int* glyphTextRectWidth, int* glyphTextRectHeight);
456 
457 
458 //Get the kerning value corresponding to a given pair of characters in a font
459 float sfFont_getKerning(const(sfFont)* font, uint first, uint second, uint characterSize);
460 
461 
462 //Get the line spacing value
463 float sfFont_getLineSpacing(const(sfFont)* font, uint characterSize);
464 
465 //Get the position of the underline
466 float sfFont_getUnderlinePosition (const(sfFont)* font, uint characterSize);
467 
468 //Get the thickness of the underline
469 float sfFont_getUnderlineThickness (const(sfFont)* font, uint characterSize);
470 
471 
472 //Get the texture pointer for a particular font
473 sfTexture* sfFont_getTexturePtr(const(sfFont)* font);
474 
475 sfTexture* sfFont_getTexture(const(sfFont)* font, uint characterSize);
476 
477 //Update the internal texture associated with the font
478 void sfFont_updateTexture(const(sfFont)* font, uint characterSize);
479 
480 const(char)* sfErr_getOutput();