text_paragraph.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. /*************************************************************************/
  2. /* text_paragraph.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "scene/resources/text_paragraph.h"
  31. void TextParagraph::_bind_methods() {
  32. ClassDB::bind_method(D_METHOD("clear"), &TextParagraph::clear);
  33. ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextParagraph::set_direction);
  34. ClassDB::bind_method(D_METHOD("get_direction"), &TextParagraph::get_direction);
  35. ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
  36. ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
  37. ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
  38. ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
  39. ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
  40. ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
  41. ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation");
  42. ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextParagraph::set_preserve_invalid);
  43. ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextParagraph::get_preserve_invalid);
  44. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid");
  45. ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextParagraph::set_preserve_control);
  46. ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextParagraph::get_preserve_control);
  47. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
  48. ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override);
  49. ClassDB::bind_method(D_METHOD("set_dropcap", "text", "font", "font_size", "dropcap_margins", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(""));
  50. ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
  51. ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant()));
  52. ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
  53. ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
  54. ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment);
  55. ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment);
  56. ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_alignment", "get_alignment");
  57. ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align);
  58. ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags);
  59. ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags);
  60. ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive,Trim Spaces"), "set_break_flags", "get_break_flags");
  61. ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags);
  62. ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags);
  63. ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags");
  64. ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior);
  65. ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior);
  66. 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");
  67. ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
  68. ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
  69. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
  70. ClassDB::bind_method(D_METHOD("get_non_wrapped_size"), &TextParagraph::get_non_wrapped_size);
  71. ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size);
  72. ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
  73. ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid);
  74. ClassDB::bind_method(D_METHOD("get_dropcap_rid"), &TextParagraph::get_dropcap_rid);
  75. ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count);
  76. ClassDB::bind_method(D_METHOD("set_max_lines_visible", "max_lines_visible"), &TextParagraph::set_max_lines_visible);
  77. ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &TextParagraph::get_max_lines_visible);
  78. ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible");
  79. ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects);
  80. ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
  81. ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
  82. ClassDB::bind_method(D_METHOD("get_line_range", "line"), &TextParagraph::get_line_range);
  83. ClassDB::bind_method(D_METHOD("get_line_ascent", "line"), &TextParagraph::get_line_ascent);
  84. ClassDB::bind_method(D_METHOD("get_line_descent", "line"), &TextParagraph::get_line_descent);
  85. ClassDB::bind_method(D_METHOD("get_line_width", "line"), &TextParagraph::get_line_width);
  86. ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position);
  87. ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness);
  88. ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size);
  89. ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines);
  90. ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "dc_color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
  91. ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "dc_color"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
  92. ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1)));
  93. ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
  94. ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1)));
  95. ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
  96. ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test);
  97. }
  98. void TextParagraph::_shape_lines() {
  99. if (lines_dirty) {
  100. for (int i = 0; i < (int)lines_rid.size(); i++) {
  101. TS->free_rid(lines_rid[i]);
  102. }
  103. lines_rid.clear();
  104. if (!tab_stops.is_empty()) {
  105. TS->shaped_text_tab_align(rid, tab_stops);
  106. }
  107. float h_offset = 0.f;
  108. float v_offset = 0.f;
  109. int start = 0;
  110. dropcap_lines = 0;
  111. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  112. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  113. v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  114. } else {
  115. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  116. v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  117. }
  118. if (h_offset > 0) {
  119. // Dropcap, flow around.
  120. PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, brk_flags);
  121. for (int i = 0; i < line_breaks.size(); i = i + 2) {
  122. RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
  123. float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
  124. if (v_offset < h) {
  125. TS->free_rid(line);
  126. break;
  127. }
  128. if (!tab_stops.is_empty()) {
  129. TS->shaped_text_tab_align(line, tab_stops);
  130. }
  131. dropcap_lines++;
  132. v_offset -= h;
  133. start = line_breaks[i + 1];
  134. lines_rid.push_back(line);
  135. }
  136. }
  137. // Use fixed for the rest of lines.
  138. PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, brk_flags);
  139. for (int i = 0; i < line_breaks.size(); i = i + 2) {
  140. RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
  141. if (!tab_stops.is_empty()) {
  142. TS->shaped_text_tab_align(line, tab_stops);
  143. }
  144. lines_rid.push_back(line);
  145. }
  146. BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM;
  147. if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
  148. switch (overrun_behavior) {
  149. case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS:
  150. overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
  151. overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
  152. overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
  153. break;
  154. case TextServer::OVERRUN_TRIM_ELLIPSIS:
  155. overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
  156. overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
  157. break;
  158. case TextServer::OVERRUN_TRIM_WORD:
  159. overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
  160. overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
  161. break;
  162. case TextServer::OVERRUN_TRIM_CHAR:
  163. overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
  164. break;
  165. case TextServer::OVERRUN_NO_TRIMMING:
  166. break;
  167. }
  168. }
  169. bool autowrap_enabled = brk_flags.has_flag(TextServer::BREAK_WORD_BOUND) || brk_flags.has_flag(TextServer::BREAK_GRAPHEME_BOUND);
  170. // Fill after min_size calculation.
  171. if (autowrap_enabled) {
  172. int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  173. bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size();
  174. if (lines_hidden) {
  175. overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
  176. }
  177. if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
  178. for (int i = 0; i < (int)lines_rid.size(); i++) {
  179. if (i < visible_lines - 1 || (int)lines_rid.size() == 1) {
  180. TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags);
  181. } else if (i == (visible_lines - 1)) {
  182. TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
  183. }
  184. }
  185. } else if (lines_hidden) {
  186. TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
  187. }
  188. } else {
  189. // Autowrap disabled.
  190. for (int i = 0; i < (int)lines_rid.size(); i++) {
  191. if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
  192. TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags);
  193. overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
  194. TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
  195. TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
  196. } else {
  197. TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
  198. }
  199. }
  200. }
  201. lines_dirty = false;
  202. }
  203. }
  204. RID TextParagraph::get_rid() const {
  205. return rid;
  206. }
  207. RID TextParagraph::get_line_rid(int p_line) const {
  208. _THREAD_SAFE_METHOD_
  209. const_cast<TextParagraph *>(this)->_shape_lines();
  210. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), RID());
  211. return lines_rid[p_line];
  212. }
  213. RID TextParagraph::get_dropcap_rid() const {
  214. return dropcap_rid;
  215. }
  216. void TextParagraph::clear() {
  217. _THREAD_SAFE_METHOD_
  218. for (int i = 0; i < (int)lines_rid.size(); i++) {
  219. TS->free_rid(lines_rid[i]);
  220. }
  221. lines_rid.clear();
  222. TS->shaped_text_clear(rid);
  223. TS->shaped_text_clear(dropcap_rid);
  224. }
  225. void TextParagraph::set_preserve_invalid(bool p_enabled) {
  226. _THREAD_SAFE_METHOD_
  227. TS->shaped_text_set_preserve_invalid(rid, p_enabled);
  228. TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
  229. lines_dirty = true;
  230. }
  231. bool TextParagraph::get_preserve_invalid() const {
  232. _THREAD_SAFE_METHOD_
  233. return TS->shaped_text_get_preserve_invalid(rid);
  234. }
  235. void TextParagraph::set_preserve_control(bool p_enabled) {
  236. _THREAD_SAFE_METHOD_
  237. TS->shaped_text_set_preserve_control(rid, p_enabled);
  238. TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
  239. lines_dirty = true;
  240. }
  241. bool TextParagraph::get_preserve_control() const {
  242. _THREAD_SAFE_METHOD_
  243. return TS->shaped_text_get_preserve_control(rid);
  244. }
  245. void TextParagraph::set_direction(TextServer::Direction p_direction) {
  246. _THREAD_SAFE_METHOD_
  247. TS->shaped_text_set_direction(rid, p_direction);
  248. TS->shaped_text_set_direction(dropcap_rid, p_direction);
  249. lines_dirty = true;
  250. }
  251. TextServer::Direction TextParagraph::get_direction() const {
  252. _THREAD_SAFE_METHOD_
  253. const_cast<TextParagraph *>(this)->_shape_lines();
  254. return TS->shaped_text_get_direction(rid);
  255. }
  256. void TextParagraph::set_custom_punctuation(const String &p_punct) {
  257. _THREAD_SAFE_METHOD_
  258. TS->shaped_text_set_custom_punctuation(rid, p_punct);
  259. lines_dirty = true;
  260. }
  261. String TextParagraph::get_custom_punctuation() const {
  262. _THREAD_SAFE_METHOD_
  263. return TS->shaped_text_get_custom_punctuation(rid);
  264. }
  265. void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
  266. _THREAD_SAFE_METHOD_
  267. TS->shaped_text_set_orientation(rid, p_orientation);
  268. TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
  269. lines_dirty = true;
  270. }
  271. TextServer::Orientation TextParagraph::get_orientation() const {
  272. _THREAD_SAFE_METHOD_
  273. const_cast<TextParagraph *>(this)->_shape_lines();
  274. return TS->shaped_text_get_orientation(rid);
  275. }
  276. bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins, const String &p_language) {
  277. _THREAD_SAFE_METHOD_
  278. ERR_FAIL_COND_V(p_font.is_null(), false);
  279. TS->shaped_text_clear(dropcap_rid);
  280. dropcap_margins = p_dropcap_margins;
  281. bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language);
  282. for (int i = 0; i < TextServer::SPACING_MAX; i++) {
  283. TS->shaped_text_set_spacing(dropcap_rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i)));
  284. }
  285. lines_dirty = true;
  286. return res;
  287. }
  288. void TextParagraph::clear_dropcap() {
  289. _THREAD_SAFE_METHOD_
  290. dropcap_margins = Rect2();
  291. TS->shaped_text_clear(dropcap_rid);
  292. lines_dirty = true;
  293. }
  294. bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) {
  295. _THREAD_SAFE_METHOD_
  296. ERR_FAIL_COND_V(p_font.is_null(), false);
  297. 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);
  298. for (int i = 0; i < TextServer::SPACING_MAX; i++) {
  299. TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i)));
  300. }
  301. lines_dirty = true;
  302. return res;
  303. }
  304. void TextParagraph::set_bidi_override(const Array &p_override) {
  305. _THREAD_SAFE_METHOD_
  306. TS->shaped_text_set_bidi_override(rid, p_override);
  307. lines_dirty = true;
  308. }
  309. bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
  310. _THREAD_SAFE_METHOD_
  311. bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
  312. lines_dirty = true;
  313. return res;
  314. }
  315. bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
  316. _THREAD_SAFE_METHOD_
  317. bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
  318. lines_dirty = true;
  319. return res;
  320. }
  321. void TextParagraph::set_alignment(HorizontalAlignment p_alignment) {
  322. _THREAD_SAFE_METHOD_
  323. if (alignment != p_alignment) {
  324. if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
  325. alignment = p_alignment;
  326. lines_dirty = true;
  327. } else {
  328. alignment = p_alignment;
  329. }
  330. }
  331. }
  332. HorizontalAlignment TextParagraph::get_alignment() const {
  333. return alignment;
  334. }
  335. void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
  336. _THREAD_SAFE_METHOD_
  337. tab_stops = p_tab_stops;
  338. lines_dirty = true;
  339. }
  340. void TextParagraph::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
  341. _THREAD_SAFE_METHOD_
  342. if (jst_flags != p_flags) {
  343. jst_flags = p_flags;
  344. lines_dirty = true;
  345. }
  346. }
  347. BitField<TextServer::JustificationFlag> TextParagraph::get_justification_flags() const {
  348. return jst_flags;
  349. }
  350. void TextParagraph::set_break_flags(BitField<TextServer::LineBreakFlag> p_flags) {
  351. _THREAD_SAFE_METHOD_
  352. if (brk_flags != p_flags) {
  353. brk_flags = p_flags;
  354. lines_dirty = true;
  355. }
  356. }
  357. BitField<TextServer::LineBreakFlag> TextParagraph::get_break_flags() const {
  358. return brk_flags;
  359. }
  360. void TextParagraph::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) {
  361. _THREAD_SAFE_METHOD_
  362. if (overrun_behavior != p_behavior) {
  363. overrun_behavior = p_behavior;
  364. lines_dirty = true;
  365. }
  366. }
  367. TextServer::OverrunBehavior TextParagraph::get_text_overrun_behavior() const {
  368. return overrun_behavior;
  369. }
  370. void TextParagraph::set_width(float p_width) {
  371. _THREAD_SAFE_METHOD_
  372. if (width != p_width) {
  373. width = p_width;
  374. lines_dirty = true;
  375. }
  376. }
  377. float TextParagraph::get_width() const {
  378. return width;
  379. }
  380. Size2 TextParagraph::get_non_wrapped_size() const {
  381. _THREAD_SAFE_METHOD_
  382. const_cast<TextParagraph *>(this)->_shape_lines();
  383. if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
  384. return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y);
  385. } else {
  386. return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y);
  387. }
  388. }
  389. Size2 TextParagraph::get_size() const {
  390. _THREAD_SAFE_METHOD_
  391. const_cast<TextParagraph *>(this)->_shape_lines();
  392. Size2 size;
  393. int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  394. for (int i = 0; i < visible_lines; i++) {
  395. Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
  396. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  397. size.x = MAX(size.x, lsize.x);
  398. size.y += lsize.y;
  399. } else {
  400. size.x += lsize.x;
  401. size.y = MAX(size.y, lsize.y);
  402. }
  403. }
  404. return size;
  405. }
  406. int TextParagraph::get_line_count() const {
  407. _THREAD_SAFE_METHOD_
  408. const_cast<TextParagraph *>(this)->_shape_lines();
  409. return (int)lines_rid.size();
  410. }
  411. void TextParagraph::set_max_lines_visible(int p_lines) {
  412. _THREAD_SAFE_METHOD_
  413. if (p_lines != max_lines_visible) {
  414. max_lines_visible = p_lines;
  415. lines_dirty = true;
  416. }
  417. }
  418. int TextParagraph::get_max_lines_visible() const {
  419. return max_lines_visible;
  420. }
  421. Array TextParagraph::get_line_objects(int p_line) const {
  422. _THREAD_SAFE_METHOD_
  423. const_cast<TextParagraph *>(this)->_shape_lines();
  424. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Array());
  425. return TS->shaped_text_get_objects(lines_rid[p_line]);
  426. }
  427. Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
  428. _THREAD_SAFE_METHOD_
  429. const_cast<TextParagraph *>(this)->_shape_lines();
  430. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2());
  431. Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
  432. for (int i = 0; i < p_line; i++) {
  433. Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
  434. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  435. xrect.position.y += lsize.y;
  436. } else {
  437. xrect.position.x += lsize.x;
  438. }
  439. }
  440. return xrect;
  441. }
  442. Size2 TextParagraph::get_line_size(int p_line) const {
  443. _THREAD_SAFE_METHOD_
  444. const_cast<TextParagraph *>(this)->_shape_lines();
  445. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2());
  446. if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
  447. return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y);
  448. } else {
  449. return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y);
  450. }
  451. }
  452. Vector2i TextParagraph::get_line_range(int p_line) const {
  453. _THREAD_SAFE_METHOD_
  454. const_cast<TextParagraph *>(this)->_shape_lines();
  455. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Vector2i());
  456. return TS->shaped_text_get_range(lines_rid[p_line]);
  457. }
  458. float TextParagraph::get_line_ascent(int p_line) const {
  459. _THREAD_SAFE_METHOD_
  460. const_cast<TextParagraph *>(this)->_shape_lines();
  461. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  462. return TS->shaped_text_get_ascent(lines_rid[p_line]);
  463. }
  464. float TextParagraph::get_line_descent(int p_line) const {
  465. _THREAD_SAFE_METHOD_
  466. const_cast<TextParagraph *>(this)->_shape_lines();
  467. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  468. return TS->shaped_text_get_descent(lines_rid[p_line]);
  469. }
  470. float TextParagraph::get_line_width(int p_line) const {
  471. _THREAD_SAFE_METHOD_
  472. const_cast<TextParagraph *>(this)->_shape_lines();
  473. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  474. return TS->shaped_text_get_width(lines_rid[p_line]);
  475. }
  476. float TextParagraph::get_line_underline_position(int p_line) const {
  477. _THREAD_SAFE_METHOD_
  478. const_cast<TextParagraph *>(this)->_shape_lines();
  479. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  480. return TS->shaped_text_get_underline_position(lines_rid[p_line]);
  481. }
  482. float TextParagraph::get_line_underline_thickness(int p_line) const {
  483. _THREAD_SAFE_METHOD_
  484. const_cast<TextParagraph *>(this)->_shape_lines();
  485. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  486. return TS->shaped_text_get_underline_thickness(lines_rid[p_line]);
  487. }
  488. Size2 TextParagraph::get_dropcap_size() const {
  489. _THREAD_SAFE_METHOD_
  490. return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position;
  491. }
  492. int TextParagraph::get_dropcap_lines() const {
  493. return dropcap_lines;
  494. }
  495. void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const {
  496. _THREAD_SAFE_METHOD_
  497. const_cast<TextParagraph *>(this)->_shape_lines();
  498. Vector2 ofs = p_pos;
  499. float h_offset = 0.f;
  500. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  501. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  502. } else {
  503. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  504. }
  505. if (h_offset > 0) {
  506. // Draw dropcap.
  507. Vector2 dc_off = ofs;
  508. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  509. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  510. dc_off.x += width - h_offset;
  511. } else {
  512. dc_off.y += width - h_offset;
  513. }
  514. }
  515. TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color);
  516. }
  517. int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  518. for (int i = 0; i < lines_visible; i++) {
  519. float l_width = width;
  520. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  521. ofs.x = p_pos.x;
  522. ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
  523. if (i <= dropcap_lines) {
  524. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  525. ofs.x -= h_offset;
  526. }
  527. l_width -= h_offset;
  528. }
  529. } else {
  530. ofs.y = p_pos.y;
  531. ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
  532. if (i <= dropcap_lines) {
  533. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  534. ofs.x -= h_offset;
  535. }
  536. l_width -= h_offset;
  537. }
  538. }
  539. float line_width = TS->shaped_text_get_width(lines_rid[i]);
  540. if (width > 0) {
  541. switch (alignment) {
  542. case HORIZONTAL_ALIGNMENT_FILL:
  543. if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  544. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  545. ofs.x += l_width - line_width;
  546. } else {
  547. ofs.y += l_width - line_width;
  548. }
  549. }
  550. break;
  551. case HORIZONTAL_ALIGNMENT_LEFT:
  552. break;
  553. case HORIZONTAL_ALIGNMENT_CENTER: {
  554. if (line_width <= l_width) {
  555. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  556. ofs.x += Math::floor((l_width - line_width) / 2.0);
  557. } else {
  558. ofs.y += Math::floor((l_width - line_width) / 2.0);
  559. }
  560. } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  561. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  562. ofs.x += l_width - line_width;
  563. } else {
  564. ofs.y += l_width - line_width;
  565. }
  566. }
  567. } break;
  568. case HORIZONTAL_ALIGNMENT_RIGHT: {
  569. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  570. ofs.x += l_width - line_width;
  571. } else {
  572. ofs.y += l_width - line_width;
  573. }
  574. } break;
  575. }
  576. }
  577. float clip_l;
  578. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  579. clip_l = MAX(0, p_pos.x - ofs.x);
  580. } else {
  581. clip_l = MAX(0, p_pos.y - ofs.y);
  582. }
  583. TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
  584. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  585. ofs.x = p_pos.x;
  586. ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
  587. } else {
  588. ofs.y = p_pos.y;
  589. ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
  590. }
  591. }
  592. }
  593. void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const {
  594. _THREAD_SAFE_METHOD_
  595. const_cast<TextParagraph *>(this)->_shape_lines();
  596. Vector2 ofs = p_pos;
  597. float h_offset = 0.f;
  598. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  599. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  600. } else {
  601. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  602. }
  603. if (h_offset > 0) {
  604. // Draw dropcap.
  605. Vector2 dc_off = ofs;
  606. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  607. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  608. dc_off.x += width - h_offset;
  609. } else {
  610. dc_off.y += width - h_offset;
  611. }
  612. }
  613. TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color);
  614. }
  615. for (int i = 0; i < (int)lines_rid.size(); i++) {
  616. float l_width = width;
  617. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  618. ofs.x = p_pos.x;
  619. ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
  620. if (i <= dropcap_lines) {
  621. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  622. ofs.x -= h_offset;
  623. }
  624. l_width -= h_offset;
  625. }
  626. } else {
  627. ofs.y = p_pos.y;
  628. ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
  629. if (i <= dropcap_lines) {
  630. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  631. ofs.x -= h_offset;
  632. }
  633. l_width -= h_offset;
  634. }
  635. }
  636. float length = TS->shaped_text_get_width(lines_rid[i]);
  637. if (width > 0) {
  638. switch (alignment) {
  639. case HORIZONTAL_ALIGNMENT_FILL:
  640. if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  641. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  642. ofs.x += l_width - length;
  643. } else {
  644. ofs.y += l_width - length;
  645. }
  646. }
  647. break;
  648. case HORIZONTAL_ALIGNMENT_LEFT:
  649. break;
  650. case HORIZONTAL_ALIGNMENT_CENTER: {
  651. if (length <= l_width) {
  652. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  653. ofs.x += Math::floor((l_width - length) / 2.0);
  654. } else {
  655. ofs.y += Math::floor((l_width - length) / 2.0);
  656. }
  657. } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  658. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  659. ofs.x += l_width - length;
  660. } else {
  661. ofs.y += l_width - length;
  662. }
  663. }
  664. } break;
  665. case HORIZONTAL_ALIGNMENT_RIGHT: {
  666. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  667. ofs.x += l_width - length;
  668. } else {
  669. ofs.y += l_width - length;
  670. }
  671. } break;
  672. }
  673. }
  674. float clip_l;
  675. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  676. clip_l = MAX(0, p_pos.x - ofs.x);
  677. } else {
  678. clip_l = MAX(0, p_pos.y - ofs.y);
  679. }
  680. TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
  681. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  682. ofs.x = p_pos.x;
  683. ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
  684. } else {
  685. ofs.y = p_pos.y;
  686. ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
  687. }
  688. }
  689. }
  690. int TextParagraph::hit_test(const Point2 &p_coords) const {
  691. _THREAD_SAFE_METHOD_
  692. const_cast<TextParagraph *>(this)->_shape_lines();
  693. Vector2 ofs;
  694. if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
  695. if (ofs.y < 0) {
  696. return 0;
  697. }
  698. } else {
  699. if (ofs.x < 0) {
  700. return 0;
  701. }
  702. }
  703. for (int i = 0; i < (int)lines_rid.size(); i++) {
  704. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  705. if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) {
  706. return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x);
  707. }
  708. ofs.y += TS->shaped_text_get_size(lines_rid[i]).y;
  709. } else {
  710. if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) {
  711. return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y);
  712. }
  713. ofs.y += TS->shaped_text_get_size(lines_rid[i]).x;
  714. }
  715. }
  716. return TS->shaped_text_get_range(rid).y;
  717. }
  718. void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
  719. _THREAD_SAFE_METHOD_
  720. Vector2 ofs = p_pos;
  721. float h_offset = 0.f;
  722. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  723. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  724. } else {
  725. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  726. }
  727. if (h_offset > 0) {
  728. // Draw dropcap.
  729. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  730. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  731. ofs.x += width - h_offset;
  732. } else {
  733. ofs.y += width - h_offset;
  734. }
  735. }
  736. TS->shaped_text_draw(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_color);
  737. }
  738. }
  739. void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const {
  740. _THREAD_SAFE_METHOD_
  741. Vector2 ofs = p_pos;
  742. float h_offset = 0.f;
  743. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  744. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  745. } else {
  746. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  747. }
  748. if (h_offset > 0) {
  749. // Draw dropcap.
  750. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  751. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  752. ofs.x += width - h_offset;
  753. } else {
  754. ofs.y += width - h_offset;
  755. }
  756. }
  757. TS->shaped_text_draw_outline(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_color);
  758. }
  759. }
  760. void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const {
  761. _THREAD_SAFE_METHOD_
  762. const_cast<TextParagraph *>(this)->_shape_lines();
  763. ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
  764. Vector2 ofs = p_pos;
  765. if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
  766. ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]);
  767. } else {
  768. ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]);
  769. }
  770. return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color);
  771. }
  772. void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const {
  773. _THREAD_SAFE_METHOD_
  774. const_cast<TextParagraph *>(this)->_shape_lines();
  775. ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
  776. Vector2 ofs = p_pos;
  777. if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
  778. ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]);
  779. } else {
  780. ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]);
  781. }
  782. return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
  783. }
  784. TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
  785. rid = TS->create_shaped_text(p_direction, p_orientation);
  786. if (p_font.is_valid()) {
  787. TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language);
  788. for (int i = 0; i < TextServer::SPACING_MAX; i++) {
  789. TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i)));
  790. }
  791. }
  792. width = p_width;
  793. }
  794. TextParagraph::TextParagraph() {
  795. rid = TS->create_shaped_text();
  796. dropcap_rid = TS->create_shaped_text();
  797. }
  798. TextParagraph::~TextParagraph() {
  799. for (int i = 0; i < (int)lines_rid.size(); i++) {
  800. TS->free_rid(lines_rid[i]);
  801. }
  802. lines_rid.clear();
  803. TS->free_rid(rid);
  804. TS->free_rid(dropcap_rid);
  805. }