Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/servers/test_text_server.h
10277 views
1
/**************************************************************************/
2
/* test_text_server.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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
31
#pragma once
32
33
#ifdef TOOLS_ENABLED
34
35
#include "editor/themes/builtin_fonts.gen.h"
36
#include "servers/text_server.h"
37
#include "tests/test_macros.h"
38
39
namespace TestTextServer {
40
41
TEST_SUITE("[TextServer]") {
42
TEST_CASE("[TextServer] Init, font loading and shaping") {
43
SUBCASE("[TextServer] Loading fonts") {
44
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
45
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
46
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
47
48
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC)) {
49
continue;
50
}
51
52
RID font = ts->create_font();
53
ts->font_set_data_ptr(font, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
54
CHECK_FALSE_MESSAGE(font == RID(), "Loading font failed.");
55
ts->free_rid(font);
56
}
57
}
58
59
SUBCASE("[TextServer] Text layout: Font fallback") {
60
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
61
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
62
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
63
64
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC) || !ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
65
continue;
66
}
67
68
RID font1 = ts->create_font();
69
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
70
ts->font_set_allow_system_fallback(font1, false);
71
RID font2 = ts->create_font();
72
ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
73
ts->font_set_allow_system_fallback(font2, false);
74
75
Array font = { font1, font2 };
76
String test = U"คนอ้วน khon uan ראה";
77
// 6^ 17^
78
79
RID ctx = ts->create_shaped_text();
80
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
81
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
82
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
83
84
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
85
int gl_size = ts->shaped_text_get_glyph_count(ctx);
86
CHECK_FALSE_MESSAGE(gl_size == 0, "Shaping failed");
87
for (int j = 0; j < gl_size; j++) {
88
if (glyphs[j].start < 6) {
89
CHECK_FALSE_MESSAGE(glyphs[j].font_rid != font[1], "Incorrect font selected.");
90
}
91
if ((glyphs[j].start > 6) && (glyphs[j].start < 16)) {
92
CHECK_FALSE_MESSAGE(glyphs[j].font_rid != font[0], "Incorrect font selected.");
93
}
94
if (glyphs[j].start > 16) {
95
CHECK_FALSE_MESSAGE(glyphs[j].font_rid != RID(), "Incorrect font selected.");
96
CHECK_FALSE_MESSAGE(glyphs[j].index != test[glyphs[j].start], "Incorrect glyph index.");
97
}
98
CHECK_FALSE_MESSAGE((glyphs[j].start < 0 || glyphs[j].end > test.length()), "Incorrect glyph range.");
99
CHECK_FALSE_MESSAGE(glyphs[j].font_size != 16, "Incorrect glyph font size.");
100
}
101
102
ts->free_rid(ctx);
103
104
for (int j = 0; j < font.size(); j++) {
105
ts->free_rid(font[j]);
106
}
107
font.clear();
108
}
109
}
110
111
SUBCASE("[TextServer] Text layout: BiDi") {
112
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
113
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
114
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
115
116
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC) || !ts->has_feature(TextServer::FEATURE_BIDI_LAYOUT)) {
117
continue;
118
}
119
120
RID font1 = ts->create_font();
121
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
122
RID font2 = ts->create_font();
123
ts->font_set_data_ptr(font2, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
124
125
Array font = { font1, font2 };
126
String test = U"Arabic (اَلْعَرَبِيَّةُ, al-ʿarabiyyah)";
127
// 7^ 26^
128
129
RID ctx = ts->create_shaped_text();
130
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
131
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
132
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
133
134
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
135
int gl_size = ts->shaped_text_get_glyph_count(ctx);
136
CHECK_FALSE_MESSAGE(gl_size == 0, "Shaping failed");
137
for (int j = 0; j < gl_size; j++) {
138
if (glyphs[j].count > 0) {
139
if (glyphs[j].start < 7) {
140
CHECK_FALSE_MESSAGE(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
141
}
142
if ((glyphs[j].start > 8) && (glyphs[j].start < 23)) {
143
CHECK_FALSE_MESSAGE(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) != TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
144
}
145
if (glyphs[j].start > 26) {
146
CHECK_FALSE_MESSAGE(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
147
}
148
}
149
}
150
151
ts->free_rid(ctx);
152
153
for (int j = 0; j < font.size(); j++) {
154
ts->free_rid(font[j]);
155
}
156
font.clear();
157
}
158
}
159
160
SUBCASE("[TextServer] Text layout: Line break and align points") {
161
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
162
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
163
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
164
165
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC) || !ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
166
continue;
167
}
168
169
RID font1 = ts->create_font();
170
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
171
ts->font_set_allow_system_fallback(font1, false);
172
RID font2 = ts->create_font();
173
ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
174
ts->font_set_allow_system_fallback(font2, false);
175
RID font3 = ts->create_font();
176
ts->font_set_data_ptr(font3, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
177
ts->font_set_allow_system_fallback(font3, false);
178
179
Array font = { font1, font2, font3 };
180
{
181
RID ctx = ts->create_shaped_text();
182
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
183
ts->shaped_text_add_string(ctx, U"Xtest", font, 10);
184
ts->shaped_text_add_string(ctx, U"xs", font, 10);
185
RID sctx = ts->shaped_text_substr(ctx, 1, 5);
186
CHECK_FALSE_MESSAGE(sctx == RID(), "Creating substring text buffer failed.");
187
PackedInt32Array sbrk = ts->shaped_text_get_character_breaks(sctx);
188
CHECK_FALSE_MESSAGE(sbrk.size() != 5, "Invalid substring char breaks number.");
189
if (sbrk.size() == 5) {
190
CHECK_FALSE_MESSAGE(sbrk[0] != 2, "Invalid substring char break position.");
191
CHECK_FALSE_MESSAGE(sbrk[1] != 3, "Invalid substring char break position.");
192
CHECK_FALSE_MESSAGE(sbrk[2] != 4, "Invalid substring char break position.");
193
CHECK_FALSE_MESSAGE(sbrk[3] != 5, "Invalid substring char break position.");
194
CHECK_FALSE_MESSAGE(sbrk[4] != 6, "Invalid substring char break position.");
195
}
196
PackedInt32Array fbrk = ts->shaped_text_get_character_breaks(ctx);
197
CHECK_FALSE_MESSAGE(fbrk.size() != 7, "Invalid char breaks number.");
198
if (fbrk.size() == 7) {
199
CHECK_FALSE_MESSAGE(fbrk[0] != 1, "Invalid char break position.");
200
CHECK_FALSE_MESSAGE(fbrk[1] != 2, "Invalid char break position.");
201
CHECK_FALSE_MESSAGE(fbrk[2] != 3, "Invalid char break position.");
202
CHECK_FALSE_MESSAGE(fbrk[3] != 4, "Invalid char break position.");
203
CHECK_FALSE_MESSAGE(fbrk[4] != 5, "Invalid char break position.");
204
CHECK_FALSE_MESSAGE(fbrk[5] != 6, "Invalid char break position.");
205
CHECK_FALSE_MESSAGE(fbrk[6] != 7, "Invalid char break position.");
206
}
207
PackedInt32Array rbrk = ts->string_get_character_breaks(U"Xtestxs");
208
CHECK_FALSE_MESSAGE(rbrk.size() != 7, "Invalid char breaks number.");
209
if (rbrk.size() == 7) {
210
CHECK_FALSE_MESSAGE(rbrk[0] != 1, "Invalid char break position.");
211
CHECK_FALSE_MESSAGE(rbrk[1] != 2, "Invalid char break position.");
212
CHECK_FALSE_MESSAGE(rbrk[2] != 3, "Invalid char break position.");
213
CHECK_FALSE_MESSAGE(rbrk[3] != 4, "Invalid char break position.");
214
CHECK_FALSE_MESSAGE(rbrk[4] != 5, "Invalid char break position.");
215
CHECK_FALSE_MESSAGE(rbrk[5] != 6, "Invalid char break position.");
216
CHECK_FALSE_MESSAGE(rbrk[6] != 7, "Invalid char break position.");
217
}
218
219
ts->free_rid(sctx);
220
ts->free_rid(ctx);
221
}
222
223
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) {
224
RID ctx = ts->create_shaped_text();
225
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
226
ts->shaped_text_add_string(ctx, U"X❤️‍🔥", font, 10);
227
ts->shaped_text_add_string(ctx, U"xs", font, 10);
228
RID sctx = ts->shaped_text_substr(ctx, 1, 5);
229
CHECK_FALSE_MESSAGE(sctx == RID(), "Creating substring text buffer failed.");
230
PackedInt32Array sbrk = ts->shaped_text_get_character_breaks(sctx);
231
CHECK_FALSE_MESSAGE(sbrk.size() != 2, "Invalid substring char breaks number.");
232
if (sbrk.size() == 2) {
233
CHECK_FALSE_MESSAGE(sbrk[0] != 5, "Invalid substring char break position.");
234
CHECK_FALSE_MESSAGE(sbrk[1] != 6, "Invalid substring char break position.");
235
}
236
PackedInt32Array fbrk = ts->shaped_text_get_character_breaks(ctx);
237
CHECK_FALSE_MESSAGE(fbrk.size() != 4, "Invalid char breaks number.");
238
if (fbrk.size() == 4) {
239
CHECK_FALSE_MESSAGE(fbrk[0] != 1, "Invalid char break position.");
240
CHECK_FALSE_MESSAGE(fbrk[1] != 5, "Invalid char break position.");
241
CHECK_FALSE_MESSAGE(fbrk[2] != 6, "Invalid char break position.");
242
CHECK_FALSE_MESSAGE(fbrk[3] != 7, "Invalid char break position.");
243
}
244
PackedInt32Array rbrk = ts->string_get_character_breaks(U"X❤️‍🔥xs");
245
CHECK_FALSE_MESSAGE(rbrk.size() != 4, "Invalid char breaks number.");
246
if (rbrk.size() == 4) {
247
CHECK_FALSE_MESSAGE(rbrk[0] != 1, "Invalid char break position.");
248
CHECK_FALSE_MESSAGE(rbrk[1] != 5, "Invalid char break position.");
249
CHECK_FALSE_MESSAGE(rbrk[2] != 6, "Invalid char break position.");
250
CHECK_FALSE_MESSAGE(rbrk[3] != 7, "Invalid char break position.");
251
}
252
253
ts->free_rid(sctx);
254
ts->free_rid(ctx);
255
}
256
257
{
258
String test = U"Test test long text long text\n";
259
RID ctx = ts->create_shaped_text();
260
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
261
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
262
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
263
ts->shaped_text_update_breaks(ctx);
264
ts->shaped_text_update_justification_ops(ctx);
265
266
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
267
int gl_size = ts->shaped_text_get_glyph_count(ctx);
268
269
CHECK_FALSE_MESSAGE(gl_size != 30, "Invalid glyph count.");
270
for (int j = 0; j < gl_size; j++) {
271
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
272
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
273
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
274
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
275
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
276
if (j == 4 || j == 9 || j == 14 || j == 19 || j == 24) {
277
CHECK_FALSE_MESSAGE((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
278
} else if (j == 29) {
279
CHECK_FALSE_MESSAGE((soft || !space || !hard || virt || elo), "Invalid glyph flags.");
280
} else {
281
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
282
}
283
}
284
ts->free_rid(ctx);
285
}
286
287
{
288
String test = U"الحمـد";
289
RID ctx = ts->create_shaped_text();
290
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
291
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
292
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
293
ts->shaped_text_update_breaks(ctx);
294
295
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
296
int gl_size = ts->shaped_text_get_glyph_count(ctx);
297
CHECK_FALSE_MESSAGE(gl_size != 6, "Invalid glyph count.");
298
for (int j = 0; j < gl_size; j++) {
299
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
300
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
301
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
302
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
303
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
304
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
305
}
306
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
307
ts->shaped_text_update_justification_ops(ctx);
308
309
glyphs = ts->shaped_text_get_glyphs(ctx);
310
gl_size = ts->shaped_text_get_glyph_count(ctx);
311
312
CHECK_FALSE_MESSAGE(gl_size != 6, "Invalid glyph count.");
313
for (int j = 0; j < gl_size; j++) {
314
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
315
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
316
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
317
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
318
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
319
if (j == 1) {
320
CHECK_FALSE_MESSAGE((soft || space || hard || virt || !elo), "Invalid glyph flags.");
321
} else {
322
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
323
}
324
}
325
}
326
ts->free_rid(ctx);
327
}
328
329
{
330
String test = U"الحمد";
331
RID ctx = ts->create_shaped_text();
332
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
333
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
334
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
335
ts->shaped_text_update_breaks(ctx);
336
337
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
338
int gl_size = ts->shaped_text_get_glyph_count(ctx);
339
CHECK_FALSE_MESSAGE(gl_size != 5, "Invalid glyph count.");
340
for (int j = 0; j < gl_size; j++) {
341
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
342
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
343
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
344
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
345
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
346
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
347
}
348
349
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
350
ts->shaped_text_update_justification_ops(ctx);
351
352
glyphs = ts->shaped_text_get_glyphs(ctx);
353
gl_size = ts->shaped_text_get_glyph_count(ctx);
354
355
CHECK_FALSE_MESSAGE(gl_size != 6, "Invalid glyph count.");
356
for (int j = 0; j < gl_size; j++) {
357
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
358
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
359
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
360
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
361
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
362
if (j == 1) {
363
CHECK_FALSE_MESSAGE((soft || space || hard || !virt || !elo), "Invalid glyph flags.");
364
} else {
365
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
366
}
367
}
368
}
369
ts->free_rid(ctx);
370
}
371
372
{
373
String test = U"الحمـد الرياضي العربي";
374
RID ctx = ts->create_shaped_text();
375
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
376
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
377
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
378
ts->shaped_text_update_breaks(ctx);
379
380
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
381
int gl_size = ts->shaped_text_get_glyph_count(ctx);
382
383
CHECK_FALSE_MESSAGE(gl_size != 21, "Invalid glyph count.");
384
for (int j = 0; j < gl_size; j++) {
385
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
386
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
387
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
388
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
389
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
390
if (j == 6 || j == 14) {
391
CHECK_FALSE_MESSAGE((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
392
} else {
393
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
394
}
395
}
396
397
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
398
ts->shaped_text_update_justification_ops(ctx);
399
400
glyphs = ts->shaped_text_get_glyphs(ctx);
401
gl_size = ts->shaped_text_get_glyph_count(ctx);
402
403
CHECK_FALSE_MESSAGE(gl_size != 23, "Invalid glyph count.");
404
for (int j = 0; j < gl_size; j++) {
405
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
406
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
407
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
408
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
409
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
410
if (j == 7 || j == 16) {
411
CHECK_FALSE_MESSAGE((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
412
} else if (j == 3 || j == 9) {
413
CHECK_FALSE_MESSAGE((soft || space || hard || !virt || !elo), "Invalid glyph flags.");
414
} else if (j == 18) {
415
CHECK_FALSE_MESSAGE((soft || space || hard || virt || !elo), "Invalid glyph flags.");
416
} else {
417
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
418
}
419
}
420
}
421
422
ts->free_rid(ctx);
423
}
424
425
{
426
String test = U"เป็น ภาษา ราชการ และ ภาษา";
427
RID ctx = ts->create_shaped_text();
428
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
429
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
430
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
431
ts->shaped_text_update_breaks(ctx);
432
ts->shaped_text_update_justification_ops(ctx);
433
434
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
435
int gl_size = ts->shaped_text_get_glyph_count(ctx);
436
437
CHECK_FALSE_MESSAGE(gl_size != 25, "Invalid glyph count.");
438
for (int j = 0; j < gl_size; j++) {
439
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
440
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
441
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
442
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
443
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
444
if (j == 4 || j == 9 || j == 16 || j == 20) {
445
CHECK_FALSE_MESSAGE((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
446
} else {
447
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
448
}
449
}
450
ts->free_rid(ctx);
451
}
452
453
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Line breaking opportunities.
454
String test = U"เป็นภาษาราชการและภาษา";
455
RID ctx = ts->create_shaped_text();
456
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
457
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
458
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
459
ts->shaped_text_update_breaks(ctx);
460
ts->shaped_text_update_justification_ops(ctx);
461
462
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
463
int gl_size = ts->shaped_text_get_glyph_count(ctx);
464
465
CHECK_FALSE_MESSAGE(gl_size != 25, "Invalid glyph count.");
466
for (int j = 0; j < gl_size; j++) {
467
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
468
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
469
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
470
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
471
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
472
if (j == 4 || j == 9 || j == 16 || j == 20) {
473
CHECK_FALSE_MESSAGE((!soft || !space || hard || !virt || elo), "Invalid glyph flags.");
474
} else {
475
CHECK_FALSE_MESSAGE((soft || space || hard || virt || elo), "Invalid glyph flags.");
476
}
477
}
478
ts->free_rid(ctx);
479
}
480
481
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Break line.
482
struct TestCase {
483
String text;
484
PackedInt32Array breaks;
485
};
486
TestCase cases[] = {
487
{ U" เมาส์ตัวนี้", { 0, 17, 17, 23 } },
488
{ U" กู้ไฟล์", { 0, 17, 17, 21 } },
489
{ U" ไม่มีคำ", { 0, 18, 18, 20 } },
490
{ U" ไม่มีคำพูด", { 0, 18, 18, 23 } },
491
{ U" ไม่มีคำ", { 0, 17, 17, 19 } },
492
{ U" มีอุปกรณ์\nนี้", { 0, 11, 11, 19, 19, 22 } },
493
{ U"الحمدا لحمدا لحمـــد", { 0, 13, 13, 20 } },
494
{ U" الحمد test", { 0, 15, 15, 19 } },
495
{ U"الحمـد الرياضي العربي", { 0, 7, 7, 15, 15, 21 } },
496
{ U"test \rtest", { 0, 6, 6, 10 } },
497
{ U"test\r test", { 0, 5, 5, 10 } },
498
{ U"test\r test \r test", { 0, 5, 5, 12, 12, 17 } },
499
};
500
for (size_t j = 0; j < std::size(cases); j++) {
501
RID ctx = ts->create_shaped_text();
502
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
503
bool ok = ts->shaped_text_add_string(ctx, cases[j].text, font, 16);
504
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
505
506
PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0);
507
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
508
509
breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false);
510
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
511
512
ts->free_rid(ctx);
513
}
514
}
515
516
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Break line and trim spaces.
517
struct TestCase {
518
String text;
519
PackedInt32Array breaks;
520
BitField<TextServer::LineBreakFlag> flags = TextServer::BREAK_NONE;
521
};
522
TestCase cases[] = {
523
{ U"test \rtest", { 0, 4, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
524
{ U"test \rtest", { 0, 6, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES },
525
{ U"test\r test", { 0, 4, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
526
{ U"test\r test", { 0, 4, 5, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_END_EDGE_SPACES },
527
{ U"test\r test \r test", { 0, 4, 6, 10, 13, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
528
{ U"test\r test \r test", { 0, 5, 6, 12, 13, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES },
529
{ U"test\r test \r test", { 0, 4, 5, 10, 12, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_END_EDGE_SPACES },
530
{ U"test\r test \r test", { 0, 5, 5, 12, 12, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND },
531
};
532
for (size_t j = 0; j < sizeof(cases) / sizeof(TestCase); j++) {
533
RID ctx = ts->create_shaped_text();
534
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
535
bool ok = ts->shaped_text_add_string(ctx, cases[j].text, font, 16);
536
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
537
538
PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0, 0, cases[j].flags);
539
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
540
541
breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false, cases[j].flags);
542
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
543
544
ts->free_rid(ctx);
545
}
546
}
547
548
for (int j = 0; j < font.size(); j++) {
549
ts->free_rid(font[j]);
550
}
551
font.clear();
552
}
553
}
554
555
SUBCASE("[TextServer] Text layout: Line breaking") {
556
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
557
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
558
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
559
560
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC) || !ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
561
continue;
562
}
563
564
String test_1 = U"test test test";
565
// 5^ 10^
566
567
RID font1 = ts->create_font();
568
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
569
RID font2 = ts->create_font();
570
ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
571
572
Array font = { font1, font2 };
573
RID ctx = ts->create_shaped_text();
574
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
575
bool ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
576
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
577
578
PackedInt32Array brks = ts->shaped_text_get_line_breaks(ctx, 1);
579
CHECK_FALSE_MESSAGE(brks.size() != 6, "Invalid line breaks number.");
580
if (brks.size() == 6) {
581
CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position.");
582
CHECK_FALSE_MESSAGE(brks[1] != 5, "Invalid line break position.");
583
584
CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position.");
585
CHECK_FALSE_MESSAGE(brks[3] != 10, "Invalid line break position.");
586
587
CHECK_FALSE_MESSAGE(brks[4] != 10, "Invalid line break position.");
588
CHECK_FALSE_MESSAGE(brks[5] != 14, "Invalid line break position.");
589
}
590
591
brks = ts->shaped_text_get_line_breaks(ctx, 35.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
592
CHECK_FALSE_MESSAGE(brks.size() != 6, "Invalid line breaks number.");
593
if (brks.size() == 6) {
594
CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position.");
595
CHECK_FALSE_MESSAGE(brks[1] != 4, "Invalid line break position.");
596
597
CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position.");
598
CHECK_FALSE_MESSAGE(brks[3] != 9, "Invalid line break position.");
599
600
CHECK_FALSE_MESSAGE(brks[4] != 10, "Invalid line break position.");
601
CHECK_FALSE_MESSAGE(brks[5] != 14, "Invalid line break position.");
602
}
603
604
ts->free_rid(ctx);
605
606
for (int j = 0; j < font.size(); j++) {
607
ts->free_rid(font[j]);
608
}
609
font.clear();
610
}
611
}
612
613
SUBCASE("[TextServer] Text layout: Justification") {
614
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
615
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
616
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
617
618
if (!ts->has_feature(TextServer::FEATURE_FONT_DYNAMIC) || !ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
619
continue;
620
}
621
622
RID font1 = ts->create_font();
623
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
624
RID font2 = ts->create_font();
625
ts->font_set_data_ptr(font2, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
626
627
Array font = { font1, font2 };
628
String test_1 = U"الحمد";
629
String test_2 = U"الحمد test";
630
String test_3 = U"test test";
631
// 7^ 26^
632
633
RID ctx;
634
bool ok;
635
float width_old, width;
636
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
637
ctx = ts->create_shaped_text();
638
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
639
ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
640
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
641
642
width_old = ts->shaped_text_get_width(ctx);
643
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
644
CHECK_FALSE_MESSAGE((width != width_old), "Invalid fill width.");
645
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
646
CHECK_FALSE_MESSAGE((width <= width_old || width > 100), "Invalid fill width.");
647
648
ts->free_rid(ctx);
649
650
ctx = ts->create_shaped_text();
651
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
652
ok = ts->shaped_text_add_string(ctx, test_2, font, 16);
653
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
654
655
width_old = ts->shaped_text_get_width(ctx);
656
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
657
CHECK_FALSE_MESSAGE((width <= width_old || width > 100), "Invalid fill width.");
658
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
659
CHECK_FALSE_MESSAGE((width <= width_old || width > 100), "Invalid fill width.");
660
661
ts->free_rid(ctx);
662
}
663
664
ctx = ts->create_shaped_text();
665
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
666
ok = ts->shaped_text_add_string(ctx, test_3, font, 16);
667
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
668
669
width_old = ts->shaped_text_get_width(ctx);
670
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
671
CHECK_FALSE_MESSAGE((width <= width_old || width > 100), "Invalid fill width.");
672
673
ts->free_rid(ctx);
674
675
for (int j = 0; j < font.size(); j++) {
676
ts->free_rid(font[j]);
677
}
678
font.clear();
679
}
680
}
681
682
SUBCASE("[TextServer] Unicode identifiers") {
683
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
684
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
685
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
686
687
static const char32_t *data[19] = { U"-30", U"100", U"10.1", U"10,1", U"1e2", U"1e-2", U"1e2e3", U"0xAB", U"AB", U"Test1", U"1Test", U"Test*1", U"test_testeT", U"test_tes teT", U"عَلَيْكُمْ", U"عَلَيْكُمْTest", U"ӒӖӚӜ", U"_test", U"ÂÃÄÅĀĂĄÇĆĈĊ" };
688
static bool isid[19] = { false, false, false, false, false, false, false, false, true, true, false, false, true, false, true, true, true, true, true };
689
for (int j = 0; j < 19; j++) {
690
String s = String(data[j]);
691
CHECK(ts->is_valid_identifier(s) == isid[j]);
692
}
693
694
if (ts->has_feature(TextServer::FEATURE_UNICODE_IDENTIFIERS)) {
695
// Test UAX 3.2 ZW(N)J usage.
696
CHECK(ts->is_valid_identifier(U"\u0646\u0627\u0645\u0647\u200C\u0627\u06CC"));
697
CHECK(ts->is_valid_identifier(U"\u0D26\u0D43\u0D15\u0D4D\u200C\u0D38\u0D3E\u0D15\u0D4D\u0D37\u0D3F"));
698
CHECK(ts->is_valid_identifier(U"\u0DC1\u0DCA\u200D\u0DBB\u0DD3"));
699
}
700
}
701
}
702
703
SUBCASE("[TextServer] Unicode letters") {
704
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
705
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
706
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
707
708
struct ul_testcase {
709
int fail_index = -1; // Expecting failure at given index.
710
char32_t text[10]; // Using 0 as the terminator.
711
};
712
ul_testcase cases[14] = {
713
{
714
0,
715
{ 0x2D, 0x33, 0x30, 0, 0, 0, 0, 0, 0, 0 }, // "-30"
716
},
717
{
718
1,
719
{ 0x61, 0x2E, 0x31, 0, 0, 0, 0, 0, 0, 0 }, // "a.1"
720
},
721
{
722
1,
723
{ 0x61, 0x2C, 0x31, 0, 0, 0, 0, 0, 0, 0 }, // "a,1"
724
},
725
{
726
0,
727
{ 0x31, 0x65, 0x2D, 0x32, 0, 0, 0, 0, 0, 0 }, // "1e-2"
728
},
729
{
730
0,
731
{ 0xAB, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // "Left-Pointing Double Angle Quotation Mark"
732
},
733
{
734
-1,
735
{ 0x41, 0x42, 0, 0, 0, 0, 0, 0, 0, 0 }, // "AB"
736
},
737
{
738
4,
739
{ 0x54, 0x65, 0x73, 0x74, 0x31, 0, 0, 0, 0, 0 }, // "Test1"
740
},
741
{
742
2,
743
{ 0x54, 0x65, 0x2A, 0x73, 0x74, 0, 0, 0, 0, 0 }, // "Te*st"
744
},
745
{
746
4,
747
{ 0x74, 0x65, 0x73, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x65 }, // "test_teste"
748
},
749
{
750
4,
751
{ 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0 }, // "test test"
752
},
753
{
754
-1,
755
{ 0x643, 0x402, 0x716, 0xB05, 0, 0, 0, 0, 0, 0 }, // "كЂܖଅ" (arabic letters),
756
},
757
{
758
-1,
759
{ 0x643, 0x402, 0x716, 0xB05, 0x54, 0x65, 0x73, 0x74, 0x30AA, 0x4E21 }, // 0-3 arabic letters, 4-7 latin letters, 8-9 CJK letters
760
},
761
{
762
-1,
763
{ 0x4D2, 0x4D6, 0x4DA, 0x4DC, 0, 0, 0, 0, 0, 0 }, // "ӒӖӚӜ" cyrillic letters
764
},
765
{
766
-1,
767
{ 0xC2, 0xC3, 0xC4, 0xC5, 0x100, 0x102, 0x104, 0xC7, 0x106, 0x108 }, // "ÂÃÄÅĀĂĄÇĆĈ" rarer latin letters
768
},
769
};
770
771
for (int j = 0; j < 14; j++) {
772
ul_testcase test = cases[j];
773
int failed_on_index = -1;
774
for (int k = 0; k < 10; k++) {
775
char32_t character = test.text[k];
776
if (character == 0) {
777
break;
778
}
779
if (!ts->is_valid_letter(character)) {
780
failed_on_index = k;
781
break;
782
}
783
}
784
785
if (test.fail_index == -1) {
786
CHECK_MESSAGE(test.fail_index == failed_on_index, "In interface ", ts->get_name() + ": In test case ", j, ", the character at index ", failed_on_index, " should have been a letter.");
787
} else {
788
CHECK_MESSAGE(test.fail_index == failed_on_index, "In interface ", ts->get_name() + ": In test case ", j, ", expected first non-letter at index ", test.fail_index, ", but found at index ", failed_on_index);
789
}
790
}
791
}
792
}
793
794
SUBCASE("[TextServer] Strip Diacritics") {
795
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
796
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
797
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
798
799
if (ts->has_feature(TextServer::FEATURE_SHAPING)) {
800
CHECK(ts->strip_diacritics(U"ٱلسَّلَامُ عَلَيْكُمْ") == U"ٱلسلام عليكم");
801
}
802
803
CHECK(ts->strip_diacritics(U"pêches épinards tomates fraises") == U"peches epinards tomates fraises");
804
CHECK(ts->strip_diacritics(U"ΆΈΉΊΌΎΏΪΫϓϔ") == U"ΑΕΗΙΟΥΩΙΥΥΥ");
805
CHECK(ts->strip_diacritics(U"άέήίΐϊΰϋόύώ") == U"αεηιιιυυουω");
806
CHECK(ts->strip_diacritics(U"ЀЁЃ ЇЌЍӢӤЙ ЎӮӰӲ ӐӒӖӚӜӞ ӦӪ Ӭ Ӵ Ӹ") == U"ЕЕГ ІКИИИИ УУУУ ААЕӘЖЗ ОӨ Э Ч Ы");
807
CHECK(ts->strip_diacritics(U"ѐёѓ їќѝӣӥй ўӯӱӳ ӑӓӗӛӝӟ ӧӫ ӭ ӵ ӹ") == U"еег ікииии уууу ааеәжз оө э ч ы");
808
CHECK(ts->strip_diacritics(U"ÀÁÂÃÄÅĀĂĄÇĆĈĊČĎÈÉÊËĒĔĖĘĚĜĞĠĢĤÌÍÎÏĨĪĬĮİĴĶĹĻĽÑŃŅŇŊÒÓÔÕÖØŌŎŐƠŔŖŘŚŜŞŠŢŤÙÚÛÜŨŪŬŮŰŲƯŴÝŶŹŻŽ") == U"AAAAAAAAACCCCCDEEEEEEEEEGGGGHIIIIIIIIIJKLLLNNNNŊOOOOOØOOOORRRSSSSTTUUUUUUUUUUUWYYZZZ");
809
CHECK(ts->strip_diacritics(U"àáâãäåāăąçćĉċčďèéêëēĕėęěĝğġģĥìíîïĩīĭįĵķĺļľñńņňŋòóôõöøōŏőơŕŗřśŝşšţťùúûüũūŭůűųưŵýÿŷźżž") == U"aaaaaaaaacccccdeeeeeeeeegggghiiiiiiiijklllnnnnŋoooooøoooorrrssssttuuuuuuuuuuuwyyyzzz");
810
CHECK(ts->strip_diacritics(U"ǍǏȈǑǪǬȌȎȪȬȮȰǓǕǗǙǛȔȖǞǠǺȀȂȦǢǼǦǴǨǸȆȐȒȘȚȞȨ Ḁ ḂḄḆ Ḉ ḊḌḎḐḒ ḔḖḘḚḜ Ḟ Ḡ ḢḤḦḨḪ ḬḮ ḰḲḴ ḶḸḺḼ ḾṀṂ ṄṆṈṊ ṌṎṐṒ ṔṖ ṘṚṜṞ ṠṢṤṦṨ ṪṬṮṰ ṲṴṶṸṺ") == U"AIIOOOOOOOOOUUUUUUUAAAAAAÆÆGGKNERRSTHE A BBB C DDDDD EEEEE F G HHHHH II KKK LLLL MMM NNNN OOOO PP RRRR SSSSS TTTT UUUUU");
811
CHECK(ts->strip_diacritics(U"ǎǐȉȋǒǫǭȍȏȫȭȯȱǔǖǘǚǜȕȗǟǡǻȁȃȧǣǽǧǵǩǹȇȑȓșțȟȩ ḁ ḃḅḇ ḉ ḋḍḏḑḓ ḟ ḡ ḭḯ ḱḳḵ ḷḹḻḽ ḿṁṃ ṅṇṉṋ ṍṏṑṓ ṗṕ ṙṛṝṟ ṡṣṥṧṩ ṫṭṯṱ ṳṵṷṹṻ") == U"aiiiooooooooouuuuuuuaaaaaaææggknerrsthe a bbb c ddddd f g ii kkk llll mmm nnnn oooo pp rrrr sssss tttt uuuuu");
812
CHECK(ts->strip_diacritics(U"ṼṾ ẀẂẄẆẈ ẊẌ Ẏ ẐẒẔ") == U"VV WWWWW XX Y ZZZ");
813
CHECK(ts->strip_diacritics(U"ṽṿ ẁẃẅẇẉ ẋẍ ẏ ẑẓẕ ẖ ẗẘẙẛ") == U"vv wwwww xx y zzz h twys");
814
}
815
}
816
817
SUBCASE("[TextServer] Word break") {
818
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
819
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
820
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
821
822
if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
823
continue;
824
}
825
826
{
827
String text1 = U"linguistically similar and effectively form";
828
// 14^ 22^ 26^ 38^
829
PackedInt32Array breaks = ts->string_get_word_breaks(text1, "en");
830
CHECK(breaks.size() == 10);
831
if (breaks.size() == 10) {
832
CHECK(breaks[0] == 0);
833
CHECK(breaks[1] == 14);
834
CHECK(breaks[2] == 15);
835
CHECK(breaks[3] == 22);
836
CHECK(breaks[4] == 23);
837
CHECK(breaks[5] == 26);
838
CHECK(breaks[6] == 27);
839
CHECK(breaks[7] == 38);
840
CHECK(breaks[8] == 39);
841
CHECK(breaks[9] == 43);
842
}
843
}
844
845
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) {
846
String text2 = U"เป็นภาษาราชการและภาษาประจำชาติของประเทศไทย";
847
// เป็น ภาษา ราชการ และ ภาษา ประจำ ชาติ ของ ประเทศไทย
848
// 3^ 7^ 13^ 16^ 20^ 25^ 29^ 32^
849
850
PackedInt32Array breaks = ts->string_get_word_breaks(text2, "th");
851
CHECK(breaks.size() == 18);
852
if (breaks.size() == 18) {
853
CHECK(breaks[0] == 0);
854
CHECK(breaks[1] == 4);
855
CHECK(breaks[2] == 4);
856
CHECK(breaks[3] == 8);
857
CHECK(breaks[4] == 8);
858
CHECK(breaks[5] == 14);
859
CHECK(breaks[6] == 14);
860
CHECK(breaks[7] == 17);
861
CHECK(breaks[8] == 17);
862
CHECK(breaks[9] == 21);
863
CHECK(breaks[10] == 21);
864
CHECK(breaks[11] == 26);
865
CHECK(breaks[12] == 26);
866
CHECK(breaks[13] == 30);
867
CHECK(breaks[14] == 30);
868
CHECK(breaks[15] == 33);
869
CHECK(breaks[16] == 33);
870
CHECK(breaks[17] == 42);
871
}
872
}
873
874
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) {
875
String text2 = U"U+2764 U+FE0F U+200D U+1F525 ; 13.1 # ❤️‍🔥";
876
877
PackedInt32Array breaks = ts->string_get_character_breaks(text2, "en");
878
CHECK(breaks.size() == 39);
879
if (breaks.size() == 39) {
880
CHECK(breaks[0] == 1);
881
CHECK(breaks[1] == 2);
882
CHECK(breaks[2] == 3);
883
CHECK(breaks[3] == 4);
884
CHECK(breaks[4] == 5);
885
CHECK(breaks[5] == 6);
886
CHECK(breaks[6] == 7);
887
CHECK(breaks[7] == 8);
888
CHECK(breaks[8] == 9);
889
CHECK(breaks[9] == 10);
890
CHECK(breaks[10] == 11);
891
CHECK(breaks[11] == 12);
892
CHECK(breaks[12] == 13);
893
CHECK(breaks[13] == 14);
894
CHECK(breaks[14] == 15);
895
CHECK(breaks[15] == 16);
896
CHECK(breaks[16] == 17);
897
CHECK(breaks[17] == 18);
898
CHECK(breaks[18] == 19);
899
CHECK(breaks[19] == 20);
900
CHECK(breaks[20] == 21);
901
CHECK(breaks[21] == 22);
902
CHECK(breaks[22] == 23);
903
CHECK(breaks[23] == 24);
904
CHECK(breaks[24] == 25);
905
CHECK(breaks[25] == 26);
906
CHECK(breaks[26] == 27);
907
CHECK(breaks[27] == 28);
908
CHECK(breaks[28] == 29);
909
CHECK(breaks[29] == 30);
910
CHECK(breaks[30] == 31);
911
CHECK(breaks[31] == 32);
912
CHECK(breaks[32] == 33);
913
CHECK(breaks[33] == 34);
914
CHECK(breaks[34] == 35);
915
CHECK(breaks[35] == 36);
916
CHECK(breaks[36] == 37);
917
CHECK(breaks[37] == 38);
918
CHECK(breaks[38] == 42);
919
}
920
}
921
}
922
}
923
924
SUBCASE("[TextServer] Buffer invalidation") {
925
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
926
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
927
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
928
929
if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
930
continue;
931
}
932
933
RID font1 = ts->create_font();
934
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
935
936
Array font = { font1 };
937
RID ctx = ts->create_shaped_text();
938
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
939
bool ok = ts->shaped_text_add_string(ctx, "T", font, 16);
940
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
941
int gl_size = ts->shaped_text_get_glyph_count(ctx);
942
CHECK_MESSAGE(gl_size == 1, "Shaping failed, invalid glyph count");
943
944
ok = ts->shaped_text_add_object(ctx, "key", Size2(20, 20), INLINE_ALIGNMENT_CENTER, 1, 0.0);
945
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
946
gl_size = ts->shaped_text_get_glyph_count(ctx);
947
CHECK_MESSAGE(gl_size == 2, "Shaping failed, invalid glyph count");
948
949
ok = ts->shaped_text_add_string(ctx, "B", font, 16);
950
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
951
gl_size = ts->shaped_text_get_glyph_count(ctx);
952
CHECK_MESSAGE(gl_size == 3, "Shaping failed, invalid glyph count");
953
954
ts->free_rid(ctx);
955
956
for (int j = 0; j < font.size(); j++) {
957
ts->free_rid(font[j]);
958
}
959
font.clear();
960
}
961
}
962
}
963
}
964
}; // namespace TestTextServer
965
966
#endif // TOOLS_ENABLED
967
968