/** * @internal * @subsection Evas_Object_Textblock_Internal Internal Textblock Object Tutorial * * This explains the internal design of the Evas Textblock Object, it's assumed * that the reader of this section has already read @ref Evas_Object_Textblock_Tutorial "Textblock's usage docs.". * * @subsection textblock_internal_intro Introduction * There are two main parts to the textblock object, the first being the node * system, and the second being the layout system. The former is just an * internal representation of the markup text, while the latter is the internal * visual representation of the text (i.e positioning, sizing, fonts and etc). * * @subsection textblock_nodes The Nodes system * The nodes mechanism consists of two main data types: * ::Evas_Object_Textblock_Node_Text and ::Evas_Object_Textblock_Node_Format * the former is for Text nodes and the latter is for format nodes. * There's always at least one text node, even if there are only formats. * * @subsection textblock_nodes_text Text nodes * Each text node is essentially a paragraph, it includes an @ref Eina_UStrbuf * that stores the actual paragraph text, a utf8 string to store the paragraph * text in utf8 (which is not used internally at all), A pointer to it's * main @ref textblock_nodes_format_internal "Format Node" and the paragraph's * @ref evas_bidi_props "BiDi properties". The pointer to the format node may be * NULL if there's no format node anywhere before the end of the text node, * not even in previous text nodes. If not NULL, it points to the first format * node pointing to text inside of the text node, or if there is none, it points * to the previous's text nodes format node. Each paragraph has a format node * representing a paragraph separator pointing to it's last position except * for the last paragraph, which has no such constraint. This constraint * happens because text nodes are paragraphs and paragraphs are delimited by * paragraph separators. * * @subsection textblock_nodes_format_internal Format Nodes - Internal * Each format node stores a group of format information, for example the * markup: \ will all be inserted * inside the same format node, although it consists of different formatting * commands. * Each node has a pointer to it's text node, this pointer is NEVER NULL, even * if there's only one format, and no text, a text node is created. Each format * node includes an offset from the last format node of the same text node. For * example, the markup "012" will create two format nodes, the first * having an offset of 1 and the second an offset of 2. Each format node also * includes a @ref Eina_Strbuf that includes the textual representation of the * format, and a boolean stating if the format is a visible format or not, see * @ref textblock_nodes_format_visible * * @subsection textblock_nodes_format_visible Visible Format Nodes * There are two types of format nodes, visible and invisible. They are the same * in every way, except for the representation in the text node. While invisible * format nodes have no representation in the text node, the visible ones do. * The Uniceode object replacement character (0xFFFC) is inserted to every place * a visible format node points to. This makes it very easy to treat visible * formats as items in the text, both for BiDi purposes and cursor handling * purposes. * Here are a few example visible an invisible formats: * Visible: newline char, tab, paragraph separator and an embedded item. * Invisible: setting the color, font or alignment of the text. * * @subsection textblock_layout The layout system * @todo write @ref textblock_layout */ #define EFL_CANVAS_FILTER_INTERNAL_PROTECTED #include "evas_common_private.h" #include "evas_private.h" //#define LYDBG(f, args...) printf(f, ##args) #define LYDBG(f, args...) #define MY_CLASS EFL_CANVAS_TEXT_CLASS #define MY_CLASS_NAME "Efl Canvas Text" #include "linebreak.h" #include "wordbreak.h" #include "graphemebreak.h" #include "evas_filter.h" #include "efl_canvas_filter_internal.eo.h" /* private magic number for textblock objects */ static const char o_type[] = "textblock"; /* The char to be inserted instead of visible formats */ #define _REPLACEMENT_CHAR 0xFFFC #define _PARAGRAPH_SEPARATOR 0x2029 #define _NEWLINE '\n' #define _TAB '\t' #define _REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBC" #define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9" #define _NEWLINE_UTF8 "\n" #define _TAB_UTF8 "\t" #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \ (((ch) == _REPLACEMENT_CHAR) || \ ((ch) == _NEWLINE) || \ ((ch) == _TAB) || \ ((ch) == _PARAGRAPH_SEPARATOR)) #ifdef CRI #undef CRI #endif #define CRI(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef ERR #undef ERR #endif #define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef WRN #undef WRN #endif #define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef INF #undef INF #endif #define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef DBG #undef DBG #endif #define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #define TB_NULL_CHECK(null_check, ...) \ do \ { \ if (!null_check) \ { \ ERR("%s is NULL while it shouldn't be, please notify developers.", #null_check); \ return __VA_ARGS__; \ } \ } \ while(0) // testing out some macros to maybe add to eina #define EINA_INLIST_REMOVE(l,i) do { l = (__typeof__(l)) eina_inlist_remove(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) #define EINA_INLIST_APPEND(l,i) do { l = (__typeof__(l)) eina_inlist_append(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) /* private struct for textblock object internal data */ /** * @internal * @typedef Efl_Canvas_Text_Data * The actual textblock object. */ typedef struct _Evas_Object_Textblock Efl_Canvas_Text_Data; /** * @internal * @typedef Evas_Object_Style_Tag * The structure used for finding style tags. */ typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag; /** * @internal * @typedef Evas_Object_Style_Tag * The structure used for finding style tags. */ typedef struct _Evas_Object_Style_Tag_Base Evas_Object_Style_Tag_Base; /** * @internal * @typedef Evas_Object_Textblock_Node_Text * A text node. */ typedef struct _Evas_Object_Textblock_Node_Text Evas_Object_Textblock_Node_Text; /* * Defined in Evas.h typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format; */ typedef struct _Evas_Textblock_Node_Format Evas_Textblock_Node_Format; /** * @internal * @typedef Evas_Object_Textblock_Paragraph * A layouting paragraph. */ typedef struct _Evas_Object_Textblock_Paragraph Evas_Object_Textblock_Paragraph; /** * @internal * @typedef Evas_Object_Textblock_Line * A layouting line. */ typedef struct _Evas_Object_Textblock_Line Evas_Object_Textblock_Line; /** * @internal * @typedef Evas_Object_Textblock_Item * A layouting item. */ typedef struct _Evas_Object_Textblock_Item Evas_Object_Textblock_Item; /** * @internal * @typedef Evas_Object_Textblock_Item * A layouting text item. */ typedef struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Text_Item; /** * @internal * @typedef Evas_Object_Textblock_Format_Item * A layouting format item. */ typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item; /** * @internal * @typedef Evas_Object_Textblock_Format * A textblock format. */ typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format; /** * @internal * @typedef Evas_Textblock_Selection_Iterator * A textblock selection iterator. */ typedef struct _Evas_Textblock_Selection_Iterator Evas_Textblock_Selection_Iterator; /** * @internal * @typedef Efl_Text_Annotate_Annotation_Iterator * A textblock annotation iterator. */ typedef struct _Efl_Text_Annotate_Annotation_Iterator Efl_Text_Annotate_Annotation_Iterator; /** * @internal * @typedef Efl_Canvas_Text_Filter * A structure holding gfx filter information for a text item */ typedef struct _Efl_Canvas_Text_Filter Efl_Canvas_Text_Filter; /** * @internal * @typedef Efl_Canvas_Text_Filter_Post_Render * Post-render data for async rendering of gfx filters */ typedef struct _Efl_Canvas_Text_Filter_Post_Render Efl_Canvas_Text_Filter_Post_Render; /** * @internal * @typedef Efl_Canvas_Text_Filter_Program * Filter name - code database */ typedef struct _Efl_Canvas_Text_Filter_Program Efl_Canvas_Text_Filter_Program; /** * @internal * @typedef Text_Item_Filter * Text item filter properties (run context, output buffer, ...) */ typedef struct _Text_Item_Filter Text_Item_Filter; /** * @internal * @def IS_AT_END(ti, ind) * Return true if ind is at the end of the text item, false otherwise. */ #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len) /** * @internal * @def MOVE_PREV_UNTIL(limit, ind) * This decrements ind as long as ind > limit. */ #define MOVE_PREV_UNTIL(limit, ind) \ do \ { \ if ((limit) < (ind)) \ (ind)--; \ } \ while (0) /** * @internal * @def MOVE_NEXT_UNTIL(limit, ind) * This increments ind as long as ind < limit */ #define MOVE_NEXT_UNTIL(limit, ind) \ do \ { \ if ((ind) < (limit)) \ (ind)++; \ } \ while (0) /** * @internal * @def GET_ITEM_LEN(it) * Returns length of item (Format or Text) */ #define GET_ITEM_LEN(it) \ (((it)->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? \ _ITEM_TEXT(it)->text_props.text_len : 1) /** * @internal * @def GET_ITEM_TEXT(ti) * Returns a const reference to the text of the ti (not null terminated). */ #define GET_ITEM_TEXT(ti) \ (((ti)->parent.text_node) ? \ (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \ (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING) /** * @internal * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len) * Returns true if closer is the closer of base. */ #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \ (!strncmp(base, closer, closer_len) && \ (!base[closer_len] || \ (base[closer_len] == '=') || \ _is_white(base[closer_len]))) struct _Evas_Object_Style_Tag_Base { const char *tag; /**< Format Identifier: b=Bold, i=Italic etc. */ const char *replace; /**< Replacement string. "font_weight=Bold", "font_style=Italic" etc. */ size_t tag_len; /**< Strlen of tag. */ }; struct _Evas_Object_Style_Tag { EINA_INLIST; Evas_Object_Style_Tag_Base tag; /**< Base style object for holding style information. */ }; struct _Evas_Object_Textblock_Node_Text { EINA_INLIST; Eina_UStrbuf *unicode; /**< Actual paragraph text. */ char *utf8; /**< Text in utf8 format. */ Evas_Object_Textblock_Node_Format *format_node; /**< Points to the last format node before the paragraph, or if there is none, to the first format node within the paragraph.*/ Evas_Object_Textblock_Paragraph *par; /**< Points to the paragraph node of which this node is a part. */ Eina_Bool dirty : 1; /**< EINA_TRUE if already handled/format changed, else EINA_FALSE. */ Eina_Bool is_new : 1; /**< EINA_TRUE if its a new paragraph, else EINA_FALSE. */ }; struct _Evas_Textblock_Node_Format { EINA_INLIST; const char *format; /**< Cached, parsed and translated version of orig_format. */ const char *orig_format; /**< Original format information. */ Evas_Object_Textblock_Node_Text *text_node; /**< The text node it's pointing to. */ Efl_Text_Annotate_Annotation *annotation; /**< Pointer to this node's annotation handle (if exists). */ size_t offset; /**< Offset from the last format node of the same text. */ struct { unsigned char l, r, t, b; } pad; /**< Amount of padding required. */ unsigned char anchor : 2; /**< ANCHOR_NONE, ANCHOR_A or ANCHOR_ITEM. */ Eina_Bool opener : 1; /**< EINA_TRUE if opener, else EINA_FALSE. */ Eina_Bool own_closer : 1; /**< EINA_TRUE if own_closer, else EINA_FALSE. */ Eina_Bool visible : 1; /**< EINA_TRUE if format is visible format, else EINA_FALSE. */ Eina_Bool format_change : 1; /**< EINA_TRUE if the format of the textblock has changed, else EINA_FALSE. */ Eina_Bool is_new : 1; /**< EINA_TRUE if its a new format node, else EINA_FALSE */ }; /* The default tags to use */ static const Evas_Object_Style_Tag_Base default_tags[] = { { "b", "+ font_weight=Bold", 1 }, { "i", "+ font_style=Italic", 1 }}; #define ANCHOR_NONE 0 #define ANCHOR_A 1 #define ANCHOR_ITEM 2 /** * @internal * @def _NODE_TEXT(x) * A convenience macro for casting to a text node. */ #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x)) /** * @internal * @def _NODE_FORMAT(x) * A convenience macro for casting to a format node. */ #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x)) /** * @internal * @def _ITEM(x) * A convenience macro for casting to a generic item. */ #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x)) /** * @internal * @def _ITEM_TEXT(x) * A convenience macro for casting to a text item. */ #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x)) /** * @internal * @def _ITEM_FORMAT(x) * A convenience macro for casting to a format item. */ #define _ITEM_FORMAT(x) ((Evas_Object_Textblock_Format_Item *) (x)) struct _Evas_Object_Textblock_Paragraph { EINA_INLIST; Evas_Object_Textblock_Line *lines; /**< Points to the first line of this paragraph. */ Evas_Object_Textblock_Node_Text *text_node; /**< Points to the first text node of this paragraph. */ Eina_List *logical_items; /**< Logical items are the properties of this paragraph, like width, height etc. */ Evas_BiDi_Paragraph_Props *bidi_props; /**< Only valid during layout. */ Evas_BiDi_Direction direction; /**< Bidi direction enum value. The display direction like right to left.*/ Evas_Coord y, w, h; /**< Text block co-ordinates. y co-ord, width and height. */ Evas_Coord last_fw; /**< Last calculated formatted width */ int line_no; /**< Line no of the text block. */ Eina_Bool is_bidi : 1; /**< EINA_TRUE if this is BiDi Paragraph, else EINA_FALSE. */ Eina_Bool visible : 1; /**< EINA_TRUE if paragraph visible, else EINA_FALSE. */ Eina_Bool rendered : 1; /**< EINA_TRUE if paragraph rendered, else EINA_FALSE. */ }; struct _Evas_Object_Textblock_Line { EINA_INLIST; Evas_Object_Textblock_Item *items; /**< Pointer to layouting text item. Contains actual text and information about its display. */ Evas_Object_Textblock_Paragraph *par; /**< Points to the paragraph of which this line is a part. */ Evas_Coord x, y, w, h; /**< Text block line co-ordinates. */ int baseline; /**< Baseline of the textblock. */ int line_no; /**< Line no of this line. */ }; typedef enum _Evas_Textblock_Item_Type { EVAS_TEXTBLOCK_ITEM_TEXT, EVAS_TEXTBLOCK_ITEM_FORMAT, } Evas_Textblock_Item_Type; typedef enum _Evas_Textblock_Align_Auto { EVAS_TEXTBLOCK_ALIGN_AUTO_NONE, EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL, EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE, EVAS_TEXTBLOCK_ALIGN_AUTO_END } Evas_Textblock_Align_Auto; struct _Evas_Object_Textblock_Item { EINA_INLIST; Evas_Object_Textblock_Node_Text *text_node; /**< Pointer to textblock node text. It contains actual text in unicode and utf8 format. */ Evas_Object_Textblock_Format *format; /**< Pointer to textblock format. It contains all the formatting information for this text block. */ Evas_Object_Textblock_Line *ln; /**< Pointer to textblock line. It contains the co-ord, baseline, and line no for this item. */ size_t text_pos; /**< Position of this item in textblock line. */ #ifdef BIDI_SUPPORT size_t visual_pos; /**< Visual position of this item. */ #endif Evas_Textblock_Item_Type type; /**< EVAS_TEXTBLOCK_ITEM_TEXT or EVAS_TEXTBLOCK_ITEM_FORMAT */ Evas_Coord adv, x, w, h; /**< Item co-ordinates. Advancement to be made, x co-ord, width and height. */ Evas_Coord yoff; /**< y offset. */ Eina_Bool merge : 1; /**< Indicates whether this item should merge to the previous item or not */ Eina_Bool visually_deleted : 1; /**< Indicates whether this item is used in the visual layout or not. */ }; struct _Evas_Object_Textblock_Text_Item { Evas_Object_Textblock_Item parent; /**< Textblock item. */ Evas_Text_Props text_props; /**< Props for this item. */ Evas_Coord x_adjustment; /**< Used to indicate by how much we adjusted sizes */ Text_Item_Filter *gfx_filter; }; struct _Evas_Object_Textblock_Format_Item { Evas_Object_Textblock_Item parent; /**< Textblock item. */ Evas_BiDi_Direction bidi_dir; /**< Bidi text direction. */ const char *item; /**< Pointer to item contents. */ int y; /**< Co-ordinate of item. */ unsigned char vsize : 2; /**< VSIZE_FULL or VSIZE_ASCENT */ unsigned char size : 2; /**< SIZE, SIZE_ABS or SIZE_REL*/ Eina_Bool formatme : 1; /**< EINA_TRUE if format required, else EINA_FALSE */ }; struct _Text_Item_Filter { EINA_INLIST; /**< list on the tb object */ Efl_Canvas_Text_Data *textblock; Evas_Object_Textblock_Text_Item *ti; /**< associated text item. if null, it was deleted */ Evas_Filter_Context *ctx; /**< running context for the filter */ Evas_Public_Data *evas; /**< evas instance */ void *output; /**< output rgba buffer for this text item (engine image) */ Eina_Bool do_async; /**< do_async flag when running the filter */ }; struct _Efl_Canvas_Text_Filter { Eina_Stringshare *name; Evas_Object *eo_obj; Evas_Public_Data *evas; void *dc; /* draw context - no clip, white, no colmul... */ Evas_Filter_Padding pad; Eina_Bool invalid; Eina_Bool redraw; }; struct _Efl_Canvas_Text_Filter_Post_Render { Evas_Filter_Context *ctx; Eina_Bool success; }; struct _Efl_Canvas_Text_Filter_Program { EINA_INLIST; Eina_Stringshare *name; Eina_Stringshare *code; Evas_Filter_Program *pgm; Eina_Bool changed; }; struct _Evas_Object_Textblock_Format { Evas_Object_Textblock_Node_Format *fnode; /**< Pointer to textblock format node. */ double halign; /**< Horizontal alignment value. */ double valign; /**< Vertical alignment value. */ struct { Evas_Font_Description *fdesc; /**< Pointer to font description. */ const char *source; /**< Pointer to object from which to search for the font. */ Evas_Font_Set *font; /**< Pointer to font set. */ Evas_Font_Size size; /**< Size of the font. */ Efl_Text_Font_Bitmap_Scalable bitmap_scalable; /**< Scalable for bitmap font. */ } font; struct { struct { unsigned char r, g, b, a; } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing, strikethrough; } color; struct { int l, r; } margin; /**< Left and right margin width. */ Efl_Canvas_Text_Filter *gfx_filter; /**< Gfx Filter to apply to the children text items */ int ref; /**< Value of the ref. */ int tabstops; /**< Value of the size of the tab character. */ int linesize; /**< Value of the size of the line of the text. */ int linegap; /**< Value to set the line gap in text. */ int underline_dash_width; /**< Valule to set the width of the underline dash. */ int underline_dash_gap; /**< Value to set the gap of the underline dash. */ double underline_height; /**< Value to set the height of the single underline. */ double linerelsize; /**< Value to set the size of line of text. */ double linerelgap; /**< Value for setting line gap. */ double linefill; /**< The value must be a percentage. */ double ellipsis; /**< The value should be a number. Any value smaller than 0.0 or greater than 1.0 disables ellipsis. A value of 0 means ellipsizing the leftmost portion of the text first, 1 on the other hand the rightmost portion. */ unsigned char style; /**< Value from Evas_Text_Style_Type enum. */ Eina_Bool wrap_word : 1; /**< EINA_TRUE if only wraps lines at word boundaries, else EINA_FALSE. */ Eina_Bool wrap_char : 1; /**< EINA_TRUE if wraps at any character, else EINA_FALSE. */ Eina_Bool wrap_mixed : 1; /**< EINA_TRUE if wrap at words if possible, else EINA_FALSE. */ Eina_Bool wrap_hyphenation : 1; /**< EINA_TRUE if wrap at mixed and hyphenate if possible, else EINA_FALSE. */ Eina_Bool underline : 1; /**< EINA_TRUE if a single line under the text, else EINA_FALSE */ Eina_Bool underline2 : 1; /**< EINA_TRUE if two lines under the text, else EINA_FALSE */ Eina_Bool underline_dash : 1; /**< EINA_TRUE if a dashed line under the text, else EINA_FALSE */ Eina_Bool strikethrough : 1; /**< EINA_TRUE if text should be stricked off, else EINA_FALSE */ Eina_Bool backing : 1; /**< EINA_TRUE if enable background color, else EINA_FALSE */ Eina_Bool password : 1; /**< EINA_TRUE if the text is password, else EINA_FALSE */ Evas_Textblock_Align_Auto halign_auto : 2; /**< Auto horizontal align mode */ }; struct _Efl_Canvas_Text_Style { const char *style_text; const char *default_tag; Evas_Object_Style_Tag *tags; Eina_List *objects; Eina_Bool delete_me : 1; Eina_Bool legacy : 1; }; typedef struct _User_Style_Entry { Evas_Textblock_Style *st; const char *key; } User_Style_Entry; struct _Efl_Text_Cursor_Cursor { Evas_Object *obj; size_t pos; Evas_Object_Textblock_Node_Text *node; Eina_Bool changed : 1; }; struct _Efl_Text_Annotate_Annotation { EINA_INLIST; Evas_Object *obj; Evas_Object_Textblock_Node_Format *start_node, *end_node; Eina_Bool is_item : 1; /**< indicates it is an item/object placeholder */ }; #define _FMT(x) (o->default_format.format.x) #define _FMT_INFO(x) (o->default_format.info.x) /* Size of the index array */ #define TEXTBLOCK_PAR_INDEX_SIZE 10 #define ASYNC_BLOCK do { \ if (o->layout_th) \ { \ ecore_thread_wait(o->layout_th, 1); \ }} while(0) #include "Ecore.h" struct _Evas_Object_Textblock { Ecore_Thread *layout_th; int layout_jobs; Evas_Textblock_Style *style; Eina_List *styles; Efl_Text_Cursor_Cursor *cursor; Eina_List *cursors; Evas_Object_Textblock_Node_Text *text_nodes; Evas_Object_Textblock_Node_Format *format_nodes; int num_paragraphs; Evas_Object_Textblock_Paragraph *paragraphs; Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE]; Evas_Object_Textblock_Text_Item *ellip_ti; Eina_List *anchors_a; Eina_List *anchors_item; Eina_List *obstacles; Eina_List *hyphen_items; /* Hyphen items storage to free when clearing lines */ Efl_Text_Annotate_Annotation *annotations; /* All currently applied annotations on the text. */ int last_w, last_h; struct { int l, r, t, b; } style_pad; struct { Evas_Object_Textblock_Format format; struct { Eina_Stringshare *font; Evas_Font_Size size; Eina_Stringshare *font_source; Eina_Stringshare *font_fallbacks; Eina_Stringshare *font_lang; Eina_Stringshare *gfx_filter_name; unsigned int font_weight; unsigned int font_slant; unsigned int font_width; Efl_Text_Style_Effect_Type effect; Efl_Text_Style_Shadow_Direction shadow_direction; Efl_Text_Format_Wrap wrap; Efl_Text_Font_Bitmap_Scalable bitmap_scalable; } info; } default_format; double valign; Eina_Stringshare *markup_text; char *utf8; void *engine_data; const char *repch; const char *bidi_delimiters; Evas_BiDi_Direction paragraph_direction : 2; struct { int w, h, oneline_h; Eina_Bool valid : 1; } formatted, native; struct { Efl_Canvas_Text_Filter_Program *programs; Evas_Filter_Data_Binding *data_bindings; Eina_Hash *sources; Text_Item_Filter *text_items; // inlist } gfx_filter; Eina_Bool redraw : 1; Eina_Bool changed : 1; Eina_Bool pause_change : 1; Eina_Bool obstacle_changed : 1; Eina_Bool content_changed : 1; Eina_Bool format_changed : 1; Eina_Bool have_ellipsis : 1; Eina_Bool hyphenating : 1; Eina_Bool legacy_newline : 1; Eina_Bool inherit_paragraph_direction : 1; Eina_Bool changed_paragraph_direction : 1; Eina_Bool multiline : 1; Eina_Bool wrap_changed : 1; Eina_Bool auto_styles : 1; }; struct _Evas_Textblock_Selection_Iterator { Eina_Iterator iterator; /**< Eina Iterator. */ Eina_List *list; /**< Head of list. */ Eina_List *current; /**< Current node in loop. */ }; struct _Efl_Text_Annotate_Annotation_Iterator { Eina_Iterator iterator; /**< Eina Iterator. */ Eina_List *list; /**< Head of list. */ Eina_List *current; /**< Current node in loop. */ }; /* private methods for textblock objects */ static void evas_object_textblock_init(Evas_Object *eo_obj); static void evas_object_textblock_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data, void *engine, void *output, void *context, void *surface, int x, int y, Eina_Bool do_async); static void evas_object_textblock_free(Evas_Object *eo_obj); static void evas_object_textblock_render_pre(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static void evas_object_textblock_render_post(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static Evas_Object_Textblock_Node_Text *_evas_textblock_node_text_new(void); static void *evas_object_textblock_engine_data_get(Evas_Object *eo_obj); static int evas_object_textblock_is_opaque(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static int evas_object_textblock_was_opaque(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static void evas_object_textblock_coords_recalc(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static const Evas_Object_Func object_func = { /* methods (compulsory) */ NULL, evas_object_textblock_render, evas_object_textblock_render_pre, evas_object_textblock_render_post, evas_object_textblock_engine_data_get, /* these are optional. NULL = nothing */ NULL, NULL, evas_object_textblock_is_opaque, evas_object_textblock_was_opaque, NULL, NULL, NULL, /*evas_object_textblock_coords_recalc, <- disable - not useful. */ NULL, NULL, NULL, NULL // render_prepare }; /* the actual api call to add a textblock */ #define TB_HEAD() \ MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \ return; \ MAGIC_CHECK_END(); \ Efl_Canvas_Text_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); #define TB_HEAD_RETURN(x) \ MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \ return (x); \ MAGIC_CHECK_END(); static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Efl_Text_Cursor_Cursor *cur); static void _evas_textblock_node_text_remove(Efl_Canvas_Text_Data *o, Evas_Object_Textblock_Node_Text *n); static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Efl_Text_Cursor_Cursor *cur); static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt); static void _evas_textblock_node_format_remove(Efl_Canvas_Text_Data *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment); static void _evas_textblock_node_format_free(Efl_Canvas_Text_Data *o, Evas_Object_Textblock_Node_Format *n); static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n); static void _evas_textblock_changed(Efl_Canvas_Text_Data *o, Evas_Object *eo_obj); static void _evas_textblock_invalidate_all(Efl_Canvas_Text_Data *o); static void _evas_textblock_cursors_update_offset(const Efl_Text_Cursor_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset); static void _evas_textblock_cursors_set_node(Efl_Canvas_Text_Data *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node); static void _evas_textblock_annotations_clear(Efl_Canvas_Text_Data *o); static void _evas_textblock_annotation_remove(Efl_Canvas_Text_Data *o, Efl_Text_Annotate_Annotation *an, Eina_Bool remove_nodes); static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Cursor *cur); static void _evas_textblock_cursor_at_format_set(Efl_Text_Cursor_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt); static void _evas_textblock_cursor_init(Efl_Text_Cursor_Cursor *cur, const Evas_Object *tb); static Evas_Filter_Program *_format_filter_program_get(Efl_Canvas_Text_Data *o, Evas_Object_Textblock_Format *fmt); static const char *_textblock_format_node_from_style_tag(Efl_Canvas_Text_Data *o, Evas_Object_Textblock_Node_Format *fnode, const char *format, size_t format_len); #ifdef HAVE_HYPHEN /* Hyphenation */ #include "evas_textblock_hyphenation.x" #endif static int _efl_canvas_text_cursor_text_append(Efl_Text_Cursor_Cursor *cur, const char *text); static Eina_Bool _evas_textblock_cursor_format_append(Efl_Text_Cursor_Cursor *cur, const char *format, Evas_Object_Textblock_Node_Format **_fnode, Eina_Bool is_item); EAPI Eina_Bool evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur); static void _evas_textblock_cursor_init(Efl_Text_Cursor_Cursor *cur, const Evas_Object *tb); static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Cursor *cur); static void _find_layout_item_line_match(Evas_Object *eo_obj, Evas_Object_Textblock_Node_Text *n, size_t pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr); static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_at_pos_get(const Efl_Text_Cursor_Cursor *cur); static int _evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Cursor *cur, const char *_text); static void _evas_textblock_cursor_copy(Efl_Text_Cursor_Cursor *dst, const Efl_Text_Cursor_Cursor *src); static void _textblock_style_generic_set(Evas_Object *eo_obj, Evas_Textblock_Style *ts, const char *key); /** selection iterator */ /** * @internal * Returns the value of the current data of list node, * and goes to the next list node. * * @param it the iterator. * @param data the data of the current list node. * @return EINA_FALSE if the current list node does not exists. * Otherwise, returns EINA_TRUE. */ static Eina_Bool _evas_textblock_selection_iterator_next(Evas_Textblock_Selection_Iterator *it, void **data) { if (!it->current) return EINA_FALSE; *data = eina_list_data_get(it->current); it->current = eina_list_next(it->current); return EINA_TRUE; } /** * @internal * Gets the iterator container (Eina_List) which created the iterator. * @param it the iterator. * @return A pointer to Eina_List. */ static Eina_List * _evas_textblock_selection_iterator_get_container(Evas_Textblock_Selection_Iterator *it) { return it->list; } /** * @internal * Frees the iterator container (Eina_List). * @param it the iterator. */ static void _evas_textblock_selection_iterator_free(Evas_Textblock_Selection_Iterator *it) { Evas_Textblock_Rectangle *tr; EINA_LIST_FREE(it->list, tr) free(tr); EINA_MAGIC_SET(&it->iterator, 0); free(it); } /** * @internal * Creates newly allocated iterator associated to a list. * @param list The list. * @return If the memory cannot be allocated, NULL is returned. * Otherwise, a valid iterator is returned. */ Eina_Iterator * _evas_textblock_selection_iterator_new(Eina_List *list) { Evas_Textblock_Selection_Iterator *it; it = calloc(1, sizeof(Evas_Textblock_Selection_Iterator)); if (!it) return NULL; EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); it->list = list; it->current = list; it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT( _evas_textblock_selection_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( _evas_textblock_selection_iterator_get_container); it->iterator.free = FUNC_ITERATOR_FREE( _evas_textblock_selection_iterator_free); return &it->iterator; } /* styles */ /** * @internal * Clears the textblock style passed except for the style_text which is replaced. * @param ts The ts to be cleared. Must not be NULL. * @param style_text the style's text. */ static void _style_replace(Evas_Textblock_Style *ts, const char *style_text) { eina_stringshare_replace(&ts->style_text, style_text); if (ts->default_tag) eina_stringshare_del(ts->default_tag); while (ts->tags) { Evas_Object_Style_Tag *tag; tag = (Evas_Object_Style_Tag *)ts->tags; ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag)); eina_stringshare_del(tag->tag.tag); eina_stringshare_del(tag->tag.replace); free(tag); } ts->default_tag = NULL; ts->tags = NULL; } /** * @internal * Clears the textblock style passed. * @param ts The ts to be cleared. Must not be NULL. */ static void _style_clear(Evas_Textblock_Style *ts) { _style_replace(ts, NULL); } /** * @internal * Searches inside the tags stored in the style for the tag matching s. * @param ts The ts to be cleared. Must not be NULL. * @param s The tag to be matched. * @param tag_len the length of the tag string. * @param[out] replace_len The length of the replacement found. - Must not be NULL. * @return The replacement string found. */ static inline const char * _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len) { Evas_Object_Style_Tag *tag; /* Try the style tags */ if (ts) { EINA_INLIST_FOREACH(ts->tags, tag) { if (tag->tag.tag_len != tag_len) continue; if (!strncmp(tag->tag.tag, s, tag_len)) { return tag->tag.replace; } } } /* Try the default tags */ { size_t i; const Evas_Object_Style_Tag_Base *btag; for (btag = default_tags, i = 0 ; i < (sizeof(default_tags) / sizeof(default_tags[0])) ; btag++, i++) { if (btag->tag_len != tag_len) continue; if (!strncmp(btag->tag, s, tag_len)) { return btag->replace; } } } return NULL; } /** * @internal * Clears all the nodes (text and format) of the textblock object. * @param obj The evas object, must not be NULL. */ static void _nodes_clear(const Evas_Object *eo_obj) { Efl_Canvas_Text_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); /* First, clear all annotations that may have spawned format nodes. */ _evas_textblock_annotations_clear(o); while (o->text_nodes) { Evas_Object_Textblock_Node_Text *n; n = o->text_nodes; o->text_nodes = _NODE_TEXT(eina_inlist_remove( EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n))); _evas_textblock_node_text_free(n); } while (o->format_nodes) { Evas_Object_Textblock_Node_Format *n; n = o->format_nodes; o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n))); _evas_textblock_node_format_free(o, n); } } /** * @internal * Unrefs and frees (if needed) a textblock format. * @param obj The Evas_Object, Must not be NULL. * @param fmt the format to be cleaned, must not be NULL. */ static void _format_unref_free(Evas_Object_Protected_Data *evas_o, Evas_Object_Textblock_Format *fmt) { Evas_Object_Protected_Data *obj = evas_o; fmt->ref--; if (fmt->ref > 0) return; if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc); if (fmt->font.source) eina_stringshare_del(fmt->font.source); if (fmt->gfx_filter) { eina_stringshare_del(fmt->gfx_filter->name); if (fmt->gfx_filter->dc) ENFN->context_free(ENC, fmt->gfx_filter->dc); free(fmt->gfx_filter); fmt->gfx_filter = NULL; } if ((obj->layer) && (obj->layer->evas)) evas_font_free(fmt->font.font); free(fmt); } static inline void _image_safe_unref(Evas_Public_Data *e, void *image, Eina_Bool async) { if (!image) return; if (async) evas_unref_queue_image_put(e, image); else e->engine.func->image_free(_evas_engine_context(e), image); } /** * @internal * Free a layout item * @param obj The evas object, must not be NULL. * @param ln the layout line on which the item is in, must not be NULL. * @param it the layout item to be freed */ static void _item_free(Evas_Public_Data *evas, Efl_Canvas_Text_Data *o, Evas_Object_Protected_Data *evas_o, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it) { if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) { Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it); evas_common_text_props_content_unref(&ti->text_props); if (EINA_UNLIKELY(ti->gfx_filter != NULL)) { if (ti->gfx_filter->output) { //Evas *eo_evas = evas_object_evas_get(eo_obj); //Evas_Public_Data *evas = efl_data_scope_get(eo_evas, EVAS_CANVAS_CLASS); Eina_Bool async = ti->gfx_filter->do_async; _image_safe_unref(evas, ti->gfx_filter->output, async); ti->gfx_filter->output = NULL; } EINA_INLIST_REMOVE(o->gfx_filter.text_items, ti->gfx_filter); if (!ti->gfx_filter->ctx) { free(ti->gfx_filter); ti->gfx_filter = NULL; } else { evas_filter_context_unref(ti->gfx_filter->ctx); ti->gfx_filter->ctx = NULL; ti->gfx_filter->ti = NULL; } } } else { Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it); if (fi->item) eina_stringshare_del(fi->item); } _format_unref_free(evas_o, it->format); if (ln) { ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove( EINA_INLIST_GET(ln->items), EINA_INLIST_GET(it)); } free(it); } /** * @internal * Free a layout line. * @param obj The evas object, must not be NULL. * @param ln the layout line to be freed, must not be NULL. */ static void _line_free(Evas_Object_Textblock_Line *ln) { /* Items are freed from the logical list, except for the ellip item */ if (ln) free(ln); } /* table of html escapes (that i can find) this should be ordered with the * sorted by there escape strings and values as it's a binary search to match - no hash for this. * * these are stored as array of struct of Escape_Value structure (no Runtime sort will happen) */ /** * @internal * @var escape_values_e_sorted[] * This array consists of Escape_Value structure sorted by escape string * And new added value must be placed sorted position, and reflected on escape_values_v_sorted */ typedef struct _Escape_Value Escape_Value; struct _Escape_Value { char *escape; char *value; size_t escape_len; size_t value_len; }; #define ESCAPE_VALUE(e,v) {e,v,strlen(e),strlen(v)} static const Escape_Value escape_values_e_sorted[] = { ESCAPE_VALUE("Á", "\xc3\x81"), ESCAPE_VALUE("Â", "\xc3\x82"), ESCAPE_VALUE("&Aelig;", "\xc3\x86"), ESCAPE_VALUE("À", "\xc3\x80"), ESCAPE_VALUE("Å", "\xc3\x85"), ESCAPE_VALUE("Ã", "\xc3\x83"), ESCAPE_VALUE("Ä", "\xc3\x84"), ESCAPE_VALUE("Ç", "\xc3\x87"), ESCAPE_VALUE("‡", "\xe2\x80\xa1"), ESCAPE_VALUE("É", "\xc3\x89"), ESCAPE_VALUE("Ê", "\xc3\x8a"), ESCAPE_VALUE("È", "\xc3\x88"), ESCAPE_VALUE("&Eth;", "\xc3\x90"), ESCAPE_VALUE("Ë", "\xc3\x8b"), ESCAPE_VALUE("Í", "\xc3\x8d"), ESCAPE_VALUE("Î", "\xc3\x8e"), ESCAPE_VALUE("Ì", "\xc3\x8c"), ESCAPE_VALUE("Ï", "\xc3\x8f"), ESCAPE_VALUE("Ñ", "\xc3\x91"), ESCAPE_VALUE("Ó", "\xc3\x93"), ESCAPE_VALUE("Ô", "\xc3\x94"), ESCAPE_VALUE("Ò", "\xc3\x92"), ESCAPE_VALUE("Ø", "\xc3\x98"), ESCAPE_VALUE("Õ", "\xc3\x95"), ESCAPE_VALUE("Ö", "\xc3\x96"), ESCAPE_VALUE("&Thorn;", "\xc3\x9e"), ESCAPE_VALUE("Ú", "\xc3\x9a"), ESCAPE_VALUE("Û", "\xc3\x9b"), ESCAPE_VALUE("Ù", "\xc3\x99"), ESCAPE_VALUE("Ý", "\xc3\x9d"), ESCAPE_VALUE("á", "\xc3\xa1"), ESCAPE_VALUE("â", "\xc3\xa2"), ESCAPE_VALUE("´", "\xc2\xb4"), ESCAPE_VALUE("æ", "\xc3\xa6"), ESCAPE_VALUE("à", "\xc3\xa0"), ESCAPE_VALUE("α", "\xce\x91"), ESCAPE_VALUE("∧", "\xe2\x88\xa7"), ESCAPE_VALUE("å", "\xc3\xa5"), ESCAPE_VALUE("ã", "\xc3\xa3"), ESCAPE_VALUE("ä", "\xc3\xa4"), ESCAPE_VALUE("β", "\xce\x92"), ESCAPE_VALUE("¦", "\xc2\xa6"), ESCAPE_VALUE("•", "\xe2\x80\xa2"), ESCAPE_VALUE("ç", "\xc3\xa7"), ESCAPE_VALUE("¸", "\xc2\xb8"), ESCAPE_VALUE("¢", "\xc2\xa2"), ESCAPE_VALUE("χ", "\xce\xa7"), ESCAPE_VALUE("©", "\xc2\xa9"), ESCAPE_VALUE("¤", "\xc2\xa4"), ESCAPE_VALUE("†", "\xe2\x80\xa0"), ESCAPE_VALUE("↓", "\xe2\x86\x93"), ESCAPE_VALUE("°", "\xc2\xb0"), ESCAPE_VALUE("δ", "\xce\x94"), ESCAPE_VALUE("÷", "\xc3\xb7"), ESCAPE_VALUE("é", "\xc3\xa9"), ESCAPE_VALUE("ê", "\xc3\xaa"), ESCAPE_VALUE("è", "\xc3\xa8"), ESCAPE_VALUE("ε", "\xce\x95"), ESCAPE_VALUE("≡", "\xe2\x89\xa1"), ESCAPE_VALUE("η", "\xce\x97"), ESCAPE_VALUE("ð", "\xc3\xb0"), ESCAPE_VALUE("ë", "\xc3\xab"), ESCAPE_VALUE("€", "\xe2\x82\xac"), ESCAPE_VALUE("∃", "\xe2\x88\x83"), ESCAPE_VALUE("∀", "\xe2\x88\x80"), ESCAPE_VALUE("½", "\xc2\xbd"), ESCAPE_VALUE("¼", "\xc2\xbc"), ESCAPE_VALUE("¾", "\xc2\xbe"), ESCAPE_VALUE("γ", "\xce\x93"), ESCAPE_VALUE("↔", "\xe2\x86\x94"), ESCAPE_VALUE("…", "\xe2\x80\xa6"), ESCAPE_VALUE("í", "\xc3\xad"), ESCAPE_VALUE("î", "\xc3\xae"), ESCAPE_VALUE("¡", "\xc2\xa1"), ESCAPE_VALUE("ì", "\xc3\xac"), ESCAPE_VALUE("∫", "\xe2\x88\xab"), ESCAPE_VALUE("ι", "\xce\x99"), ESCAPE_VALUE("¿", "\xc2\xbf"), ESCAPE_VALUE("ï", "\xc3\xaf"), ESCAPE_VALUE("κ", "\xce\x9a"), ESCAPE_VALUE("λ", "\xce\x9b"), ESCAPE_VALUE("«", "\xc2\xab"), ESCAPE_VALUE("←", "\xe2\x86\x90"), ESCAPE_VALUE("←", "\xe2\x87\x90"), ESCAPE_VALUE("‎", "\xe2\x80\x8e"), ESCAPE_VALUE("¯", "\xc2\xaf"), ESCAPE_VALUE("µ", "\xc2\xb5"), ESCAPE_VALUE("·", "\xc2\xb7"), ESCAPE_VALUE("μ", "\xce\x9c"), ESCAPE_VALUE("∇", "\xe2\x88\x87"), ESCAPE_VALUE(" ", "\xc2\xa0"), ESCAPE_VALUE("≠", "\xe2\x89\xa0"), ESCAPE_VALUE("¬", "\xc2\xac"), ESCAPE_VALUE("ñ", "\xc3\xb1"), ESCAPE_VALUE("ν", "\xce\x9d"), ESCAPE_VALUE("ó", "\xc3\xb3"), ESCAPE_VALUE("ô", "\xc3\xb4"), ESCAPE_VALUE("ò", "\xc3\xb2"), ESCAPE_VALUE("ω", "\xce\xa9"), ESCAPE_VALUE("ο", "\xce\x9f"), ESCAPE_VALUE("⊕", "\xe2\x8a\x95"), ESCAPE_VALUE("∨", "\xe2\x88\xa8"), ESCAPE_VALUE("ª", "\xc2\xaa"), ESCAPE_VALUE("º", "\xc2\xba"), ESCAPE_VALUE("ø", "\xc3\xb8"), ESCAPE_VALUE("õ", "\xc3\xb5"), ESCAPE_VALUE("ö", "\xc3\xb6"), ESCAPE_VALUE("¶", "\xc2\xb6"), ESCAPE_VALUE("⊥", "\xe2\x8a\xa5"), ESCAPE_VALUE("φ", "\xce\xa6"), ESCAPE_VALUE("π", "\xce\xa0"), ESCAPE_VALUE("±", "\xc2\xb1"), ESCAPE_VALUE("£", "\xc2\xa3"), ESCAPE_VALUE("∏", "\xe2\x88\x8f"), ESCAPE_VALUE("ψ", "\xce\xa8"), ESCAPE_VALUE("»", "\xc2\xbb"), ESCAPE_VALUE("→", "\xe2\x86\x92"), ESCAPE_VALUE("→", "\xe2\x87\x92"), ESCAPE_VALUE("®", "\xc2\xae"), ESCAPE_VALUE("ρ", "\xce\xa1"), ESCAPE_VALUE("‏", "\xe2\x80\x8f"), ESCAPE_VALUE("§", "\xc2\xa7"), ESCAPE_VALUE("­", "\xc2\xad"), ESCAPE_VALUE("σ", "\xce\xa3"), ESCAPE_VALUE("∑", "\xe2\x88\x91"), ESCAPE_VALUE("¹", "\xc2\xb9"), ESCAPE_VALUE("²", "\xc2\xb2"), ESCAPE_VALUE("³", "\xc2\xb3"), ESCAPE_VALUE("ß", "\xc3\x9f"), ESCAPE_VALUE("τ", "\xce\xa4"), ESCAPE_VALUE("θ", "\xce\x98"), ESCAPE_VALUE("þ", "\xc3\xbe"), ESCAPE_VALUE("×", "\xc3\x97"), ESCAPE_VALUE("ú", "\xc3\xba"), ESCAPE_VALUE("↑", "\xe2\x86\x91"), ESCAPE_VALUE("û", "\xc3\xbb"), ESCAPE_VALUE("ù", "\xc3\xb9"), ESCAPE_VALUE("¨", "\xc2\xa8"), ESCAPE_VALUE("υ", "\xce\xa5"), ESCAPE_VALUE("ü", "\xc3\xbc"), ESCAPE_VALUE("ξ", "\xce\x9e"), ESCAPE_VALUE("ý", "\xc3\xbd"), ESCAPE_VALUE("¥", "\xc2\xa5"), ESCAPE_VALUE("ÿ", "\xc3\xbf"), ESCAPE_VALUE("ζ", "\xce\x96"), ESCAPE_VALUE("‍", "\xe2\x80\x8d"), ESCAPE_VALUE("‌", "\xe2\x80\x8c"), }; /** * @internal * @var escape_values_e_common_sorted[] * same as escape_values_e_sorted with small subset of common escapes */ static const Escape_Value escape_values_e_common_sorted[] = { ESCAPE_VALUE("&", "\x26"), ESCAPE_VALUE("'", "\x27"), ESCAPE_VALUE(">", "\x3e"), ESCAPE_VALUE("<", "\x3c"), ESCAPE_VALUE(""", "\x22"), }; /** * @internal * @var escape_values_v_sorted[] * This array consists of Escape_Value structure sorted by escape value * And new added value must be placed sorted position, and reflected on escape_values_e_sorted */ static const Escape_Value escape_values_v_sorted[] = { ESCAPE_VALUE(" ", "\xc2\xa0"), ESCAPE_VALUE("¡", "\xc2\xa1"), ESCAPE_VALUE("¢", "\xc2\xa2"), ESCAPE_VALUE("£", "\xc2\xa3"), ESCAPE_VALUE("¤", "\xc2\xa4"), ESCAPE_VALUE("¥", "\xc2\xa5"), ESCAPE_VALUE("¦", "\xc2\xa6"), ESCAPE_VALUE("§", "\xc2\xa7"), ESCAPE_VALUE("¨", "\xc2\xa8"), ESCAPE_VALUE("©", "\xc2\xa9"), ESCAPE_VALUE("ª", "\xc2\xaa"), ESCAPE_VALUE("«", "\xc2\xab"), ESCAPE_VALUE("¬", "\xc2\xac"), ESCAPE_VALUE("­", "\xc2\xad"), ESCAPE_VALUE("®", "\xc2\xae"), ESCAPE_VALUE("¯", "\xc2\xaf"), ESCAPE_VALUE("°", "\xc2\xb0"), ESCAPE_VALUE("±", "\xc2\xb1"), ESCAPE_VALUE("²", "\xc2\xb2"), ESCAPE_VALUE("³", "\xc2\xb3"), ESCAPE_VALUE("´", "\xc2\xb4"), ESCAPE_VALUE("µ", "\xc2\xb5"), ESCAPE_VALUE("¶", "\xc2\xb6"), ESCAPE_VALUE("·", "\xc2\xb7"), ESCAPE_VALUE("¸", "\xc2\xb8"), ESCAPE_VALUE("¹", "\xc2\xb9"), ESCAPE_VALUE("º", "\xc2\xba"), ESCAPE_VALUE("»", "\xc2\xbb"), ESCAPE_VALUE("¼", "\xc2\xbc"), ESCAPE_VALUE("½", "\xc2\xbd"), ESCAPE_VALUE("¾", "\xc2\xbe"), ESCAPE_VALUE("¿", "\xc2\xbf"), ESCAPE_VALUE("À", "\xc3\x80"), ESCAPE_VALUE("Á", "\xc3\x81"), ESCAPE_VALUE("Â", "\xc3\x82"), ESCAPE_VALUE("Ã", "\xc3\x83"), ESCAPE_VALUE("Ä", "\xc3\x84"), ESCAPE_VALUE("Å", "\xc3\x85"), ESCAPE_VALUE("&Aelig;", "\xc3\x86"), ESCAPE_VALUE("Ç", "\xc3\x87"), ESCAPE_VALUE("È", "\xc3\x88"), ESCAPE_VALUE("É", "\xc3\x89"), ESCAPE_VALUE("Ê", "\xc3\x8a"), ESCAPE_VALUE("Ë", "\xc3\x8b"), ESCAPE_VALUE("Ì", "\xc3\x8c"), ESCAPE_VALUE("Í", "\xc3\x8d"), ESCAPE_VALUE("Î", "\xc3\x8e"), ESCAPE_VALUE("Ï", "\xc3\x8f"), ESCAPE_VALUE("&Eth;", "\xc3\x90"), ESCAPE_VALUE("Ñ", "\xc3\x91"), ESCAPE_VALUE("Ò", "\xc3\x92"), ESCAPE_VALUE("Ó", "\xc3\x93"), ESCAPE_VALUE("Ô", "\xc3\x94"), ESCAPE_VALUE("Õ", "\xc3\x95"), ESCAPE_VALUE("Ö", "\xc3\x96"), ESCAPE_VALUE("×", "\xc3\x97"), ESCAPE_VALUE("Ø", "\xc3\x98"), ESCAPE_VALUE("Ù", "\xc3\x99"), ESCAPE_VALUE("Ú", "\xc3\x9a"), ESCAPE_VALUE("Û", "\xc3\x9b"), ESCAPE_VALUE("Ý", "\xc3\x9d"), ESCAPE_VALUE("&Thorn;", "\xc3\x9e"), ESCAPE_VALUE("ß", "\xc3\x9f"), ESCAPE_VALUE("à", "\xc3\xa0"), ESCAPE_VALUE("á", "\xc3\xa1"), ESCAPE_VALUE("â", "\xc3\xa2"), ESCAPE_VALUE("ã", "\xc3\xa3"), ESCAPE_VALUE("ä", "\xc3\xa4"), ESCAPE_VALUE("å", "\xc3\xa5"), ESCAPE_VALUE("æ", "\xc3\xa6"), ESCAPE_VALUE("ç", "\xc3\xa7"), ESCAPE_VALUE("è", "\xc3\xa8"), ESCAPE_VALUE("é", "\xc3\xa9"), ESCAPE_VALUE("ê", "\xc3\xaa"), ESCAPE_VALUE("ë", "\xc3\xab"), ESCAPE_VALUE("ì", "\xc3\xac"), ESCAPE_VALUE("í", "\xc3\xad"), ESCAPE_VALUE("î", "\xc3\xae"), ESCAPE_VALUE("ï", "\xc3\xaf"), ESCAPE_VALUE("ð", "\xc3\xb0"), ESCAPE_VALUE("ñ", "\xc3\xb1"), ESCAPE_VALUE("ò", "\xc3\xb2"), ESCAPE_VALUE("ó", "\xc3\xb3"), ESCAPE_VALUE("ô", "\xc3\xb4"), ESCAPE_VALUE("õ", "\xc3\xb5"), ESCAPE_VALUE("ö", "\xc3\xb6"), ESCAPE_VALUE("÷", "\xc3\xb7"), ESCAPE_VALUE("ø", "\xc3\xb8"), ESCAPE_VALUE("ù", "\xc3\xb9"), ESCAPE_VALUE("ú", "\xc3\xba"), ESCAPE_VALUE("û", "\xc3\xbb"), ESCAPE_VALUE("ü", "\xc3\xbc"), ESCAPE_VALUE("ý", "\xc3\xbd"), ESCAPE_VALUE("þ", "\xc3\xbe"), ESCAPE_VALUE("ÿ", "\xc3\xbf"), ESCAPE_VALUE("α", "\xce\x91"), ESCAPE_VALUE("β", "\xce\x92"), ESCAPE_VALUE("γ", "\xce\x93"), ESCAPE_VALUE("δ", "\xce\x94"), ESCAPE_VALUE("ε", "\xce\x95"), ESCAPE_VALUE("ζ", "\xce\x96"), ESCAPE_VALUE("η", "\xce\x97"), ESCAPE_VALUE("θ", "\xce\x98"), ESCAPE_VALUE("ι", "\xce\x99"), ESCAPE_VALUE("κ", "\xce\x9a"), ESCAPE_VALUE("λ", "\xce\x9b"), ESCAPE_VALUE("μ", "\xce\x9c"), ESCAPE_VALUE("ν", "\xce\x9d"), ESCAPE_VALUE("ξ", "\xce\x9e"), ESCAPE_VALUE("ο", "\xce\x9f"), ESCAPE_VALUE("π", "\xce\xa0"), ESCAPE_VALUE("ρ", "\xce\xa1"), ESCAPE_VALUE("σ", "\xce\xa3"), ESCAPE_VALUE("τ", "\xce\xa4"), ESCAPE_VALUE("υ", "\xce\xa5"), ESCAPE_VALUE("φ", "\xce\xa6"), ESCAPE_VALUE("χ", "\xce\xa7"), ESCAPE_VALUE("ψ", "\xce\xa8"), ESCAPE_VALUE("ω", "\xce\xa9"), ESCAPE_VALUE("‌", "\xe2\x80\x8c"), ESCAPE_VALUE("‍", "\xe2\x80\x8d"), ESCAPE_VALUE("‎", "\xe2\x80\x8e"), ESCAPE_VALUE("‏", "\xe2\x80\x8f"), ESCAPE_VALUE("†", "\xe2\x80\xa0"), ESCAPE_VALUE("‡", "\xe2\x80\xa1"), ESCAPE_VALUE("•", "\xe2\x80\xa2"), ESCAPE_VALUE("…", "\xe2\x80\xa6"), ESCAPE_VALUE("€", "\xe2\x82\xac"), ESCAPE_VALUE("←", "\xe2\x86\x90"), ESCAPE_VALUE("↑", "\xe2\x86\x91"), ESCAPE_VALUE("→", "\xe2\x86\x92"), ESCAPE_VALUE("↓", "\xe2\x86\x93"), ESCAPE_VALUE("↔", "\xe2\x86\x94"), ESCAPE_VALUE("←", "\xe2\x87\x90"), ESCAPE_VALUE("→", "\xe2\x87\x92"), ESCAPE_VALUE("∀", "\xe2\x88\x80"), ESCAPE_VALUE("∃", "\xe2\x88\x83"), ESCAPE_VALUE("∇", "\xe2\x88\x87"), ESCAPE_VALUE("∏", "\xe2\x88\x8f"), ESCAPE_VALUE("∑", "\xe2\x88\x91"), ESCAPE_VALUE("∧", "\xe2\x88\xa7"), ESCAPE_VALUE("∨", "\xe2\x88\xa8"), ESCAPE_VALUE("∫", "\xe2\x88\xab"), ESCAPE_VALUE("≠", "\xe2\x89\xa0"), ESCAPE_VALUE("≡", "\xe2\x89\xa1"), ESCAPE_VALUE("⊕", "\xe2\x8a\x95"), ESCAPE_VALUE("⊥", "\xe2\x8a\xa5"), }; /** * @internal * @var escape_values_v_common_sorted[] * same as escape_values_v_sorted with small subset of common escapes */ static const Escape_Value escape_values_v_common_sorted[] = { ESCAPE_VALUE(""", "\x22"), ESCAPE_VALUE("&", "\x26"), ESCAPE_VALUE("'", "\x27"), ESCAPE_VALUE("<", "\x3c"), ESCAPE_VALUE(">", "\x3e"), }; /** * @internal * Checks if a char is a whitespace. * @param c the unicode codepoint. * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE * otherwise. */ static Eina_Bool _is_white(Eina_Unicode c) { /* * unicode list of whitespace chars * * 0009..000D .. * 0020 SPACE * 0085 * 00A0 NO-BREAK SPACE * 1680 OGHAM SPACE MARK * 180E MONGOLIAN VOWEL SEPARATOR * 2000..200A EN QUAD..HAIR SPACE * 2028 LINE SEPARATOR * 2029 PARAGRAPH SEPARATOR * 202F NARROW NO-BREAK SPACE * 205F MEDIUM MATHEMATICAL SPACE * 3000 IDEOGRAPHIC SPACE */ if ( (c == 0x20) || ((c >= 0x9) && (c <= 0xd)) || (c == 0x85) || (c == 0xa0) || (c == 0x1680) || (c == 0x180e) || ((c >= 0x2000) && (c <= 0x200a)) || (c == 0x2028) || (c == 0x2029) || (c == 0x202f) || (c == 0x205f) || (c == 0x3000) ) return EINA_TRUE; return EINA_FALSE; } /** * @internal * Prepends the text between s and p to the main cursor of the object. * * @param cur the cursor to prepend to. * @param[in] s start of the string * @param[in] p end of the string */ static void _prepend_text_run(Efl_Text_Cursor_Cursor *cur, const char *s, const char *p) { if ((s) && (p > s)) { char *ts; ts = alloca(p - s + 1); strncpy(ts, s, p - s); ts[p - s] = 0; evas_textblock_cursor_text_prepend(cur, ts); } } /* The refcount for the formats. */ static int format_refcount = 0; /* Holders for the stringshares */ static const char *fontstr = NULL; static const char *font_fallbacksstr = NULL; static const char *font_sizestr = NULL; static const char *font_sourcestr = NULL; static const char *font_weightstr = NULL; static const char *font_stylestr = NULL; static const char *font_widthstr = NULL; static const char *langstr = NULL; static const char *colorstr = NULL; static const char *underline_colorstr = NULL; static const char *underline2_colorstr = NULL; static const char *underline_dash_colorstr = NULL; static const char *outline_colorstr = NULL; static const char *shadow_colorstr = NULL; static const char *glow_colorstr = NULL; static const char *glow2_colorstr = NULL; static const char *backing_colorstr = NULL; static const char *strikethrough_colorstr = NULL; static const char *alignstr = NULL; static const char *valignstr = NULL; static const char *text_valignstr = NULL; static const char *wrapstr = NULL; static const char *left_marginstr = NULL; static const char *right_marginstr = NULL; static const char *underlinestr = NULL; static const char *strikethroughstr = NULL; static const char *backingstr = NULL; static const char *stylestr = NULL; static const char *tabstopsstr = NULL; static const char *linesizestr = NULL; static const char *linerelsizestr = NULL; static const char *linegapstr = NULL; static const char *linerelgapstr = NULL; static const char *itemstr = NULL; static const char *linefillstr = NULL; static const char *ellipsisstr = NULL; static const char *passwordstr = NULL; static const char *replacement_charstr = NULL; static const char *underline_dash_widthstr = NULL; static const char *underline_dash_gapstr = NULL; static const char *underline_heightstr = NULL; static const char *gfx_filterstr = NULL; /** * @page evas_textblock_style_page Evas Textblock Style Options * * @brief This page describes how to style text in an Evas Text Block. */ /** * @internal * Init the format strings. */ static void _format_command_init(void) { if (format_refcount == 0) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @section evas_textblock_style_index Index * * The following styling commands are accepted: * @li @ref evas_textblock_style_font * @li @ref evas_textblock_style_font_fallback * @li @ref evas_textblock_style_font_size * @li @ref evas_textblock_style_font_source * @li @ref evas_textblock_style_font_weight * @li @ref evas_textblock_style_font_style * @li @ref evas_textblock_style_font_width * @li @ref evas_textblock_style_lang * @li @ref evas_textblock_style_color * @li @ref evas_textblock_style_underline_color * @li @ref evas_textblock_style_underline2_color * @li @ref evas_textblock_style_underline_dash_color * @li @ref evas_textblock_style_outline_color * @li @ref evas_textblock_style_shadow_color * @li @ref evas_textblock_style_glow_color * @li @ref evas_textblock_style_glow2_color * @li @ref evas_textblock_style_backing_color * @li @ref evas_textblock_style_strikethrough_color * @li @ref evas_textblock_style_align * @li @ref evas_textblock_style_valign * @li @ref evas_textblock_style_wrap * @li @ref evas_textblock_style_left_margin * @li @ref evas_textblock_style_right_margin * @li @ref evas_textblock_style_underline * @li @ref evas_textblock_style_strikethrough * @li @ref evas_textblock_style_backing * @li @ref evas_textblock_style_style * @li @ref evas_textblock_style_tabstops * @li @ref evas_textblock_style_linesize * @li @ref evas_textblock_style_linerelsize * @li @ref evas_textblock_style_linegap * @li @ref evas_textblock_style_linerelgap * @li @ref evas_textblock_style_item * @li @ref evas_textblock_style_linefill * @li @ref evas_textblock_style_ellipsis * @li @ref evas_textblock_style_password * @li @ref evas_textblock_style_underline_dash_width * @li @ref evas_textblock_style_underline_dash_gap * @li @ref evas_textblock_style_underline_height * * @section evas_textblock_style_contents Contents */ fontstr = eina_stringshare_add("font"); font_fallbacksstr = eina_stringshare_add("font_fallbacks"); font_sizestr = eina_stringshare_add("font_size"); font_sourcestr = eina_stringshare_add("font_source"); font_weightstr = eina_stringshare_add("font_weight"); font_stylestr = eina_stringshare_add("font_style"); font_widthstr = eina_stringshare_add("font_width"); langstr = eina_stringshare_add("lang"); colorstr = eina_stringshare_add("color"); underline_colorstr = eina_stringshare_add("underline_color"); underline2_colorstr = eina_stringshare_add("underline2_color"); underline_dash_colorstr = eina_stringshare_add("underline_dash_color"); outline_colorstr = eina_stringshare_add("outline_color"); shadow_colorstr = eina_stringshare_add("shadow_color"); glow_colorstr = eina_stringshare_add("glow_color"); glow2_colorstr = eina_stringshare_add("glow2_color"); backing_colorstr = eina_stringshare_add("backing_color"); strikethrough_colorstr = eina_stringshare_add("strikethrough_color"); alignstr = eina_stringshare_add("align"); valignstr = eina_stringshare_add("valign"); text_valignstr = eina_stringshare_add("text_valign"); wrapstr = eina_stringshare_add("wrap"); left_marginstr = eina_stringshare_add("left_margin"); right_marginstr = eina_stringshare_add("right_margin"); underlinestr = eina_stringshare_add("underline"); strikethroughstr = eina_stringshare_add("strikethrough"); backingstr = eina_stringshare_add("backing"); stylestr = eina_stringshare_add("style"); tabstopsstr = eina_stringshare_add("tabstops"); linesizestr = eina_stringshare_add("linesize"); linerelsizestr = eina_stringshare_add("linerelsize"); linegapstr = eina_stringshare_add("linegap"); linerelgapstr = eina_stringshare_add("linerelgap"); itemstr = eina_stringshare_add("item"); linefillstr = eina_stringshare_add("linefill"); ellipsisstr = eina_stringshare_add("ellipsis"); passwordstr = eina_stringshare_add("password"); replacement_charstr = eina_stringshare_add("replacement_char"); underline_dash_widthstr = eina_stringshare_add("underline_dash_width"); underline_dash_gapstr = eina_stringshare_add("underline_dash_gap"); underline_heightstr = eina_stringshare_add("underline_height"); gfx_filterstr = eina_stringshare_add("gfx_filter"); // FIXME: bg, fg filters } format_refcount++; } /** * @internal * Shutdown the format strings. */ static void _format_command_shutdown(void) { if (--format_refcount > 0) return; eina_stringshare_del(fontstr); eina_stringshare_del(font_fallbacksstr); eina_stringshare_del(font_sizestr); eina_stringshare_del(font_sourcestr); eina_stringshare_del(font_weightstr); eina_stringshare_del(font_stylestr); eina_stringshare_del(font_widthstr); eina_stringshare_del(langstr); eina_stringshare_del(colorstr); eina_stringshare_del(underline_colorstr); eina_stringshare_del(underline2_colorstr); eina_stringshare_del(underline_dash_colorstr); eina_stringshare_del(outline_colorstr); eina_stringshare_del(shadow_colorstr); eina_stringshare_del(glow_colorstr); eina_stringshare_del(glow2_colorstr); eina_stringshare_del(backing_colorstr); eina_stringshare_del(strikethrough_colorstr); eina_stringshare_del(alignstr); eina_stringshare_del(valignstr); eina_stringshare_del(text_valignstr); eina_stringshare_del(wrapstr); eina_stringshare_del(left_marginstr); eina_stringshare_del(right_marginstr); eina_stringshare_del(underlinestr); eina_stringshare_del(strikethroughstr); eina_stringshare_del(backingstr); eina_stringshare_del(stylestr); eina_stringshare_del(tabstopsstr); eina_stringshare_del(linesizestr); eina_stringshare_del(linerelsizestr); eina_stringshare_del(linegapstr); eina_stringshare_del(linerelgapstr); eina_stringshare_del(itemstr); eina_stringshare_del(linefillstr); eina_stringshare_del(ellipsisstr); eina_stringshare_del(passwordstr); eina_stringshare_del(replacement_charstr); eina_stringshare_del(underline_dash_widthstr); eina_stringshare_del(underline_dash_gapstr); eina_stringshare_del(underline_heightstr); eina_stringshare_del(gfx_filterstr); } /** * @internal * Copies str to dst while removing the \\ char, i.e unescape the escape sequences. * * @param[out] dst the destination string - Should not be NULL. * @param[in] src the source string - Should not be NULL. */ static int _format_clean_param(char *s) { char *ss; char *ds; int len = 0; ds = s; for (ss = s; *ss; ss++, ds++, len++) { if ((*ss == '\\') && *(ss + 1)) ss++; if (ds != ss) *ds = *ss; } *ds = 0; return len; } /** * @internal * Parses the cmd and parameter and adds the parsed format to fmt. * * @param obj the evas object - should not be NULL. * @param fmt The format to populate - should not be NULL. * @param[in] cmd the command to process, should be stringshared. * @param[in] param the parameter of the command. may modify the string. */ static void _format_command(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param) { int len; len = _format_clean_param(param); /* If we are changing the font, create the fdesc. */ if ((cmd == font_weightstr) || (cmd == font_widthstr) || (cmd == font_stylestr) || (cmd == langstr) || (cmd == fontstr) || (cmd == font_fallbacksstr)) { if (!fmt->font.fdesc) { fmt->font.fdesc = evas_font_desc_new(); /* Set default language according to locale. */ eina_stringshare_replace(&(fmt->font.fdesc->lang), evas_font_lang_normalize("auto")); } else if (!fmt->font.fdesc->is_new) { Evas_Font_Description *old = fmt->font.fdesc; fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc); if (old) evas_font_desc_unref(old); } } if (cmd == fontstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font Font * * This sets the name of the font to be used. * @code * font= * @endcode */ evas_font_name_parse(fmt->font.fdesc, param); } else if (cmd == font_fallbacksstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_fallback Font fallback * * This sets the name of the fallback font to be used. This font will * be used if the primary font is not available. * @code * font_fallbacks= * @endcode */ eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), param); } else if (cmd == font_sizestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_size Font size * * This sets the the size of font in points to be used. * @code * font_size= * @endcode */ int v; v = atoi(param); if (v != fmt->font.size) { fmt->font.size = v; } } else if (cmd == font_sourcestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_source Font source * * Specify an object from which to search for the font. * @code * font_source= * @endcode */ if ((!fmt->font.source) || ((fmt->font.source) && (strcmp(fmt->font.source, param)))) { eina_stringshare_replace(&(fmt->font.source), param); } } else if (cmd == font_weightstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_weight Font weight * * Sets the weight of the font. The value must be one of: * @li "normal" * @li "thin" * @li "ultralight" * @li "extralight" * @li "light" * @li "book" * @li "medium" * @li "semibold" * @li "bold" * @li "ultrabold" * @li "extrabold" * @li "black" * @li "extrablack" * @code * font_weight= * @endcode */ fmt->font.fdesc->weight = evas_font_style_find(param, param + len, EVAS_FONT_STYLE_WEIGHT); } else if (cmd == font_stylestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_style Font style * * Sets the style of the font. The value must be one of: * @li "normal" * @li "oblique" * @li "italic" * @code * font_style=