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 dsfml.graphics.font;
97 
98 import dsfml.graphics.texture;
99 import dsfml.graphics.glyph;
100 import dsfml.system.inputstream;
101 import dsfml.system.err;
102 
103 /**
104  * Class for loading and manipulating character fonts.
105  */
106 class Font
107 {
108 
109     /// Holds various information about a font.
110     struct Info
111     {
112         /// The font family.
113         const(char)[] family;
114     }
115 
116     package sfFont* sfPtr;
117     private Info m_info;
118     private Texture[int] textures;
119 
120     //keeps an instance of the C++ stream stored if used
121     private fontStream m_stream;
122 
123     /**
124      * Default constructor.
125      *
126      * Defines an empty font.
127      */
128     this()
129     {
130         sfPtr = sfFont_construct();
131     }
132 
133     package this(sfFont* newFont)
134     {
135         sfPtr = newFont;
136     }
137 
138     /// Destructor.
139     ~this()
140     {
141         import dsfml.system.config;
142         mixin(destructorOutput);
143         sfFont_destroy(sfPtr);
144     }
145 
146     /**
147      * Load the font from a file.
148      *
149      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
150      * X11 PCF, Windows FNT, BDF, PFR and Type 42. Note that this function know
151      * nothing about the standard fonts installed on the user's system, thus you
152      * can't load them directly.
153      *
154      * DSFML cannot preload all the font data in this function, so the file has
155      * to remain accessible until the Font object loads a new font or is
156      * destroyed.
157      *
158      * Params:
159      * 		filename	= Path of the font file to load
160      *
161      * Returns: true if loading succeeded, false if it failed.
162      */
163     bool loadFromFile(const(char)[] filename)
164     {
165         return sfFont_loadFromFile(sfPtr, filename.ptr, filename.length);
166     }
167 
168     /**
169      * Load the font from a file in memory.
170      *
171      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
172      * X11 PCF, Windows FNT, BDF, PFR and Type 42.
173      *
174      * DSFML cannot preload all the font data in this function, so the buffer
175      * pointed by data has to remain valid until the Font object loads a new
176      * font or is destroyed.
177      *
178      * Params:
179      * 		data	= data holding the font file
180      *
181      * Returns: true if loading succeeded, false if it failed.
182      */
183     bool loadFromMemory(const(void)[] data)
184     {
185         return sfFont_loadFromMemory(sfPtr, data.ptr, data.length);
186     }
187 
188     /**
189      * Load the font from a custom stream.
190      *
191      * The supported font formats are: TrueType, Type 1, CFF, OpenType, SFNT,
192      * X11 PCF, Windows FNT, BDF, PFR and Type 42.
193      *
194      * DSFML cannot preload all the font data in this function, so the contents
195      * of stream have to remain valid as long as the font is used.
196      *
197      * Params:
198      * 		stream	= Source stream to read from
199      *
200      * Returns: true if loading succeeded, false if it failed.
201      */
202     bool loadFromStream(InputStream stream)
203     {
204         m_stream = new fontStream(stream);
205         return sfFont_loadFromStream(sfPtr, m_stream);
206     }
207 
208     ref const(Info) getInfo() const
209     {
210         return m_info;
211     }
212 
213     /**
214      * Retrieve a glyph of the font.
215      *
216      * Params:
217      * 		codePoint		 = Unicode code point of the character ot get
218      * 		characterSize	 = Reference character size
219      * 		bold			 = Retrieve the bold version or the regular one?
220      *      outlineThickness = Thickness of outline (when != 0 the glyph will not be filled)
221      *
222      * Returns: The glyph corresponding to codePoint and characterSize.
223      */
224     Glyph getGlyph(dchar codePoint, uint characterSize, bool bold, float outlineThickness = 0) const
225     {
226         Glyph temp;
227 
228         sfFont_getGlyph(sfPtr, cast(uint)codePoint, characterSize, bold, outlineThickness, &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);
229 
230         return temp;
231     }
232 
233     /**
234      * Get the kerning offset of two glyphs.
235      *
236      * The kerning is an extra offset (negative) to apply between two glyphs
237      * when rendering them, to make the pair look more "natural". For example,
238      * the pair "AV" have a special kerning to make them closer than other
239      * characters. Most of the glyphs pairs have a kerning offset of zero,
240      * though.
241      *
242      * Params:
243      * 		first			= Unicode code point of the first character
244      * 		second			= Unicode code point of the second character
245      * 		characterSize	= Reference character size
246      *
247      * Returns: Kerning value for first and second, in pixels.
248      */
249     float getKerning (dchar first, dchar second, uint characterSize) const
250     {
251         return sfFont_getKerning(sfPtr, cast(uint)first, cast(uint)second, characterSize);
252     }
253 
254     /**
255      * Get the line spacing.
256      *
257      * The spacing is the vertical offset to apply between consecutive lines of
258      * text.
259      *
260      * Params:
261      * 		characterSize	= Reference character size
262      *
263      * Returns: Line spacing, in pixels.
264      */
265     float getLineSpacing (uint characterSize) const
266     {
267         return sfFont_getLineSpacing(sfPtr, characterSize);
268     }
269 
270     /**
271      * Get the position of the underline.
272      *
273      * Underline position is the vertical offset to apply between the baseline
274      * and the underline.
275      *
276      * Params:
277      * 		characterSize	= Reference character size
278      *
279      * Returns: Underline position, in pixels.
280      */
281     float getUnderlinePosition (uint characterSize) const
282     {
283         return sfFont_getUnderlinePosition(sfPtr, characterSize);
284     }
285 
286     /**
287      * Get the thickness of the underline.
288      *
289      * Underline thickness is the vertical size of the underline.
290      *
291      * Params:
292      * 		characterSize	= Reference character size
293      *
294      * Returns: Underline thickness, in pixels.
295      */
296     float getUnderlineThickness (uint characterSize) const
297     {
298         return sfFont_getUnderlineThickness(sfPtr, characterSize);
299     }
300 
301     /**
302      * Retrieve the texture containing the loaded glyphs of a certain size.
303      *
304      * The contents of the returned texture changes as more glyphs are
305      * requested, thus it is not very relevant. It is mainly used internally by
306      * Text.
307      *
308      * Params:
309      * 		characterSize	= Reference character size
310      *
311      * Returns: Texture containing the glyphs of the requested size.
312      */
313     const(Texture) getTexture (uint characterSize)
314     {
315         Texture ret = textures.get(characterSize, null);
316 
317         if(ret is null)
318         {
319             ret = new Texture(sfFont_getTexture(sfPtr, characterSize));
320             textures[characterSize] = ret;
321         }
322 
323         return ret;
324     }
325 
326     /**
327      * Performs a deep copy on the font.
328      *
329      * Returns: The duplicated font.
330      */
331     @property
332     Font dup() const
333     {
334         return new Font(sfFont_copy(sfPtr));
335     }
336 }
337 
338 unittest
339 {
340     version(DSFML_Unittest_Graphics)
341     {
342         import std.stdio;
343 
344         import dsfml.graphics.text;
345 
346         writeln("Unitest for Font");
347 
348         auto font = new Font();
349         assert(font.loadFromFile("res/Warenhaus-Standard.ttf"));
350 
351         Text text;
352         text = new Text("Sample String", font);
353 
354 
355         //draw text or something
356 
357         writeln();
358     }
359 }
360 
361 
362 private:
363 private extern(C++) interface fontInputStream
364 {
365     long read(void* data, long size);
366 
367     long seek(long position);
368 
369     long tell();
370 
371     long getSize();
372 }
373 
374 
375 private class fontStream:fontInputStream
376 {
377     private InputStream myStream;
378 
379     this(InputStream stream)
380     {
381         myStream = stream;
382     }
383 
384     extern(C++)long read(void* data, long size)
385     {
386         return myStream.read(data[0..cast(size_t)size]);
387     }
388 
389     extern(C++)long seek(long position)
390     {
391         return myStream.seek(position);
392     }
393 
394     extern(C++)long tell()
395     {
396         return myStream.tell();
397     }
398 
399     extern(C++)long getSize()
400     {
401         return myStream.getSize();
402     }
403 }
404 
405 package extern(C) struct sfFont;
406 
407 private extern(C):
408 
409 sfFont* sfFont_construct();
410 
411 //Create a new font from a file
412 bool sfFont_loadFromFile(sfFont* font, const(char)* filename, size_t length);
413 
414 //Create a new image font a file in memory
415 bool sfFont_loadFromMemory(sfFont* font, const(void)* data, size_t sizeInBytes);
416 
417 //Create a new image font a custom stream
418 bool sfFont_loadFromStream(sfFont* font, fontInputStream stream);
419 
420 // Copy an existing font
421 sfFont* sfFont_copy(const sfFont* font);
422 
423 //Destroy an existing font
424 void sfFont_destroy(sfFont* font);
425 
426 //Get a glyph in a font
427 void sfFont_getGlyph(const(sfFont)* font, uint codePoint, int characterSize, bool bold, float outlineThickness, float* glyphAdvance, float* glyphBoundsLeft, float* glyphBoundsTop, float* glyphBoundsWidth, float* glyphBoundsHeight, int* glyphTextRectLeft, int* glyphTextRectTop, int* glyphTextRectWidth, int* glyphTextRectHeight);
428 
429 //Get the kerning value corresponding to a given pair of characters in a font
430 float sfFont_getKerning(const(sfFont)* font, uint first, uint second, uint characterSize);
431 
432 //Get the line spacing value
433 float sfFont_getLineSpacing(const(sfFont)* font, uint characterSize);
434 
435 //Get the position of the underline
436 float sfFont_getUnderlinePosition (const(sfFont)* font, uint characterSize);
437 
438 //Get the thickness of the underline
439 float sfFont_getUnderlineThickness (const(sfFont)* font, uint characterSize);
440 
441 //Get the font texture for a given character size
442 sfTexture* sfFont_getTexture(const(sfFont)* font, uint characterSize);