1 /**********************************************************************************************
3 * raylib.text - Basic functions to load Fonts and draw Text
7 * #define SUPPORT_FILEFORMAT_FNT
8 * #define SUPPORT_FILEFORMAT_TTF
9 * Selected desired fileformats to be supported for loading. Some of those formats are
10 * supported by default, to remove support, just comment unrequired #define in this module
12 * #define SUPPORT_DEFAULT_FONT
13 * Load default raylib font on initialization to be used by DrawText() and MeasureText().
14 * If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
16 * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
17 * TextSplit() function static buffer max size
19 * #define MAX_TEXTSPLIT_COUNT
20 * TextSplit() function static substrings pointers array (pointing to static buffer)
24 * stb_truetype - Load TTF file and rasterize characters data
25 * stb_rect_pack - Rectangles packing algorythms, required for font atlas generation
28 * LICENSE: zlib/libpng
30 * Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
32 * This software is provided "as-is", without any express or implied warranty. In no event
33 * will the authors be held liable for any damages arising from the use of this software.
35 * Permission is granted to anyone to use this software for any purpose, including commercial
36 * applications, and to alter it and redistribute it freely, subject to the following restrictions:
38 * 1. The origin of this software must not be misrepresented; you must not claim that you
39 * wrote the original software. If you use this software in a product, an acknowledgment
40 * in the product documentation would be appreciated but is not required.
42 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
43 * as being the original software.
45 * 3. This notice may not be removed or altered from any source distribution.
47 **********************************************************************************************/
49 #include "raylib.h" // Declares module functions
51 // Check if config flags have been externally provided on compilation line
52 #if !defined(EXTERNAL_CONFIG_FLAGS)
53 #include "config.h" // Defines module configuration flags
56 #include <stdlib.h> // Required for: malloc(), free()
57 #include <stdio.h> // Required for: vsprintf()
58 #include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
59 #include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
60 #include <ctype.h> // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
62 #include "utils.h" // Required for: LoadFileText()
64 #if defined(SUPPORT_FILEFORMAT_TTF)
65 #define STB_RECT_PACK_IMPLEMENTATION
66 #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging
69 #define STB_TRUETYPE_IMPLEMENTATION
70 #include "external/stb_truetype.h" // Required for: ttf font data reading
73 //----------------------------------------------------------------------------------
75 //----------------------------------------------------------------------------------
76 #ifndef MAX_TEXT_BUFFER_LENGTH
77 #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
78 // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
80 #ifndef MAX_TEXT_UNICODE_CHARS
81 #define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints: GetCodepoints()
83 #ifndef MAX_TEXTSPLIT_COUNT
84 #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
87 //----------------------------------------------------------------------------------
88 // Types and Structures Definition
89 //----------------------------------------------------------------------------------
92 //----------------------------------------------------------------------------------
94 //----------------------------------------------------------------------------------
95 #if defined(SUPPORT_DEFAULT_FONT)
96 // Default font provided by raylib
97 // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
98 static Font defaultFont = { 0 };
101 //----------------------------------------------------------------------------------
102 // Other Modules Functions Declaration (required by text)
103 //----------------------------------------------------------------------------------
106 //----------------------------------------------------------------------------------
107 // Module specific Functions Declaration
108 //----------------------------------------------------------------------------------
109 #if defined(SUPPORT_FILEFORMAT_FNT)
110 static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
113 #if defined(SUPPORT_DEFAULT_FONT)
114 extern void LoadFontDefault(void);
115 extern void UnloadFontDefault(void);
118 //----------------------------------------------------------------------------------
119 // Module Functions Definition
120 //----------------------------------------------------------------------------------
121 #if defined(SUPPORT_DEFAULT_FONT)
123 // Load raylib default font
124 extern void LoadFontDefault(void)
126 #define BIT_CHECK(a,b) ((a) & (1u << (b)))
128 // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
129 // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
131 defaultFont.charsCount = 224; // Number of chars included in our default font
132 defaultFont.charsPadding = 0; // Characters padding
134 // Default font is directly defined here (data generated from a sprite font image)
135 // This way, we reconstruct Font without creating large global variables
136 // This data is automatically allocated to Stack and automatically deallocated at the end of this function
137 unsigned int defaultFontData[512] = {
138 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f,
139 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de,
140 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f,
141 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
142 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048,
143 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048,
144 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000,
145 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180,
146 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090,
147 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082,
148 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
149 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800,
150 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820,
151 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0,
152 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
153 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000f01, 0x00000000, 0x06000000, 0x24000000, 0x00000f01, 0x00000000, 0x09108000,
154 0x24fa28a2, 0x00000f01, 0x00000000, 0x013e0000, 0x2242252a, 0x00000f52, 0x00000000, 0x038a8000, 0x2422222a, 0x00000f29, 0x00000000, 0x010a8000,
155 0x2412252a, 0x00000f01, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000f01, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000,
156 0x0003e000, 0x00000f00, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03,
157 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202,
158 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002,
159 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002,
160 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002,
161 0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010,
162 0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7,
163 0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
164 0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a,
165 0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b,
166 0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008,
167 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210,
168 0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe,
169 0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2,
170 0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000,
171 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000,
172 0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000,
173 0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000,
174 0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
175 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
176 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
177 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
178 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
179 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
180 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
182 int charsHeight = 10;
183 int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically
185 int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6,
186 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5,
187 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4,
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189 1, 1, 5, 5, 5, 7, 1, 5, 3, 7, 3, 5, 4, 1, 7, 4, 3, 5, 3, 3, 2, 5, 6, 1, 2, 2, 3, 5, 6, 6, 6, 6,
190 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 3, 3, 3, 3, 7, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 4, 6,
191 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 2, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 5 };
193 // Re-construct image from defaultFontData and generate OpenGL texture
194 //----------------------------------------------------------------------
196 .data = calloc(128*128, 2), // 2 bytes per pixel (gray + alpha)
199 .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
203 // Fill image.data with defaultFontData (convert from bit to pixel!)
204 for (int i = 0, counter = 0; i < imFont.width*imFont.height; i += 32)
206 for (int j = 31; j >= 0; j--)
208 if (BIT_CHECK(defaultFontData[counter], j))
210 // NOTE: We are unreferencing data as short, so,
211 // we must consider data as little-endian order (alpha + gray)
212 ((unsigned short *)imFont.data)[i + j] = 0xffff;
214 else ((unsigned short *)imFont.data)[i + j] = 0x00ff;
220 defaultFont.texture = LoadTextureFromImage(imFont);
222 // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
223 //------------------------------------------------------------------------------
225 // Allocate space for our characters info data
226 // NOTE: This memory should be freed at end! --> CloseWindow()
227 defaultFont.chars = (CharInfo *)RL_MALLOC(defaultFont.charsCount*sizeof(CharInfo));
228 defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.charsCount*sizeof(Rectangle));
231 int currentPosX = charsDivisor;
232 int testPosX = charsDivisor;
234 for (int i = 0; i < defaultFont.charsCount; i++)
236 defaultFont.chars[i].value = 32 + i; // First char is 32
238 defaultFont.recs[i].x = (float)currentPosX;
239 defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
240 defaultFont.recs[i].width = (float)charsWidth[i];
241 defaultFont.recs[i].height = (float)charsHeight;
243 testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor);
245 if (testPosX >= defaultFont.texture.width)
248 currentPosX = 2*charsDivisor + charsWidth[i];
249 testPosX = currentPosX;
251 defaultFont.recs[i].x = (float)charsDivisor;
252 defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
254 else currentPosX = testPosX;
256 // NOTE: On default font character offsets and xAdvance are not required
257 defaultFont.chars[i].offsetX = 0;
258 defaultFont.chars[i].offsetY = 0;
259 defaultFont.chars[i].advanceX = 0;
261 // Fill character image data from fontClear data
262 defaultFont.chars[i].image = ImageFromImage(imFont, defaultFont.recs[i]);
267 defaultFont.baseSize = (int)defaultFont.recs[0].height;
269 TRACELOG(LOG_INFO, "FONT: Default font loaded successfully");
272 // Unload raylib default font
273 extern void UnloadFontDefault(void)
275 for (int i = 0; i < defaultFont.charsCount; i++) UnloadImage(defaultFont.chars[i].image);
276 UnloadTexture(defaultFont.texture);
277 RL_FREE(defaultFont.chars);
278 RL_FREE(defaultFont.recs);
280 #endif // SUPPORT_DEFAULT_FONT
282 // Get the default font, useful to be used with extended parameters
283 Font GetFontDefault()
285 #if defined(SUPPORT_DEFAULT_FONT)
293 // Load Font from file into GPU memory (VRAM)
294 Font LoadFont(const char *fileName)
296 // Default values for ttf font generation
297 #ifndef FONT_TTF_DEFAULT_SIZE
298 #define FONT_TTF_DEFAULT_SIZE 32 // TTF font generation default char size (char-height)
300 #ifndef FONT_TTF_DEFAULT_NUMCHARS
301 #define FONT_TTF_DEFAULT_NUMCHARS 95 // TTF font generation default charset: 95 glyphs (ASCII 32..126)
303 #ifndef FONT_TTF_DEFAULT_FIRST_CHAR
304 #define FONT_TTF_DEFAULT_FIRST_CHAR 32 // TTF font generation default first char for image sprite font (32-Space)
306 #ifndef FONT_TTF_DEFAULT_CHARS_PADDING
307 #define FONT_TTF_DEFAULT_CHARS_PADDING 4 // TTF font generation default chars padding
312 #if defined(SUPPORT_FILEFORMAT_TTF)
313 if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
316 #if defined(SUPPORT_FILEFORMAT_FNT)
317 if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
321 Image image = LoadImage(fileName);
322 if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
326 if (font.texture.id == 0)
328 TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
329 font = GetFontDefault();
331 else SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance)
336 // Load Font from TTF font file with generation parameters
337 // NOTE: You can pass an array with desired characters, those characters should be available in the font
338 // if array is NULL, default char set is selected 32..126
339 Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount)
343 // Loading file to memory
344 unsigned int fileSize = 0;
345 unsigned char *fileData = LoadFileData(fileName, &fileSize);
347 if (fileData != NULL)
349 // Loading font from memory data
350 font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, charsCount);
354 else font = GetFontDefault();
359 // Load an Image font file (XNA style)
360 Font LoadFontFromImage(Image image, Color key, int firstChar)
362 #ifndef MAX_GLYPHS_FROM_IMAGE
363 #define MAX_GLYPHS_FROM_IMAGE 256 // Maximum number of glyphs supported on image scan
366 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
374 // We allocate a temporal arrays for chars data measures,
375 // once we get the actual number of chars, we copy data to a sized arrays
376 int tempCharValues[MAX_GLYPHS_FROM_IMAGE];
377 Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE];
379 Color *pixels = LoadImageColors(image);
381 // Parse image data to get charSpacing and lineSpacing
382 for (y = 0; y < image.height; y++)
384 for (x = 0; x < image.width; x++)
386 if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
389 if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
398 while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
402 // Check array values to get characters: value, x, y, w, h
405 int xPosToRead = charSpacing;
407 // Parse image data to get rectangle sizes
408 while ((lineSpacing + lineToRead*(charHeight + lineSpacing)) < image.height)
410 while ((xPosToRead < image.width) &&
411 !COLOR_EQUAL((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead]), key))
413 tempCharValues[index] = firstChar + index;
415 tempCharRecs[index].x = (float)xPosToRead;
416 tempCharRecs[index].y = (float)(lineSpacing + lineToRead*(charHeight + lineSpacing));
417 tempCharRecs[index].height = (float)charHeight;
421 while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
423 tempCharRecs[index].width = (float)charWidth;
427 xPosToRead += (charWidth + charSpacing);
431 xPosToRead = charSpacing;
434 // NOTE: We need to remove key color borders from image to avoid weird
435 // artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR
436 for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
438 // Create a new image with the processed color data (key color replaced by BLANK)
441 .width = image.width,
442 .height = image.height,
443 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
447 // Create spritefont with all data parsed from image
450 font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
451 font.charsCount = index;
452 font.charsPadding = 0;
454 // We got tempCharValues and tempCharsRecs populated with chars data
455 // Now we move temp data to sized charValues and charRecs arrays
456 font.chars = (CharInfo *)RL_MALLOC(font.charsCount*sizeof(CharInfo));
457 font.recs = (Rectangle *)RL_MALLOC(font.charsCount*sizeof(Rectangle));
459 for (int i = 0; i < font.charsCount; i++)
461 font.chars[i].value = tempCharValues[i];
463 // Get character rectangle in the font atlas texture
464 font.recs[i] = tempCharRecs[i];
466 // NOTE: On image based fonts (XNA style), character offsets and xAdvance are not required (set to 0)
467 font.chars[i].offsetX = 0;
468 font.chars[i].offsetY = 0;
469 font.chars[i].advanceX = 0;
471 // Fill character image data from fontClear data
472 font.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]);
475 UnloadImage(fontClear); // Unload processed image once converted to texture
477 font.baseSize = (int)font.recs[0].height;
482 // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
483 Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount)
487 char fileExtLower[16] = { 0 };
488 strcpy(fileExtLower, TextToLower(fileType));
490 #if defined(SUPPORT_FILEFORMAT_TTF)
491 if (TextIsEqual(fileExtLower, ".ttf") ||
492 TextIsEqual(fileExtLower, ".otf"))
494 font.baseSize = fontSize;
495 font.charsCount = (charsCount > 0)? charsCount : 95;
496 font.charsPadding = 0;
497 font.chars = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
499 if (font.chars != NULL)
501 font.charsPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
503 Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, font.charsPadding, 0);
504 font.texture = LoadTextureFromImage(atlas);
506 // Update chars[i].image to use alpha, required to be used on ImageDrawText()
507 for (int i = 0; i < font.charsCount; i++)
509 UnloadImage(font.chars[i].image);
510 font.chars[i].image = ImageFromImage(atlas, font.recs[i]);
515 else font = GetFontDefault();
518 font = GetFontDefault();
524 // Load font data for further use
525 // NOTE: Requires TTF font memory data and can generate SDF data
526 CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type)
528 // NOTE: Using some SDF generation default values,
529 // trades off precision with ability to handle *smaller* sizes
530 #ifndef FONT_SDF_CHAR_PADDING
531 #define FONT_SDF_CHAR_PADDING 4 // SDF font generation char padding
533 #ifndef FONT_SDF_ON_EDGE_VALUE
534 #define FONT_SDF_ON_EDGE_VALUE 128 // SDF font generation on edge value
536 #ifndef FONT_SDF_PIXEL_DIST_SCALE
537 #define FONT_SDF_PIXEL_DIST_SCALE 64.0f // SDF font generation pixel distance scale
539 #ifndef FONT_BITMAP_ALPHA_THRESHOLD
540 #define FONT_BITMAP_ALPHA_THRESHOLD 80 // Bitmap (B&W) font generation alpha threshold
543 CharInfo *chars = NULL;
545 #if defined(SUPPORT_FILEFORMAT_TTF)
546 // Load font data (including pixel data) from TTF memory file
547 // NOTE: Loaded information should be enough to generate font image atlas, using any packaging method
548 if (fileData != NULL)
550 int genFontChars = false;
551 stbtt_fontinfo fontInfo = { 0 };
553 if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Init font for data reading
555 // Calculate font scale factor
556 float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
558 // Calculate font basic metrics
559 // NOTE: ascent is equivalent to font baseline
560 int ascent, descent, lineGap;
561 stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
563 // In case no chars count provided, default to 95
564 charsCount = (charsCount > 0)? charsCount : 95;
566 // Fill fontChars in case not provided externally
567 // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
569 if (fontChars == NULL)
571 fontChars = (int *)RL_MALLOC(charsCount*sizeof(int));
572 for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
576 chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
578 // NOTE: Using simple packaging, one char after another
579 for (int i = 0; i < charsCount; i++)
581 int chw = 0, chh = 0; // Character width and height (on generation)
582 int ch = fontChars[i]; // Character value to get info for
585 // Render a unicode codepoint to a bitmap
586 // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
587 // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
588 // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
590 if (type != FONT_SDF) chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
591 else if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
592 else chars[i].image.data = NULL;
594 stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
595 chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
597 // Load characters images
598 chars[i].image.width = chw;
599 chars[i].image.height = chh;
600 chars[i].image.mipmaps = 1;
601 chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
603 chars[i].offsetY += (int)((float)ascent*scaleFactor);
605 // NOTE: We create an empty image for space character, it could be further required for atlas packing
609 .data = calloc(chars[i].advanceX*fontSize, 2),
610 .width = chars[i].advanceX,
612 .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,
616 chars[i].image = imSpace;
619 if (type == FONT_BITMAP)
621 // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
622 // NOTE: For optimum results, bitmap font should be generated at base pixel size
623 for (int p = 0; p < chw*chh; p++)
625 if (((unsigned char *)chars[i].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0;
626 else ((unsigned char *)chars[i].image.data)[p] = 255;
630 // Get bounding box for character (may be offset to account for chars that dip above or below the line)
632 int chX1, chY1, chX2, chY2;
633 stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
635 TRACELOGD("FONT: Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
636 TRACELOGD("FONT: Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
640 else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
642 if (genFontChars) RL_FREE(fontChars);
649 // Generate image font atlas using chars info
650 // NOTE: Packing method: 0-Default, 1-Skyline
651 #if defined(SUPPORT_FILEFORMAT_TTF)
652 Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCount, int fontSize, int padding, int packMethod)
658 TraceLog(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas");
664 // In case no chars count provided we suppose default of 95
665 charsCount = (charsCount > 0)? charsCount : 95;
667 // NOTE: Rectangles memory is loaded here!
668 Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
670 // Calculate image size based on required pixel area
671 // NOTE 1: Image is forced to be squared and POT... very conservative!
672 // NOTE 2: SDF font characters already contain an internal padding,
673 // so image size would result bigger than default font type
674 float requiredArea = 0;
675 for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(chars[i].image.height + 2*padding));
676 float guessSize = sqrtf(requiredArea)*1.3f;
677 int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
679 atlas.width = imageSize; // Atlas bitmap width
680 atlas.height = imageSize; // Atlas bitmap height
681 atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
682 atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
685 // DEBUG: We can see padding in the generated image setting a gray background...
686 //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
688 if (packMethod == 0) // Use basic packing algorythm
690 int offsetX = padding;
691 int offsetY = padding;
693 // NOTE: Using simple packaging, one char after another
694 for (int i = 0; i < charsCount; i++)
696 // Copy pixel data from fc.data to atlas
697 for (int y = 0; y < chars[i].image.height; y++)
699 for (int x = 0; x < chars[i].image.width; x++)
701 ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
705 // Fill chars rectangles in atlas info
706 recs[i].x = (float)offsetX;
707 recs[i].y = (float)offsetY;
708 recs[i].width = (float)chars[i].image.width;
709 recs[i].height = (float)chars[i].image.height;
711 // Move atlas position X for next character drawing
712 offsetX += (chars[i].image.width + 2*padding);
714 if (offsetX >= (atlas.width - chars[i].image.width - 2*padding))
718 // NOTE: Be careful on offsetY for SDF fonts, by default SDF
719 // use an internal padding of 4 pixels, it means char rectangle
720 // height is bigger than fontSize, it could be up to (fontSize + 8)
721 offsetY += (fontSize + 2*padding);
723 if (offsetY > (atlas.height - fontSize - padding)) break;
727 else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect)
729 stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context));
730 stbrp_node *nodes = (stbrp_node *)RL_MALLOC(charsCount*sizeof(*nodes));
732 stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount);
733 stbrp_rect *rects = (stbrp_rect *)RL_MALLOC(charsCount*sizeof(stbrp_rect));
735 // Fill rectangles for packaging
736 for (int i = 0; i < charsCount; i++)
739 rects[i].w = chars[i].image.width + 2*padding;
740 rects[i].h = chars[i].image.height + 2*padding;
743 // Package rectangles into atlas
744 stbrp_pack_rects(context, rects, charsCount);
746 for (int i = 0; i < charsCount; i++)
748 // It return char rectangles in atlas
749 recs[i].x = rects[i].x + (float)padding;
750 recs[i].y = rects[i].y + (float)padding;
751 recs[i].width = (float)chars[i].image.width;
752 recs[i].height = (float)chars[i].image.height;
754 if (rects[i].was_packed)
756 // Copy pixel data from fc.data to atlas
757 for (int y = 0; y < chars[i].image.height; y++)
759 for (int x = 0; x < chars[i].image.width; x++)
761 ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
765 else TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", i);
773 // TODO: Crop image if required for smaller size
775 // Convert image data from GRAYSCALE to GRAY_ALPHA
776 unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
778 for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2)
780 dataGrayAlpha[k] = 255;
781 dataGrayAlpha[k + 1] = ((unsigned char *)atlas.data)[i];
785 atlas.data = dataGrayAlpha;
786 atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
794 // Unload font chars info data (RAM)
795 void UnloadFontData(CharInfo *chars, int charsCount)
797 for (int i = 0; i < charsCount; i++) UnloadImage(chars[i].image);
802 // Unload Font from GPU memory (VRAM)
803 void UnloadFont(Font font)
805 // NOTE: Make sure font is not default font (fallback)
806 if (font.texture.id != GetFontDefault().texture.id)
808 UnloadFontData(font.chars, font.charsCount);
809 UnloadTexture(font.texture);
812 TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
817 // NOTE: Uses default font
818 void DrawFPS(int posX, int posY)
820 Color color = LIME; // good fps
823 if (fps < 30 && fps >= 15) color = ORANGE; // warning FPS
824 else if (fps < 15) color = RED; // bad FPS
826 DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color);
829 // Draw text (using default font)
830 // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
831 // NOTE: chars spacing is proportional to fontSize
832 void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
834 // Check if default font has been loaded
835 if (GetFontDefault().texture.id != 0)
837 Vector2 position = { (float)posX, (float)posY };
839 int defaultFontSize = 10; // Default Font chars height in pixel
840 if (fontSize < defaultFontSize) fontSize = defaultFontSize;
841 int spacing = fontSize/defaultFontSize;
843 DrawTextEx(GetFontDefault(), text, position, (float)fontSize, (float)spacing, color);
847 // Draw text using Font
848 // NOTE: chars spacing is NOT proportional to fontSize
849 void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
851 if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font
853 int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
855 int textOffsetY = 0; // Offset between lines (on line break '\n')
856 float textOffsetX = 0.0f; // Offset X to next character to draw
858 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
860 for (int i = 0; i < length;)
862 // Get next codepoint from byte string and glyph index in font
863 int codepointByteCount = 0;
864 int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
865 int index = GetGlyphIndex(font, codepoint);
867 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
868 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
869 if (codepoint == 0x3f) codepointByteCount = 1;
871 if (codepoint == '\n')
873 // NOTE: Fixed line spacing of 1.5 line-height
874 // TODO: Support custom line spacing defined by user
875 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
880 if ((codepoint != ' ') && (codepoint != '\t'))
882 DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
885 if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
886 else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
889 i += codepointByteCount; // Move text bytes counter to next codepoint
893 // Draw text using font inside rectangle limits
894 void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
896 DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
899 // Draw text using font inside rectangle limits with support for text selection
900 void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
902 int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
904 int textOffsetY = 0; // Offset between lines (on line break '\n')
905 float textOffsetX = 0.0f; // Offset X to next character to draw
907 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
909 // Word/character wrapping mechanism variables
910 enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
911 int state = wordWrap? MEASURE_STATE : DRAW_STATE;
913 int startLine = -1; // Index where to begin drawing (where a line begins)
914 int endLine = -1; // Index where to stop drawing (where a line ends)
915 int lastk = -1; // Holds last value of the character position
917 for (int i = 0, k = 0; i < length; i++, k++)
919 // Get next codepoint from byte string and glyph index in font
920 int codepointByteCount = 0;
921 int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
922 int index = GetGlyphIndex(font, codepoint);
924 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
925 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
926 if (codepoint == 0x3f) codepointByteCount = 1;
927 i += (codepointByteCount - 1);
930 if (codepoint != '\n')
932 glyphWidth = (font.chars[index].advanceX == 0)?
933 (int)(font.recs[index].width*scaleFactor + spacing):
934 (int)(font.chars[index].advanceX*scaleFactor + spacing);
937 // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
938 // We store this info in startLine and endLine, then we change states, draw the text between those two variables
939 // and change states again and again recursively until the end of the text (or until we get outside of the container).
940 // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
941 // and begin drawing on the next line before we can get outside the container.
942 if (state == MEASURE_STATE)
944 // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
945 // Ref: http://jkorpela.fi/chars/spaces.html
946 if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
948 if ((textOffsetX + glyphWidth + 1) >= rec.width)
950 endLine = (endLine < 1)? i : endLine;
951 if (i == endLine) endLine -= codepointByteCount;
952 if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
956 else if ((i + 1) == length)
962 else if (codepoint == '\n') state = !state;
964 if (state == DRAW_STATE)
970 // Save character position when we switch states
978 if (codepoint == '\n')
982 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
988 if (!wordWrap && ((textOffsetX + glyphWidth + 1) >= rec.width))
990 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
994 // When text overflows rectangle height limit, just stop drawing
995 if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
997 // Draw selection background
998 bool isGlyphSelected = false;
999 if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
1001 DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, (float)glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
1002 isGlyphSelected = true;
1005 // Draw current character glyph
1006 if ((codepoint != ' ') && (codepoint != '\t'))
1008 DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
1012 if (wordWrap && (i == endLine))
1014 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
1016 startLine = endLine;
1019 selectStart += lastk - k;
1026 textOffsetX += glyphWidth;
1030 // Draw one character (codepoint)
1031 void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint)
1033 // Character index position in sprite font
1034 // NOTE: In case a codepoint is not available in the font, index returned points to '?'
1035 int index = GetGlyphIndex(font, codepoint);
1036 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
1038 // Character destination rectangle on screen
1039 // NOTE: We consider charsPadding on drawing
1040 Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor,
1041 position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor,
1042 (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor,
1043 (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor };
1045 // Character source rectangle from font texture atlas
1046 // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
1047 Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
1048 font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
1050 // Draw the character texture on the screen
1051 DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
1054 // Measure string width for default font
1055 int MeasureText(const char *text, int fontSize)
1057 Vector2 vec = { 0.0f, 0.0f };
1059 // Check if default font has been loaded
1060 if (GetFontDefault().texture.id != 0)
1062 int defaultFontSize = 10; // Default Font chars height in pixel
1063 if (fontSize < defaultFontSize) fontSize = defaultFontSize;
1064 int spacing = fontSize/defaultFontSize;
1066 vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
1072 // Measure string size for Font
1073 Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
1075 int len = TextLength(text);
1076 int tempLen = 0; // Used to count longer text line num chars
1079 float textWidth = 0.0f;
1080 float tempTextWidth = 0.0f; // Used to count longer text line width
1082 float textHeight = (float)font.baseSize;
1083 float scaleFactor = fontSize/(float)font.baseSize;
1085 int letter = 0; // Current character
1086 int index = 0; // Index position in sprite font
1088 for (int i = 0; i < len; i++)
1093 letter = GetNextCodepoint(&text[i], &next);
1094 index = GetGlyphIndex(font, letter);
1096 // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
1097 // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
1098 if (letter == 0x3f) next = 1;
1103 if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX;
1104 else textWidth += (font.recs[index].width + font.chars[index].offsetX);
1108 if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1111 textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines
1114 if (tempLen < lenCounter) tempLen = lenCounter;
1117 if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1119 Vector2 vec = { 0 };
1120 vec.x = tempTextWidth*scaleFactor + (float)((tempLen - 1)*spacing); // Adds chars spacing to measure
1121 vec.y = textHeight*scaleFactor;
1126 // Returns index position for a unicode character on spritefont
1127 int GetGlyphIndex(Font font, int codepoint)
1129 #ifndef GLYPH_NOTFOUND_CHAR_FALLBACK
1130 #define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?'
1133 // Support charsets with any characters order
1134 #define SUPPORT_UNORDERED_CHARSET
1135 #if defined(SUPPORT_UNORDERED_CHARSET)
1136 int index = GLYPH_NOTFOUND_CHAR_FALLBACK;
1138 for (int i = 0; i < font.charsCount; i++)
1140 if (font.chars[i].value == codepoint)
1149 return (codepoint - 32);
1153 //----------------------------------------------------------------------------------
1154 // Text strings management functions
1155 //----------------------------------------------------------------------------------
1156 // Get text length in bytes, check for \0 character
1157 unsigned int TextLength(const char *text)
1159 unsigned int length = 0; //strlen(text)
1163 while (*text++) length++;
1169 // Formatting of text with variables to 'embed'
1170 // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
1171 const char *TextFormat(const char *text, ...)
1173 #ifndef MAX_TEXTFORMAT_BUFFERS
1174 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
1177 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
1178 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
1179 static int index = 0;
1181 char *currentBuffer = buffers[index];
1182 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
1185 va_start(args, text);
1186 vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
1189 index += 1; // Move to next buffer for next function call
1190 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
1192 return currentBuffer;
1195 // Get integer value from text
1196 // NOTE: This function replaces atoi() [stdlib.h]
1197 int TextToInteger(const char *text)
1202 if ((text[0] == '+') || (text[0] == '-'))
1204 if (text[0] == '-') sign = -1;
1208 for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
1213 #if defined(SUPPORT_TEXT_MANIPULATION)
1214 // Copy one string to another, returns bytes copied
1215 int TextCopy(char *dst, const char *src)
1221 while (*src != '\0')
1236 // Check if two text string are equal
1237 // REQUIRES: strcmp()
1238 bool TextIsEqual(const char *text1, const char *text2)
1240 bool result = false;
1242 if (strcmp(text1, text2) == 0) result = true;
1247 // Get a piece of a text string
1248 const char *TextSubtext(const char *text, int position, int length)
1250 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1252 int textLength = TextLength(text);
1254 if (position >= textLength)
1256 position = textLength - 1;
1260 if (length >= textLength) length = textLength;
1262 for (int c = 0 ; c < length ; c++)
1264 *(buffer + c) = *(text + position);
1268 *(buffer + length) = '\0';
1273 // Replace text string
1274 // REQUIRES: strstr(), strncpy(), strcpy()
1275 // WARNING: Internally allocated memory must be freed by the user (if return != NULL)
1276 char *TextReplace(char *text, const char *replace, const char *by)
1278 // Sanity checks and initialization
1279 if (!text || !replace || !by) return NULL;
1283 char *insertPoint; // Next insert point
1284 char *temp; // Temp pointer
1285 int replaceLen; // Replace string length of (the string to remove)
1286 int byLen; // Replacement length (the string to replace replace by)
1287 int lastReplacePos; // Distance between replace and end of last replace
1288 int count; // Number of replacements
1290 replaceLen = TextLength(replace);
1291 if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count
1293 byLen = TextLength(by);
1295 // Count the number of replacements needed
1297 for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
1299 // Allocate returning string and point temp to it
1300 temp = result = RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1);
1302 if (!result) return NULL; // Memory could not be allocated
1304 // First time through the loop, all the variable are set correctly from here on,
1305 // temp points to the end of the result string
1306 // insertPoint points to the next occurrence of replace in text
1307 // text points to the remainder of text after "end of replace"
1310 insertPoint = strstr(text, replace);
1311 lastReplacePos = (int)(insertPoint - text);
1312 temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
1313 temp = strcpy(temp, by) + byLen;
1314 text += lastReplacePos + replaceLen; // Move to next "end of replace"
1317 // Copy remaind text part after replacement to result (pointed by moving temp)
1323 // Insert text in a specific position, moves all text forward
1324 // WARNING: Allocated memory should be manually freed
1325 char *TextInsert(const char *text, const char *insert, int position)
1327 int textLen = TextLength(text);
1328 int insertLen = TextLength(insert);
1330 char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
1332 for (int i = 0; i < position; i++) result[i] = text[i];
1333 for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
1334 for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
1336 result[textLen + insertLen] = '\0'; // Make sure text string is valid!
1341 // Join text strings with delimiter
1342 // REQUIRES: memset(), memcpy()
1343 const char *TextJoin(const char **textList, int count, const char *delimiter)
1345 static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1346 memset(text, 0, MAX_TEXT_BUFFER_LENGTH);
1347 char *textPtr = text;
1349 int totalLength = 0;
1350 int delimiterLen = TextLength(delimiter);
1352 for (int i = 0; i < count; i++)
1354 int textLength = TextLength(textList[i]);
1356 // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
1357 if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH)
1359 memcpy(textPtr, textList[i], textLength);
1360 totalLength += textLength;
1361 textPtr += textLength;
1363 if ((delimiterLen > 0) && (i < (count - 1)))
1365 memcpy(textPtr, delimiter, delimiterLen);
1366 totalLength += delimiterLen;
1367 textPtr += delimiterLen;
1375 // Split string into multiple strings
1376 // REQUIRES: memset()
1377 const char **TextSplit(const char *text, char delimiter, int *count)
1379 // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
1380 // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
1381 // all used memory is static... it has some limitations:
1382 // 1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT
1383 // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
1385 static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL };
1386 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1387 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
1396 // Count how many substrings we have on text and point to every one
1397 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1399 buffer[i] = text[i];
1400 if (buffer[i] == '\0') break;
1401 else if (buffer[i] == delimiter)
1403 buffer[i] = '\0'; // Set an end of string at this point
1404 result[counter] = buffer + i + 1;
1407 if (counter == MAX_TEXTSPLIT_COUNT) break;
1416 // Append text at specific position and move cursor!
1417 // REQUIRES: strcpy()
1418 void TextAppend(char *text, const char *append, int *position)
1420 strcpy(text + *position, append);
1421 *position += TextLength(append);
1424 // Find first text occurrence within a string
1425 // REQUIRES: strstr()
1426 int TextFindIndex(const char *text, const char *find)
1430 char *ptr = strstr(text, find);
1432 if (ptr != NULL) position = (int)(ptr - text);
1437 // Get upper case version of provided string
1438 // REQUIRES: toupper()
1439 const char *TextToUpper(const char *text)
1441 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1443 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1445 if (text[i] != '\0')
1447 buffer[i] = (char)toupper(text[i]);
1448 //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
1450 // TODO: Support Utf8 diacritics!
1451 //if ((text[i] >= 'à ') && (text[i] <= 'ý')) buffer[i] = text[i] - 32;
1453 else { buffer[i] = '\0'; break; }
1459 // Get lower case version of provided string
1460 // REQUIRES: tolower()
1461 const char *TextToLower(const char *text)
1463 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1465 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1467 if (text[i] != '\0')
1469 buffer[i] = (char)tolower(text[i]);
1470 //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
1472 else { buffer[i] = '\0'; break; }
1478 // Get Pascal case notation version of provided string
1479 // REQUIRES: toupper()
1480 const char *TextToPascal(const char *text)
1482 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1484 buffer[0] = (char)toupper(text[0]);
1486 for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++)
1488 if (text[j] != '\0')
1490 if (text[j] != '_') buffer[i] = text[j];
1494 buffer[i] = (char)toupper(text[j]);
1497 else { buffer[i] = '\0'; break; }
1503 // Encode text codepoint into utf8 text
1504 // REQUIRES: memcpy()
1505 // WARNING: Allocated memory should be manually freed
1506 char *TextToUtf8(int *codepoints, int length)
1508 // We allocate enough memory fo fit all possible codepoints
1509 // NOTE: 5 bytes for every codepoint should be enough
1510 char *text = (char *)RL_CALLOC(length*5, 1);
1511 const char *utf8 = NULL;
1514 for (int i = 0, bytes = 0; i < length; i++)
1516 utf8 = CodepointToUtf8(codepoints[i], &bytes);
1517 memcpy(text + size, utf8, bytes);
1521 // Resize memory to text length + string NULL terminator
1522 void *ptr = RL_REALLOC(text, size + 1);
1524 if (ptr != NULL) text = (char *)ptr;
1529 // Encode codepoint into utf8 text (char array length returned as parameter)
1530 RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength)
1532 static char utf8[6] = { 0 };
1535 if (codepoint <= 0x7f)
1537 utf8[0] = (char)codepoint;
1540 else if (codepoint <= 0x7ff)
1542 utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
1543 utf8[1] = (char)((codepoint & 0x3f) | 0x80);
1546 else if (codepoint <= 0xffff)
1548 utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
1549 utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
1550 utf8[2] = (char)((codepoint & 0x3f) | 0x80);
1553 else if (codepoint <= 0x10ffff)
1555 utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
1556 utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
1557 utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
1558 utf8[3] = (char)((codepoint & 0x3f) | 0x80);
1562 *byteLength = length;
1567 // Get all codepoints in a string, codepoints count returned by parameters
1568 // REQUIRES: memset()
1569 int *GetCodepoints(const char *text, int *count)
1571 static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 };
1572 memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int));
1574 int bytesProcessed = 0;
1575 int textLength = TextLength(text);
1576 int codepointsCount = 0;
1578 for (int i = 0; i < textLength; codepointsCount++)
1580 codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed);
1581 i += bytesProcessed;
1584 *count = codepointsCount;
1589 // Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found
1590 // NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead
1591 int GetCodepointsCount(const char *text)
1593 unsigned int len = 0;
1594 char *ptr = (char *)&text[0];
1596 while (*ptr != '\0')
1599 int letter = GetNextCodepoint(ptr, &next);
1601 if (letter == 0x3f) ptr += 1;
1609 #endif // SUPPORT_TEXT_MANIPULATION
1611 // Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
1612 // When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
1613 // Total number of bytes processed are returned as a parameter
1614 // NOTE: the standard says U+FFFD should be returned in case of errors
1615 // but that character is not supported by the default font in raylib
1616 // TODO: Optimize this code for speed!!
1617 int GetNextCodepoint(const char *text, int *bytesProcessed)
1620 UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt
1622 Char. number range | UTF-8 octet sequence
1623 (hexadecimal) | (binary)
1624 --------------------+---------------------------------------------
1625 0000 0000-0000 007F | 0xxxxxxx
1626 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1627 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1628 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1630 // NOTE: on decode errors we return as soon as possible
1632 int code = 0x3f; // Codepoint (defaults to '?')
1633 int octet = (unsigned char)(text[0]); // The first UTF8 octet
1634 *bytesProcessed = 1;
1638 // Only one octet (ASCII range x00-7F)
1641 else if ((octet & 0xe0) == 0xc0)
1644 // [0]xC2-DF [1]UTF8-tail(x80-BF)
1645 unsigned char octet1 = text[1];
1647 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1649 if ((octet >= 0xc2) && (octet <= 0xdf))
1651 code = ((octet & 0x1f) << 6) | (octet1 & 0x3f);
1652 *bytesProcessed = 2;
1655 else if ((octet & 0xf0) == 0xe0)
1658 unsigned char octet1 = text[1];
1659 unsigned char octet2 = '\0';
1661 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1665 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
1668 [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF)
1669 [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF)
1670 [0]xED [1]x80-9F [2]UTF8-tail(x80-BF)
1671 [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF)
1674 if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
1675 ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
1677 if ((octet >= 0xe0) && (0 <= 0xef))
1679 code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
1680 *bytesProcessed = 3;
1683 else if ((octet & 0xf8) == 0xf0)
1686 if (octet > 0xf4) return code;
1688 unsigned char octet1 = text[1];
1689 unsigned char octet2 = '\0';
1690 unsigned char octet3 = '\0';
1692 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1696 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
1700 if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence
1703 [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail
1704 [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail
1705 [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail
1708 if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) ||
1709 ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence
1713 code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f);
1714 *bytesProcessed = 4;
1718 if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid
1723 //----------------------------------------------------------------------------------
1724 // Module specific Functions Definition
1725 //----------------------------------------------------------------------------------
1726 #if defined(SUPPORT_FILEFORMAT_FNT)
1728 // Read a line from memory
1729 // REQUIRES: memcpy()
1730 // NOTE: Returns the number of bytes read
1731 static int GetLine(const char *origin, char *buffer, int maxLength)
1734 for (; count < maxLength; count++) if (origin[count] == '\n') break;
1735 memcpy(buffer, origin, count);
1739 // Load a BMFont file (AngelCode font file)
1740 // REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
1741 static Font LoadBMFont(const char *fileName)
1743 #define MAX_BUFFER_SIZE 256
1747 char buffer[MAX_BUFFER_SIZE] = { 0 };
1748 char *searchPoint = NULL;
1755 char imFileName[129];
1757 int base = 0; // Useless data
1759 char *fileText = LoadFileText(fileName);
1761 if (fileText == NULL) return font;
1763 char *fileTextPtr = fileText;
1765 // NOTE: We skip first line, it contains no useful information
1766 int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1767 fileTextPtr += (lineBytes + 1);
1770 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1771 searchPoint = strstr(buffer, "lineHeight");
1772 sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
1773 fileTextPtr += (lineBytes + 1);
1775 TRACELOGD("FONT: [%s] Loaded font info:", fileName);
1776 TRACELOGD(" > Base size: %i", fontSize);
1777 TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight);
1779 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1780 searchPoint = strstr(buffer, "file");
1781 sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
1782 fileTextPtr += (lineBytes + 1);
1784 TRACELOGD(" > Texture filename: %s", imFileName);
1786 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1787 searchPoint = strstr(buffer, "count");
1788 sscanf(searchPoint, "count=%i", &charsCount);
1789 fileTextPtr += (lineBytes + 1);
1791 TRACELOGD(" > Chars count: %i", charsCount);
1793 // Compose correct path using route of .fnt file (fileName) and imFileName
1794 char *imPath = NULL;
1795 char *lastSlash = NULL;
1797 lastSlash = strrchr(fileName, '/');
1798 if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
1800 if (lastSlash != NULL)
1802 // NOTE: We need some extra space to avoid memory corruption on next allocations!
1803 imPath = RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1);
1804 memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
1805 memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName));
1807 else imPath = imFileName;
1809 TRACELOGD(" > Image loading path: %s", imPath);
1811 Image imFont = LoadImage(imPath);
1813 if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
1815 // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
1816 Image imFontAlpha = {
1817 .data = calloc(imFont.width*imFont.height, 2),
1818 .width = imFont.width,
1819 .height = imFont.height,
1820 .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
1824 for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
1826 ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
1827 ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
1830 UnloadImage(imFont);
1831 imFont = imFontAlpha;
1834 font.texture = LoadTextureFromImage(imFont);
1836 if (lastSlash != NULL) RL_FREE(imPath);
1838 // Fill font characters info data
1839 font.baseSize = fontSize;
1840 font.charsCount = charsCount;
1841 font.charsPadding = 0;
1842 font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
1843 font.recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
1845 int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
1847 for (int i = 0; i < charsCount; i++)
1849 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1850 sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
1851 &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
1852 fileTextPtr += (lineBytes + 1);
1854 // Get character rectangle in the font atlas texture
1855 font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
1857 // Save data properly in sprite font
1858 font.chars[i].value = charId;
1859 font.chars[i].offsetX = charOffsetX;
1860 font.chars[i].offsetY = charOffsetY;
1861 font.chars[i].advanceX = charAdvanceX;
1863 // Fill character image data from imFont data
1864 font.chars[i].image = ImageFromImage(imFont, font.recs[i]);
1867 UnloadImage(imFont);
1870 if (font.texture.id == 0)
1873 font = GetFontDefault();
1874 TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load texture, reverted to default font", fileName);
1876 else TRACELOG(LOG_INFO, "FONT: [%s] Font loaded successfully", fileName);