diff --git a/examples/text/resources/NotoSansTC-Regular.ttf b/examples/text/resources/NotoSansTC-Regular.ttf new file mode 100644 index 000000000000..e838ef549bbe Binary files /dev/null and b/examples/text/resources/NotoSansTC-Regular.ttf differ diff --git a/examples/text/text_unicode_font.c b/examples/text/text_unicode_font.c new file mode 100644 index 000000000000..316d8c8749c1 --- /dev/null +++ b/examples/text/text_unicode_font.c @@ -0,0 +1,127 @@ +#include +#include + +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 450 + +typedef struct { + int* data; + int count; + int capacity; +} CodepointsArray; + +static void AddRange(CodepointsArray* array, int start, int stop) { + int rangeSize = stop - start + 1; + + if (array->count + rangeSize > array->capacity) { + array->capacity = array->count + rangeSize + 1024; + array->data = (int*)MemRealloc(array->data, array->capacity * sizeof(int)); + if (!array->data) { + TraceLog(LOG_ERROR, "FONTUTIL: Memory allocation failed"); + exit(1); + } + } + + for (int i = start; i <= stop; i++) { + array->data[array->count++] = i; + } +} + +Font LoadUnicodeFont(const char* fileName, int fontSize, int textureFilter) { + CodepointsArray cp = {0}; + cp.capacity = 2048; + cp.data = (int*)MemAlloc(cp.capacity * sizeof(int)); + + if (!cp.data) { + TraceLog(LOG_ERROR, "FONTUTIL: Initial allocation failed"); + return GetFontDefault(); + } + + // Basic ASCII + AddRange(&cp, 32, 126); + + // European Languages + AddRange(&cp, 0xC0, 0x17F); + AddRange(&cp, 0x180, 0x24F); + AddRange(&cp, 0x1E00, 0x1EFF); + AddRange(&cp, 0x2C60, 0x2C7F); + + // Greek + AddRange(&cp, 0x370, 0x3FF); + AddRange(&cp, 0x1F00, 0x1FFF); + + // Cyrillic + AddRange(&cp, 0x400, 0x4FF); + AddRange(&cp, 0x500, 0x52F); + AddRange(&cp, 0x2DE0, 0x2DFF); + AddRange(&cp, 0xA640, 0xA69F); + + // CJK + AddRange(&cp, 0x4E00, 0x9FFF); + AddRange(&cp, 0x3400, 0x4DBF); + AddRange(&cp, 0x3000, 0x303F); + AddRange(&cp, 0x3040, 0x309F); + AddRange(&cp, 0x30A0, 0x30FF); + AddRange(&cp, 0x31F0, 0x31FF); + AddRange(&cp, 0xFF00, 0xFFEF); + AddRange(&cp, 0xAC00, 0xD7AF); + AddRange(&cp, 0x1100, 0x11FF); + + // Other + AddRange(&cp, 0x900, 0x97F); // Devanagari + AddRange(&cp, 0x600, 0x6FF); // Arabic + AddRange(&cp, 0x5D0, 0x5EA); // Hebrew + + Font font = {0}; + + if (FileExists(fileName)) { + font = LoadFontEx(fileName, fontSize, cp.data, cp.count); + } + + if (font.texture.id == 0) { + font = GetFontDefault(); + TraceLog(LOG_WARNING, "FONTUTIL: Using default font"); + } + + SetTextureFilter(font.texture, textureFilter); + MemFree(cp.data); + + return font; +} + +/** + * Main entry point + */ +int main(void) +{ + // Initialize window + InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Unicode Font Example"); + SetTargetFPS(60); + + // Load font with Unicode support + Font myFont = LoadUnicodeFont("resources/NotoSansTC-Regular.ttf", 36, TEXTURE_FILTER_BILINEAR); + + // Main render loop + while (!WindowShouldClose()) + { + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Render test strings in different languages + DrawTextEx(myFont, "English: Hello World!", (Vector2){50, 50}, 36, 1, DARKGRAY); + DrawTextEx(myFont, "Русский: Привет мир!", (Vector2){50, 100}, 36, 0, DARKGRAY); + DrawTextEx(myFont, "中文: 你好世界!", (Vector2){50, 150}, 36, 1, DARKGRAY); + DrawTextEx(myFont, "日本語: こんにちは世界!", (Vector2){50, 200}, 36, 1, DARKGRAY); + + // Display font attribution + DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", + 10, SCREEN_HEIGHT - 20, 10, GRAY); + EndDrawing(); + } + + // Cleanup resources + UnloadFont(myFont); + CloseWindow(); + + return 0; +}