diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index 8259ed8b0c..bcdb484af3 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -243,6 +243,7 @@ void test_label_wrap(void *data, Evas_Object *obj, void *event_info); void test_label_ellipsis(void *data, Evas_Object *obj, void *event_info); void test_label_colors(void *data, Evas_Object *obj, void *event_info); void test_label_emoji(void *data, Evas_Object *obj, void *event_info); +void test_label_variation_sequence(void *data, Evas_Object *obj, void *event_info); void test_conformant(void *data, Evas_Object *obj, void *event_info); void test_conformant2(void *data, Evas_Object *obj, void *event_info); void test_conformant_indicator(void *data, Evas_Object *obj, void *event_info); @@ -1210,6 +1211,7 @@ add_tests: ADD_TEST(NULL, "Text", "Label Ellipsis", test_label_ellipsis); ADD_TEST(NULL, "Text", "Label Colors", test_label_colors); ADD_TEST(NULL, "Text", "Label Emoji", test_label_emoji); + ADD_TEST(NULL, "Text", "Label Variation Sequnece", test_label_variation_sequence); ADD_TEST_EO(NULL, "Text", "Efl.Ui.Textpath", test_ui_textpath); //------------------------------// diff --git a/src/bin/elementary/test_label.c b/src/bin/elementary/test_label.c index 233ce015b4..72cb2aeca3 100644 --- a/src/bin/elementary/test_label.c +++ b/src/bin/elementary/test_label.c @@ -403,6 +403,30 @@ test_label_colors(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *ev evas_object_show(win); } +/*** Label variation sequence **************************************************************/ +void +test_label_variation_sequence(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win, *lb; + + win = elm_win_util_standard_add("label-variation sequence", "Label variation sequnece"); + elm_win_autodel_set(win, EINA_TRUE); + + lb = elm_label_add(win); + elm_object_text_set(lb, + "You need to have at least on font contains variation sequence
" + "Three different 8 glyphs :
" + "88️8️⃣
" + "line with 3 variation glyphs :
" + "8️⃣☪️AAA☪︎1234567️⃣" + ); + evas_object_size_hint_weight_set(lb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, lb); + evas_object_show(lb); + + evas_object_show(win); +} + /*** Label Emoji *************************************************************/ static char * _fontlist_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED) diff --git a/src/lib/evas/common/evas_font.h b/src/lib/evas/common/evas_font.h index e16785520d..b4490ea992 100644 --- a/src/lib/evas/common/evas_font.h +++ b/src/lib/evas/common/evas_font.h @@ -56,7 +56,32 @@ typedef unsigned long long DATA64; #define LKU(x) eina_lock_release(&(x)) #define LKDBG(x) eina_lock_debug(&(x)) +/** + * See explanation of variation_sequences at: + * https://unicode.org/Public/UCD/latest/ucd/StandardizedVariants.txt + * https://unicode.org/reports/tr37/ + * https://unicode.org/ivd/ + * https://www.freetype.org/freetype2/docs/reference/ft2-glyph_variants.html +*/ +#define VAR_SEQ(x) GENERIC_VARIATION_SEQUENCES(x) | IDEOGRAPHICS_VARIATION_SEQUENCES(x) | MANGOLIAN_VARIATION_SEQUENCES(x) +#define GENERIC_VARIATION_SEQUENCES(x) (x>=0xFE00 && x<=0xFE0F) ? x : 0 +#define IDEOGRAPHICS_VARIATION_SEQUENCES(x) (x>=0xE0100 && x<=0xE01EF) ? x : 0 +#define MANGOLIAN_VARIATION_SEQUENCES(x) (x>=0x180B && x<=0x180D) ? x : 0 +/** + * http://unicode.org/emoji/charts/emoji-variants.html +*/ +#define VARIATION_EMOJI_PRESENTATION 0xFE0F +#define VARIATION_TEXT_PRESENTATION 0xFE0E +/** + * These Options (Flags) are used with evas_common_font_glyph_search function + */ +#define EVAS_FONT_SEARCH_OPTION_NONE 0x0000 +#define EVAS_FONT_SEARCH_OPTION_SKIP_COLOR 0x0001 + + +#define FASH_INT_MAGIC 0x01012345 +#define FASH_GLYPH_MAGIC 0x02012345 enum _Evas_Font_Style { @@ -128,6 +153,10 @@ typedef struct _RGBA_Font_Source RGBA_Font_Source; typedef struct _RGBA_Font_Glyph RGBA_Font_Glyph; typedef struct _RGBA_Font_Glyph_Out RGBA_Font_Glyph_Out; +typedef struct _Fash_Item_variation_Index_Item Fash_Item_variation_Index_Item; +typedef struct _Fash_Item_variation_List Fash_Item_variation_List; +typedef struct _Fash_Item_Index_Map_Variations Fash_Item_Index_Map_Variations; + typedef struct _Fash_Item_Index_Map Fash_Item_Index_Map; typedef struct _Fash_Int_Map Fash_Int_Map; typedef struct _Fash_Int_Map2 Fash_Int_Map2; @@ -139,9 +168,25 @@ struct _Fash_Item_Index_Map RGBA_Font_Int *fint; int index; }; +struct _Fash_Item_variation_Index_Item +{ + Fash_Item_Index_Map item; + Eina_Unicode variation_sequence; +}; +struct _Fash_Item_variation_List +{ + Fash_Item_variation_Index_Item *list; + size_t length; + size_t capacity; +}; +struct _Fash_Item_Index_Map_Variations +{ + Fash_Item_Index_Map item; + Fash_Item_variation_List *variations; +}; struct _Fash_Int_Map { - Fash_Item_Index_Map item[256]; + Fash_Item_Index_Map_Variations items[256]; }; struct _Fash_Int_Map2 { @@ -149,6 +194,7 @@ struct _Fash_Int_Map2 }; struct _Fash_Int { + unsigned int MAGIC; Fash_Int_Map2 *bucket[256]; void (*freeme) (Fash_Int *fash); }; @@ -166,6 +212,7 @@ struct _Fash_Glyph_Map2 }; struct _Fash_Glyph { + unsigned int MAGIC; Fash_Glyph_Map2 *bucket[256]; void (*freeme) (Fash_Glyph *fash); }; @@ -347,7 +394,7 @@ void *evas_common_font_freetype_face_get(RGBA_Font *font); /* XXX: Not EAPI on p EAPI RGBA_Font_Glyph *evas_common_font_int_cache_glyph_get (RGBA_Font_Int *fi, FT_UInt index); EAPI Eina_Bool evas_common_font_int_cache_glyph_render(RGBA_Font_Glyph *fg); -EAPI FT_UInt evas_common_get_char_index (RGBA_Font_Int* fi, Eina_Unicode gl); +EAPI FT_UInt evas_common_get_char_index (RGBA_Font_Int* fi, Eina_Unicode gl, Eina_Unicode variation_sequence); /* load */ EAPI void evas_common_font_dpi_set (int dpi_h, int dpi_v); @@ -397,7 +444,7 @@ EAPI void evas_common_font_ascent_descent_get(RGBA_Font *fn, const EAPI void *evas_common_font_glyph_compress(void *data, int num_grays, int pixel_mode, int pitch_data, int w, int h, int *size_ret); EAPI DATA8 *evas_common_font_glyph_uncompress(RGBA_Font_Glyph *fg, int *wret, int *hret); -EAPI int evas_common_font_glyph_search (RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl); +EAPI int evas_common_font_glyph_search (RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl, Eina_Unicode variation_sequence, uint32_t evas_font_search_options); void evas_common_font_load_init(void); void evas_common_font_load_shutdown(void); diff --git a/src/lib/evas/common/evas_font_load.c b/src/lib/evas/common/evas_font_load.c index f2011bd72f..3aa2070499 100644 --- a/src/lib/evas/common/evas_font_load.c +++ b/src/lib/evas/common/evas_font_load.c @@ -687,7 +687,11 @@ evas_common_font_free(RGBA_Font *fn) evas_common_font_int_unref(fi); evas_common_font_flush(); eina_list_free(fn->fonts); - if (fn->fash) fn->fash->freeme(fn->fash); + if (fn->fash) + { + fn->fash->freeme(fn->fash); + fn->fash = NULL; + } LKD(fn->lock); free(fn); } diff --git a/src/lib/evas/common/evas_font_main.c b/src/lib/evas/common/evas_font_main.c index 1a163748ca..c41e350dcf 100644 --- a/src/lib/evas/common/evas_font_main.c +++ b/src/lib/evas/common/evas_font_main.c @@ -355,33 +355,182 @@ end: /* Set of common functions that are used in a couple of places. */ static void -_fash_int2_free(Fash_Int_Map2 *fash) -{ +_fash_int_map_and_variations_free(Fash_Int_Map *map) + { + if(!map) + return; int i; - for (i = 0; i < 256; i++) if (fash->bucket[i]) free(fash->bucket[i]); - free(fash); + for (i = 0; i < 256; i++) + { + if (map->items[i].variations) + { + if (map->items[i].variations->list) + { + free(map->items[i].variations->list); + map->items[i].variations->list = NULL; + map->items[i].variations->capacity = 0; + map->items[i].variations->length = 0; + } + free(map->items[i].variations); + map->items[i].variations = NULL; + } + } + + free(map); +} + +static void +_fash_int2_free(Fash_Int_Map2 *fash) + { + int i; + if (fash) + { + for (i = 0; i < 256; i++) + if (fash->bucket[i]) + { + _fash_int_map_and_variations_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + free(fash); + fash = NULL; + } } static void _fash_int_free(Fash_Int *fash) { int i; + if (fash) + { + if (fash->MAGIC != FASH_INT_MAGIC) + { + return; + } - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_int2_free(fash->bucket[i]); - free(fash); + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_int2_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } + free(fash); + } } static Fash_Int * _fash_int_new(void) { Fash_Int *fash = calloc(1, sizeof(Fash_Int)); + EINA_SAFETY_ON_NULL_RETURN_VAL(fash, NULL); + fash->MAGIC = FASH_INT_MAGIC; fash->freeme = _fash_int_free; return fash; } +static Fash_Item_variation_List * +_variations_list_new(void) +{ + Fash_Item_variation_List *variations = calloc(1, sizeof(Fash_Item_variation_List)); + EINA_SAFETY_ON_NULL_RETURN_VAL(variations, NULL); + variations->capacity = 0; + variations->length = 0; + variations->list = 0; + return variations; +} + +static void +_variations_list_add(Fash_Item_variation_List *variations,RGBA_Font_Int *fint, int index, Eina_Unicode variation_sequence) +{ + Fash_Item_variation_Index_Item *list = variations->list; + if (variations->capacity == variations->length) + { + list = (Fash_Item_variation_Index_Item *) realloc(list, (variations->capacity + 4) * sizeof(Fash_Item_variation_Index_Item)); + if (list) + { + variations->list = list; + variations->capacity += 4; + } + } + + EINA_SAFETY_ON_NULL_RETURN(list); + + int start = 0; + int end = variations->length; + if (end == 0) + { + // if only on element just add it in 0 index + variations->list[0].item.fint = fint; + variations->list[0].item.index = index; + variations->list[0].variation_sequence = variation_sequence; + variations->length++; + } + else + { + // find lower bound + while (end > start) + { + int middle = start + (end - start) / 2; + if (variations->list[middle].variation_sequence >= variation_sequence) + end = middle; + else + start = middle + 1; + } + + // if passed value founded in list, just replace it + if (start < (int)variations->length && variations->list[start].variation_sequence == variation_sequence) + { + variations->list[start].item.fint = fint; + variations->list[start].item.index = index; + variations->list[start].variation_sequence = variation_sequence; + return; + } + + // shift array to insert item + for (int i = (variations->length - 1) ; i >= start; i--) + { + variations->list[i + 1] = variations->list[i]; + } + + // insert new item and keep array sorted + variations->list[start].item.fint = fint; + variations->list[start].item.index = index; + variations->list[start].variation_sequence = variation_sequence; + variations->length++; + } +} + + static Fash_Item_Index_Map * -_fash_int_find(Fash_Int *fash, int item) +_variations_list_find(Fash_Item_variation_List * variations, Eina_Unicode variation_sequence) +{ + if (!variations) + return NULL; + + if (!variations->list) + return NULL; + + int start = 0; + int end = variations->length; + + while(end > start) + { + int middle = start + (end - start) / 2; + if (variations->list[middle].variation_sequence == variation_sequence) + return &(variations->list[middle].item); + else if (variations->list[middle].variation_sequence < variation_sequence) + start = middle + 1; + else + end = middle - 1; + } + + return NULL; +} + +static const Fash_Item_Index_Map * +_fash_int_find(Fash_Int *fash, int item, Eina_Unicode variation_sequence) { int grp, maj, min; @@ -391,14 +540,22 @@ _fash_int_find(Fash_Int *fash, int item) min = item & 0xff; if (!fash->bucket[grp]) return NULL; if (!fash->bucket[grp]->bucket[maj]) return NULL; - return &(fash->bucket[grp]->bucket[maj]->item[min]); + if (!variation_sequence) + return &(fash->bucket[grp]->bucket[maj]->items[min].item); + else + return _variations_list_find(fash->bucket[grp]->bucket[maj]->items[min].variations, variation_sequence); } static void -_fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx) +_fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx, Eina_Unicode variation_sequence) { int grp, maj, min; + // If we already have cached passed item, skip adding it again + const Fash_Item_Index_Map *fm = _fash_int_find(fash, item, variation_sequence); + if (fm && fm->fint) + return; + // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) grp = (item >> 16) & 0xff; maj = (item >> 8) & 0xff; @@ -409,8 +566,20 @@ _fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx) if (!fash->bucket[grp]->bucket[maj]) fash->bucket[grp]->bucket[maj] = calloc(1, sizeof(Fash_Int_Map)); EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]); - fash->bucket[grp]->bucket[maj]->item[min].fint = fint; - fash->bucket[grp]->bucket[maj]->item[min].index = idx; + if (variation_sequence) + { + if (!fash->bucket[grp]->bucket[maj]->items[min].variations) + { + fash->bucket[grp]->bucket[maj]->items[min].variations =_variations_list_new(); + EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]->items[min].variations); + } + _variations_list_add(fash->bucket[grp]->bucket[maj]->items[min].variations, fint, idx, variation_sequence); + } + else + { + fash->bucket[grp]->bucket[maj]->items[min].item.fint = fint; + fash->bucket[grp]->bucket[maj]->items[min].item.index = idx; + } } static void @@ -462,24 +631,45 @@ _fash_gl2_free(Fash_Glyph_Map2 *fash) int i; // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_glyph_free(fash->bucket[i]); + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_glyph_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } free(fash); } static void _fash_gl_free(Fash_Glyph *fash) { - int i; + if (fash) + { + if (fash->MAGIC != FASH_GLYPH_MAGIC) + return; - // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_gl2_free(fash->bucket[i]); - free(fash); + int i; + // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_gl2_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } + free(fash); + } } static Fash_Glyph * _fash_gl_new(void) { Fash_Glyph *fash = calloc(1, sizeof(Fash_Glyph)); + EINA_SAFETY_ON_NULL_RETURN_VAL(fash, NULL); + fash->MAGIC = FASH_GLYPH_MAGIC; fash->freeme = _fash_gl_free; return fash; } @@ -680,7 +870,7 @@ struct _Font_Char_Index }; EAPI FT_UInt -evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) +evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl, Eina_Unicode variation_sequence) { static const unsigned short mapfix[] = { @@ -742,7 +932,10 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) * that something else try to use it. */ /* FTLOCK(); */ - result.index = FT_Get_Char_Index(fi->src->ft.face, gl); + if (variation_sequence) + result.index = FT_Face_GetCharVariantIndex(fi->src->ft.face, gl, variation_sequence); + else + result.index = FT_Get_Char_Index(fi->src->ft.face, gl); /* FTUNLOCK(); */ result.gl = gl; @@ -774,7 +967,10 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) { gl = mapfix[(i << 1) + 1]; FTLOCK(); - result.index = FT_Get_Char_Index(fi->src->ft.face, gl); + if (variation_sequence) + result.index = FT_Face_GetCharVariantIndex(fi->src->ft.face, gl, variation_sequence); + else + result.index = FT_Get_Char_Index(fi->src->ft.face, gl); FTUNLOCK(); break; } @@ -799,20 +995,49 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) return result.index; } + +/* + * @internal + * Search for unicode glyph inside all font files, and return font and glyph index + * + * @param[in] fn the font to use. + * @param[out] fi_ret founded font. + * @param[in] gl unicode glyph to search for + * @param[in] variation_sequence for the gl glyph + * @param[in] evas_font_search_options search options when searching font files + * + */ + EAPI int -evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl) +evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl, Eina_Unicode variation_sequence, uint32_t evas_font_search_options) { Eina_List *l; if (fn->fash) { - Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl); + const Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl, variation_sequence); if (fm) { if (fm->fint) { - *fi_ret = fm->fint; - return fm->index; + if (evas_font_search_options == EVAS_FONT_SEARCH_OPTION_NONE) + { + *fi_ret = fm->fint; + return fm->index; + } + else if( (evas_font_search_options & EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) == EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) + { + if (!fm->fint->src->ft.face) + { + evas_common_font_int_reload(fm->fint); + } + + if (fm->fint->src->ft.face && !FT_HAS_COLOR(fm->fint->src->ft.face)) + { + *fi_ret = fm->fint; + return fm->index; + } + } } else if (fm->index == -1) return 0; } @@ -851,20 +1076,35 @@ evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicod } if (fi->src->ft.face) { - idx = evas_common_get_char_index(fi, gl); + Eina_Bool is_color_only = (evas_font_search_options & EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) == EVAS_FONT_SEARCH_OPTION_SKIP_COLOR && + FT_HAS_COLOR(fi->src->ft.face); + + if (is_color_only) + { + /* This is color font ignore it */ + continue; + } + + idx = (int) evas_common_get_char_index(fi, gl, variation_sequence); if (idx != 0) { if (!fi->ft.size) evas_common_font_int_load_complete(fi); - if (!fn->fash) fn->fash = _fash_int_new(); - if (fn->fash) _fash_int_add(fn->fash, gl, fi, idx); + if (!is_color_only) + { + if (!fn->fash) fn->fash = _fash_int_new(); + if (fn->fash) _fash_int_add(fn->fash, gl, fi, idx, variation_sequence); + } *fi_ret = fi; return idx; } else { - if (!fn->fash) fn->fash = _fash_int_new(); - if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1); + if (!is_color_only) + { + if (!fn->fash) fn->fash = _fash_int_new(); + if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1, variation_sequence); + } } } } diff --git a/src/lib/evas/common/evas_font_query.c b/src/lib/evas/common/evas_font_query.c index 1ff0447963..7b50a74e62 100644 --- a/src/lib/evas/common/evas_font_query.c +++ b/src/lib/evas/common/evas_font_query.c @@ -32,6 +32,9 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi (evas_common_language_char_script_get(*base_char) != script) ; base_char++) ; + /* If counter reach variation sequence it is safe to pick default font */ + if(VAR_SEQ(*base_char) || (base_char != run_end && VAR_SEQ(*(base_char+1)))) goto get_top_font; + if (base_char == run_end) base_char = text; /* Find the first renderable char */ @@ -40,7 +43,7 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* 0x1F is the last ASCII contral char, just a hack in * the meanwhile. */ if ((*base_char > 0x1F) && - evas_common_font_glyph_search(fn, &fi, *base_char)) + evas_common_font_glyph_search(fn, &fi, *base_char, 0, EVAS_FONT_SEARCH_OPTION_NONE)) break; base_char++; } @@ -49,7 +52,8 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If everything else fails, at least try to find a font for the * replacement char */ if (base_char == run_end) - evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR); + evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR, 0, EVAS_FONT_SEARCH_OPTION_NONE); +get_top_font: if (!fi) fi = fn->fonts->data; @@ -80,15 +84,17 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi if (evas_common_language_char_script_get(*itr) == EVAS_SCRIPT_INHERITED) continue; + Eina_Unicode variation_sequence = VAR_SEQ(*(itr+1)); + /* Break if either it's not in the font, or if it is in the * script's font. */ - if (!evas_common_get_char_index(fi, *itr)) - break; + if (!evas_common_get_char_index(fi, *itr, variation_sequence)) + break; if (fi != *script_fi) { - if (evas_common_get_char_index(*script_fi, *itr)) - break; + if (evas_common_get_char_index(*script_fi, *itr, variation_sequence)) + break; } } @@ -102,10 +108,25 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If we can find a font, use it. Otherwise, find the first * char the run of chars that can't be rendered until the first * one that can. */ - if (evas_common_font_glyph_search(fn, &tmp_fi, *itr)) + Eina_Unicode variation_sequence = VAR_SEQ(*(itr+1)); + if (evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE)) { fi = tmp_fi; } + else if ( (variation_sequence == VARIATION_TEXT_PRESENTATION) && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_SKIP_COLOR)) + { + /* If we can not find unicode with variation sequence, then we will + Search and find for non color glyph because variation sequence is Text + */ + fi = tmp_fi; + } + else if ( variation_sequence && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE)) + { + /* If we can not find unicode with variation sequence, then we will + Search and find glyph without the variation sequence + */ + fi = tmp_fi; + } else { itr++; @@ -113,9 +134,13 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi * font */ for ( ; itr < run_end ; itr++) { + if(VAR_SEQ(*itr)) + continue; + + Eina_Unicode variation_sequence = VAR_SEQ(*(itr+1)); tmp_fi = fi; - if (evas_common_get_char_index(fi, *itr) || - evas_common_font_glyph_search(fn, &tmp_fi, *itr)) + if (evas_common_get_char_index(fi, *itr, variation_sequence) || + evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE)) { fi = tmp_fi; break; @@ -127,9 +152,9 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi * find a font most suitable for the replacement char and * break */ if ((itr == run_end) || - !evas_common_get_char_index(fi, REPLACEMENT_CHAR)) + !evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0)) { - evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR); + evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR, 0, EVAS_FONT_SEARCH_OPTION_NONE); break; } } @@ -140,8 +165,11 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If this char is not renderable by any font, but the replacement * char can be rendered using the currentfont, continue this * run. */ - if (!evas_common_font_glyph_search(fn, &tmp_fi, *itr) && - evas_common_get_char_index(fi, REPLACEMENT_CHAR)) + Eina_Unicode variation_sequence = VAR_SEQ(*(itr+1)); + + if (!evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE) && + (variation_sequence ? !evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE) : 1) && + evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0)) { itr++; } diff --git a/src/lib/evas/common/evas_text_utils.c b/src/lib/evas/common/evas_text_utils.c index e9c5cc2c94..acf7cf8fb7 100644 --- a/src/lib/evas/common/evas_text_utils.c +++ b/src/lib/evas/common/evas_text_utils.c @@ -1066,7 +1066,7 @@ _content_create_ot(RGBA_Font_Int *fi, const Eina_Unicode *text, /* If we got a malformed index, show the replacement char instead */ if (gl_itr->index == 0) { - gl_itr->index = evas_common_get_char_index(fi, REPLACEMENT_CHAR); + gl_itr->index = evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0); is_replacement = EINA_TRUE; } idx = gl_itr->index; @@ -1172,10 +1172,10 @@ _content_create_regular(RGBA_Font_Int *fi, const Eina_Unicode *text, _gl = *text; if (_gl == 0) break; - idx = evas_common_get_char_index(fi, _gl); + idx = evas_common_get_char_index(fi, _gl, 0); if (idx == 0) { - idx = evas_common_get_char_index(fi, REPLACEMENT_CHAR); + idx = evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0); } fg = evas_common_font_int_cache_glyph_get(fi, idx); diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index f61574c065..2db6888b12 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -1,6 +1,6 @@ /* * TODO: - * * Test different font lodaing mechanisms. + * * Test different font loading mechanisms. */ #ifdef HAVE_CONFIG_H @@ -4129,6 +4129,23 @@ EFL_START_TEST(evas_textblock_obstacle) } EFL_END_TEST; +EFL_START_TEST(evas_textblock_variation_sequence) +{ + START_TB_TEST(); + evas_font_path_global_prepend(".."); + const char *str1 = "8️☪️"; + const char *str2 = "8︎☪︎"; + Evas_Coord fw, fh,fw_new, fh_new; + evas_object_textblock_text_markup_set(tb, str1); + evas_object_textblock_size_formatted_get(tb, &fw, &fh); + evas_object_textblock_text_markup_set(tb, str2); + evas_object_textblock_size_formatted_get(tb, &fw_new, &fh_new); + fail_if(fw_new == fw && fh_new == fh); + + END_TB_TEST(); +} +EFL_END_TEST; + #ifdef HAVE_HYPHEN static void _hyphenation_width_stress(Evas_Object *tb, Evas_Textblock_Cursor *cur) @@ -4729,6 +4746,7 @@ void evas_test_textblock(TCase *tc) tcase_add_test(tc, evas_textblock_items); tcase_add_test(tc, evas_textblock_delete); tcase_add_test(tc, evas_textblock_obstacle); + tcase_add_test(tc, evas_textblock_variation_sequence); #ifdef HAVE_HYPHEN tcase_add_test(tc, evas_textblock_hyphenation); #endif diff --git a/src/tests/evas/fonts/NotoColorEmoji.ttf b/src/tests/evas/fonts/NotoColorEmoji.ttf new file mode 100644 index 0000000000..69cf21a1a9 Binary files /dev/null and b/src/tests/evas/fonts/NotoColorEmoji.ttf differ diff --git a/src/tests/evas/fonts/NotoEmoji-Regular.ttf b/src/tests/evas/fonts/NotoEmoji-Regular.ttf new file mode 100644 index 0000000000..19b7badf4a Binary files /dev/null and b/src/tests/evas/fonts/NotoEmoji-Regular.ttf differ