1 | /**************************************************************************/ |
2 | /* text_line.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 "text_line.h" |
32 | |
33 | void TextLine::_bind_methods() { |
34 | ClassDB::bind_method(D_METHOD("clear" ), &TextLine::clear); |
35 | |
36 | ClassDB::bind_method(D_METHOD("set_direction" , "direction" ), &TextLine::set_direction); |
37 | ClassDB::bind_method(D_METHOD("get_direction" ), &TextLine::get_direction); |
38 | |
39 | ADD_PROPERTY(PropertyInfo(Variant::INT, "direction" , PROPERTY_HINT_ENUM, "Auto,Left-to-right,Right-to-left" ), "set_direction" , "get_direction" ); |
40 | |
41 | ClassDB::bind_method(D_METHOD("set_orientation" , "orientation" ), &TextLine::set_orientation); |
42 | ClassDB::bind_method(D_METHOD("get_orientation" ), &TextLine::get_orientation); |
43 | |
44 | ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation" , PROPERTY_HINT_ENUM, "Horizontal,Orientation" ), "set_orientation" , "get_orientation" ); |
45 | |
46 | ClassDB::bind_method(D_METHOD("set_preserve_invalid" , "enabled" ), &TextLine::set_preserve_invalid); |
47 | ClassDB::bind_method(D_METHOD("get_preserve_invalid" ), &TextLine::get_preserve_invalid); |
48 | |
49 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid" ), "set_preserve_invalid" , "get_preserve_invalid" ); |
50 | |
51 | ClassDB::bind_method(D_METHOD("set_preserve_control" , "enabled" ), &TextLine::set_preserve_control); |
52 | ClassDB::bind_method(D_METHOD("get_preserve_control" ), &TextLine::get_preserve_control); |
53 | |
54 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control" ), "set_preserve_control" , "get_preserve_control" ); |
55 | |
56 | ClassDB::bind_method(D_METHOD("set_bidi_override" , "override" ), &TextLine::set_bidi_override); |
57 | |
58 | ClassDB::bind_method(D_METHOD("add_string" , "text" , "font" , "font_size" , "language" , "meta" ), &TextLine::add_string, DEFVAL("" ), DEFVAL(Variant())); |
59 | ClassDB::bind_method(D_METHOD("add_object" , "key" , "size" , "inline_align" , "length" , "baseline" ), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); |
60 | ClassDB::bind_method(D_METHOD("resize_object" , "key" , "size" , "inline_align" , "baseline" ), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); |
61 | |
62 | ClassDB::bind_method(D_METHOD("set_width" , "width" ), &TextLine::set_width); |
63 | ClassDB::bind_method(D_METHOD("get_width" ), &TextLine::get_width); |
64 | |
65 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width" ), "set_width" , "get_width" ); |
66 | |
67 | ClassDB::bind_method(D_METHOD("set_horizontal_alignment" , "alignment" ), &TextLine::set_horizontal_alignment); |
68 | ClassDB::bind_method(D_METHOD("get_horizontal_alignment" ), &TextLine::get_horizontal_alignment); |
69 | |
70 | ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment" , PROPERTY_HINT_ENUM, "Left,Center,Right,Fill" ), "set_horizontal_alignment" , "get_horizontal_alignment" ); |
71 | |
72 | ClassDB::bind_method(D_METHOD("tab_align" , "tab_stops" ), &TextLine::tab_align); |
73 | |
74 | ClassDB::bind_method(D_METHOD("set_flags" , "flags" ), &TextLine::set_flags); |
75 | ClassDB::bind_method(D_METHOD("get_flags" ), &TextLine::get_flags); |
76 | |
77 | ADD_PROPERTY(PropertyInfo(Variant::INT, "flags" , PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis" ), "set_flags" , "get_flags" ); |
78 | |
79 | ClassDB::bind_method(D_METHOD("set_text_overrun_behavior" , "overrun_behavior" ), &TextLine::set_text_overrun_behavior); |
80 | ClassDB::bind_method(D_METHOD("get_text_overrun_behavior" ), &TextLine::get_text_overrun_behavior); |
81 | |
82 | ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior" , PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis" ), "set_text_overrun_behavior" , "get_text_overrun_behavior" ); |
83 | |
84 | ClassDB::bind_method(D_METHOD("get_objects" ), &TextLine::get_objects); |
85 | ClassDB::bind_method(D_METHOD("get_object_rect" , "key" ), &TextLine::get_object_rect); |
86 | |
87 | ClassDB::bind_method(D_METHOD("get_size" ), &TextLine::get_size); |
88 | |
89 | ClassDB::bind_method(D_METHOD("get_rid" ), &TextLine::get_rid); |
90 | |
91 | ClassDB::bind_method(D_METHOD("get_line_ascent" ), &TextLine::get_line_ascent); |
92 | ClassDB::bind_method(D_METHOD("get_line_descent" ), &TextLine::get_line_descent); |
93 | ClassDB::bind_method(D_METHOD("get_line_width" ), &TextLine::get_line_width); |
94 | ClassDB::bind_method(D_METHOD("get_line_underline_position" ), &TextLine::get_line_underline_position); |
95 | ClassDB::bind_method(D_METHOD("get_line_underline_thickness" ), &TextLine::get_line_underline_thickness); |
96 | |
97 | ClassDB::bind_method(D_METHOD("draw" , "canvas" , "pos" , "color" ), &TextLine::draw, DEFVAL(Color(1, 1, 1))); |
98 | ClassDB::bind_method(D_METHOD("draw_outline" , "canvas" , "pos" , "outline_size" , "color" ), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); |
99 | |
100 | ClassDB::bind_method(D_METHOD("hit_test" , "coords" ), &TextLine::hit_test); |
101 | } |
102 | |
103 | void TextLine::_shape() { |
104 | // When a shaped text is invalidated by an external source, we want to reshape it. |
105 | if (!TS->shaped_text_is_ready(rid)) { |
106 | dirty = true; |
107 | } |
108 | |
109 | if (dirty) { |
110 | if (!tab_stops.is_empty()) { |
111 | TS->shaped_text_tab_align(rid, tab_stops); |
112 | } |
113 | |
114 | BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; |
115 | if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { |
116 | switch (overrun_behavior) { |
117 | case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: |
118 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
119 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); |
120 | overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); |
121 | break; |
122 | case TextServer::OVERRUN_TRIM_ELLIPSIS: |
123 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
124 | overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); |
125 | break; |
126 | case TextServer::OVERRUN_TRIM_WORD: |
127 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
128 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); |
129 | break; |
130 | case TextServer::OVERRUN_TRIM_CHAR: |
131 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
132 | break; |
133 | case TextServer::OVERRUN_NO_TRIMMING: |
134 | break; |
135 | } |
136 | |
137 | if (alignment == HORIZONTAL_ALIGNMENT_FILL) { |
138 | TS->shaped_text_fit_to_width(rid, width, flags); |
139 | overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); |
140 | TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); |
141 | } else { |
142 | TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); |
143 | } |
144 | } else if (alignment == HORIZONTAL_ALIGNMENT_FILL) { |
145 | TS->shaped_text_fit_to_width(rid, width, flags); |
146 | } |
147 | dirty = false; |
148 | } |
149 | } |
150 | |
151 | RID TextLine::get_rid() const { |
152 | return rid; |
153 | } |
154 | |
155 | void TextLine::clear() { |
156 | TS->shaped_text_clear(rid); |
157 | } |
158 | |
159 | void TextLine::set_preserve_invalid(bool p_enabled) { |
160 | TS->shaped_text_set_preserve_invalid(rid, p_enabled); |
161 | dirty = true; |
162 | } |
163 | |
164 | bool TextLine::get_preserve_invalid() const { |
165 | return TS->shaped_text_get_preserve_invalid(rid); |
166 | } |
167 | |
168 | void TextLine::set_preserve_control(bool p_enabled) { |
169 | TS->shaped_text_set_preserve_control(rid, p_enabled); |
170 | dirty = true; |
171 | } |
172 | |
173 | bool TextLine::get_preserve_control() const { |
174 | return TS->shaped_text_get_preserve_control(rid); |
175 | } |
176 | |
177 | void TextLine::set_direction(TextServer::Direction p_direction) { |
178 | TS->shaped_text_set_direction(rid, p_direction); |
179 | dirty = true; |
180 | } |
181 | |
182 | TextServer::Direction TextLine::get_direction() const { |
183 | return TS->shaped_text_get_direction(rid); |
184 | } |
185 | |
186 | void TextLine::set_orientation(TextServer::Orientation p_orientation) { |
187 | TS->shaped_text_set_orientation(rid, p_orientation); |
188 | dirty = true; |
189 | } |
190 | |
191 | TextServer::Orientation TextLine::get_orientation() const { |
192 | return TS->shaped_text_get_orientation(rid); |
193 | } |
194 | |
195 | void TextLine::set_bidi_override(const Array &p_override) { |
196 | TS->shaped_text_set_bidi_override(rid, p_override); |
197 | dirty = true; |
198 | } |
199 | |
200 | bool TextLine::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) { |
201 | ERR_FAIL_COND_V(p_font.is_null(), false); |
202 | bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta); |
203 | for (int i = 0; i < TextServer::SPACING_MAX; i++) { |
204 | TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); |
205 | } |
206 | dirty = true; |
207 | return res; |
208 | } |
209 | |
210 | bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) { |
211 | bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline); |
212 | dirty = true; |
213 | return res; |
214 | } |
215 | |
216 | bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { |
217 | const_cast<TextLine *>(this)->_shape(); |
218 | return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline); |
219 | } |
220 | |
221 | Array TextLine::get_objects() const { |
222 | return TS->shaped_text_get_objects(rid); |
223 | } |
224 | |
225 | Rect2 TextLine::get_object_rect(Variant p_key) const { |
226 | Vector2 ofs; |
227 | |
228 | float length = TS->shaped_text_get_width(rid); |
229 | if (width > 0) { |
230 | switch (alignment) { |
231 | case HORIZONTAL_ALIGNMENT_FILL: |
232 | case HORIZONTAL_ALIGNMENT_LEFT: |
233 | break; |
234 | case HORIZONTAL_ALIGNMENT_CENTER: { |
235 | if (length <= width) { |
236 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
237 | ofs.x += Math::floor((width - length) / 2.0); |
238 | } else { |
239 | ofs.y += Math::floor((width - length) / 2.0); |
240 | } |
241 | } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { |
242 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
243 | ofs.x += width - length; |
244 | } else { |
245 | ofs.y += width - length; |
246 | } |
247 | } |
248 | } break; |
249 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
250 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
251 | ofs.x += width - length; |
252 | } else { |
253 | ofs.y += width - length; |
254 | } |
255 | } break; |
256 | } |
257 | } |
258 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
259 | ofs.y += TS->shaped_text_get_ascent(rid); |
260 | } else { |
261 | ofs.x += TS->shaped_text_get_ascent(rid); |
262 | } |
263 | |
264 | Rect2 rect = TS->shaped_text_get_object_rect(rid, p_key); |
265 | rect.position += ofs; |
266 | |
267 | return rect; |
268 | } |
269 | |
270 | void TextLine::set_horizontal_alignment(HorizontalAlignment p_alignment) { |
271 | if (alignment != p_alignment) { |
272 | if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
273 | alignment = p_alignment; |
274 | dirty = true; |
275 | } else { |
276 | alignment = p_alignment; |
277 | } |
278 | } |
279 | } |
280 | |
281 | HorizontalAlignment TextLine::get_horizontal_alignment() const { |
282 | return alignment; |
283 | } |
284 | |
285 | void TextLine::tab_align(const Vector<float> &p_tab_stops) { |
286 | tab_stops = p_tab_stops; |
287 | dirty = true; |
288 | } |
289 | |
290 | void TextLine::set_flags(BitField<TextServer::JustificationFlag> p_flags) { |
291 | if (flags != p_flags) { |
292 | flags = p_flags; |
293 | dirty = true; |
294 | } |
295 | } |
296 | |
297 | BitField<TextServer::JustificationFlag> TextLine::get_flags() const { |
298 | return flags; |
299 | } |
300 | |
301 | void TextLine::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { |
302 | if (overrun_behavior != p_behavior) { |
303 | overrun_behavior = p_behavior; |
304 | dirty = true; |
305 | } |
306 | } |
307 | |
308 | TextServer::OverrunBehavior TextLine::get_text_overrun_behavior() const { |
309 | return overrun_behavior; |
310 | } |
311 | |
312 | void TextLine::set_width(float p_width) { |
313 | width = p_width; |
314 | if (alignment == HORIZONTAL_ALIGNMENT_FILL || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { |
315 | dirty = true; |
316 | } |
317 | } |
318 | |
319 | float TextLine::get_width() const { |
320 | return width; |
321 | } |
322 | |
323 | Size2 TextLine::get_size() const { |
324 | const_cast<TextLine *>(this)->_shape(); |
325 | return TS->shaped_text_get_size(rid); |
326 | } |
327 | |
328 | float TextLine::get_line_ascent() const { |
329 | const_cast<TextLine *>(this)->_shape(); |
330 | return TS->shaped_text_get_ascent(rid); |
331 | } |
332 | |
333 | float TextLine::get_line_descent() const { |
334 | const_cast<TextLine *>(this)->_shape(); |
335 | return TS->shaped_text_get_descent(rid); |
336 | } |
337 | |
338 | float TextLine::get_line_width() const { |
339 | const_cast<TextLine *>(this)->_shape(); |
340 | return TS->shaped_text_get_width(rid); |
341 | } |
342 | |
343 | float TextLine::get_line_underline_position() const { |
344 | const_cast<TextLine *>(this)->_shape(); |
345 | return TS->shaped_text_get_underline_position(rid); |
346 | } |
347 | |
348 | float TextLine::get_line_underline_thickness() const { |
349 | const_cast<TextLine *>(this)->_shape(); |
350 | return TS->shaped_text_get_underline_thickness(rid); |
351 | } |
352 | |
353 | void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { |
354 | const_cast<TextLine *>(this)->_shape(); |
355 | |
356 | Vector2 ofs = p_pos; |
357 | |
358 | float length = TS->shaped_text_get_width(rid); |
359 | if (width > 0) { |
360 | switch (alignment) { |
361 | case HORIZONTAL_ALIGNMENT_FILL: |
362 | case HORIZONTAL_ALIGNMENT_LEFT: |
363 | break; |
364 | case HORIZONTAL_ALIGNMENT_CENTER: { |
365 | if (length <= width) { |
366 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
367 | ofs.x += Math::floor((width - length) / 2.0); |
368 | } else { |
369 | ofs.y += Math::floor((width - length) / 2.0); |
370 | } |
371 | } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { |
372 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
373 | ofs.x += width - length; |
374 | } else { |
375 | ofs.y += width - length; |
376 | } |
377 | } |
378 | } break; |
379 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
380 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
381 | ofs.x += width - length; |
382 | } else { |
383 | ofs.y += width - length; |
384 | } |
385 | } break; |
386 | } |
387 | } |
388 | |
389 | float clip_l; |
390 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
391 | ofs.y += TS->shaped_text_get_ascent(rid); |
392 | clip_l = MAX(0, p_pos.x - ofs.x); |
393 | } else { |
394 | ofs.x += TS->shaped_text_get_ascent(rid); |
395 | clip_l = MAX(0, p_pos.y - ofs.y); |
396 | } |
397 | return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color); |
398 | } |
399 | |
400 | void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { |
401 | const_cast<TextLine *>(this)->_shape(); |
402 | |
403 | Vector2 ofs = p_pos; |
404 | |
405 | float length = TS->shaped_text_get_width(rid); |
406 | if (width > 0) { |
407 | switch (alignment) { |
408 | case HORIZONTAL_ALIGNMENT_FILL: |
409 | case HORIZONTAL_ALIGNMENT_LEFT: |
410 | break; |
411 | case HORIZONTAL_ALIGNMENT_CENTER: { |
412 | if (length <= width) { |
413 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
414 | ofs.x += Math::floor((width - length) / 2.0); |
415 | } else { |
416 | ofs.y += Math::floor((width - length) / 2.0); |
417 | } |
418 | } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { |
419 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
420 | ofs.x += width - length; |
421 | } else { |
422 | ofs.y += width - length; |
423 | } |
424 | } |
425 | } break; |
426 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
427 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
428 | ofs.x += width - length; |
429 | } else { |
430 | ofs.y += width - length; |
431 | } |
432 | } break; |
433 | } |
434 | } |
435 | |
436 | float clip_l; |
437 | if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { |
438 | ofs.y += TS->shaped_text_get_ascent(rid); |
439 | clip_l = MAX(0, p_pos.x - ofs.x); |
440 | } else { |
441 | ofs.x += TS->shaped_text_get_ascent(rid); |
442 | clip_l = MAX(0, p_pos.y - ofs.y); |
443 | } |
444 | return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color); |
445 | } |
446 | |
447 | int TextLine::hit_test(float p_coords) const { |
448 | const_cast<TextLine *>(this)->_shape(); |
449 | |
450 | return TS->shaped_text_hit_test_position(rid, p_coords); |
451 | } |
452 | |
453 | TextLine::TextLine(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { |
454 | rid = TS->create_shaped_text(p_direction, p_orientation); |
455 | if (p_font.is_valid()) { |
456 | TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); |
457 | for (int i = 0; i < TextServer::SPACING_MAX; i++) { |
458 | TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); |
459 | } |
460 | } |
461 | } |
462 | |
463 | TextLine::TextLine() { |
464 | rid = TS->create_shaped_text(); |
465 | } |
466 | |
467 | TextLine::~TextLine() { |
468 | TS->free_rid(rid); |
469 | } |
470 | |