1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2018 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  * DSFML is based on SFML (Copyright Laurent Gomila)
26  */
27 
28 /**
29  *
30  * Fonts can be loaded from a file, from memory or from a custom stream, and
31  * supports the most common types of fonts. See the `loadFromFile` function for
32  * the complete list of supported formats.
33  *
34  * Once it is loaded, a $(U Font) instance provides three types of information
35  * about the font:
36  * $(UL
37  * $(LI Global metrics, such as the line spacing)
38  * $(LI Per-glyph metrics, such as bounding box or kerning)
39  * $(LI Pixel representation of glyphs))
40  *
41  * $(PARA
42  * Fonts alone are not very useful: they hold the font data but cannot make
43  * anything useful of it. To do so you need to use the $(TEXT_LINK) class, which
44  * is able to properly output text with several options such as character size,
45  * style, color, position, rotation, etc.
46  * This separation allows more flexibility and better performances: indeed a
47  & $(U Font) is a heavy resource, and any operation on it is slow (often too
48  * slow for real-time applications). On the other side, a $(TEXT_LINK) is a
49  * lightweight object which can combine the glyphs data and metrics of a
50  * $(U Font) to display any text on a render target.
51  * Note that it is also possible to bind several $(TEXT_LINK) instances to the
52  * same $(U Font).
53  *
54  * It is important to note that the $(TEXT_LINK) instance doesn't copy the font
55  * that it uses, it only keeps a reference to it. Thus, a $(U Font) must not be
56  * destructed while it is used by a $(TEXT_LINK).)
57  *
58  * Example:
59  * ---
60  * // Declare a new font
61  * auto font = new Font();
62  *
63  * // Load it from a file
64  * if (!font.loadFromFile("arial.ttf"))
65  * {
66  *     // error...
67  * }
68  *
69  * // Create a text which uses our font
70  * auto text1 = new Text();
71  * text1.setFont(font);
72  * text1.setCharacterSize(30);
73  * text1.setStyle(Text.Style.Regular);
74  *
75  * // Create another text using the same font, but with different parameters
76  * auto text2 = new Text();
77  * text2.setFont(font);
78  * text2.setCharacterSize(50);
79  * text2.setStyle(Text.Style.Italic);
80  * ---
81  *
82  * $(PARA Apart from loading font files, and passing them to instances of
83  * $(TEXT_LINK), you should normally not have to deal directly with this class.
84  * However, it may be useful to access the font metrics or rasterized glyphs for
85  * advanced usage.
86  *
87  * Note that if the font is a bitmap font, it is not scalable, thus not all
88  * requested sizes will be available to use. This needs to be taken into
89  * consideration when using $(TEXT_LINK).
90  * If you need to display text of a certain size, make sure the corresponding
91  * bitmap font that supports that size is used.)
92  *
93  * See_Also:
94  * $(TEXT_LINK)
95  */
96 module nudsfml.graphics.font;
97 
98 import bindbc.sfml.graphics;
99 
100 import std.string;
101 
102 import nudsfml.graphics.texture;
103 import nudsfml.graphics.glyph;
104 import nudsfml.system.inputstream;
105 //import nudsfml.system.err;
106 
107 /**
108  * Class for loading and manipulating character fonts.
109  */
110 class Font
111 {
112 
113     /// Holds various information about a font.
114     struct Info
115     {
116         /// The font family.
117         const(char)[] family;
118     }
119 
120     package sfFont* sfPtr;
121     private Info m_info;
122     private Texture[int] textures;
123 
124     //keeps an instance of the C++ stream stored if used
125     private fontStream m_stream;
126 
127     /**
128      * Default constructor.
129      *
130      * Defines an empty font.
131      */
132     this() {
133         sfPtr = null;//sfFont_construct();
134     }
135 
136     package this(sfFont* newFont){
137         sfPtr = newFont;
138     }
139 
140     /// Destructor.
141     ~this() {
142         import nudsfml.system.config;
143         mixin(destructorOutput);
144         if(sfPtr != null) {
145             sfFont_destroy(sfPtr);
146         }
147     }
148 
149     /**
150      * Load the font from a file.
151      *
152      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
153      * X11 PCF, Windows FNT, BDF, PFR and Type 42. Note that this function know
154      * nothing about the standard fonts installed on the user's system, thus you
155      * can't load them directly.
156      *
157      * DSFML cannot preload all the font data in this function, so the file has
158      * to remain accessible until the Font object loads a new font or is
159      * destroyed.
160      *
161      * Params:
162      * 		filename	= Path of the font file to load
163      *
164      * Returns: true if loading succeeded, false if it failed.
165      */
166     bool loadFromFile(const(char)[] filename){
167         if (sfPtr !is null) {
168             sfFont_destroy(sfPtr);
169         }
170         sfPtr = sfFont_createFromFile(filename.toStringz);
171         return sfPtr !is null;
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         if (sfPtr !is null) {
191             sfFont_destroy(sfPtr);
192         }
193         sfPtr = sfFont_createFromMemory(data.ptr, data.length);
194         return sfPtr !is null;
195     }
196 
197     /**
198      * Load the font from a custom stream.
199      *
200      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
201      * X11 PCF, Windows FNT, BDF, PFR and Type 42.
202      *
203      * DSFML cannot preload all the font data in this function, so the contents
204      * of stream have to remain valid as long as the font is used.
205      *
206      * Params:
207      * 		stream	= Source stream to read from
208      *
209      * Returns: true if loading succeeded, false if it failed.
210      */
211     /*bool loadFromStream(InputStream stream)
212     {
213         m_stream = new fontStream(stream);
214         return sfFont_loadFromStream(sfPtr, m_stream);
215     }*/
216 
217     ref const(Info) getInfo() const {
218         return m_info;
219     }
220 
221     /**
222      * Retrieve a glyph of the font.
223      *
224      * Params:
225      * 		codePoint		 = Unicode code point of the character ot get
226      * 		characterSize	 = Reference character size
227      * 		bold			 = Retrieve the bold version or the regular one?
228      *      outlineThickness = Thickness of outline (when != 0 the glyph will not be filled)
229      *
230      * Returns: The glyph corresponding to codePoint and characterSize.
231      */
232     Glyph getGlyph(dchar codePoint, uint characterSize, bool bold, float outlineThickness = 0) const{
233         int b = bold ? 1 : 0;
234         Glyph temp = cast(Glyph)sfFont_getGlyph(sfPtr, cast(uint)codePoint, characterSize, b, outlineThickness);;
235         return temp;
236     }
237 
238     /**
239      * Get the kerning offset of two glyphs.
240      *
241      * The kerning is an extra offset (negative) to apply between two glyphs
242      * when rendering them, to make the pair look more "natural". For example,
243      * the pair "AV" have a special kerning to make them closer than other
244      * characters. Most of the glyphs pairs have a kerning offset of zero,
245      * though.
246      *
247      * Params:
248      * 		first			= Unicode code point of the first character
249      * 		second			= Unicode code point of the second character
250      * 		characterSize	= Reference character size
251      *
252      * Returns: Kerning value for first and second, in pixels.
253      */
254     float getKerning (dchar first, dchar second, uint characterSize) const
255     {
256         sfFont * f = cast(sfFont*)sfPtr;
257         return sfFont_getKerning(f, cast(uint)first, cast(uint)second, characterSize);
258     }
259 
260     /**
261      * Get the line spacing.
262      *
263      * The spacing is the vertical offset to apply between consecutive lines of
264      * text.
265      *
266      * Params:
267      * 		characterSize	= Reference character size
268      *
269      * Returns: Line spacing, in pixels.
270      */
271     float getLineSpacing (uint characterSize) const
272     {
273         sfFont * f = cast(sfFont*)sfPtr;
274         return sfFont_getLineSpacing(f, characterSize);
275     }
276 
277     /**
278      * Get the position of the underline.
279      *
280      * Underline position is the vertical offset to apply between the baseline
281      * and the underline.
282      *
283      * Params:
284      * 		characterSize	= Reference character size
285      *
286      * Returns: Underline position, in pixels.
287      */
288     float getUnderlinePosition (uint characterSize) const
289     {
290         sfFont * f = cast(sfFont*)sfPtr;
291         return sfFont_getUnderlinePosition(f, characterSize);
292     }
293 
294     /**
295      * Get the thickness of the underline.
296      *
297      * Underline thickness is the vertical size of the underline.
298      *
299      * Params:
300      * 		characterSize	= Reference character size
301      *
302      * Returns: Underline thickness, in pixels.
303      */
304     float getUnderlineThickness (uint characterSize) const
305     {
306         return sfFont_getUnderlineThickness(sfPtr, characterSize);
307     }
308 
309     /**
310      * Retrieve the texture containing the loaded glyphs of a certain size.
311      *
312      * The contents of the returned texture changes as more glyphs are
313      * requested, thus it is not very relevant. It is mainly used internally by
314      * Text.
315      *
316      * Params:
317      * 		characterSize	= Reference character size
318      *
319      * Returns: Texture containing the glyphs of the requested size.
320      */
321     const(Texture) getTexture (uint characterSize){
322         import std.stdio;
323         Texture ret; // = textures.get(characterSize, null);
324         
325         sfTexture* p = cast(sfTexture*)sfFont_getTexture(sfPtr, characterSize);
326         ret = new Texture(p);
327         
328         return ret;
329     }
330 
331     /**
332      * Performs a deep copy on the font.
333      *
334      * Returns: The duplicated font.
335      */
336     @property
337     Font dup() const
338     {
339         return new Font(sfFont_copy(sfPtr));
340     }
341 }
342 
343 unittest {
344     version(DSFML_Unittest_Graphics) {
345         import std.stdio;
346         import nudsfml.graphics;
347         import nudsfml.window;
348 
349         import nudsfml.graphics.text;
350 
351         writeln("Unitest for Font");
352 
353         auto font = new Font();
354         assert(font.loadFromFile("data/CamingoCode-Regular.ttf"));
355 
356         Text text;
357         text = new Text("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}", font);
358         text.position = Vector2f(10, 300);
359 
360         RenderWindow win = new RenderWindow(VideoMode(800, 600), "Font Test");
361 
362         Clock clock = new Clock();
363         while(win.isOpen){
364             Event event;
365             while(win.pollEvent(event)){
366                 if(event.type == Event.Type.Closed)
367                     win.close();
368             }
369 
370             if(clock.getElapsedTime().asSeconds() > 1.0f){
371                 win.close();
372             }
373 
374             win.clear();
375             win.draw(text);
376             win.display();
377         }
378 
379 
380         //draw text or something
381 
382         writeln();
383     }
384 }
385 
386 
387 //private:
388 private extern(C++) interface fontInputStream {
389     long read(void* data, long size);
390 
391     long seek(long position);
392 
393     long tell();
394 
395     long getSize();
396 }
397 
398 
399 private class fontStream:fontInputStream {
400     private InputStream myStream;
401 
402     this(InputStream stream) {
403         myStream = stream;
404     }
405 
406     extern(C++)long read(void* data, long size) {
407         return myStream.read(data[0..cast(size_t)size]);
408     }
409 
410     extern(C++)long seek(long position) {
411         return myStream.seek(position);
412     }
413 
414     extern(C++)long tell() {
415         return myStream.tell();
416     }
417 
418     extern(C++)long getSize(){
419         return myStream.getSize();
420     }
421 }