Я пытаюсь отобразить двунаправленный текст , используя fribidi + harfbuzz + freetype . Код, который дал мне наилучший возможный результат, здесь, наряду с изображением результата.
Проблема очевидна на изображении: некоторые буквы появляются как квадраты.
Как я могу решить проблему?
Я использую все библиотеки от MSYS2.
Раздел отладки:
Я пробовал два других шрифта: Notosansarabic и amiri .
Они оба работают нормально, а результат показан на следующих изображениях. />
< /p>
Однако проблема сохраняется с помощью cairo-regular, что, кстати, я проверял в Годоте, и она работает нормально, и результат показан на следующем изображении. alt = "Каирский тест в годо" src = "https://i.sstatic.net/tcmngjpy.png"/>
Я пытаюсь отобразить [b] двунаправленный текст [/b], используя fribidi + harfbuzz + freetype . Код, который дал мне наилучший возможный результат, здесь, наряду с изображением результата. Проблема очевидна на изображении: некоторые буквы появляются как квадраты. Как я могу решить проблему? Я использую все библиотеки от MSYS2.[code]#include #include #include #include #include
#include #include
#include #include FT_FREETYPE_H
#include #include
#include // Include the FriBidi header
#include #include #include
// Window settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600;
// Struct to hold information about a rendered glyph struct Character { unsigned int TextureID; glm::ivec2 Size; glm::ivec2 Bearing; };
// A cache to store loaded characters to avoid reloading them every frame std::map CharactersCache; unsigned int textVAO, textVBO;
// ====================== BiDi Analysis and Reordering using FriBidi ====================== const char *text = "مرحبا انا استخدم freetype"; const FriBidiStrIndex byte_len = strlen(text);
// 1. Decode the UTF-8 string into Unicode Codepoints (Logical Order) std::vector logical_str(byte_len); // Fix 1: Capture the actual number of codepoints, which is different from the byte length. const FriBidiStrIndex codepoint_len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, byte_len, logical_str.data());
// 2. Analyze the text to determine paragraph direction and embedding levels FriBidiParType par_type = FRIBIDI_PAR_ON; // ON = Auto-detect paragraph direction // Fix 2: Use the correct codepoint length for containers std::vector levels(codepoint_len); fribidi_get_par_embedding_levels_ex(logical_str.data(), NULL, codepoint_len, &par_type, levels.data());
// 3. Reorder: Convert the text from Logical Order to Visual Order std::vector visual_str(codepoint_len); fribidi_log2vis(logical_str.data(), codepoint_len, &par_type, visual_str.data(), NULL, NULL, NULL); // ====================================================================================
// ====================== Shape the visually ordered text using HarfBuzz ====================== hb_buffer_t* hb_buf = hb_buffer_create();
// Use add_codepoints because we are now dealing with Unicode codepoints directly // Fix 3: Use the correct codepoint length when adding characters to the buffer hb_buffer_add_codepoints(hb_buf, visual_str.data(), codepoint_len, 0, codepoint_len);
// Since the text is now visually ordered, we always process it Left-to-Right hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR);
hb_buffer_guess_segment_properties(hb_buf); // Let HarfBuzz guess script/language for shaping hb_shape(hb_font, hb_buf, NULL, 0); // ===================================================================================
// --- Calculate text width for alignment --- float text_width = 0; unsigned int glyph_count; hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &glyph_count); for (unsigned int i = 0; i < glyph_count; ++i) { text_width += glyph_pos[i].x_advance / 64.0f; }
// Since the entire text is now visually LTR, we always align to the left for centering float start_x = (SCR_WIDTH / 2.0f) - (text_width / 2.0f); float text_y = 300.0f;
float cursor_x = x; for (unsigned int i = 0; i < glyph_count; i++) { unsigned int glyph_index = glyph_info[i].codepoint;
// Check if we have already loaded this glyph if (CharactersCache.find(glyph_index) == CharactersCache.end()) { // Load and render the glyph using FreeType if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER)) continue;
// Store character for later use Character character = { texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top) }; CharactersCache.insert({glyph_index, character}); }
Character ch = CharactersCache[glyph_index];
// Calculate glyph position using HarfBuzz's precise metrics float x_pos = cursor_x + (glyph_pos[i].x_offset / 64.0f); float y_pos = y + (glyph_pos[i].y_offset / 64.0f);
// Adjust position based on FreeType's bearing values x_pos += ch.Bearing.x; y_pos -= (ch.Size.y - ch.Bearing.y);
// Render the glyph texture over the quad if(ch.TextureID != 0) { glBindTexture(GL_TEXTURE_2D, ch.TextureID); glBindBuffer(GL_ARRAY_BUFFER, textVBO); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawArrays(GL_TRIANGLES, 0, 6); }
// Advance the cursor for the next glyph cursor_x += (glyph_pos[i].x_advance / 64.0f); } glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } [/code]
Построить файл: [code]cmake_minimum_required(VERSION 3.10) project(freetype_test)
# Set C++ standards set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF)
# Set MSYS2 path set(MSYS2_MINGW_PREFIX "D:/MSYS2/mingw64") set(CMAKE_PREFIX_PATH ${MSYS2_MINGW_PREFIX})
# Find GLFW package find_package(glfw3 REQUIRED)
# Create the application add_executable(app src/main.cpp src/glad.cpp )
# Add include directories target_include_directories(app PRIVATE include # glad.h ${MSYS2_MINGW_PREFIX}/include ${MSYS2_MINGW_PREFIX}/include/freetype2 ${MSYS2_MINGW_PREFIX}/include/harfbuzz ${MSYS2_MINGW_PREFIX}/include/fribidi )
# Copy fonts folder to build directory add_custom_command( TARGET app POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/fonts ${CMAKE_BINARY_DIR}/fonts ) < /code> Структура проекта: < /p> project/ --build/ --fonts/ --Cairo-Regular.ttf --src/ --main.cpp --glad.cpp --include --glm/ --glad/ --glad.h --CMakelists.txt [/code] Раздел отладки: Я пробовал два других шрифта: Notosansarabic и amiri . Они оба работают нормально, а результат показан на следующих изображениях. /> < /p> Однако проблема сохраняется с помощью cairo-regular, что, кстати, я проверял в Годоте, и она работает нормально, и результат показан на следующем изображении. alt = "Каирский тест в годо" src = "https://i.sstatic.net/tcmngjpy.png"/>