Ver código fonte

Add multilingual (Hebrew, CJK for now) support

Evgeniy Parfenyuk 2 semanas atrás
pai
commit
0fa1148fc2
5 arquivos alterados com 101 adições e 17 exclusões
  1. BIN
      assets/fonts/NotoSans-Regular.ttf
  2. BIN
      assets/fonts/unifont.otf
  3. 45 14
      src/main.c
  4. 53 0
      src/ui/font_manager.h
  5. 3 3
      src/ui/text_box.c

BIN
assets/fonts/NotoSans-Regular.ttf


BIN
assets/fonts/unifont.otf


+ 45 - 14
src/main.c

@@ -21,6 +21,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include "raylib.h"
 #include <stdio.h>
 #include "ui/text_box.h"
+#include "ui/font_manager.h"
 
 int main(void)
 {
@@ -32,23 +33,48 @@ int main(void)
     SetWindowMinSize(800, 600);
     SetTargetFPS(60);
 
-    Font gameFont = LoadFontEx("assets/fonts/NotoSans-Regular.ttf", 32, 
-                                NULL, 1240);
+    int codepointCount = 0;
+    int *myCodepoints = GenerateFontCodepoints(&codepointCount);
+    Font gameFont = LoadFontEx("assets/fonts/unifont.otf", 32, 
+                                myCodepoints, codepointCount);
+    free(myCodepoints);
     SetTextureFilter(gameFont.texture, TEXTURE_FILTER_BILINEAR);
-
-    Rectangle box_rect = { 40, (float)screenHeight - 180, 
-                               (float)screenWidth  - 80, 160 };
+    
+    Rectangle box_rect = { 
+        ((float)screenWidth - (float)screenWidth * 5.0f / 6.0f) / 2.0f, 
+        (float)screenHeight - 180, 
+        (float)screenWidth * 5.0f / 6.0f, 
+        160 
+    };
     TextBox dialogueBox;
     TextBoxInit(&dialogueBox, box_rect, gameFont);
 
     TextBoxSetText(&dialogueBox,
-    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
-    "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
-    "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "
-    "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
-    "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
-    "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa "
-    "qui officia deserunt mollit anim id est laborum.");
+    // LINE 1: English (ASCII)
+    "\x4E\x65\x76\x65\x72\x20\x67\x6F\x6E\x6E\x61\x20\x67\x69\x76\x65"
+    "\x20\x79\x6F\x75\x20\x75\x70\x2E\x0A"
+
+    // LINE 2: Russian (Cyrillic)
+    "\xD0\x92\xD1\x8B\x20\xD0\xBF\xD1\x80\xD0\xBE\xD0\xB4\xD0\xB0\xD1"
+    "\x91\xD1\x82\xD0\xB5\x20\xD1\x80\xD1\x8B\xD0\xB1\xD0\xBE\xD0\xB2"
+    "\x3F\x20\xD0\x9D\xD0\xB5\xD1\x82\x2C\x20\xD1\x82\xD0\xBE\xD0\xBB"
+    "\xD1\x8C\xD0\xBA\xD0\xBE\x20\xD0\xBF\xD0\xBE\xD0\xBA\xD0\xB0\xD0"
+    "\xB7\xD1\x8B\xD0\xB2\xD0\xB0\xD1\x8E\x2E\x20\xD0\x9A\xD1\x80\xD0"
+    "\xB0\xD1\x81\xD0\xB8\xD0\xB2\xD0\xBE\xD0\xB5\x2E\x2E\x2E\x0A"
+
+    // LINE 3: Hebrew (RTL unsupported yet)
+    "\xD7\x91\xD7\xA8\xD7\x90\xD7\xA9\xD7\x99\xD7\xAA\x20\xD7\x91\xD7"
+    "\xA8\xD7\x90\x20\xD7\x90\xD7\x9C\xD7\x94\xD7\x99\xD7\x9D\x20\xD7"
+    "\x90\xD7\xAA\x20\xD7\x94\xD7\xA9\xD7\x9E\xD7\x99\xD7\x9D\x20\xD7"
+    "\x95\xD7\x90\xD7\xAA\x20\xD7\x94\xD7\x90\xD7\xA8\xD7\xA5\x2E\x0A"
+
+    // LINE 4: Japanese (CJK)
+    "\xE3\x80\x8C\xE7\xB5\x82\xE6\x9C\xAB\xE3\x81\xAA\xE3\x81\xAB\xE3"
+    "\x81\x97\xE3\x81\xA6\xE3\x81\xBE\xE3\x81\x99\xE3\x81\x8B\xEF\xBC"
+    "\x9F\xE5\xBF\x99\xE3\x81\x97\xE3\x81\x84\xE3\x81\xA7\xE3\x81\x99"
+    "\xE3\x81\x8B\xEF\xBC\x9F\xE6\x95\x91\xE3\x81\xA3\xE3\x81\xA6\xE3"
+    "\x82\x82\xE3\x82\x89\xE3\x81\xA3\xE3\x81\xA6\xE3\x81\x84\xE3\x81"
+    "\xA4\xE3\x81\x99\xE3\x81\x8B\xEF\xBC\x9F\xE3\x80\x8D");
     while (!WindowShouldClose())
     {
         float dt = GetFrameTime();
@@ -57,8 +83,13 @@ int main(void)
         if (IsWindowResized()) {
             screenWidth  = GetScreenWidth();
             screenHeight = GetScreenHeight();
-            dialogueBox.rect = (Rectangle){40, (float)screenHeight - 180, 
-                                               (float)screenWidth  - 80, 160};
+            dialogueBox.rect = 
+                (Rectangle){ 
+                    ((float)screenWidth - (float)screenWidth*5.0f/6.0f)/2.0f, 
+                    (float)screenHeight - 180, 
+                    (float)screenWidth * 5.0f / 6.0f, 
+                    160 
+                };
             TextBoxReflow(&dialogueBox);
         }
 

+ 53 - 0
src/ui/font_manager.h

@@ -0,0 +1,53 @@
+#ifndef FONT_MANAGER_H
+#define FONT_MANAGER_H
+
+#include <stdlib.h>
+#include "raylib.h"
+
+typedef struct {
+    int start;
+    int end;
+    const char *name;
+} UnicodeBlock;
+
+int *GenerateFontCodepoints(int *outCount) 
+{
+    UnicodeBlock blocks[] = {
+        { 32, 126, "ASCII (English, Numbers, Punctuation)" },
+        { 0x0400, 0x04FF, "Cyrillic (Russian)" },
+        { 0x0590, 0x05FF, "Hebrew" },
+        { 0x3000, 0x303F, "Japanese CJK Symbols and Punctuation" },
+        { 0x3040, 0x309F, "Japanese Hiragana" },
+        { 0x30A0, 0x30FF, "Japanese Katakana" },
+        { 0x4E00, 0x9FFF, "CJK Unified Ideographs (Japanese Kanji / Chinese)" }
+    };
+    int blockCount = sizeof(blocks) / sizeof(blocks[0]);
+
+    int totalCapacity = 0;
+    for (int i = 0; i < blockCount; i++) {
+        totalCapacity += (blocks[i].end - blocks[i].start + 1);
+    }
+
+    int *codepoints = (int *)malloc(sizeof(int) * totalCapacity);
+    if (codepoints == NULL) {
+        TraceLog(LOG_ERROR, "[Vinora] Language font loading error!");
+        *outCount = 0;
+        return NULL;
+    }
+
+    int index = 0;
+    for (int i = 0; i < blockCount; i++) {
+        TraceLog(LOG_INFO, 
+                "[Vinora] Adding codepoint range: %s (0x%04X - 0x%04X)", 
+                blocks[i].name, blocks[i].start, blocks[i].end);
+        
+        for (int cp = blocks[i].start; cp <= blocks[i].end; cp++) {
+            codepoints[index++] = cp;
+        }
+    }
+
+    *outCount = index; 
+    return codepoints;
+}
+
+#endif

+ 3 - 3
src/ui/text_box.c

@@ -41,7 +41,7 @@ void TextBoxReflow(TextBox *tb)
         }
         return;
     }
-    float min_size = 12.0f;
+    float min_size = 16.0f;
     float current_size = tb->base_font_size;
     
     float max_width = tb->rect.width - (tb->padding * 2);
@@ -49,7 +49,7 @@ void TextBoxReflow(TextBox *tb)
     
     char *temp_wrapped = NULL;
     
-    while (current_size >= min_size)
+    while (current_size > min_size)
     {
         if (temp_wrapped) {
             free(temp_wrapped);
@@ -71,7 +71,7 @@ void TextBoxReflow(TextBox *tb)
 
     if (current_size < min_size)
         TraceLog(LOG_WARNING, 
-                "[Vinora] Text too long! Even with %f font_size!", min_size);
+            "[Vinora] Text too long! Even with %f font_size!", current_size);
 }
 
 void TextBoxUpdate(TextBox *tb, float dt)