1/**************************************************************************/
2/* text_server.cpp */
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#include "servers/text_server.h"
32#include "core/variant/typed_array.h"
33#include "servers/rendering_server.h"
34
35TextServerManager *TextServerManager::singleton = nullptr;
36
37void TextServerManager::_bind_methods() {
38 ClassDB::bind_method(D_METHOD("add_interface", "interface"), &TextServerManager::add_interface);
39 ClassDB::bind_method(D_METHOD("get_interface_count"), &TextServerManager::get_interface_count);
40 ClassDB::bind_method(D_METHOD("remove_interface", "interface"), &TextServerManager::remove_interface);
41 ClassDB::bind_method(D_METHOD("get_interface", "idx"), &TextServerManager::get_interface);
42 ClassDB::bind_method(D_METHOD("get_interfaces"), &TextServerManager::get_interfaces);
43 ClassDB::bind_method(D_METHOD("find_interface", "name"), &TextServerManager::find_interface);
44
45 ClassDB::bind_method(D_METHOD("set_primary_interface", "index"), &TextServerManager::set_primary_interface);
46 ClassDB::bind_method(D_METHOD("get_primary_interface"), &TextServerManager::get_primary_interface);
47
48 ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name")));
49 ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name")));
50}
51
52void TextServerManager::add_interface(const Ref<TextServer> &p_interface) {
53 ERR_FAIL_COND(p_interface.is_null());
54
55 for (int i = 0; i < interfaces.size(); i++) {
56 if (interfaces[i] == p_interface) {
57 ERR_PRINT("TextServer: Interface was already added.");
58 return;
59 };
60 };
61
62 interfaces.push_back(p_interface);
63 print_verbose("TextServer: Added interface \"" + p_interface->get_name() + "\"");
64 emit_signal(SNAME("interface_added"), p_interface->get_name());
65}
66
67void TextServerManager::remove_interface(const Ref<TextServer> &p_interface) {
68 ERR_FAIL_COND(p_interface.is_null());
69 ERR_FAIL_COND_MSG(p_interface == primary_interface, "TextServer: Can't remove primary interface.");
70
71 int idx = -1;
72 for (int i = 0; i < interfaces.size(); i++) {
73 if (interfaces[i] == p_interface) {
74 idx = i;
75 break;
76 };
77 };
78
79 ERR_FAIL_COND_MSG(idx == -1, "Interface not found.");
80 print_verbose("TextServer: Removed interface \"" + p_interface->get_name() + "\"");
81 emit_signal(SNAME("interface_removed"), p_interface->get_name());
82 interfaces.remove_at(idx);
83}
84
85int TextServerManager::get_interface_count() const {
86 return interfaces.size();
87}
88
89Ref<TextServer> TextServerManager::get_interface(int p_index) const {
90 ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr);
91 return interfaces[p_index];
92}
93
94Ref<TextServer> TextServerManager::find_interface(const String &p_name) const {
95 int idx = -1;
96 for (int i = 0; i < interfaces.size(); i++) {
97 if (interfaces[i]->get_name() == p_name) {
98 idx = i;
99 break;
100 };
101 };
102
103 ERR_FAIL_COND_V_MSG(idx == -1, nullptr, "Interface not found.");
104 return interfaces[idx];
105}
106
107TypedArray<Dictionary> TextServerManager::get_interfaces() const {
108 TypedArray<Dictionary> ret;
109
110 for (int i = 0; i < interfaces.size(); i++) {
111 Dictionary iface_info;
112
113 iface_info["id"] = i;
114 iface_info["name"] = interfaces[i]->get_name();
115
116 ret.push_back(iface_info);
117 };
118
119 return ret;
120}
121
122void TextServerManager::set_primary_interface(const Ref<TextServer> &p_primary_interface) {
123 if (p_primary_interface.is_null()) {
124 print_verbose("TextServer: Clearing primary interface");
125 primary_interface.unref();
126 } else {
127 primary_interface = p_primary_interface;
128 print_verbose("TextServer: Primary interface set to: \"" + primary_interface->get_name() + "\".");
129
130 if (OS::get_singleton()->get_main_loop()) {
131 OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED);
132 }
133 }
134}
135
136TextServerManager::TextServerManager() {
137 singleton = this;
138}
139
140TextServerManager::~TextServerManager() {
141 if (primary_interface.is_valid()) {
142 primary_interface.unref();
143 }
144 while (interfaces.size() > 0) {
145 interfaces.remove_at(0);
146 }
147 singleton = nullptr;
148}
149
150/*************************************************************************/
151
152bool Glyph::operator==(const Glyph &p_a) const {
153 return (p_a.index == index) && (p_a.font_rid == font_rid) && (p_a.font_size == font_size) && (p_a.start == start);
154}
155
156bool Glyph::operator!=(const Glyph &p_a) const {
157 return (p_a.index != index) || (p_a.font_rid != font_rid) || (p_a.font_size != font_size) || (p_a.start != start);
158}
159
160bool Glyph::operator<(const Glyph &p_a) const {
161 if (p_a.start == start) {
162 if (p_a.count == count) {
163 if ((p_a.flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL) {
164 return true;
165 } else {
166 return false;
167 }
168 }
169 return p_a.count > count;
170 }
171 return p_a.start < start;
172}
173
174bool Glyph::operator>(const Glyph &p_a) const {
175 if (p_a.start == start) {
176 if (p_a.count == count) {
177 if ((p_a.flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL) {
178 return false;
179 } else {
180 return true;
181 }
182 }
183 return p_a.count < count;
184 }
185 return p_a.start > start;
186}
187
188void TextServer::_bind_methods() {
189 ClassDB::bind_method(D_METHOD("has_feature", "feature"), &TextServer::has_feature);
190 ClassDB::bind_method(D_METHOD("get_name"), &TextServer::get_name);
191 ClassDB::bind_method(D_METHOD("get_features"), &TextServer::get_features);
192 ClassDB::bind_method(D_METHOD("load_support_data", "filename"), &TextServer::load_support_data);
193
194 ClassDB::bind_method(D_METHOD("get_support_data_filename"), &TextServer::get_support_data_filename);
195 ClassDB::bind_method(D_METHOD("get_support_data_info"), &TextServer::get_support_data_info);
196 ClassDB::bind_method(D_METHOD("save_support_data", "filename"), &TextServer::save_support_data);
197
198 ClassDB::bind_method(D_METHOD("is_locale_right_to_left", "locale"), &TextServer::is_locale_right_to_left);
199
200 ClassDB::bind_method(D_METHOD("name_to_tag", "name"), &TextServer::name_to_tag);
201 ClassDB::bind_method(D_METHOD("tag_to_name", "tag"), &TextServer::tag_to_name);
202
203 ClassDB::bind_method(D_METHOD("has", "rid"), &TextServer::has);
204 ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free_rid);
205
206 /* Font Interface */
207
208 ClassDB::bind_method(D_METHOD("create_font"), &TextServer::create_font);
209
210 ClassDB::bind_method(D_METHOD("font_set_data", "font_rid", "data"), &TextServer::font_set_data);
211
212 ClassDB::bind_method(D_METHOD("font_set_face_index", "font_rid", "face_index"), &TextServer::font_set_face_index);
213 ClassDB::bind_method(D_METHOD("font_get_face_index", "font_rid"), &TextServer::font_get_face_index);
214
215 ClassDB::bind_method(D_METHOD("font_get_face_count", "font_rid"), &TextServer::font_get_face_count);
216
217 ClassDB::bind_method(D_METHOD("font_set_style", "font_rid", "style"), &TextServer::font_set_style);
218 ClassDB::bind_method(D_METHOD("font_get_style", "font_rid"), &TextServer::font_get_style);
219
220 ClassDB::bind_method(D_METHOD("font_set_name", "font_rid", "name"), &TextServer::font_set_name);
221 ClassDB::bind_method(D_METHOD("font_get_name", "font_rid"), &TextServer::font_get_name);
222 ClassDB::bind_method(D_METHOD("font_get_ot_name_strings", "font_rid"), &TextServer::font_get_ot_name_strings);
223
224 ClassDB::bind_method(D_METHOD("font_set_style_name", "font_rid", "name"), &TextServer::font_set_style_name);
225 ClassDB::bind_method(D_METHOD("font_get_style_name", "font_rid"), &TextServer::font_get_style_name);
226
227 ClassDB::bind_method(D_METHOD("font_set_weight", "font_rid", "weight"), &TextServer::font_set_weight);
228 ClassDB::bind_method(D_METHOD("font_get_weight", "font_rid"), &TextServer::font_get_weight);
229
230 ClassDB::bind_method(D_METHOD("font_set_stretch", "font_rid", "weight"), &TextServer::font_set_stretch);
231 ClassDB::bind_method(D_METHOD("font_get_stretch", "font_rid"), &TextServer::font_get_stretch);
232
233 ClassDB::bind_method(D_METHOD("font_set_antialiasing", "font_rid", "antialiasing"), &TextServer::font_set_antialiasing);
234 ClassDB::bind_method(D_METHOD("font_get_antialiasing", "font_rid"), &TextServer::font_get_antialiasing);
235
236 ClassDB::bind_method(D_METHOD("font_set_generate_mipmaps", "font_rid", "generate_mipmaps"), &TextServer::font_set_generate_mipmaps);
237 ClassDB::bind_method(D_METHOD("font_get_generate_mipmaps", "font_rid"), &TextServer::font_get_generate_mipmaps);
238
239 ClassDB::bind_method(D_METHOD("font_set_multichannel_signed_distance_field", "font_rid", "msdf"), &TextServer::font_set_multichannel_signed_distance_field);
240 ClassDB::bind_method(D_METHOD("font_is_multichannel_signed_distance_field", "font_rid"), &TextServer::font_is_multichannel_signed_distance_field);
241
242 ClassDB::bind_method(D_METHOD("font_set_msdf_pixel_range", "font_rid", "msdf_pixel_range"), &TextServer::font_set_msdf_pixel_range);
243 ClassDB::bind_method(D_METHOD("font_get_msdf_pixel_range", "font_rid"), &TextServer::font_get_msdf_pixel_range);
244
245 ClassDB::bind_method(D_METHOD("font_set_msdf_size", "font_rid", "msdf_size"), &TextServer::font_set_msdf_size);
246 ClassDB::bind_method(D_METHOD("font_get_msdf_size", "font_rid"), &TextServer::font_get_msdf_size);
247
248 ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size);
249 ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size);
250
251 ClassDB::bind_method(D_METHOD("font_set_allow_system_fallback", "font_rid", "allow_system_fallback"), &TextServer::font_set_allow_system_fallback);
252 ClassDB::bind_method(D_METHOD("font_is_allow_system_fallback", "font_rid"), &TextServer::font_is_allow_system_fallback);
253
254 ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
255 ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
256
257 ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting);
258 ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
259
260 ClassDB::bind_method(D_METHOD("font_set_subpixel_positioning", "font_rid", "subpixel_positioning"), &TextServer::font_set_subpixel_positioning);
261 ClassDB::bind_method(D_METHOD("font_get_subpixel_positioning", "font_rid"), &TextServer::font_get_subpixel_positioning);
262
263 ClassDB::bind_method(D_METHOD("font_set_embolden", "font_rid", "strength"), &TextServer::font_set_embolden);
264 ClassDB::bind_method(D_METHOD("font_get_embolden", "font_rid"), &TextServer::font_get_embolden);
265
266 ClassDB::bind_method(D_METHOD("font_set_spacing", "font_rid", "spacing", "value"), &TextServer::font_set_spacing);
267 ClassDB::bind_method(D_METHOD("font_get_spacing", "font_rid", "spacing"), &TextServer::font_get_spacing);
268
269 ClassDB::bind_method(D_METHOD("font_set_transform", "font_rid", "transform"), &TextServer::font_set_transform);
270 ClassDB::bind_method(D_METHOD("font_get_transform", "font_rid"), &TextServer::font_get_transform);
271
272 ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates);
273 ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates);
274
275 ClassDB::bind_method(D_METHOD("font_set_oversampling", "font_rid", "oversampling"), &TextServer::font_set_oversampling);
276 ClassDB::bind_method(D_METHOD("font_get_oversampling", "font_rid"), &TextServer::font_get_oversampling);
277
278 ClassDB::bind_method(D_METHOD("font_get_size_cache_list", "font_rid"), &TextServer::font_get_size_cache_list);
279 ClassDB::bind_method(D_METHOD("font_clear_size_cache", "font_rid"), &TextServer::font_clear_size_cache);
280 ClassDB::bind_method(D_METHOD("font_remove_size_cache", "font_rid", "size"), &TextServer::font_remove_size_cache);
281
282 ClassDB::bind_method(D_METHOD("font_set_ascent", "font_rid", "size", "ascent"), &TextServer::font_set_ascent);
283 ClassDB::bind_method(D_METHOD("font_get_ascent", "font_rid", "size"), &TextServer::font_get_ascent);
284
285 ClassDB::bind_method(D_METHOD("font_set_descent", "font_rid", "size", "descent"), &TextServer::font_set_descent);
286 ClassDB::bind_method(D_METHOD("font_get_descent", "font_rid", "size"), &TextServer::font_get_descent);
287
288 ClassDB::bind_method(D_METHOD("font_set_underline_position", "font_rid", "size", "underline_position"), &TextServer::font_set_underline_position);
289 ClassDB::bind_method(D_METHOD("font_get_underline_position", "font_rid", "size"), &TextServer::font_get_underline_position);
290
291 ClassDB::bind_method(D_METHOD("font_set_underline_thickness", "font_rid", "size", "underline_thickness"), &TextServer::font_set_underline_thickness);
292 ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font_rid", "size"), &TextServer::font_get_underline_thickness);
293
294 ClassDB::bind_method(D_METHOD("font_set_scale", "font_rid", "size", "scale"), &TextServer::font_set_scale);
295 ClassDB::bind_method(D_METHOD("font_get_scale", "font_rid", "size"), &TextServer::font_get_scale);
296
297 ClassDB::bind_method(D_METHOD("font_get_texture_count", "font_rid", "size"), &TextServer::font_get_texture_count);
298 ClassDB::bind_method(D_METHOD("font_clear_textures", "font_rid", "size"), &TextServer::font_clear_textures);
299 ClassDB::bind_method(D_METHOD("font_remove_texture", "font_rid", "size", "texture_index"), &TextServer::font_remove_texture);
300
301 ClassDB::bind_method(D_METHOD("font_set_texture_image", "font_rid", "size", "texture_index", "image"), &TextServer::font_set_texture_image);
302 ClassDB::bind_method(D_METHOD("font_get_texture_image", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_image);
303
304 ClassDB::bind_method(D_METHOD("font_set_texture_offsets", "font_rid", "size", "texture_index", "offset"), &TextServer::font_set_texture_offsets);
305 ClassDB::bind_method(D_METHOD("font_get_texture_offsets", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_offsets);
306
307 ClassDB::bind_method(D_METHOD("font_get_glyph_list", "font_rid", "size"), &TextServer::font_get_glyph_list);
308 ClassDB::bind_method(D_METHOD("font_clear_glyphs", "font_rid", "size"), &TextServer::font_clear_glyphs);
309 ClassDB::bind_method(D_METHOD("font_remove_glyph", "font_rid", "size", "glyph"), &TextServer::font_remove_glyph);
310
311 ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_advance);
312 ClassDB::bind_method(D_METHOD("font_set_glyph_advance", "font_rid", "size", "glyph", "advance"), &TextServer::font_set_glyph_advance);
313
314 ClassDB::bind_method(D_METHOD("font_get_glyph_offset", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_offset);
315 ClassDB::bind_method(D_METHOD("font_set_glyph_offset", "font_rid", "size", "glyph", "offset"), &TextServer::font_set_glyph_offset);
316
317 ClassDB::bind_method(D_METHOD("font_get_glyph_size", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_size);
318 ClassDB::bind_method(D_METHOD("font_set_glyph_size", "font_rid", "size", "glyph", "gl_size"), &TextServer::font_set_glyph_size);
319
320 ClassDB::bind_method(D_METHOD("font_get_glyph_uv_rect", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_uv_rect);
321 ClassDB::bind_method(D_METHOD("font_set_glyph_uv_rect", "font_rid", "size", "glyph", "uv_rect"), &TextServer::font_set_glyph_uv_rect);
322
323 ClassDB::bind_method(D_METHOD("font_get_glyph_texture_idx", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_idx);
324 ClassDB::bind_method(D_METHOD("font_set_glyph_texture_idx", "font_rid", "size", "glyph", "texture_idx"), &TextServer::font_set_glyph_texture_idx);
325
326 ClassDB::bind_method(D_METHOD("font_get_glyph_texture_rid", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_rid);
327 ClassDB::bind_method(D_METHOD("font_get_glyph_texture_size", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_size);
328
329 ClassDB::bind_method(D_METHOD("font_get_glyph_contours", "font", "size", "index"), &TextServer::font_get_glyph_contours);
330
331 ClassDB::bind_method(D_METHOD("font_get_kerning_list", "font_rid", "size"), &TextServer::font_get_kerning_list);
332 ClassDB::bind_method(D_METHOD("font_clear_kerning_map", "font_rid", "size"), &TextServer::font_clear_kerning_map);
333 ClassDB::bind_method(D_METHOD("font_remove_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_remove_kerning);
334
335 ClassDB::bind_method(D_METHOD("font_set_kerning", "font_rid", "size", "glyph_pair", "kerning"), &TextServer::font_set_kerning);
336 ClassDB::bind_method(D_METHOD("font_get_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_get_kerning);
337
338 ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font_rid", "size", "char", "variation_selector"), &TextServer::font_get_glyph_index);
339 ClassDB::bind_method(D_METHOD("font_get_char_from_glyph_index", "font_rid", "size", "glyph_index"), &TextServer::font_get_char_from_glyph_index);
340
341 ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char);
342 ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars);
343
344 ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range);
345 ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph);
346
347 ClassDB::bind_method(D_METHOD("font_draw_glyph", "font_rid", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1)));
348 ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font_rid", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
349
350 ClassDB::bind_method(D_METHOD("font_is_language_supported", "font_rid", "language"), &TextServer::font_is_language_supported);
351 ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font_rid", "language", "supported"), &TextServer::font_set_language_support_override);
352 ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font_rid", "language"), &TextServer::font_get_language_support_override);
353 ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font_rid", "language"), &TextServer::font_remove_language_support_override);
354 ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font_rid"), &TextServer::font_get_language_support_overrides);
355
356 ClassDB::bind_method(D_METHOD("font_is_script_supported", "font_rid", "script"), &TextServer::font_is_script_supported);
357 ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font_rid", "script", "supported"), &TextServer::font_set_script_support_override);
358 ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font_rid", "script"), &TextServer::font_get_script_support_override);
359 ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font_rid", "script"), &TextServer::font_remove_script_support_override);
360 ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font_rid"), &TextServer::font_get_script_support_overrides);
361
362 ClassDB::bind_method(D_METHOD("font_set_opentype_feature_overrides", "font_rid", "overrides"), &TextServer::font_set_opentype_feature_overrides);
363 ClassDB::bind_method(D_METHOD("font_get_opentype_feature_overrides", "font_rid"), &TextServer::font_get_opentype_feature_overrides);
364
365 ClassDB::bind_method(D_METHOD("font_supported_feature_list", "font_rid"), &TextServer::font_supported_feature_list);
366 ClassDB::bind_method(D_METHOD("font_supported_variation_list", "font_rid"), &TextServer::font_supported_variation_list);
367
368 ClassDB::bind_method(D_METHOD("font_get_global_oversampling"), &TextServer::font_get_global_oversampling);
369 ClassDB::bind_method(D_METHOD("font_set_global_oversampling", "oversampling"), &TextServer::font_set_global_oversampling);
370
371 ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size);
372 ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box);
373
374 /* Shaped text buffer interface */
375
376 ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL));
377
378 ClassDB::bind_method(D_METHOD("shaped_text_clear", "rid"), &TextServer::shaped_text_clear);
379
380 ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO));
381 ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction);
382 ClassDB::bind_method(D_METHOD("shaped_text_get_inferred_direction", "shaped"), &TextServer::shaped_text_get_inferred_direction);
383
384 ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
385
386 ClassDB::bind_method(D_METHOD("shaped_text_set_custom_punctuation", "shaped", "punct"), &TextServer::shaped_text_set_custom_punctuation);
387 ClassDB::bind_method(D_METHOD("shaped_text_get_custom_punctuation", "shaped"), &TextServer::shaped_text_get_custom_punctuation);
388
389 ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL));
390 ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation);
391
392 ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_invalid", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_invalid);
393 ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_invalid", "shaped"), &TextServer::shaped_text_get_preserve_invalid);
394
395 ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control);
396 ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control);
397
398 ClassDB::bind_method(D_METHOD("shaped_text_set_spacing", "shaped", "spacing", "value"), &TextServer::shaped_text_set_spacing);
399 ClassDB::bind_method(D_METHOD("shaped_text_get_spacing", "shaped", "spacing"), &TextServer::shaped_text_get_spacing);
400
401 ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
402 ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length", "baseline"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0));
403 ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align", "baseline"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0));
404
405 ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count);
406 ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta);
407 ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary()));
408
409 ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr);
410 ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent);
411 ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "justification_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA));
412 ClassDB::bind_method(D_METHOD("shaped_text_tab_align", "shaped", "tab_stops"), &TextServer::shaped_text_tab_align);
413
414 ClassDB::bind_method(D_METHOD("shaped_text_shape", "shaped"), &TextServer::shaped_text_shape);
415 ClassDB::bind_method(D_METHOD("shaped_text_is_ready", "shaped"), &TextServer::shaped_text_is_ready);
416 ClassDB::bind_method(D_METHOD("shaped_text_has_visible_chars", "shaped"), &TextServer::shaped_text_has_visible_chars);
417
418 ClassDB::bind_method(D_METHOD("shaped_text_get_glyphs", "shaped"), &TextServer::_shaped_text_get_glyphs_wrapper);
419 ClassDB::bind_method(D_METHOD("shaped_text_sort_logical", "shaped"), &TextServer::_shaped_text_sort_logical_wrapper);
420 ClassDB::bind_method(D_METHOD("shaped_text_get_glyph_count", "shaped"), &TextServer::shaped_text_get_glyph_count);
421
422 ClassDB::bind_method(D_METHOD("shaped_text_get_range", "shaped"), &TextServer::shaped_text_get_range);
423 ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks_adv", "shaped", "width", "start", "once", "break_flags"), &TextServer::shaped_text_get_line_breaks_adv, DEFVAL(0), DEFVAL(true), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND));
424 ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks", "shaped", "width", "start", "break_flags"), &TextServer::shaped_text_get_line_breaks, DEFVAL(0), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND));
425 ClassDB::bind_method(D_METHOD("shaped_text_get_word_breaks", "shaped", "grapheme_flags"), &TextServer::shaped_text_get_word_breaks, DEFVAL(GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION));
426
427 ClassDB::bind_method(D_METHOD("shaped_text_get_trim_pos", "shaped"), &TextServer::shaped_text_get_trim_pos);
428 ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_pos", "shaped"), &TextServer::shaped_text_get_ellipsis_pos);
429 ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyphs", "shaped"), &TextServer::_shaped_text_get_ellipsis_glyphs_wrapper);
430 ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyph_count", "shaped"), &TextServer::shaped_text_get_ellipsis_glyph_count);
431
432 ClassDB::bind_method(D_METHOD("shaped_text_overrun_trim_to_width", "shaped", "width", "overrun_trim_flags"), &TextServer::shaped_text_overrun_trim_to_width, DEFVAL(0), DEFVAL(OVERRUN_NO_TRIM));
433
434 ClassDB::bind_method(D_METHOD("shaped_text_get_objects", "shaped"), &TextServer::shaped_text_get_objects);
435 ClassDB::bind_method(D_METHOD("shaped_text_get_object_rect", "shaped", "key"), &TextServer::shaped_text_get_object_rect);
436
437 ClassDB::bind_method(D_METHOD("shaped_text_get_size", "shaped"), &TextServer::shaped_text_get_size);
438 ClassDB::bind_method(D_METHOD("shaped_text_get_ascent", "shaped"), &TextServer::shaped_text_get_ascent);
439 ClassDB::bind_method(D_METHOD("shaped_text_get_descent", "shaped"), &TextServer::shaped_text_get_descent);
440 ClassDB::bind_method(D_METHOD("shaped_text_get_width", "shaped"), &TextServer::shaped_text_get_width);
441 ClassDB::bind_method(D_METHOD("shaped_text_get_underline_position", "shaped"), &TextServer::shaped_text_get_underline_position);
442 ClassDB::bind_method(D_METHOD("shaped_text_get_underline_thickness", "shaped"), &TextServer::shaped_text_get_underline_thickness);
443
444 ClassDB::bind_method(D_METHOD("shaped_text_get_carets", "shaped", "position"), &TextServer::_shaped_text_get_carets_wrapper);
445 ClassDB::bind_method(D_METHOD("shaped_text_get_selection", "shaped", "start", "end"), &TextServer::shaped_text_get_selection);
446
447 ClassDB::bind_method(D_METHOD("shaped_text_hit_test_grapheme", "shaped", "coords"), &TextServer::shaped_text_hit_test_grapheme);
448 ClassDB::bind_method(D_METHOD("shaped_text_hit_test_position", "shaped", "coords"), &TextServer::shaped_text_hit_test_position);
449
450 ClassDB::bind_method(D_METHOD("shaped_text_get_grapheme_bounds", "shaped", "pos"), &TextServer::shaped_text_get_grapheme_bounds);
451 ClassDB::bind_method(D_METHOD("shaped_text_next_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_next_grapheme_pos);
452 ClassDB::bind_method(D_METHOD("shaped_text_prev_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_prev_grapheme_pos);
453
454 ClassDB::bind_method(D_METHOD("shaped_text_get_character_breaks", "shaped"), &TextServer::shaped_text_get_character_breaks);
455 ClassDB::bind_method(D_METHOD("shaped_text_next_character_pos", "shaped", "pos"), &TextServer::shaped_text_next_character_pos);
456 ClassDB::bind_method(D_METHOD("shaped_text_prev_character_pos", "shaped", "pos"), &TextServer::shaped_text_prev_character_pos);
457 ClassDB::bind_method(D_METHOD("shaped_text_closest_character_pos", "shaped", "pos"), &TextServer::shaped_text_closest_character_pos);
458
459 ClassDB::bind_method(D_METHOD("shaped_text_draw", "shaped", "canvas", "pos", "clip_l", "clip_r", "color"), &TextServer::shaped_text_draw, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
460 ClassDB::bind_method(D_METHOD("shaped_text_draw_outline", "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color"), &TextServer::shaped_text_draw_outline, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1)));
461
462 ClassDB::bind_method(D_METHOD("shaped_text_get_dominant_direction_in_range", "shaped", "start", "end"), &TextServer::shaped_text_get_dominant_direction_in_range);
463
464 ClassDB::bind_method(D_METHOD("format_number", "number", "language"), &TextServer::format_number, DEFVAL(""));
465 ClassDB::bind_method(D_METHOD("parse_number", "number", "language"), &TextServer::parse_number, DEFVAL(""));
466 ClassDB::bind_method(D_METHOD("percent_sign", "language"), &TextServer::percent_sign, DEFVAL(""));
467
468 ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language", "chars_per_line"), &TextServer::string_get_word_breaks, DEFVAL(""), DEFVAL(0));
469 ClassDB::bind_method(D_METHOD("string_get_character_breaks", "string", "language"), &TextServer::string_get_character_breaks, DEFVAL(""));
470
471 ClassDB::bind_method(D_METHOD("is_confusable", "string", "dict"), &TextServer::is_confusable);
472 ClassDB::bind_method(D_METHOD("spoof_check", "string"), &TextServer::spoof_check);
473
474 ClassDB::bind_method(D_METHOD("strip_diacritics", "string"), &TextServer::strip_diacritics);
475 ClassDB::bind_method(D_METHOD("is_valid_identifier", "string"), &TextServer::is_valid_identifier);
476
477 ClassDB::bind_method(D_METHOD("string_to_upper", "string", "language"), &TextServer::string_to_upper, DEFVAL(""));
478 ClassDB::bind_method(D_METHOD("string_to_lower", "string", "language"), &TextServer::string_to_lower, DEFVAL(""));
479
480 ClassDB::bind_method(D_METHOD("parse_structured_text", "parser_type", "args", "text"), &TextServer::parse_structured_text);
481
482 /* Font AA */
483 BIND_ENUM_CONSTANT(FONT_ANTIALIASING_NONE);
484 BIND_ENUM_CONSTANT(FONT_ANTIALIASING_GRAY);
485 BIND_ENUM_CONSTANT(FONT_ANTIALIASING_LCD);
486
487 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_NONE);
488 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_HRGB);
489 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_HBGR);
490 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_VRGB);
491 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_VBGR);
492 BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_MAX);
493
494 /* Direction */
495 BIND_ENUM_CONSTANT(DIRECTION_AUTO);
496 BIND_ENUM_CONSTANT(DIRECTION_LTR);
497 BIND_ENUM_CONSTANT(DIRECTION_RTL);
498 BIND_ENUM_CONSTANT(DIRECTION_INHERITED);
499
500 /* Orientation */
501 BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL);
502 BIND_ENUM_CONSTANT(ORIENTATION_VERTICAL);
503
504 /* JustificationFlag */
505 BIND_BITFIELD_FLAG(JUSTIFICATION_NONE);
506 BIND_BITFIELD_FLAG(JUSTIFICATION_KASHIDA);
507 BIND_BITFIELD_FLAG(JUSTIFICATION_WORD_BOUND);
508 BIND_BITFIELD_FLAG(JUSTIFICATION_TRIM_EDGE_SPACES);
509 BIND_BITFIELD_FLAG(JUSTIFICATION_AFTER_LAST_TAB);
510 BIND_BITFIELD_FLAG(JUSTIFICATION_CONSTRAIN_ELLIPSIS);
511 BIND_BITFIELD_FLAG(JUSTIFICATION_SKIP_LAST_LINE);
512 BIND_BITFIELD_FLAG(JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS);
513 BIND_BITFIELD_FLAG(JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE);
514
515 /* AutowrapMode */
516 BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
517 BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
518 BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
519 BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
520
521 /* LineBreakFlag */
522 BIND_BITFIELD_FLAG(BREAK_NONE);
523 BIND_BITFIELD_FLAG(BREAK_MANDATORY);
524 BIND_BITFIELD_FLAG(BREAK_WORD_BOUND);
525 BIND_BITFIELD_FLAG(BREAK_GRAPHEME_BOUND);
526 BIND_BITFIELD_FLAG(BREAK_ADAPTIVE);
527 BIND_BITFIELD_FLAG(BREAK_TRIM_EDGE_SPACES);
528
529 /* VisibleCharactersBehavior */
530 BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
531 BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
532 BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
533 BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
534 BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
535
536 /* OverrunBehavior */
537 BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
538 BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
539 BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
540 BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
541 BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
542
543 /* TextOverrunFlag */
544 BIND_BITFIELD_FLAG(OVERRUN_NO_TRIM);
545 BIND_BITFIELD_FLAG(OVERRUN_TRIM);
546 BIND_BITFIELD_FLAG(OVERRUN_TRIM_WORD_ONLY);
547 BIND_BITFIELD_FLAG(OVERRUN_ADD_ELLIPSIS);
548 BIND_BITFIELD_FLAG(OVERRUN_ENFORCE_ELLIPSIS);
549 BIND_BITFIELD_FLAG(OVERRUN_JUSTIFICATION_AWARE);
550
551 /* GraphemeFlag */
552 BIND_BITFIELD_FLAG(GRAPHEME_IS_VALID);
553 BIND_BITFIELD_FLAG(GRAPHEME_IS_RTL);
554 BIND_BITFIELD_FLAG(GRAPHEME_IS_VIRTUAL);
555 BIND_BITFIELD_FLAG(GRAPHEME_IS_SPACE);
556 BIND_BITFIELD_FLAG(GRAPHEME_IS_BREAK_HARD);
557 BIND_BITFIELD_FLAG(GRAPHEME_IS_BREAK_SOFT);
558 BIND_BITFIELD_FLAG(GRAPHEME_IS_TAB);
559 BIND_BITFIELD_FLAG(GRAPHEME_IS_ELONGATION);
560 BIND_BITFIELD_FLAG(GRAPHEME_IS_PUNCTUATION);
561 BIND_BITFIELD_FLAG(GRAPHEME_IS_UNDERSCORE);
562 BIND_BITFIELD_FLAG(GRAPHEME_IS_CONNECTED);
563 BIND_BITFIELD_FLAG(GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL);
564 BIND_BITFIELD_FLAG(GRAPHEME_IS_EMBEDDED_OBJECT);
565
566 /* Hinting */
567 BIND_ENUM_CONSTANT(HINTING_NONE);
568 BIND_ENUM_CONSTANT(HINTING_LIGHT);
569 BIND_ENUM_CONSTANT(HINTING_NORMAL);
570
571 /* SubpixelPositioning */
572 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_DISABLED);
573 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_AUTO);
574 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF);
575 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER);
576 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
577 BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE);
578
579 /* Feature */
580 BIND_ENUM_CONSTANT(FEATURE_SIMPLE_LAYOUT);
581 BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT);
582 BIND_ENUM_CONSTANT(FEATURE_VERTICAL_LAYOUT);
583 BIND_ENUM_CONSTANT(FEATURE_SHAPING);
584 BIND_ENUM_CONSTANT(FEATURE_KASHIDA_JUSTIFICATION);
585 BIND_ENUM_CONSTANT(FEATURE_BREAK_ITERATORS);
586 BIND_ENUM_CONSTANT(FEATURE_FONT_BITMAP);
587 BIND_ENUM_CONSTANT(FEATURE_FONT_DYNAMIC);
588 BIND_ENUM_CONSTANT(FEATURE_FONT_MSDF);
589 BIND_ENUM_CONSTANT(FEATURE_FONT_SYSTEM);
590 BIND_ENUM_CONSTANT(FEATURE_FONT_VARIABLE);
591 BIND_ENUM_CONSTANT(FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION);
592 BIND_ENUM_CONSTANT(FEATURE_USE_SUPPORT_DATA);
593 BIND_ENUM_CONSTANT(FEATURE_UNICODE_IDENTIFIERS);
594 BIND_ENUM_CONSTANT(FEATURE_UNICODE_SECURITY);
595
596 /* FT Contour Point Types */
597 BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_ON);
598 BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CONIC);
599 BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CUBIC);
600
601 /* Font Spacing */
602 BIND_ENUM_CONSTANT(SPACING_GLYPH);
603 BIND_ENUM_CONSTANT(SPACING_SPACE);
604 BIND_ENUM_CONSTANT(SPACING_TOP);
605 BIND_ENUM_CONSTANT(SPACING_BOTTOM);
606 BIND_ENUM_CONSTANT(SPACING_MAX);
607
608 /* Font Style */
609 BIND_BITFIELD_FLAG(FONT_BOLD);
610 BIND_BITFIELD_FLAG(FONT_ITALIC);
611 BIND_BITFIELD_FLAG(FONT_FIXED_WIDTH);
612
613 /* Structured text parser */
614 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_DEFAULT);
615 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_URI);
616 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
617 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
618 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
619 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
620 BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
621}
622
623Vector2 TextServer::get_hex_code_box_size(int64_t p_size, int64_t p_index) const {
624 int w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3));
625 int sp = MAX(0, w - 1);
626 int sz = MAX(1, Math::round(p_size / 15.f));
627
628 return Vector2(4 + 3 * w + sp + 1, 15) * sz;
629}
630
631void TextServer::_draw_hex_code_box_number(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, uint8_t p_index, const Color &p_color) const {
632 static uint8_t chars[] = { 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47, 0x00 };
633 uint8_t x = chars[p_index];
634 if (x & (1 << 6)) {
635 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos, Size2(3, 1) * p_size), p_color);
636 }
637 if (x & (1 << 5)) {
638 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(2, 0) * p_size, Size2(1, 3) * p_size), p_color);
639 }
640 if (x & (1 << 4)) {
641 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(2, 2) * p_size, Size2(1, 3) * p_size), p_color);
642 }
643 if (x & (1 << 3)) {
644 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 4) * p_size, Size2(3, 1) * p_size), p_color);
645 }
646 if (x & (1 << 2)) {
647 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 2) * p_size, Size2(1, 3) * p_size), p_color);
648 }
649 if (x & (1 << 1)) {
650 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos, Size2(1, 3) * p_size), p_color);
651 }
652 if (x & (1 << 0)) {
653 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 2) * p_size, Size2(3, 1) * p_size), p_color);
654 }
655}
656
657void TextServer::draw_hex_code_box(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
658 if (p_index == 0) {
659 return;
660 }
661
662 int w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3));
663 int sp = MAX(0, w - 1);
664 int sz = MAX(1, Math::round(p_size / 15.f));
665
666 Size2 size = Vector2(4 + 3 * w + sp, 15) * sz;
667 Point2 pos = p_pos - Point2i(0, size.y * 0.85);
668
669 // Draw frame.
670 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(sz, size.y)), p_color);
671 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(size.x - sz, 0), Size2(sz, size.y)), p_color);
672 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(size.x, sz)), p_color);
673 RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, size.y - sz), Size2(size.x, sz)), p_color);
674
675 uint8_t a = p_index & 0x0F;
676 uint8_t b = (p_index >> 4) & 0x0F;
677 uint8_t c = (p_index >> 8) & 0x0F;
678 uint8_t d = (p_index >> 12) & 0x0F;
679 uint8_t e = (p_index >> 16) & 0x0F;
680 uint8_t f = (p_index >> 20) & 0x0F;
681
682 // Draw hex code.
683 if (p_index <= 0xFF) {
684 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, b, p_color);
685 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, a, p_color);
686 } else if (p_index <= 0xFFFF) {
687 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, d, p_color);
688 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 2) * sz, c, p_color);
689 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, b, p_color);
690 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 8) * sz, a, p_color);
691 } else {
692 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, f, p_color);
693 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 2) * sz, e, p_color);
694 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(10, 2) * sz, d, p_color);
695 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, c, p_color);
696 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 8) * sz, b, p_color);
697 _draw_hex_code_box_number(p_canvas, sz, pos + Point2(10, 8) * sz, a, p_color);
698 }
699}
700
701bool TextServer::shaped_text_has_visible_chars(const RID &p_shaped) const {
702 int v_size = shaped_text_get_glyph_count(p_shaped);
703 if (v_size == 0) {
704 return false;
705 }
706
707 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
708 for (int i = 0; i < v_size; i++) {
709 if (glyphs[i].index != 0 && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) {
710 return true;
711 }
712 }
713 return false;
714}
715
716PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped, const PackedFloat32Array &p_width, int64_t p_start, bool p_once, BitField<TextServer::LineBreakFlag> p_break_flags) const {
717 PackedInt32Array lines;
718
719 ERR_FAIL_COND_V(p_width.is_empty(), lines);
720
721 const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped);
722 const Vector2i &range = shaped_text_get_range(p_shaped);
723
724 real_t width = 0.f;
725 int line_start = MAX(p_start, range.x);
726 int last_end = line_start;
727 int prev_safe_break = 0;
728 int last_safe_break = -1;
729 int word_count = 0;
730 int chunk = 0;
731 bool trim_next = false;
732
733 int l_size = shaped_text_get_glyph_count(p_shaped);
734 const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
735
736 for (int i = 0; i < l_size; i++) {
737 if (l_gl[i].start < p_start) {
738 prev_safe_break = i + 1;
739 continue;
740 }
741 if (l_gl[i].count > 0) {
742 if ((p_width[chunk] > 0) && (width + l_gl[i].advance > p_width[chunk]) && (last_safe_break >= 0)) {
743 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
744 int start_pos = prev_safe_break;
745 int end_pos = last_safe_break;
746 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
747 start_pos += l_gl[start_pos].count;
748 }
749 while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
750 end_pos -= l_gl[end_pos].count;
751 }
752 if (last_end <= l_gl[start_pos].start) {
753 lines.push_back(l_gl[start_pos].start);
754 lines.push_back(l_gl[end_pos].end);
755 last_end = l_gl[end_pos].end;
756 }
757 trim_next = true;
758 } else {
759 if (last_end <= line_start) {
760 lines.push_back(line_start);
761 lines.push_back(l_gl[last_safe_break].end);
762 last_end = l_gl[last_safe_break].end;
763 }
764 }
765 line_start = l_gl[last_safe_break].end;
766 prev_safe_break = last_safe_break + 1;
767 i = last_safe_break;
768 last_safe_break = -1;
769 width = 0;
770 word_count = 0;
771 chunk++;
772 if (chunk >= p_width.size()) {
773 chunk = 0;
774 if (p_once) {
775 return lines;
776 }
777 }
778 continue;
779 }
780 if (p_break_flags.has_flag(BREAK_MANDATORY)) {
781 if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
782 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
783 int start_pos = prev_safe_break;
784 int end_pos = i;
785 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
786 start_pos += l_gl[start_pos].count;
787 }
788 while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
789 end_pos -= l_gl[end_pos].count;
790 }
791 if (last_end <= l_gl[start_pos].start) {
792 lines.push_back(l_gl[start_pos].start);
793 lines.push_back(l_gl[end_pos].end);
794 last_end = l_gl[end_pos].end;
795 }
796 trim_next = false;
797 } else {
798 if (last_end <= line_start) {
799 lines.push_back(line_start);
800 lines.push_back(l_gl[i].end);
801 last_end = l_gl[i].end;
802 }
803 }
804 line_start = l_gl[i].end;
805 prev_safe_break = i + 1;
806 last_safe_break = -1;
807 width = 0;
808 chunk = 0;
809 if (p_once) {
810 return lines;
811 }
812 continue;
813 }
814 }
815 if (p_break_flags.has_flag(BREAK_WORD_BOUND)) {
816 if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
817 last_safe_break = i;
818 word_count++;
819 }
820 }
821 if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND) && word_count == 0) {
822 last_safe_break = i;
823 }
824 }
825 width += l_gl[i].advance;
826 }
827
828 if (l_size > 0) {
829 if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) {
830 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
831 int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1;
832 int end_pos = l_size - 1;
833 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
834 start_pos += l_gl[start_pos].count;
835 }
836 lines.push_back(l_gl[start_pos].start);
837 } else {
838 lines.push_back(line_start);
839 }
840 lines.push_back(range.y);
841 }
842 } else {
843 lines.push_back(0);
844 lines.push_back(0);
845 }
846
847 return lines;
848}
849
850PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, double p_width, int64_t p_start, BitField<TextServer::LineBreakFlag> p_break_flags) const {
851 PackedInt32Array lines;
852
853 const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped);
854 const Vector2i &range = shaped_text_get_range(p_shaped);
855
856 double width = 0.f;
857 int line_start = MAX(p_start, range.x);
858 int last_end = line_start;
859 int prev_safe_break = 0;
860 int last_safe_break = -1;
861 int word_count = 0;
862 bool trim_next = false;
863
864 int l_size = shaped_text_get_glyph_count(p_shaped);
865 const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
866
867 for (int i = 0; i < l_size; i++) {
868 if (l_gl[i].start < p_start) {
869 prev_safe_break = i + 1;
870 continue;
871 }
872 if (l_gl[i].count > 0) {
873 if ((p_width > 0) && (width + l_gl[i].advance * l_gl[i].repeat > p_width) && (last_safe_break >= 0)) {
874 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
875 int start_pos = prev_safe_break;
876 int end_pos = last_safe_break;
877 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
878 start_pos += l_gl[start_pos].count;
879 }
880 while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
881 end_pos -= l_gl[end_pos].count;
882 }
883 if (last_end <= l_gl[start_pos].start) {
884 lines.push_back(l_gl[start_pos].start);
885 lines.push_back(l_gl[end_pos].end);
886 last_end = l_gl[end_pos].end;
887 }
888 trim_next = true;
889 } else {
890 if (last_end <= line_start) {
891 lines.push_back(line_start);
892 lines.push_back(l_gl[last_safe_break].end);
893 last_end = l_gl[last_safe_break].end;
894 }
895 }
896 line_start = l_gl[last_safe_break].end;
897 prev_safe_break = last_safe_break + 1;
898 i = last_safe_break;
899 last_safe_break = -1;
900 width = 0;
901 word_count = 0;
902 continue;
903 }
904 if (p_break_flags.has_flag(BREAK_MANDATORY)) {
905 if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
906 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
907 int start_pos = prev_safe_break;
908 int end_pos = i;
909 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
910 start_pos += l_gl[start_pos].count;
911 }
912 while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
913 end_pos -= l_gl[end_pos].count;
914 }
915 trim_next = false;
916 if (last_end <= l_gl[start_pos].start) {
917 lines.push_back(l_gl[start_pos].start);
918 lines.push_back(l_gl[end_pos].end);
919 last_end = l_gl[end_pos].end;
920 }
921 } else {
922 if (last_end <= line_start) {
923 lines.push_back(line_start);
924 lines.push_back(l_gl[i].end);
925 last_end = l_gl[i].end;
926 }
927 }
928 line_start = l_gl[i].end;
929 prev_safe_break = i + 1;
930 last_safe_break = -1;
931 width = 0;
932 continue;
933 }
934 }
935 if (p_break_flags.has_flag(BREAK_WORD_BOUND)) {
936 if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
937 last_safe_break = i;
938 word_count++;
939 }
940 if (p_break_flags.has_flag(BREAK_ADAPTIVE) && word_count == 0) {
941 last_safe_break = i;
942 }
943 }
944 if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND)) {
945 last_safe_break = i;
946 }
947 }
948 width += l_gl[i].advance * l_gl[i].repeat;
949 }
950
951 if (l_size > 0) {
952 if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) {
953 if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
954 int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1;
955 int end_pos = l_size - 1;
956 while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
957 start_pos += l_gl[start_pos].count;
958 }
959 lines.push_back(l_gl[start_pos].start);
960 } else {
961 lines.push_back(line_start);
962 }
963 lines.push_back(range.y);
964 }
965 } else {
966 lines.push_back(0);
967 lines.push_back(0);
968 }
969
970 return lines;
971}
972
973PackedInt32Array TextServer::shaped_text_get_word_breaks(const RID &p_shaped, BitField<TextServer::GraphemeFlag> p_grapheme_flags) const {
974 PackedInt32Array words;
975
976 const_cast<TextServer *>(this)->shaped_text_update_justification_ops(p_shaped);
977 const Vector2i &range = shaped_text_get_range(p_shaped);
978
979 int word_start = range.x;
980
981 const int l_size = shaped_text_get_glyph_count(p_shaped);
982 const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
983
984 for (int i = 0; i < l_size; i++) {
985 if (l_gl[i].count > 0) {
986 if ((l_gl[i].flags & p_grapheme_flags) != 0) {
987 if (word_start != l_gl[i].start) {
988 words.push_back(word_start);
989 words.push_back(l_gl[i].start);
990 }
991 word_start = l_gl[i].end;
992 }
993 }
994 }
995 if (l_size > 0) {
996 if (word_start != range.y) {
997 words.push_back(word_start);
998 words.push_back(range.y);
999 }
1000 }
1001
1002 return words;
1003}
1004
1005CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_position) const {
1006 Vector<Rect2> carets;
1007
1008 TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
1009 const Vector2 &range = shaped_text_get_range(p_shaped);
1010 real_t ascent = shaped_text_get_ascent(p_shaped);
1011 real_t descent = shaped_text_get_descent(p_shaped);
1012 real_t height = (ascent + descent) / 2;
1013
1014 real_t off = 0.0f;
1015 CaretInfo caret;
1016 caret.l_dir = DIRECTION_AUTO;
1017 caret.t_dir = DIRECTION_AUTO;
1018
1019 int v_size = shaped_text_get_glyph_count(p_shaped);
1020 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1021
1022 for (int i = 0; i < v_size; i++) {
1023 if (glyphs[i].count > 0) {
1024 // Caret before grapheme (top / left).
1025 if (p_position == glyphs[i].start && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) {
1026 real_t advance = 0.f;
1027 for (int j = 0; j < glyphs[i].count; j++) {
1028 advance += glyphs[i + j].advance * glyphs[i + j].repeat;
1029 }
1030 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1031 Rect2 cr;
1032 if (orientation == ORIENTATION_HORIZONTAL) {
1033 if (glyphs[i].start == range.x) {
1034 cr.size.y = height * 2;
1035 } else {
1036 cr.size.y = height;
1037 }
1038 cr.position.y = -ascent;
1039 cr.position.x = off;
1040 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1041 caret.t_dir = DIRECTION_RTL;
1042 cr.position.x += advance;
1043 cr.size.x = -char_adv;
1044 } else {
1045 caret.t_dir = DIRECTION_LTR;
1046 cr.size.x = char_adv;
1047 }
1048 } else {
1049 if (glyphs[i].start == range.x) {
1050 cr.size.x = height * 2;
1051 } else {
1052 cr.size.x = height;
1053 }
1054 cr.position.x = -ascent;
1055 cr.position.y = off;
1056 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1057 caret.t_dir = DIRECTION_RTL;
1058 cr.position.y += advance;
1059 cr.size.y = -char_adv;
1060 } else {
1061 caret.t_dir = DIRECTION_LTR;
1062 cr.size.y = char_adv;
1063 }
1064 }
1065 caret.t_caret = cr;
1066 }
1067 // Caret after grapheme (bottom / right).
1068 if (p_position == glyphs[i].end && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) {
1069 real_t advance = 0.f;
1070 for (int j = 0; j < glyphs[i].count; j++) {
1071 advance += glyphs[i + j].advance * glyphs[i + j].repeat;
1072 }
1073 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1074 Rect2 cr;
1075 if (orientation == ORIENTATION_HORIZONTAL) {
1076 if (glyphs[i].end == range.y) {
1077 cr.size.y = height * 2;
1078 cr.position.y = -ascent;
1079 } else {
1080 cr.size.y = height;
1081 cr.position.y = -ascent + height;
1082 }
1083 cr.position.x = off;
1084 if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) {
1085 caret.l_dir = DIRECTION_LTR;
1086 cr.position.x += advance;
1087 cr.size.x = -char_adv;
1088 } else {
1089 caret.l_dir = DIRECTION_RTL;
1090 cr.size.x = char_adv;
1091 }
1092 } else {
1093 cr.size.y = 1.0f;
1094 if (glyphs[i].end == range.y) {
1095 cr.size.x = height * 2;
1096 cr.position.x = -ascent;
1097 } else {
1098 cr.size.x = height;
1099 cr.position.x = -ascent + height;
1100 }
1101 cr.position.y = off;
1102 if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) {
1103 caret.l_dir = DIRECTION_LTR;
1104 cr.position.y += advance;
1105 cr.size.y = -char_adv;
1106 } else {
1107 caret.l_dir = DIRECTION_RTL;
1108 cr.position.x += advance;
1109 cr.size.y = char_adv;
1110 }
1111 }
1112 caret.l_caret = cr;
1113 }
1114 // Caret inside grapheme (middle).
1115 if (p_position > glyphs[i].start && p_position < glyphs[i].end && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) {
1116 real_t advance = 0.f;
1117 for (int j = 0; j < glyphs[i].count; j++) {
1118 advance += glyphs[i + j].advance * glyphs[i + j].repeat;
1119 }
1120 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1121 Rect2 cr;
1122 if (orientation == ORIENTATION_HORIZONTAL) {
1123 cr.size.y = height * 2;
1124 cr.position.y = -ascent;
1125 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1126 cr.position.x = off + char_adv * (glyphs[i].end - p_position);
1127 cr.size.x = -char_adv;
1128 } else {
1129 cr.position.x = off + char_adv * (p_position - glyphs[i].start);
1130 cr.size.x = char_adv;
1131 }
1132 } else {
1133 cr.size.x = height * 2;
1134 cr.position.x = -ascent;
1135 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1136 cr.position.y = off + char_adv * (glyphs[i].end - p_position);
1137 cr.size.y = -char_adv;
1138 } else {
1139 cr.position.y = off + char_adv * (p_position - glyphs[i].start);
1140 cr.size.y = char_adv;
1141 }
1142 }
1143 caret.t_caret = cr;
1144 caret.l_caret = cr;
1145 }
1146 }
1147 off += glyphs[i].advance * glyphs[i].repeat;
1148 }
1149 return caret;
1150}
1151
1152Dictionary TextServer::_shaped_text_get_carets_wrapper(const RID &p_shaped, int64_t p_position) const {
1153 Dictionary ret;
1154
1155 CaretInfo caret = shaped_text_get_carets(p_shaped, p_position);
1156
1157 ret["leading_rect"] = caret.l_caret;
1158 ret["leading_direction"] = caret.l_dir;
1159 ret["trailing_rect"] = caret.t_caret;
1160 ret["trailing_direction"] = caret.t_dir;
1161
1162 return ret;
1163}
1164
1165TextServer::Direction TextServer::shaped_text_get_dominant_direction_in_range(const RID &p_shaped, int64_t p_start, int64_t p_end) const {
1166 if (p_start == p_end) {
1167 return DIRECTION_AUTO;
1168 }
1169
1170 int start = MIN(p_start, p_end);
1171 int end = MAX(p_start, p_end);
1172
1173 int rtl = 0;
1174 int ltr = 0;
1175
1176 int v_size = shaped_text_get_glyph_count(p_shaped);
1177 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1178
1179 for (int i = 0; i < v_size; i++) {
1180 if ((glyphs[i].end > start) && (glyphs[i].start < end)) {
1181 if (glyphs[i].count > 0) {
1182 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1183 rtl++;
1184 } else {
1185 ltr++;
1186 }
1187 }
1188 }
1189 }
1190 if (ltr == rtl) {
1191 return DIRECTION_AUTO;
1192 } else if (ltr > rtl) {
1193 return DIRECTION_LTR;
1194 } else {
1195 return DIRECTION_RTL;
1196 }
1197}
1198
1199_FORCE_INLINE_ void _push_range(Vector<Vector2> &r_vector, real_t p_start, real_t p_end) {
1200 if (!r_vector.is_empty() && Math::is_equal_approx(r_vector[r_vector.size() - 1].y, p_start, (real_t)UNIT_EPSILON)) {
1201 r_vector.write[r_vector.size() - 1].y = p_end;
1202 } else {
1203 r_vector.push_back(Vector2(p_start, p_end));
1204 }
1205}
1206
1207Vector<Vector2> TextServer::shaped_text_get_selection(const RID &p_shaped, int64_t p_start, int64_t p_end) const {
1208 Vector<Vector2> ranges;
1209
1210 if (p_start == p_end) {
1211 return ranges;
1212 }
1213
1214 int start = MIN(p_start, p_end);
1215 int end = MAX(p_start, p_end);
1216
1217 int v_size = shaped_text_get_glyph_count(p_shaped);
1218 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1219
1220 real_t off = 0.0f;
1221 for (int i = 0; i < v_size; i++) {
1222 for (int k = 0; k < glyphs[i].repeat; k++) {
1223 if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) {
1224 if (glyphs[i].start < end && glyphs[i].end > start) {
1225 // Grapheme fully in selection range.
1226 if (glyphs[i].start >= start && glyphs[i].end <= end) {
1227 real_t advance = 0.f;
1228 for (int j = 0; j < glyphs[i].count; j++) {
1229 advance += glyphs[i + j].advance;
1230 }
1231 _push_range(ranges, off, off + advance);
1232 }
1233 // Only start of grapheme is in selection range.
1234 if (glyphs[i].start >= start && glyphs[i].end > end) {
1235 real_t advance = 0.f;
1236 for (int j = 0; j < glyphs[i].count; j++) {
1237 advance += glyphs[i + j].advance;
1238 }
1239 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1240 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1241 _push_range(ranges, off + char_adv * (glyphs[i].end - end), off + advance);
1242 } else {
1243 _push_range(ranges, off, off + char_adv * (end - glyphs[i].start));
1244 }
1245 }
1246 // Only end of grapheme is in selection range.
1247 if (glyphs[i].start < start && glyphs[i].end <= end) {
1248 real_t advance = 0.f;
1249 for (int j = 0; j < glyphs[i].count; j++) {
1250 advance += glyphs[i + j].advance;
1251 }
1252 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1253 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1254 _push_range(ranges, off, off + char_adv * (glyphs[i].end - start));
1255 } else {
1256 _push_range(ranges, off + char_adv * (start - glyphs[i].start), off + advance);
1257 }
1258 }
1259 // Selection range is within grapheme.
1260 if (glyphs[i].start < start && glyphs[i].end > end) {
1261 real_t advance = 0.f;
1262 for (int j = 0; j < glyphs[i].count; j++) {
1263 advance += glyphs[i + j].advance;
1264 }
1265 real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
1266 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1267 _push_range(ranges, off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start));
1268 } else {
1269 _push_range(ranges, off + char_adv * (start - glyphs[i].start), off + char_adv * (end - glyphs[i].start));
1270 }
1271 }
1272 }
1273 }
1274 off += glyphs[i].advance;
1275 }
1276 }
1277
1278 return ranges;
1279}
1280
1281int64_t TextServer::shaped_text_hit_test_grapheme(const RID &p_shaped, double p_coords) const {
1282 // Exact grapheme hit test, return -1 if missed.
1283 double off = 0.0f;
1284
1285 int v_size = shaped_text_get_glyph_count(p_shaped);
1286 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1287
1288 for (int i = 0; i < v_size; i++) {
1289 for (int j = 0; j < glyphs[i].repeat; j++) {
1290 if (p_coords >= off && p_coords < off + glyphs[i].advance) {
1291 return i;
1292 }
1293 off += glyphs[i].advance;
1294 }
1295 }
1296 return -1;
1297}
1298
1299int64_t TextServer::shaped_text_hit_test_position(const RID &p_shaped, double p_coords) const {
1300 int v_size = shaped_text_get_glyph_count(p_shaped);
1301 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1302
1303 // Cursor placement hit test.
1304
1305 // Place caret to the left of the leftmost grapheme, or to position 0 if string is empty.
1306 if (p_coords <= 0) {
1307 if (v_size > 0) {
1308 if ((glyphs[0].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1309 return glyphs[0].end;
1310 } else {
1311 return glyphs[0].start;
1312 }
1313 } else {
1314 return 0;
1315 }
1316 }
1317
1318 // Place caret to the right of the rightmost grapheme, or to position 0 if string is empty.
1319 if (p_coords >= shaped_text_get_width(p_shaped)) {
1320 if (v_size > 0) {
1321 if ((glyphs[v_size - 1].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1322 return glyphs[v_size - 1].start;
1323 } else {
1324 return glyphs[v_size - 1].end;
1325 }
1326 } else {
1327 return 0;
1328 }
1329 }
1330
1331 real_t off = 0.0f;
1332 for (int i = 0; i < v_size; i++) {
1333 if (glyphs[i].count > 0) {
1334 real_t advance = 0.f;
1335 for (int j = 0; j < glyphs[i].count; j++) {
1336 advance += glyphs[i + j].advance * glyphs[i + j].repeat;
1337 }
1338 if (((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) && (p_coords >= off && p_coords < off + advance)) {
1339 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1340 return glyphs[i].end;
1341 } else {
1342 return glyphs[i].start;
1343 }
1344 }
1345 // Ligature, handle mid-grapheme hit.
1346 if (p_coords >= off && p_coords < off + advance && glyphs[i].end > glyphs[i].start + 1) {
1347 int cnt = glyphs[i].end - glyphs[i].start;
1348 real_t char_adv = advance / (real_t)(cnt);
1349 real_t sub_off = off;
1350 for (int j = 0; j < cnt; j++) {
1351 // Place caret to the left of clicked sub-grapheme.
1352 if (p_coords >= sub_off && p_coords < sub_off + char_adv / 2) {
1353 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1354 return glyphs[i].end - j;
1355 } else {
1356 return glyphs[i].start + j;
1357 }
1358 }
1359 // Place caret to the right of clicked sub-grapheme.
1360 if (p_coords >= sub_off + char_adv / 2 && p_coords < sub_off + char_adv) {
1361 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1362 return glyphs[i].start + (cnt - 1) - j;
1363 } else {
1364 return glyphs[i].end - (cnt - 1) + j;
1365 }
1366 }
1367 sub_off += char_adv;
1368 }
1369 }
1370 // Place caret to the left of clicked grapheme.
1371 if (p_coords >= off && p_coords < off + advance / 2) {
1372 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1373 return glyphs[i].end;
1374 } else {
1375 return glyphs[i].start;
1376 }
1377 }
1378 // Place caret to the right of clicked grapheme.
1379 if (p_coords >= off + advance / 2 && p_coords < off + advance) {
1380 if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
1381 return glyphs[i].start;
1382 } else {
1383 return glyphs[i].end;
1384 }
1385 }
1386 }
1387 off += glyphs[i].advance * glyphs[i].repeat;
1388 }
1389 return 0;
1390}
1391
1392Vector2 TextServer::shaped_text_get_grapheme_bounds(const RID &p_shaped, int64_t p_pos) const {
1393 int v_size = shaped_text_get_glyph_count(p_shaped);
1394 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1395
1396 real_t off = 0.0f;
1397 for (int i = 0; i < v_size; i++) {
1398 if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) {
1399 if (glyphs[i].start <= p_pos && glyphs[i].end >= p_pos) {
1400 real_t advance = 0.f;
1401 for (int j = 0; j < glyphs[i].count; j++) {
1402 advance += glyphs[i + j].advance;
1403 }
1404 return Vector2(off, off + advance);
1405 }
1406 }
1407 off += glyphs[i].advance * glyphs[i].repeat;
1408 }
1409
1410 return Vector2();
1411}
1412
1413int64_t TextServer::shaped_text_next_grapheme_pos(const RID &p_shaped, int64_t p_pos) const {
1414 int v_size = shaped_text_get_glyph_count(p_shaped);
1415 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1416 for (int i = 0; i < v_size; i++) {
1417 if (p_pos >= glyphs[i].start && p_pos < glyphs[i].end) {
1418 return glyphs[i].end;
1419 }
1420 }
1421 return p_pos;
1422}
1423
1424int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p_pos) const {
1425 int v_size = shaped_text_get_glyph_count(p_shaped);
1426 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1427 for (int i = 0; i < v_size; i++) {
1428 if (p_pos > glyphs[i].start && p_pos <= glyphs[i].end) {
1429 return glyphs[i].start;
1430 }
1431 }
1432
1433 return p_pos;
1434}
1435
1436int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const {
1437 const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
1438 int64_t prev = 0;
1439 for (const int32_t &E : chars) {
1440 if (E >= p_pos) {
1441 return prev;
1442 }
1443 prev = E;
1444 }
1445 return prev;
1446}
1447
1448int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const {
1449 const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
1450 int64_t prev = 0;
1451 for (const int32_t &E : chars) {
1452 if (E > p_pos) {
1453 return E;
1454 }
1455 prev = E;
1456 }
1457 return prev;
1458}
1459
1460int64_t TextServer::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const {
1461 const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
1462 int64_t prev = 0;
1463 for (const int32_t &E : chars) {
1464 if (E == p_pos) {
1465 return E;
1466 } else if (E > p_pos) {
1467 if ((E - p_pos) < (p_pos - prev)) {
1468 return E;
1469 } else {
1470 return prev;
1471 }
1472 }
1473 prev = E;
1474 }
1475 return prev;
1476}
1477
1478PackedInt32Array TextServer::string_get_character_breaks(const String &p_string, const String &p_language) const {
1479 PackedInt32Array ret;
1480 if (!p_string.is_empty()) {
1481 ret.resize(p_string.size() - 1);
1482 for (int i = 0; i < p_string.size() - 1; i++) {
1483 ret.write[i] = i + 1;
1484 }
1485 }
1486 return ret;
1487}
1488
1489void TextServer::shaped_text_draw(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l, double p_clip_r, const Color &p_color) const {
1490 TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
1491 bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped);
1492
1493 bool rtl = shaped_text_get_direction(p_shaped) == DIRECTION_RTL;
1494
1495 int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped);
1496 int trim_pos = shaped_text_get_trim_pos(p_shaped);
1497
1498 const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped);
1499 int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped);
1500
1501 int v_size = shaped_text_get_glyph_count(p_shaped);
1502 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1503
1504 Vector2 ofs = p_pos;
1505 // Draw RTL ellipsis string when needed.
1506 if (rtl && ellipsis_pos >= 0) {
1507 for (int i = ellipsis_gl_size - 1; i >= 0; i--) {
1508 for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
1509 font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
1510 if (orientation == ORIENTATION_HORIZONTAL) {
1511 ofs.x += ellipsis_glyphs[i].advance;
1512 } else {
1513 ofs.y += ellipsis_glyphs[i].advance;
1514 }
1515 }
1516 }
1517 }
1518 // Draw at the baseline.
1519 for (int i = 0; i < v_size; i++) {
1520 if (trim_pos >= 0) {
1521 if (rtl) {
1522 if (i < trim_pos) {
1523 continue;
1524 }
1525 } else {
1526 if (i >= trim_pos) {
1527 break;
1528 }
1529 }
1530 }
1531 for (int j = 0; j < glyphs[i].repeat; j++) {
1532 if (p_clip_r > 0) {
1533 // Clip right / bottom.
1534 if (orientation == ORIENTATION_HORIZONTAL) {
1535 if (ofs.x - p_pos.x + glyphs[i].advance > p_clip_r) {
1536 return;
1537 }
1538 } else {
1539 if (ofs.y - p_pos.y + glyphs[i].advance > p_clip_r) {
1540 return;
1541 }
1542 }
1543 }
1544 if (p_clip_l > 0) {
1545 // Clip left / top.
1546 if (orientation == ORIENTATION_HORIZONTAL) {
1547 if (ofs.x - p_pos.x < p_clip_l) {
1548 ofs.x += glyphs[i].advance;
1549 continue;
1550 }
1551 } else {
1552 if (ofs.y - p_pos.y < p_clip_l) {
1553 ofs.y += glyphs[i].advance;
1554 continue;
1555 }
1556 }
1557 }
1558
1559 if (glyphs[i].font_rid != RID()) {
1560 font_draw_glyph(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
1561 } else if (hex_codes && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & GRAPHEME_IS_EMBEDDED_OBJECT) != GRAPHEME_IS_EMBEDDED_OBJECT)) {
1562 TextServer::draw_hex_code_box(p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
1563 }
1564 if (orientation == ORIENTATION_HORIZONTAL) {
1565 ofs.x += glyphs[i].advance;
1566 } else {
1567 ofs.y += glyphs[i].advance;
1568 }
1569 }
1570 }
1571 // Draw LTR ellipsis string when needed.
1572 if (!rtl && ellipsis_pos >= 0) {
1573 for (int i = 0; i < ellipsis_gl_size; i++) {
1574 for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
1575 font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
1576 if (orientation == ORIENTATION_HORIZONTAL) {
1577 ofs.x += ellipsis_glyphs[i].advance;
1578 } else {
1579 ofs.y += ellipsis_glyphs[i].advance;
1580 }
1581 }
1582 }
1583 }
1584}
1585
1586void TextServer::shaped_text_draw_outline(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l, double p_clip_r, int64_t p_outline_size, const Color &p_color) const {
1587 TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
1588
1589 bool rtl = (shaped_text_get_inferred_direction(p_shaped) == DIRECTION_RTL);
1590
1591 int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped);
1592 int trim_pos = shaped_text_get_trim_pos(p_shaped);
1593
1594 const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped);
1595 int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped);
1596
1597 int v_size = shaped_text_get_glyph_count(p_shaped);
1598 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1599 Vector2 ofs = p_pos;
1600 // Draw RTL ellipsis string when needed.
1601 if (rtl && ellipsis_pos >= 0) {
1602 for (int i = ellipsis_gl_size - 1; i >= 0; i--) {
1603 for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
1604 font_draw_glyph_outline(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, p_outline_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
1605 if (orientation == ORIENTATION_HORIZONTAL) {
1606 ofs.x += ellipsis_glyphs[i].advance;
1607 } else {
1608 ofs.y += ellipsis_glyphs[i].advance;
1609 }
1610 }
1611 }
1612 }
1613 // Draw at the baseline.
1614 for (int i = 0; i < v_size; i++) {
1615 if (trim_pos >= 0) {
1616 if (rtl) {
1617 if (i < trim_pos) {
1618 continue;
1619 }
1620 } else {
1621 if (i >= trim_pos) {
1622 break;
1623 }
1624 }
1625 }
1626 for (int j = 0; j < glyphs[i].repeat; j++) {
1627 if (p_clip_r > 0) {
1628 // Clip right / bottom.
1629 if (orientation == ORIENTATION_HORIZONTAL) {
1630 if (ofs.x - p_pos.x + glyphs[i].advance > p_clip_r) {
1631 return;
1632 }
1633 } else {
1634 if (ofs.y - p_pos.y + glyphs[i].advance > p_clip_r) {
1635 return;
1636 }
1637 }
1638 }
1639 if (p_clip_l > 0) {
1640 // Clip left / top.
1641 if (orientation == ORIENTATION_HORIZONTAL) {
1642 if (ofs.x - p_pos.x < p_clip_l) {
1643 ofs.x += glyphs[i].advance;
1644 continue;
1645 }
1646 } else {
1647 if (ofs.y - p_pos.y < p_clip_l) {
1648 ofs.y += glyphs[i].advance;
1649 continue;
1650 }
1651 }
1652 }
1653 if (glyphs[i].font_rid != RID()) {
1654 font_draw_glyph_outline(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, p_outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
1655 }
1656 if (orientation == ORIENTATION_HORIZONTAL) {
1657 ofs.x += glyphs[i].advance;
1658 } else {
1659 ofs.y += glyphs[i].advance;
1660 }
1661 }
1662 }
1663 // Draw LTR ellipsis string when needed.
1664 if (!rtl && ellipsis_pos >= 0) {
1665 for (int i = 0; i < ellipsis_gl_size; i++) {
1666 for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
1667 font_draw_glyph_outline(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, p_outline_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
1668 if (orientation == ORIENTATION_HORIZONTAL) {
1669 ofs.x += ellipsis_glyphs[i].advance;
1670 } else {
1671 ofs.y += ellipsis_glyphs[i].advance;
1672 }
1673 }
1674 }
1675 }
1676}
1677
1678void TextServer::_diacritics_map_add(const String &p_from, char32_t p_to) {
1679 for (int i = 0; i < p_from.size(); i++) {
1680 diacritics_map[p_from[i]] = p_to;
1681 }
1682}
1683
1684void TextServer::_init_diacritics_map() {
1685 diacritics_map.clear();
1686
1687 // Latin.
1688 _diacritics_map_add(U"ÀÁÂÃÄÅĀĂĄǍǞǠǺȀȂȦḀẠẢẤẦẨẪẬẮẰẲẴẶ", U'A');
1689 _diacritics_map_add(U"àáâãäåāăąǎǟǡǻȁȃȧḁẚạảấầẩẫậắằẳẵặ", U'a');
1690 _diacritics_map_add(U"ǢǼ", U'Æ');
1691 _diacritics_map_add(U"ǣǽ", U'æ');
1692 _diacritics_map_add(U"ḂḄḆ", U'B');
1693 _diacritics_map_add(U"ḃḅḇ", U'b');
1694 _diacritics_map_add(U"ÇĆĈĊČḈ", U'C');
1695 _diacritics_map_add(U"çćĉċčḉ", U'c');
1696 _diacritics_map_add(U"ĎḊḌḎḐḒ", U'D');
1697 _diacritics_map_add(U"ďḋḍḏḑḓ", U'd');
1698 _diacritics_map_add(U"ÈÉÊËĒĔĖĘĚȆȨḔḖḘḚḜẸẺẼẾỀỂỄỆ", U'E');
1699 _diacritics_map_add(U"èéêëēĕėęěȇȩḕḗḙḛḝẹẻẽếềểễệ", U'e');
1700 _diacritics_map_add(U"Ḟ", U'F');
1701 _diacritics_map_add(U"ḟ", U'f');
1702 _diacritics_map_add(U"ĜĞĠĢǦǴḠ", U'G');
1703 _diacritics_map_add(U"ĝğġģǧǵḡ", U'g');
1704 _diacritics_map_add(U"ĤȞḢḤḦḨḪ", U'H');
1705 _diacritics_map_add(U"ĥȟḣḥḧḩḫẖ", U'h');
1706 _diacritics_map_add(U"ÌÍÎÏĨĪĬĮİǏȈȊḬḮỈỊ", U'I');
1707 _diacritics_map_add(U"ìíîïĩīĭįıǐȉȋḭḯỉị", U'i');
1708 _diacritics_map_add(U"Ĵ", U'J');
1709 _diacritics_map_add(U"ĵ", U'j');
1710 _diacritics_map_add(U"ĶǨḰḲḴ", U'K');
1711 _diacritics_map_add(U"ķĸǩḱḳḵ", U'k');
1712 _diacritics_map_add(U"ĹĻĽĿḶḸḺḼ", U'L');
1713 _diacritics_map_add(U"ĺļľŀḷḹḻḽ", U'l');
1714 _diacritics_map_add(U"ḾṀṂ", U'M');
1715 _diacritics_map_add(U"ḿṁṃ", U'm');
1716 _diacritics_map_add(U"ÑŃŅŇǸṄṆṈṊ", U'N');
1717 _diacritics_map_add(U"ñńņňʼnǹṅṇṉṋ", U'n');
1718 _diacritics_map_add(U"ÒÓÔÕÖŌŎŐƠǑǪǬȌȎȪȬȮȰṌṎṐṒỌỎỐỒỔỖỘỚỜỞỠỢ", U'O');
1719 _diacritics_map_add(U"òóôõöōŏőơǒǫǭȍȏȫȭȯȱṍṏṑṓọỏốồổỗộớờởỡợ", U'o');
1720 _diacritics_map_add(U"ṔṖ", U'P');
1721 _diacritics_map_add(U"ṗṕ", U'p');
1722 _diacritics_map_add(U"ŔŖŘȐȒṘṚṜṞ", U'R');
1723 _diacritics_map_add(U"ŕŗřȑȓṙṛṝṟ", U'r');
1724 _diacritics_map_add(U"ŚŜŞŠȘṠṢṤṦṨ", U'S');
1725 _diacritics_map_add(U"śŝşšſșṡṣṥṧṩẛẜẝ", U's');
1726 _diacritics_map_add(U"ŢŤȚṪṬṮṰ", U'T');
1727 _diacritics_map_add(U"ţťțṫṭṯṱẗ", U't');
1728 _diacritics_map_add(U"ÙÚÛÜŨŪŬŮŰŲƯǓǕǗǙǛȔȖṲṴṶṸṺỤỦỨỪỬỮỰ", U'U');
1729 _diacritics_map_add(U"ùúûüũūŭůűųưǔǖǘǚǜȕȗṳṵṷṹṻụủứừửữự", U'u');
1730 _diacritics_map_add(U"ṼṾ", U'V');
1731 _diacritics_map_add(U"ṽṿ", U'v');
1732 _diacritics_map_add(U"ŴẀẂẄẆẈ", U'W');
1733 _diacritics_map_add(U"ŵẁẃẅẇẉẘ", U'w');
1734 _diacritics_map_add(U"ẊẌ", U'X');
1735 _diacritics_map_add(U"ẋẍ", U'x');
1736 _diacritics_map_add(U"ÝŶẎỲỴỶỸỾ", U'Y');
1737 _diacritics_map_add(U"ýÿŷẏẙỳỵỷỹỿ", U'y');
1738 _diacritics_map_add(U"ŹŻŽẐẒẔ", U'Z');
1739 _diacritics_map_add(U"źżžẑẓẕ", U'z');
1740
1741 // Greek.
1742 _diacritics_map_add(U"ΆἈἉἊἋἌἍἎἏᾈᾉᾊᾋᾌᾍᾎᾏᾸᾹᾺΆᾼ", U'Α');
1743 _diacritics_map_add(U"άἀἁἂἃἄἅἆἇὰάᾀᾁᾂᾃᾄᾅᾆᾇᾰᾱᾲᾳᾴᾶᾷ", U'α');
1744 _diacritics_map_add(U"ΈἘἙἚἛἜἝῈΈ", U'Ε');
1745 _diacritics_map_add(U"έἐἑἒἓἔἕὲέ", U'ε');
1746 _diacritics_map_add(U"ΉἨἩἪἫἬἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟῊΉῌ", U'Η');
1747 _diacritics_map_add(U"ήἠἡἢἣἤἥἦἧὴήᾐᾑᾒᾓᾔᾕᾖᾗῂῃῄῆῇ", U'η');
1748 _diacritics_map_add(U"ΊΪἸἹἺἻἼἽἾἿῘῙῚΊ", U'Ι');
1749 _diacritics_map_add(U"ίΐϊἰἱἲἳἴἵἶἷὶίῐῑῒΐῖῗ", U'ι');
1750 _diacritics_map_add(U"ΌὈὉὊὋὌὍῸΌ", U'Ο');
1751 _diacritics_map_add(U"όὀὁὂὃὄὅὸό", U'ο');
1752 _diacritics_map_add(U"Ῥ", U'Ρ');
1753 _diacritics_map_add(U"ῤῥ", U'ρ');
1754 _diacritics_map_add(U"ΎΫϓϔὙὛὝὟῨῩῪΎ", U'Υ');
1755 _diacritics_map_add(U"ΰϋύὐὑὒὓὔὕὖὗὺύῠῡῢΰῦῧ", U'υ');
1756 _diacritics_map_add(U"ΏὨὩὪὫὬὭὮὯᾨᾩᾪᾫᾬᾭᾮᾯῺΏῼ", U'Ω');
1757 _diacritics_map_add(U"ώὠὡὢὣὤὥὦὧὼώᾠᾡᾢᾣᾤᾥᾦᾧῲῳῴῶῷ", U'ω');
1758
1759 // Cyrillic.
1760 _diacritics_map_add(U"ӐӒ", U'А');
1761 _diacritics_map_add(U"ӑӓ", U'а');
1762 _diacritics_map_add(U"ЀЁӖ", U'Е');
1763 _diacritics_map_add(U"ѐёӗ", U'е');
1764 _diacritics_map_add(U"Ӛ", U'Ә');
1765 _diacritics_map_add(U"ӛ", U'ә');
1766 _diacritics_map_add(U"Ӝ", U'Ж');
1767 _diacritics_map_add(U"ӝ", U'ж');
1768 _diacritics_map_add(U"Ӟ", U'З');
1769 _diacritics_map_add(U"ӟ", U'з');
1770 _diacritics_map_add(U"Ѓ", U'Г');
1771 _diacritics_map_add(U"ѓ", U'г');
1772 _diacritics_map_add(U"Ї", U'І');
1773 _diacritics_map_add(U"ї", U'і');
1774 _diacritics_map_add(U"ЍӢӤЙ", U'И');
1775 _diacritics_map_add(U"ѝӣӥй", U'и');
1776 _diacritics_map_add(U"Ќ", U'К');
1777 _diacritics_map_add(U"ќ", U'к');
1778 _diacritics_map_add(U"Ӧ", U'О');
1779 _diacritics_map_add(U"ӧ", U'о');
1780 _diacritics_map_add(U"Ӫ", U'Ө');
1781 _diacritics_map_add(U"ӫ", U'ө');
1782 _diacritics_map_add(U"Ӭ", U'Э');
1783 _diacritics_map_add(U"ӭ", U'э');
1784 _diacritics_map_add(U"ЎӮӰӲ", U'У');
1785 _diacritics_map_add(U"ўӯӱӳ", U'у');
1786 _diacritics_map_add(U"Ӵ", U'Ч');
1787 _diacritics_map_add(U"ӵ", U'ч');
1788 _diacritics_map_add(U"Ӹ", U'Ы');
1789 _diacritics_map_add(U"ӹ", U'ы');
1790}
1791
1792String TextServer::strip_diacritics(const String &p_string) const {
1793 String result;
1794 for (int i = 0; i < p_string.length(); i++) {
1795 if (p_string[i] < 0x02B0 || p_string[i] > 0x036F) { // Skip combining diacritics.
1796 if (diacritics_map.has(p_string[i])) {
1797 result += diacritics_map[p_string[i]];
1798 } else {
1799 result += p_string[i];
1800 }
1801 }
1802 }
1803 return result;
1804}
1805
1806TypedArray<Vector3i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
1807 TypedArray<Vector3i> ret;
1808 switch (p_parser_type) {
1809 case STRUCTURED_TEXT_URI: {
1810 int prev = 0;
1811 for (int i = 0; i < p_text.length(); i++) {
1812 if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) {
1813 if (prev != i) {
1814 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1815 }
1816 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1817 prev = i + 1;
1818 }
1819 }
1820 if (prev != p_text.length()) {
1821 ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
1822 }
1823 } break;
1824 case STRUCTURED_TEXT_FILE: {
1825 int prev = 0;
1826 for (int i = 0; i < p_text.length(); i++) {
1827 if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
1828 if (prev != i) {
1829 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1830 }
1831 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1832 prev = i + 1;
1833 }
1834 }
1835 if (prev != p_text.length()) {
1836 ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
1837 }
1838 } break;
1839 case STRUCTURED_TEXT_EMAIL: {
1840 bool local = true;
1841 int prev = 0;
1842 for (int i = 0; i < p_text.length(); i++) {
1843 if ((p_text[i] == '@') && local) { // Add full "local" as single context.
1844 local = false;
1845 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1846 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1847 prev = i + 1;
1848 } else if (!local && (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
1849 if (prev != i) {
1850 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1851 }
1852 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1853 prev = i + 1;
1854 }
1855 }
1856 if (prev != p_text.length()) {
1857 ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
1858 }
1859 } break;
1860 case STRUCTURED_TEXT_LIST: {
1861 if (p_args.size() == 1 && p_args[0].get_type() == Variant::STRING) {
1862 Vector<String> tags = p_text.split(String(p_args[0]));
1863 int prev = 0;
1864 for (int i = 0; i < tags.size(); i++) {
1865 if (prev != i) {
1866 ret.push_back(Vector3i(prev, prev + tags[i].length(), TextServer::DIRECTION_INHERITED));
1867 }
1868 ret.push_back(Vector3i(prev + tags[i].length(), prev + tags[i].length() + 1, TextServer::DIRECTION_INHERITED));
1869 prev = prev + tags[i].length() + 1;
1870 }
1871 }
1872 } break;
1873 case STRUCTURED_TEXT_GDSCRIPT: {
1874 bool in_string_literal = false;
1875 bool in_string_literal_single = false;
1876 bool in_id = false;
1877
1878 int prev = 0;
1879 for (int i = 0; i < p_text.length(); i++) {
1880 char32_t c = p_text[i];
1881 if (in_string_literal) {
1882 if (c == '\\') {
1883 i++;
1884 continue; // Skip escaped chars.
1885 } else if (c == '\"') {
1886 // String literal end, push string and ".
1887 if (prev != i) {
1888 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1889 }
1890 prev = i + 1;
1891 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1892 in_string_literal = false;
1893 }
1894 } else if (in_string_literal_single) {
1895 if (c == '\\') {
1896 i++;
1897 continue; // Skip escaped chars.
1898 } else if (c == '\'') {
1899 // String literal end, push string and '.
1900 if (prev != i) {
1901 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1902 }
1903 prev = i + 1;
1904 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1905 in_string_literal_single = false;
1906 }
1907 } else if (in_id) {
1908 if (!is_unicode_identifier_continue(c)) {
1909 // End of id, push id.
1910 if (prev != i) {
1911 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1912 }
1913 prev = i;
1914 in_id = false;
1915 }
1916 } else if (is_unicode_identifier_start(c)) {
1917 // Start of new id, push prev element.
1918 if (prev != i) {
1919 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1920 }
1921 prev = i;
1922 in_id = true;
1923 } else if (c == '\"') {
1924 // String literal start, push prev element and ".
1925 if (prev != i) {
1926 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1927 }
1928 prev = i + 1;
1929 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1930 in_string_literal = true;
1931 } else if (c == '\'') {
1932 // String literal start, push prev element and '.
1933 if (prev != i) {
1934 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1935 }
1936 prev = i + 1;
1937 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1938 in_string_literal_single = true;
1939 } else if (c == '#') {
1940 // Start of comment, push prev element and #, skip the rest of the text.
1941 if (prev != i) {
1942 ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
1943 }
1944 prev = i + 1;
1945 ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
1946 break;
1947 }
1948 }
1949 if (prev < p_text.length()) {
1950 ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
1951 }
1952 } break;
1953 case STRUCTURED_TEXT_CUSTOM:
1954 case STRUCTURED_TEXT_DEFAULT:
1955 default: {
1956 ret.push_back(Vector3i(0, p_text.length(), TextServer::DIRECTION_INHERITED));
1957 }
1958 }
1959 return ret;
1960}
1961
1962TypedArray<Dictionary> TextServer::_shaped_text_get_glyphs_wrapper(const RID &p_shaped) const {
1963 TypedArray<Dictionary> ret;
1964
1965 const Glyph *glyphs = shaped_text_get_glyphs(p_shaped);
1966 int gl_size = shaped_text_get_glyph_count(p_shaped);
1967 for (int i = 0; i < gl_size; i++) {
1968 Dictionary glyph;
1969
1970 glyph["start"] = glyphs[i].start;
1971 glyph["end"] = glyphs[i].end;
1972 glyph["repeat"] = glyphs[i].repeat;
1973 glyph["count"] = glyphs[i].count;
1974 glyph["flags"] = glyphs[i].flags;
1975 glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off);
1976 glyph["advance"] = glyphs[i].advance;
1977 glyph["font_rid"] = glyphs[i].font_rid;
1978 glyph["font_size"] = glyphs[i].font_size;
1979 glyph["index"] = glyphs[i].index;
1980
1981 ret.push_back(glyph);
1982 }
1983
1984 return ret;
1985}
1986
1987TypedArray<Dictionary> TextServer::_shaped_text_sort_logical_wrapper(const RID &p_shaped) {
1988 Array ret;
1989
1990 const Glyph *glyphs = shaped_text_sort_logical(p_shaped);
1991 int gl_size = shaped_text_get_glyph_count(p_shaped);
1992 for (int i = 0; i < gl_size; i++) {
1993 Dictionary glyph;
1994
1995 glyph["start"] = glyphs[i].start;
1996 glyph["end"] = glyphs[i].end;
1997 glyph["repeat"] = glyphs[i].repeat;
1998 glyph["count"] = glyphs[i].count;
1999 glyph["flags"] = glyphs[i].flags;
2000 glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off);
2001 glyph["advance"] = glyphs[i].advance;
2002 glyph["font_rid"] = glyphs[i].font_rid;
2003 glyph["font_size"] = glyphs[i].font_size;
2004 glyph["index"] = glyphs[i].index;
2005
2006 ret.push_back(glyph);
2007 }
2008
2009 return ret;
2010}
2011
2012TypedArray<Dictionary> TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(const RID &p_shaped) const {
2013 TypedArray<Dictionary> ret;
2014
2015 const Glyph *glyphs = shaped_text_get_ellipsis_glyphs(p_shaped);
2016 int gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped);
2017 for (int i = 0; i < gl_size; i++) {
2018 Dictionary glyph;
2019
2020 glyph["start"] = glyphs[i].start;
2021 glyph["end"] = glyphs[i].end;
2022 glyph["repeat"] = glyphs[i].repeat;
2023 glyph["count"] = glyphs[i].count;
2024 glyph["flags"] = glyphs[i].flags;
2025 glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off);
2026 glyph["advance"] = glyphs[i].advance;
2027 glyph["font_rid"] = glyphs[i].font_rid;
2028 glyph["font_size"] = glyphs[i].font_size;
2029 glyph["index"] = glyphs[i].index;
2030
2031 ret.push_back(glyph);
2032 }
2033
2034 return ret;
2035}
2036
2037bool TextServer::is_valid_identifier(const String &p_string) const {
2038 const char32_t *str = p_string.ptr();
2039 int len = p_string.length();
2040
2041 if (len == 0) {
2042 return false; // Empty string.
2043 }
2044
2045 if (!is_unicode_identifier_start(str[0])) {
2046 return false;
2047 }
2048
2049 for (int i = 1; i < len; i++) {
2050 if (!is_unicode_identifier_continue(str[i])) {
2051 return false;
2052 }
2053 }
2054 return true;
2055}
2056
2057TextServer::TextServer() {
2058 _init_diacritics_map();
2059}
2060
2061TextServer::~TextServer() {
2062}
2063