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 int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
853 int textOffsetY = 0; // Offset between lines (on line break '\n')
854 float textOffsetX = 0.0f; // Offset X to next character to draw
856 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
858 for (int i = 0; i < length;)
860 // Get next codepoint from byte string and glyph index in font
861 int codepointByteCount = 0;
862 int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
863 int index = GetGlyphIndex(font, codepoint);
865 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
866 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
867 if (codepoint == 0x3f) codepointByteCount = 1;
869 if (codepoint == '\n')
871 // NOTE: Fixed line spacing of 1.5 line-height
872 // TODO: Support custom line spacing defined by user
873 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
878 if ((codepoint != ' ') && (codepoint != '\t'))
880 DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
883 if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
884 else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
887 i += codepointByteCount; // Move text bytes counter to next codepoint
891 // Draw text using font inside rectangle limits
892 void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
894 DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
897 // Draw text using font inside rectangle limits with support for text selection
898 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)
900 int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
902 int textOffsetY = 0; // Offset between lines (on line break '\n')
903 float textOffsetX = 0.0f; // Offset X to next character to draw
905 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
907 // Word/character wrapping mechanism variables
908 enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
909 int state = wordWrap? MEASURE_STATE : DRAW_STATE;
911 int startLine = -1; // Index where to begin drawing (where a line begins)
912 int endLine = -1; // Index where to stop drawing (where a line ends)
913 int lastk = -1; // Holds last value of the character position
915 for (int i = 0, k = 0; i < length; i++, k++)
917 // Get next codepoint from byte string and glyph index in font
918 int codepointByteCount = 0;
919 int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
920 int index = GetGlyphIndex(font, codepoint);
922 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
923 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
924 if (codepoint == 0x3f) codepointByteCount = 1;
925 i += (codepointByteCount - 1);
928 if (codepoint != '\n')
930 glyphWidth = (font.chars[index].advanceX == 0)?
931 (int)(font.recs[index].width*scaleFactor + spacing):
932 (int)(font.chars[index].advanceX*scaleFactor + spacing);
935 // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
936 // We store this info in startLine and endLine, then we change states, draw the text between those two variables
937 // and change states again and again recursively until the end of the text (or until we get outside of the container).
938 // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
939 // and begin drawing on the next line before we can get outside the container.
940 if (state == MEASURE_STATE)
942 // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
943 // Ref: http://jkorpela.fi/chars/spaces.html
944 if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
946 if ((textOffsetX + glyphWidth + 1) >= rec.width)
948 endLine = (endLine < 1)? i : endLine;
949 if (i == endLine) endLine -= codepointByteCount;
950 if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
954 else if ((i + 1) == length)
960 else if (codepoint == '\n') state = !state;
962 if (state == DRAW_STATE)
968 // Save character position when we switch states
976 if (codepoint == '\n')
980 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
986 if (!wordWrap && ((textOffsetX + glyphWidth + 1) >= rec.width))
988 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
992 // When text overflows rectangle height limit, just stop drawing
993 if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
995 // Draw selection background
996 bool isGlyphSelected = false;
997 if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
999 DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, (float)glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
1000 isGlyphSelected = true;
1003 // Draw current character glyph
1004 if ((codepoint != ' ') && (codepoint != '\t'))
1006 DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
1010 if (wordWrap && (i == endLine))
1012 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
1014 startLine = endLine;
1017 selectStart += lastk - k;
1024 textOffsetX += glyphWidth;
1028 // Draw one character (codepoint)
1029 void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint)
1031 // Character index position in sprite font
1032 // NOTE: In case a codepoint is not available in the font, index returned points to '?'
1033 int index = GetGlyphIndex(font, codepoint);
1034 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
1036 // Character destination rectangle on screen
1037 // NOTE: We consider charsPadding on drawing
1038 Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor,
1039 position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor,
1040 (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor,
1041 (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor };
1043 // Character source rectangle from font texture atlas
1044 // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
1045 Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
1046 font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
1048 // Draw the character texture on the screen
1049 DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
1052 // Measure string width for default font
1053 int MeasureText(const char *text, int fontSize)
1055 Vector2 vec = { 0.0f, 0.0f };
1057 // Check if default font has been loaded
1058 if (GetFontDefault().texture.id != 0)
1060 int defaultFontSize = 10; // Default Font chars height in pixel
1061 if (fontSize < defaultFontSize) fontSize = defaultFontSize;
1062 int spacing = fontSize/defaultFontSize;
1064 vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
1070 // Measure string size for Font
1071 Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
1073 int len = TextLength(text);
1074 int tempLen = 0; // Used to count longer text line num chars
1077 float textWidth = 0.0f;
1078 float tempTextWidth = 0.0f; // Used to count longer text line width
1080 float textHeight = (float)font.baseSize;
1081 float scaleFactor = fontSize/(float)font.baseSize;
1083 int letter = 0; // Current character
1084 int index = 0; // Index position in sprite font
1086 for (int i = 0; i < len; i++)
1091 letter = GetNextCodepoint(&text[i], &next);
1092 index = GetGlyphIndex(font, letter);
1094 // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
1095 // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
1096 if (letter == 0x3f) next = 1;
1101 if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX;
1102 else textWidth += (font.recs[index].width + font.chars[index].offsetX);
1106 if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1109 textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines
1112 if (tempLen < lenCounter) tempLen = lenCounter;
1115 if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1117 Vector2 vec = { 0 };
1118 vec.x = tempTextWidth*scaleFactor + (float)((tempLen - 1)*spacing); // Adds chars spacing to measure
1119 vec.y = textHeight*scaleFactor;
1124 // Returns index position for a unicode character on spritefont
1125 int GetGlyphIndex(Font font, int codepoint)
1127 #ifndef GLYPH_NOTFOUND_CHAR_FALLBACK
1128 #define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?'
1131 // Support charsets with any characters order
1132 #define SUPPORT_UNORDERED_CHARSET
1133 #if defined(SUPPORT_UNORDERED_CHARSET)
1134 int index = GLYPH_NOTFOUND_CHAR_FALLBACK;
1136 for (int i = 0; i < font.charsCount; i++)
1138 if (font.chars[i].value == codepoint)
1147 return (codepoint - 32);
1151 //----------------------------------------------------------------------------------
1152 // Text strings management functions
1153 //----------------------------------------------------------------------------------
1154 // Get text length in bytes, check for \0 character
1155 unsigned int TextLength(const char *text)
1157 unsigned int length = 0; //strlen(text)
1161 while (*text++) length++;
1167 // Formatting of text with variables to 'embed'
1168 // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
1169 const char *TextFormat(const char *text, ...)
1171 #ifndef MAX_TEXTFORMAT_BUFFERS
1172 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
1175 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
1176 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
1177 static int index = 0;
1179 char *currentBuffer = buffers[index];
1180 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
1183 va_start(args, text);
1184 vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
1187 index += 1; // Move to next buffer for next function call
1188 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
1190 return currentBuffer;
1193 // Get integer value from text
1194 // NOTE: This function replaces atoi() [stdlib.h]
1195 int TextToInteger(const char *text)
1200 if ((text[0] == '+') || (text[0] == '-'))
1202 if (text[0] == '-') sign = -1;
1206 for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
1211 #if defined(SUPPORT_TEXT_MANIPULATION)
1212 // Copy one string to another, returns bytes copied
1213 int TextCopy(char *dst, const char *src)
1219 while (*src != '\0')
1234 // Check if two text string are equal
1235 // REQUIRES: strcmp()
1236 bool TextIsEqual(const char *text1, const char *text2)
1238 bool result = false;
1240 if (strcmp(text1, text2) == 0) result = true;
1245 // Get a piece of a text string
1246 const char *TextSubtext(const char *text, int position, int length)
1248 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1250 int textLength = TextLength(text);
1252 if (position >= textLength)
1254 position = textLength - 1;
1258 if (length >= textLength) length = textLength;
1260 for (int c = 0 ; c < length ; c++)
1262 *(buffer + c) = *(text + position);
1266 *(buffer + length) = '\0';
1271 // Replace text string
1272 // REQUIRES: strstr(), strncpy(), strcpy()
1273 // WARNING: Internally allocated memory must be freed by the user (if return != NULL)
1274 char *TextReplace(char *text, const char *replace, const char *by)
1276 // Sanity checks and initialization
1277 if (!text || !replace || !by) return NULL;
1281 char *insertPoint; // Next insert point
1282 char *temp; // Temp pointer
1283 int replaceLen; // Replace string length of (the string to remove)
1284 int byLen; // Replacement length (the string to replace replace by)
1285 int lastReplacePos; // Distance between replace and end of last replace
1286 int count; // Number of replacements
1288 replaceLen = TextLength(replace);
1289 if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count
1291 byLen = TextLength(by);
1293 // Count the number of replacements needed
1295 for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
1297 // Allocate returning string and point temp to it
1298 temp = result = RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1);
1300 if (!result) return NULL; // Memory could not be allocated
1302 // First time through the loop, all the variable are set correctly from here on,
1303 // temp points to the end of the result string
1304 // insertPoint points to the next occurrence of replace in text
1305 // text points to the remainder of text after "end of replace"
1308 insertPoint = strstr(text, replace);
1309 lastReplacePos = (int)(insertPoint - text);
1310 temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
1311 temp = strcpy(temp, by) + byLen;
1312 text += lastReplacePos + replaceLen; // Move to next "end of replace"
1315 // Copy remaind text part after replacement to result (pointed by moving temp)
1321 // Insert text in a specific position, moves all text forward
1322 // WARNING: Allocated memory should be manually freed
1323 char *TextInsert(const char *text, const char *insert, int position)
1325 int textLen = TextLength(text);
1326 int insertLen = TextLength(insert);
1328 char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
1330 for (int i = 0; i < position; i++) result[i] = text[i];
1331 for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
1332 for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
1334 result[textLen + insertLen] = '\0'; // Make sure text string is valid!
1339 // Join text strings with delimiter
1340 // REQUIRES: memset(), memcpy()
1341 const char *TextJoin(const char **textList, int count, const char *delimiter)
1343 static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1344 memset(text, 0, MAX_TEXT_BUFFER_LENGTH);
1345 char *textPtr = text;
1347 int totalLength = 0;
1348 int delimiterLen = TextLength(delimiter);
1350 for (int i = 0; i < count; i++)
1352 int textLength = TextLength(textList[i]);
1354 // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
1355 if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH)
1357 memcpy(textPtr, textList[i], textLength);
1358 totalLength += textLength;
1359 textPtr += textLength;
1361 if ((delimiterLen > 0) && (i < (count - 1)))
1363 memcpy(textPtr, delimiter, delimiterLen);
1364 totalLength += delimiterLen;
1365 textPtr += delimiterLen;
1373 // Split string into multiple strings
1374 // REQUIRES: memset()
1375 const char **TextSplit(const char *text, char delimiter, int *count)
1377 // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
1378 // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
1379 // all used memory is static... it has some limitations:
1380 // 1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT
1381 // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
1383 static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL };
1384 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1385 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
1394 // Count how many substrings we have on text and point to every one
1395 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1397 buffer[i] = text[i];
1398 if (buffer[i] == '\0') break;
1399 else if (buffer[i] == delimiter)
1401 buffer[i] = '\0'; // Set an end of string at this point
1402 result[counter] = buffer + i + 1;
1405 if (counter == MAX_TEXTSPLIT_COUNT) break;
1414 // Append text at specific position and move cursor!
1415 // REQUIRES: strcpy()
1416 void TextAppend(char *text, const char *append, int *position)
1418 strcpy(text + *position, append);
1419 *position += TextLength(append);
1422 // Find first text occurrence within a string
1423 // REQUIRES: strstr()
1424 int TextFindIndex(const char *text, const char *find)
1428 char *ptr = strstr(text, find);
1430 if (ptr != NULL) position = (int)(ptr - text);
1435 // Get upper case version of provided string
1436 // REQUIRES: toupper()
1437 const char *TextToUpper(const char *text)
1439 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1441 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1443 if (text[i] != '\0')
1445 buffer[i] = (char)toupper(text[i]);
1446 //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
1448 // TODO: Support Utf8 diacritics!
1449 //if ((text[i] >= 'à ') && (text[i] <= 'ý')) buffer[i] = text[i] - 32;
1451 else { buffer[i] = '\0'; break; }
1457 // Get lower case version of provided string
1458 // REQUIRES: tolower()
1459 const char *TextToLower(const char *text)
1461 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1463 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1465 if (text[i] != '\0')
1467 buffer[i] = (char)tolower(text[i]);
1468 //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
1470 else { buffer[i] = '\0'; break; }
1476 // Get Pascal case notation version of provided string
1477 // REQUIRES: toupper()
1478 const char *TextToPascal(const char *text)
1480 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1482 buffer[0] = (char)toupper(text[0]);
1484 for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++)
1486 if (text[j] != '\0')
1488 if (text[j] != '_') buffer[i] = text[j];
1492 buffer[i] = (char)toupper(text[j]);
1495 else { buffer[i] = '\0'; break; }
1501 // Encode text codepoint into utf8 text
1502 // REQUIRES: memcpy()
1503 // WARNING: Allocated memory should be manually freed
1504 char *TextToUtf8(int *codepoints, int length)
1506 // We allocate enough memory fo fit all possible codepoints
1507 // NOTE: 5 bytes for every codepoint should be enough
1508 char *text = (char *)RL_CALLOC(length*5, 1);
1509 const char *utf8 = NULL;
1512 for (int i = 0, bytes = 0; i < length; i++)
1514 utf8 = CodepointToUtf8(codepoints[i], &bytes);
1515 memcpy(text + size, utf8, bytes);
1519 // Resize memory to text length + string NULL terminator
1520 void *ptr = RL_REALLOC(text, size + 1);
1522 if (ptr != NULL) text = (char *)ptr;
1527 // Encode codepoint into utf8 text (char array length returned as parameter)
1528 RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength)
1530 static char utf8[6] = { 0 };
1533 if (codepoint <= 0x7f)
1535 utf8[0] = (char)codepoint;
1538 else if (codepoint <= 0x7ff)
1540 utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
1541 utf8[1] = (char)((codepoint & 0x3f) | 0x80);
1544 else if (codepoint <= 0xffff)
1546 utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
1547 utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
1548 utf8[2] = (char)((codepoint & 0x3f) | 0x80);
1551 else if (codepoint <= 0x10ffff)
1553 utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
1554 utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
1555 utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
1556 utf8[3] = (char)((codepoint & 0x3f) | 0x80);
1560 *byteLength = length;
1565 // Get all codepoints in a string, codepoints count returned by parameters
1566 // REQUIRES: memset()
1567 int *GetCodepoints(const char *text, int *count)
1569 static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 };
1570 memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int));
1572 int bytesProcessed = 0;
1573 int textLength = TextLength(text);
1574 int codepointsCount = 0;
1576 for (int i = 0; i < textLength; codepointsCount++)
1578 codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed);
1579 i += bytesProcessed;
1582 *count = codepointsCount;
1587 // Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found
1588 // NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead
1589 int GetCodepointsCount(const char *text)
1591 unsigned int len = 0;
1592 char *ptr = (char *)&text[0];
1594 while (*ptr != '\0')
1597 int letter = GetNextCodepoint(ptr, &next);
1599 if (letter == 0x3f) ptr += 1;
1607 #endif // SUPPORT_TEXT_MANIPULATION
1609 // Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
1610 // When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
1611 // Total number of bytes processed are returned as a parameter
1612 // NOTE: the standard says U+FFFD should be returned in case of errors
1613 // but that character is not supported by the default font in raylib
1614 // TODO: Optimize this code for speed!!
1615 int GetNextCodepoint(const char *text, int *bytesProcessed)
1618 UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt
1620 Char. number range | UTF-8 octet sequence
1621 (hexadecimal) | (binary)
1622 --------------------+---------------------------------------------
1623 0000 0000-0000 007F | 0xxxxxxx
1624 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1625 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1626 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1628 // NOTE: on decode errors we return as soon as possible
1630 int code = 0x3f; // Codepoint (defaults to '?')
1631 int octet = (unsigned char)(text[0]); // The first UTF8 octet
1632 *bytesProcessed = 1;
1636 // Only one octet (ASCII range x00-7F)
1639 else if ((octet & 0xe0) == 0xc0)
1642 // [0]xC2-DF [1]UTF8-tail(x80-BF)
1643 unsigned char octet1 = text[1];
1645 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1647 if ((octet >= 0xc2) && (octet <= 0xdf))
1649 code = ((octet & 0x1f) << 6) | (octet1 & 0x3f);
1650 *bytesProcessed = 2;
1653 else if ((octet & 0xf0) == 0xe0)
1656 unsigned char octet1 = text[1];
1657 unsigned char octet2 = '\0';
1659 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1663 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
1666 [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF)
1667 [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF)
1668 [0]xED [1]x80-9F [2]UTF8-tail(x80-BF)
1669 [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF)
1672 if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
1673 ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
1675 if ((octet >= 0xe0) && (0 <= 0xef))
1677 code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
1678 *bytesProcessed = 3;
1681 else if ((octet & 0xf8) == 0xf0)
1684 if (octet > 0xf4) return code;
1686 unsigned char octet1 = text[1];
1687 unsigned char octet2 = '\0';
1688 unsigned char octet3 = '\0';
1690 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1694 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
1698 if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence
1701 [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail
1702 [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail
1703 [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail
1706 if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) ||
1707 ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence
1711 code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f);
1712 *bytesProcessed = 4;
1716 if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid
1721 //----------------------------------------------------------------------------------
1722 // Module specific Functions Definition
1723 //----------------------------------------------------------------------------------
1724 #if defined(SUPPORT_FILEFORMAT_FNT)
1726 // Read a line from memory
1727 // REQUIRES: memcpy()
1728 // NOTE: Returns the number of bytes read
1729 static int GetLine(const char *origin, char *buffer, int maxLength)
1732 for (; count < maxLength; count++) if (origin[count] == '\n') break;
1733 memcpy(buffer, origin, count);
1737 // Load a BMFont file (AngelCode font file)
1738 // REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
1739 static Font LoadBMFont(const char *fileName)
1741 #define MAX_BUFFER_SIZE 256
1745 char buffer[MAX_BUFFER_SIZE] = { 0 };
1746 char *searchPoint = NULL;
1753 char imFileName[129];
1755 int base = 0; // Useless data
1757 char *fileText = LoadFileText(fileName);
1759 if (fileText == NULL) return font;
1761 char *fileTextPtr = fileText;
1763 // NOTE: We skip first line, it contains no useful information
1764 int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1765 fileTextPtr += (lineBytes + 1);
1768 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1769 searchPoint = strstr(buffer, "lineHeight");
1770 sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
1771 fileTextPtr += (lineBytes + 1);
1773 TRACELOGD("FONT: [%s] Loaded font info:", fileName);
1774 TRACELOGD(" > Base size: %i", fontSize);
1775 TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight);
1777 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1778 searchPoint = strstr(buffer, "file");
1779 sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
1780 fileTextPtr += (lineBytes + 1);
1782 TRACELOGD(" > Texture filename: %s", imFileName);
1784 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1785 searchPoint = strstr(buffer, "count");
1786 sscanf(searchPoint, "count=%i", &charsCount);
1787 fileTextPtr += (lineBytes + 1);
1789 TRACELOGD(" > Chars count: %i", charsCount);
1791 // Compose correct path using route of .fnt file (fileName) and imFileName
1792 char *imPath = NULL;
1793 char *lastSlash = NULL;
1795 lastSlash = strrchr(fileName, '/');
1796 if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
1798 if (lastSlash != NULL)
1800 // NOTE: We need some extra space to avoid memory corruption on next allocations!
1801 imPath = RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1);
1802 memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
1803 memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName));
1805 else imPath = imFileName;
1807 TRACELOGD(" > Image loading path: %s", imPath);
1809 Image imFont = LoadImage(imPath);
1811 if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
1813 // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
1814 Image imFontAlpha = {
1815 .data = calloc(imFont.width*imFont.height, 2),
1816 .width = imFont.width,
1817 .height = imFont.height,
1818 .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
1822 for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
1824 ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
1825 ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
1828 UnloadImage(imFont);
1829 imFont = imFontAlpha;
1832 font.texture = LoadTextureFromImage(imFont);
1834 if (lastSlash != NULL) RL_FREE(imPath);
1836 // Fill font characters info data
1837 font.baseSize = fontSize;
1838 font.charsCount = charsCount;
1839 font.charsPadding = 0;
1840 font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
1841 font.recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
1843 int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
1845 for (int i = 0; i < charsCount; i++)
1847 lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1848 sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
1849 &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
1850 fileTextPtr += (lineBytes + 1);
1852 // Get character rectangle in the font atlas texture
1853 font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
1855 // Save data properly in sprite font
1856 font.chars[i].value = charId;
1857 font.chars[i].offsetX = charOffsetX;
1858 font.chars[i].offsetY = charOffsetY;
1859 font.chars[i].advanceX = charAdvanceX;
1861 // Fill character image data from imFont data
1862 font.chars[i].image = ImageFromImage(imFont, font.recs[i]);
1865 UnloadImage(imFont);
1868 if (font.texture.id == 0)
1871 font = GetFontDefault();
1872 TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load texture, reverted to default font", fileName);
1874 else TRACELOG(LOG_INFO, "FONT: [%s] Font loaded successfully", fileName);