2020-09-03 19:22:16 +08:00
/*************************************************************************/
/* text_line.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-04 04:27:34 +08:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-09-03 19:22:16 +08:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "text_line.h"
void TextLine : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " clear " ) , & TextLine : : clear ) ;
ClassDB : : bind_method ( D_METHOD ( " set_direction " , " direction " ) , & TextLine : : set_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " get_direction " ) , & TextLine : : get_direction ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " direction " , PROPERTY_HINT_ENUM , " Auto,Light-to-right,Right-to-left " ) , " set_direction " , " get_direction " ) ;
ClassDB : : bind_method ( D_METHOD ( " set_orientation " , " orientation " ) , & TextLine : : set_orientation ) ;
ClassDB : : bind_method ( D_METHOD ( " get_orientation " ) , & TextLine : : get_orientation ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " orientation " , PROPERTY_HINT_ENUM , " Horizontal,Orientation " ) , " set_orientation " , " get_orientation " ) ;
ClassDB : : bind_method ( D_METHOD ( " set_preserve_invalid " , " enabled " ) , & TextLine : : set_preserve_invalid ) ;
ClassDB : : bind_method ( D_METHOD ( " get_preserve_invalid " ) , & TextLine : : get_preserve_invalid ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " preserve_invalid " ) , " set_preserve_invalid " , " get_preserve_invalid " ) ;
ClassDB : : bind_method ( D_METHOD ( " set_preserve_control " , " enabled " ) , & TextLine : : set_preserve_control ) ;
ClassDB : : bind_method ( D_METHOD ( " get_preserve_control " ) , & TextLine : : get_preserve_control ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " preserve_control " ) , " set_preserve_control " , " get_preserve_control " ) ;
2021-08-28 05:19:51 +08:00
ClassDB : : bind_method ( D_METHOD ( " set_bidi_override " , " override " ) , & TextLine : : set_bidi_override ) ;
2020-09-03 19:22:16 +08:00
ClassDB : : bind_method ( D_METHOD ( " add_string " , " text " , " fonts " , " size " , " opentype_features " , " language " ) , & TextLine : : add_string , DEFVAL ( Dictionary ( ) ) , DEFVAL ( " " ) ) ;
2021-11-25 10:58:47 +08:00
ClassDB : : bind_method ( D_METHOD ( " add_object " , " key " , " size " , " inline_align " , " length " ) , & TextLine : : add_object , DEFVAL ( INLINE_ALIGNMENT_CENTER ) , DEFVAL ( 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " resize_object " , " key " , " size " , " inline_align " ) , & TextLine : : resize_object , DEFVAL ( INLINE_ALIGNMENT_CENTER ) ) ;
2020-09-03 19:22:16 +08:00
ClassDB : : bind_method ( D_METHOD ( " set_width " , " width " ) , & TextLine : : set_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_width " ) , & TextLine : : get_width ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " width " ) , " set_width " , " get_width " ) ;
2021-11-25 10:58:47 +08:00
ClassDB : : bind_method ( D_METHOD ( " set_horizontal_alignment " , " alignment " ) , & TextLine : : set_horizontal_alignment ) ;
ClassDB : : bind_method ( D_METHOD ( " get_horizontal_alignment " ) , & TextLine : : get_horizontal_alignment ) ;
2020-09-03 19:22:16 +08:00
2021-11-25 10:58:47 +08:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " alignment " , PROPERTY_HINT_ENUM , " Left,Center,Right,Fill " ) , " set_horizontal_alignment " , " get_horizontal_alignment " ) ;
2020-09-03 19:22:16 +08:00
ClassDB : : bind_method ( D_METHOD ( " tab_align " , " tab_stops " ) , & TextLine : : tab_align ) ;
ClassDB : : bind_method ( D_METHOD ( " set_flags " , " flags " ) , & TextLine : : set_flags ) ;
ClassDB : : bind_method ( D_METHOD ( " get_flags " ) , & TextLine : : get_flags ) ;
2021-05-22 10:30:58 +08:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " flags " , PROPERTY_HINT_FLAGS , " Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab " ) , " set_flags " , " get_flags " ) ;
2020-09-03 19:22:16 +08:00
2021-08-11 06:09:48 +08:00
ClassDB : : bind_method ( D_METHOD ( " set_text_overrun_behavior " , " overrun_behavior " ) , & TextLine : : set_text_overrun_behavior ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text_overrun_behavior " ) , & TextLine : : get_text_overrun_behavior ) ;
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 " ) ;
2020-09-03 19:22:16 +08:00
ClassDB : : bind_method ( D_METHOD ( " get_objects " ) , & TextLine : : get_objects ) ;
ClassDB : : bind_method ( D_METHOD ( " get_object_rect " , " key " ) , & TextLine : : get_object_rect ) ;
ClassDB : : bind_method ( D_METHOD ( " get_size " ) , & TextLine : : get_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_rid " ) , & TextLine : : get_rid ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_ascent " ) , & TextLine : : get_line_ascent ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_descent " ) , & TextLine : : get_line_descent ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_width " ) , & TextLine : : get_line_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_underline_position " ) , & TextLine : : get_line_underline_position ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_underline_thickness " ) , & TextLine : : get_line_underline_thickness ) ;
ClassDB : : bind_method ( D_METHOD ( " draw " , " canvas " , " pos " , " color " ) , & TextLine : : draw , DEFVAL ( Color ( 1 , 1 , 1 ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " draw_outline " , " canvas " , " pos " , " outline_size " , " color " ) , & TextLine : : draw_outline , DEFVAL ( 1 ) , DEFVAL ( Color ( 1 , 1 , 1 ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " hit_test " , " coords " ) , & TextLine : : hit_test ) ;
2021-08-11 06:09:48 +08:00
BIND_ENUM_CONSTANT ( OVERRUN_NO_TRIMMING ) ;
BIND_ENUM_CONSTANT ( OVERRUN_TRIM_CHAR ) ;
BIND_ENUM_CONSTANT ( OVERRUN_TRIM_WORD ) ;
BIND_ENUM_CONSTANT ( OVERRUN_TRIM_ELLIPSIS ) ;
BIND_ENUM_CONSTANT ( OVERRUN_TRIM_WORD_ELLIPSIS ) ;
2020-09-03 19:22:16 +08:00
}
void TextLine : : _shape ( ) {
if ( dirty ) {
2020-12-15 20:04:21 +08:00
if ( ! tab_stops . is_empty ( ) ) {
2020-09-03 19:22:16 +08:00
TS - > shaped_text_tab_align ( rid , tab_stops ) ;
}
2021-08-11 06:09:48 +08:00
2021-08-28 05:19:51 +08:00
uint16_t overrun_flags = TextServer : : OVERRUN_NO_TRIMMING ;
2021-08-11 06:09:48 +08:00
if ( overrun_behavior ! = OVERRUN_NO_TRIMMING ) {
switch ( overrun_behavior ) {
case OVERRUN_TRIM_WORD_ELLIPSIS :
overrun_flags | = TextServer : : OVERRUN_TRIM ;
overrun_flags | = TextServer : : OVERRUN_TRIM_WORD_ONLY ;
overrun_flags | = TextServer : : OVERRUN_ADD_ELLIPSIS ;
break ;
case OVERRUN_TRIM_ELLIPSIS :
overrun_flags | = TextServer : : OVERRUN_TRIM ;
overrun_flags | = TextServer : : OVERRUN_ADD_ELLIPSIS ;
break ;
case OVERRUN_TRIM_WORD :
overrun_flags | = TextServer : : OVERRUN_TRIM ;
overrun_flags | = TextServer : : OVERRUN_TRIM_WORD_ONLY ;
break ;
case OVERRUN_TRIM_CHAR :
overrun_flags | = TextServer : : OVERRUN_TRIM ;
break ;
case OVERRUN_NO_TRIMMING :
break ;
}
2021-11-25 10:58:47 +08:00
if ( alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
2021-08-11 06:09:48 +08:00
TS - > shaped_text_fit_to_width ( rid , width , flags ) ;
overrun_flags | = TextServer : : OVERRUN_JUSTIFICATION_AWARE ;
TS - > shaped_text_overrun_trim_to_width ( rid , width , overrun_flags ) ;
} else {
TS - > shaped_text_overrun_trim_to_width ( rid , width , overrun_flags ) ;
}
2021-11-25 10:58:47 +08:00
} else if ( alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
2020-09-03 19:22:16 +08:00
TS - > shaped_text_fit_to_width ( rid , width , flags ) ;
}
dirty = false ;
}
}
RID TextLine : : get_rid ( ) const {
return rid ;
}
void TextLine : : clear ( ) {
TS - > shaped_text_clear ( rid ) ;
2020-11-30 17:48:42 +08:00
spacing_top = 0 ;
spacing_bottom = 0 ;
2020-09-03 19:22:16 +08:00
}
void TextLine : : set_preserve_invalid ( bool p_enabled ) {
TS - > shaped_text_set_preserve_invalid ( rid , p_enabled ) ;
dirty = true ;
}
bool TextLine : : get_preserve_invalid ( ) const {
return TS - > shaped_text_get_preserve_invalid ( rid ) ;
}
void TextLine : : set_preserve_control ( bool p_enabled ) {
TS - > shaped_text_set_preserve_control ( rid , p_enabled ) ;
dirty = true ;
}
bool TextLine : : get_preserve_control ( ) const {
return TS - > shaped_text_get_preserve_control ( rid ) ;
}
void TextLine : : set_direction ( TextServer : : Direction p_direction ) {
TS - > shaped_text_set_direction ( rid , p_direction ) ;
dirty = true ;
}
TextServer : : Direction TextLine : : get_direction ( ) const {
return TS - > shaped_text_get_direction ( rid ) ;
}
void TextLine : : set_orientation ( TextServer : : Orientation p_orientation ) {
TS - > shaped_text_set_orientation ( rid , p_orientation ) ;
dirty = true ;
}
TextServer : : Orientation TextLine : : get_orientation ( ) const {
return TS - > shaped_text_get_orientation ( rid ) ;
}
2021-08-28 05:19:51 +08:00
void TextLine : : set_bidi_override ( const Array & p_override ) {
2020-09-03 19:22:16 +08:00
TS - > shaped_text_set_bidi_override ( rid , p_override ) ;
dirty = true ;
}
bool TextLine : : add_string ( const String & p_text , const Ref < Font > & p_fonts , int p_size , const Dictionary & p_opentype_features , const String & p_language ) {
2021-02-16 04:57:42 +08:00
ERR_FAIL_COND_V ( p_fonts . is_null ( ) , false ) ;
2020-09-03 19:22:16 +08:00
bool res = TS - > shaped_text_add_string ( rid , p_text , p_fonts - > get_rids ( ) , p_size , p_opentype_features , p_language ) ;
2020-12-27 21:30:33 +08:00
spacing_top = p_fonts - > get_spacing ( TextServer : : SPACING_TOP ) ;
spacing_bottom = p_fonts - > get_spacing ( TextServer : : SPACING_BOTTOM ) ;
2020-09-03 19:22:16 +08:00
dirty = true ;
return res ;
}
2021-11-25 10:58:47 +08:00
bool TextLine : : add_object ( Variant p_key , const Size2 & p_size , InlineAlignment p_inline_align , int p_length ) {
2020-09-03 19:22:16 +08:00
bool res = TS - > shaped_text_add_object ( rid , p_key , p_size , p_inline_align , p_length ) ;
dirty = true ;
return res ;
}
2021-11-25 10:58:47 +08:00
bool TextLine : : resize_object ( Variant p_key , const Size2 & p_size , InlineAlignment p_inline_align ) {
2020-09-03 19:22:16 +08:00
const_cast < TextLine * > ( this ) - > _shape ( ) ;
return TS - > shaped_text_resize_object ( rid , p_key , p_size , p_inline_align ) ;
}
Array TextLine : : get_objects ( ) const {
return TS - > shaped_text_get_objects ( rid ) ;
}
Rect2 TextLine : : get_object_rect ( Variant p_key ) const {
return TS - > shaped_text_get_object_rect ( rid , p_key ) ;
}
2021-11-25 10:58:47 +08:00
void TextLine : : set_horizontal_alignment ( HorizontalAlignment p_alignment ) {
if ( alignment ! = p_alignment ) {
if ( alignment = = HORIZONTAL_ALIGNMENT_FILL | | p_alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
alignment = p_alignment ;
2020-09-03 19:22:16 +08:00
dirty = true ;
} else {
2021-11-25 10:58:47 +08:00
alignment = p_alignment ;
2020-09-03 19:22:16 +08:00
}
}
}
2021-11-25 10:58:47 +08:00
HorizontalAlignment TextLine : : get_horizontal_alignment ( ) const {
return alignment ;
2020-09-03 19:22:16 +08:00
}
void TextLine : : tab_align ( const Vector < float > & p_tab_stops ) {
tab_stops = p_tab_stops ;
dirty = true ;
}
2021-08-28 05:19:51 +08:00
void TextLine : : set_flags ( uint16_t p_flags ) {
2020-09-03 19:22:16 +08:00
if ( flags ! = p_flags ) {
flags = p_flags ;
dirty = true ;
}
}
2021-08-28 05:19:51 +08:00
uint16_t TextLine : : get_flags ( ) const {
2020-09-03 19:22:16 +08:00
return flags ;
}
2021-08-11 06:09:48 +08:00
void TextLine : : set_text_overrun_behavior ( TextLine : : OverrunBehavior p_behavior ) {
if ( overrun_behavior ! = p_behavior ) {
overrun_behavior = p_behavior ;
dirty = true ;
}
}
TextLine : : OverrunBehavior TextLine : : get_text_overrun_behavior ( ) const {
return overrun_behavior ;
}
2020-09-03 19:22:16 +08:00
void TextLine : : set_width ( float p_width ) {
width = p_width ;
2021-11-25 10:58:47 +08:00
if ( alignment = = HORIZONTAL_ALIGNMENT_FILL | | overrun_behavior ! = OVERRUN_NO_TRIMMING ) {
2020-09-03 19:22:16 +08:00
dirty = true ;
}
}
float TextLine : : get_width ( ) const {
return width ;
}
Size2 TextLine : : get_size ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
2020-11-30 17:48:42 +08:00
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
return Size2 ( TS - > shaped_text_get_size ( rid ) . x , TS - > shaped_text_get_size ( rid ) . y + spacing_top + spacing_bottom ) ;
} else {
return Size2 ( TS - > shaped_text_get_size ( rid ) . x + spacing_top + spacing_bottom , TS - > shaped_text_get_size ( rid ) . y ) ;
}
2020-09-03 19:22:16 +08:00
}
float TextLine : : get_line_ascent ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
2020-11-30 17:48:42 +08:00
return TS - > shaped_text_get_ascent ( rid ) + spacing_top ;
2020-09-03 19:22:16 +08:00
}
float TextLine : : get_line_descent ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
2020-11-30 17:48:42 +08:00
return TS - > shaped_text_get_descent ( rid ) + spacing_bottom ;
2020-09-03 19:22:16 +08:00
}
float TextLine : : get_line_width ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
return TS - > shaped_text_get_width ( rid ) ;
}
float TextLine : : get_line_underline_position ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
return TS - > shaped_text_get_underline_position ( rid ) ;
}
float TextLine : : get_line_underline_thickness ( ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
return TS - > shaped_text_get_underline_thickness ( rid ) ;
}
void TextLine : : draw ( RID p_canvas , const Vector2 & p_pos , const Color & p_color ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
Vector2 ofs = p_pos ;
float length = TS - > shaped_text_get_width ( rid ) ;
if ( width > 0 ) {
2021-11-25 10:58:47 +08:00
switch ( alignment ) {
case HORIZONTAL_ALIGNMENT_FILL :
case HORIZONTAL_ALIGNMENT_LEFT :
2020-09-03 19:22:16 +08:00
break ;
2021-11-25 10:58:47 +08:00
case HORIZONTAL_ALIGNMENT_CENTER : {
2020-09-03 19:22:16 +08:00
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
ofs . x + = Math : : floor ( ( width - length ) / 2.0 ) ;
} else {
ofs . y + = Math : : floor ( ( width - length ) / 2.0 ) ;
}
} break ;
2021-11-25 10:58:47 +08:00
case HORIZONTAL_ALIGNMENT_RIGHT : {
2020-09-03 19:22:16 +08:00
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
ofs . x + = width - length ;
} else {
ofs . y + = width - length ;
}
} break ;
}
}
float clip_l ;
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
2020-11-30 17:48:42 +08:00
ofs . y + = TS - > shaped_text_get_ascent ( rid ) + spacing_top ;
2020-09-03 19:22:16 +08:00
clip_l = MAX ( 0 , p_pos . x - ofs . x ) ;
} else {
2020-11-30 17:48:42 +08:00
ofs . x + = TS - > shaped_text_get_ascent ( rid ) + spacing_top ;
2020-09-03 19:22:16 +08:00
clip_l = MAX ( 0 , p_pos . y - ofs . y ) ;
}
return TS - > shaped_text_draw ( rid , p_canvas , ofs , clip_l , clip_l + width , p_color ) ;
}
void TextLine : : draw_outline ( RID p_canvas , const Vector2 & p_pos , int p_outline_size , const Color & p_color ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
Vector2 ofs = p_pos ;
float length = TS - > shaped_text_get_width ( rid ) ;
if ( width > 0 ) {
2021-11-25 10:58:47 +08:00
switch ( alignment ) {
case HORIZONTAL_ALIGNMENT_FILL :
case HORIZONTAL_ALIGNMENT_LEFT :
2020-09-03 19:22:16 +08:00
break ;
2021-11-25 10:58:47 +08:00
case HORIZONTAL_ALIGNMENT_CENTER : {
2020-09-03 19:22:16 +08:00
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
ofs . x + = Math : : floor ( ( width - length ) / 2.0 ) ;
} else {
ofs . y + = Math : : floor ( ( width - length ) / 2.0 ) ;
}
} break ;
2021-11-25 10:58:47 +08:00
case HORIZONTAL_ALIGNMENT_RIGHT : {
2020-09-03 19:22:16 +08:00
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
ofs . x + = width - length ;
} else {
ofs . y + = width - length ;
}
} break ;
}
}
float clip_l ;
if ( TS - > shaped_text_get_orientation ( rid ) = = TextServer : : ORIENTATION_HORIZONTAL ) {
2020-11-30 17:48:42 +08:00
ofs . y + = TS - > shaped_text_get_ascent ( rid ) + spacing_top ;
2020-09-03 19:22:16 +08:00
clip_l = MAX ( 0 , p_pos . x - ofs . x ) ;
} else {
2020-11-30 17:48:42 +08:00
ofs . x + = TS - > shaped_text_get_ascent ( rid ) + spacing_top ;
2020-09-03 19:22:16 +08:00
clip_l = MAX ( 0 , p_pos . y - ofs . y ) ;
}
return TS - > shaped_text_draw_outline ( rid , p_canvas , ofs , clip_l , clip_l + width , p_outline_size , p_color ) ;
}
int TextLine : : hit_test ( float p_coords ) const {
const_cast < TextLine * > ( this ) - > _shape ( ) ;
return TS - > shaped_text_hit_test_position ( rid , p_coords ) ;
}
TextLine : : TextLine ( const String & p_text , const Ref < Font > & p_fonts , int p_size , const Dictionary & p_opentype_features , const String & p_language , TextServer : : Direction p_direction , TextServer : : Orientation p_orientation ) {
rid = TS - > create_shaped_text ( p_direction , p_orientation ) ;
2020-12-27 21:30:33 +08:00
spacing_top = p_fonts - > get_spacing ( TextServer : : SPACING_TOP ) ;
spacing_bottom = p_fonts - > get_spacing ( TextServer : : SPACING_BOTTOM ) ;
2020-09-03 19:22:16 +08:00
TS - > shaped_text_add_string ( rid , p_text , p_fonts - > get_rids ( ) , p_size , p_opentype_features , p_language ) ;
}
TextLine : : TextLine ( ) {
rid = TS - > create_shaped_text ( ) ;
}
TextLine : : ~ TextLine ( ) {
TS - > free ( rid ) ;
}