test_text_server.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*************************************************************************/
  2. /* test_text_server.h */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #ifdef TOOLS_ENABLED
  31. #ifndef TEST_TEXT_SERVER_H
  32. #define TEST_TEXT_SERVER_H
  33. #include "editor/builtin_fonts.gen.h"
  34. #include "servers/text_server.h"
  35. #include "tests/test_macros.h"
  36. namespace TestTextServer {
  37. TEST_SUITE("[[TextServer]") {
  38. TEST_CASE("[TextServer] Init, font loading and shaping") {
  39. TextServerManager *tsman = memnew(TextServerManager);
  40. Error err = OK;
  41. SUBCASE("[TextServer] Init") {
  42. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  43. TextServer *ts = TextServerManager::initialize(i, err);
  44. TEST_FAIL_COND((err != OK || ts == nullptr), "Text server ", TextServerManager::get_interface_name(i), " init failed.");
  45. }
  46. }
  47. SUBCASE("[TextServer] Loading fonts") {
  48. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  49. TextServer *ts = TextServerManager::initialize(i, err);
  50. RID font = ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf");
  51. TEST_FAIL_COND(font == RID(), "Loading font failed.");
  52. ts->free(font);
  53. }
  54. }
  55. SUBCASE("[TextServer] Text layout: Font fallback") {
  56. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  57. TextServer *ts = TextServerManager::initialize(i, err);
  58. Vector<RID> font;
  59. font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
  60. font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
  61. String test = U"คนอ้วน khon uan ראה";
  62. // 6^ 17^
  63. RID ctx = ts->create_shaped_text();
  64. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  65. bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
  66. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  67. Vector<TextServer::Glyph> glyphs = ts->shaped_text_get_glyphs(ctx);
  68. TEST_FAIL_COND(glyphs.size() == 0, "Shaping failed");
  69. for (int j = 0; j < glyphs.size(); j++) {
  70. if (glyphs[j].start < 6) {
  71. TEST_FAIL_COND(glyphs[j].font_rid != font[1], "Incorrect font selected.");
  72. }
  73. if ((glyphs[j].start > 6) && (glyphs[j].start < 16)) {
  74. TEST_FAIL_COND(glyphs[j].font_rid != font[0], "Incorrect font selected.");
  75. }
  76. if (glyphs[j].start > 16) {
  77. TEST_FAIL_COND(glyphs[j].font_rid != RID(), "Incorrect font selected.");
  78. TEST_FAIL_COND(glyphs[j].index != test[glyphs[j].start], "Incorrect glyph index.");
  79. }
  80. TEST_FAIL_COND((glyphs[j].start < 0 || glyphs[j].end > test.length()), "Incorrect glyph range.");
  81. TEST_FAIL_COND(glyphs[j].font_size != 16, "Incorrect glyph font size.");
  82. }
  83. ts->free(ctx);
  84. for (int j = 0; j < font.size(); j++) {
  85. ts->free(font[j]);
  86. }
  87. font.clear();
  88. }
  89. }
  90. SUBCASE("[TextServer] Text layout: BiDi") {
  91. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  92. TextServer *ts = TextServerManager::initialize(i, err);
  93. if (!ts->has_feature(TextServer::FEATURE_BIDI_LAYOUT)) {
  94. continue;
  95. }
  96. Vector<RID> font;
  97. font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
  98. font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
  99. String test = U"Arabic (اَلْعَرَبِيَّةُ, al-ʿarabiyyah)";
  100. // 7^ 26^
  101. RID ctx = ts->create_shaped_text();
  102. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  103. bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
  104. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  105. Vector<TextServer::Glyph> glyphs = ts->shaped_text_get_glyphs(ctx);
  106. TEST_FAIL_COND(glyphs.size() == 0, "Shaping failed");
  107. for (int j = 0; j < glyphs.size(); j++) {
  108. if (glyphs[j].count > 0) {
  109. if (glyphs[j].start < 7) {
  110. TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
  111. }
  112. if ((glyphs[j].start > 8) && (glyphs[j].start < 23)) {
  113. TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) != TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
  114. }
  115. if (glyphs[j].start > 26) {
  116. TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
  117. }
  118. }
  119. }
  120. ts->free(ctx);
  121. for (int j = 0; j < font.size(); j++) {
  122. ts->free(font[j]);
  123. }
  124. font.clear();
  125. }
  126. }
  127. SUBCASE("[TextServer] Text layout: Line breaking") {
  128. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  129. TextServer *ts = TextServerManager::initialize(i, err);
  130. String test_1 = U"test test test";
  131. // 5^ 10^
  132. Vector<RID> font;
  133. font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
  134. font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
  135. RID ctx = ts->create_shaped_text();
  136. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  137. bool ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
  138. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  139. Vector<Vector2i> brks = ts->shaped_text_get_line_breaks(ctx, 1);
  140. TEST_FAIL_COND(brks.size() != 3, "Invalid line breaks number.");
  141. if (brks.size() == 3) {
  142. TEST_FAIL_COND(brks[0] != Vector2i(0, 5), "Invalid line break position.");
  143. TEST_FAIL_COND(brks[1] != Vector2i(5, 10), "Invalid line break position.");
  144. TEST_FAIL_COND(brks[2] != Vector2i(10, 14), "Invalid line break position.");
  145. }
  146. ts->free(ctx);
  147. for (int j = 0; j < font.size(); j++) {
  148. ts->free(font[j]);
  149. }
  150. font.clear();
  151. }
  152. }
  153. SUBCASE("[TextServer] Text layout: Justification") {
  154. for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
  155. TextServer *ts = TextServerManager::initialize(i, err);
  156. Vector<RID> font;
  157. font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
  158. font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
  159. String test_1 = U"الحمد";
  160. String test_2 = U"الحمد test";
  161. String test_3 = U"test test";
  162. // 7^ 26^
  163. RID ctx;
  164. bool ok;
  165. float width_old, width;
  166. if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
  167. ctx = ts->create_shaped_text();
  168. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  169. ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
  170. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  171. width_old = ts->shaped_text_get_width(ctx);
  172. width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
  173. TEST_FAIL_COND((width != width_old), "Invalid fill width.");
  174. width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
  175. TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
  176. ts->free(ctx);
  177. ctx = ts->create_shaped_text();
  178. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  179. ok = ts->shaped_text_add_string(ctx, test_2, font, 16);
  180. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  181. width_old = ts->shaped_text_get_width(ctx);
  182. width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
  183. TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
  184. width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
  185. TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
  186. ts->free(ctx);
  187. }
  188. ctx = ts->create_shaped_text();
  189. TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
  190. ok = ts->shaped_text_add_string(ctx, test_3, font, 16);
  191. TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
  192. width_old = ts->shaped_text_get_width(ctx);
  193. width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
  194. TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
  195. ts->free(ctx);
  196. for (int j = 0; j < font.size(); j++) {
  197. ts->free(font[j]);
  198. }
  199. font.clear();
  200. }
  201. }
  202. memdelete(tsman);
  203. }
  204. }
  205. }; // namespace TestTextServer
  206. #endif // TEST_TEXT_SERVER_H
  207. #endif // TOOLS_ENABLED