diff options
-rw-r--r-- | pango2/emoji_presentation_scanner.c | 615 | ||||
-rw-r--r-- | pango2/emoji_presentation_scanner.rl | 17 | ||||
-rw-r--r-- | pango2/itemize.c | 30 | ||||
-rw-r--r-- | pango2/pango-attr-list.c | 4 | ||||
-rw-r--r-- | pango2/pango-attributes.c | 19 | ||||
-rw-r--r-- | pango2/pango-attributes.h | 5 | ||||
-rw-r--r-- | pango2/pango-context-private.h | 2 | ||||
-rw-r--r-- | pango2/pango-context.c | 42 | ||||
-rw-r--r-- | pango2/pango-context.h | 5 | ||||
-rw-r--r-- | pango2/pango-emoji-private.h | 26 | ||||
-rw-r--r-- | pango2/pango-emoji.c | 59 | ||||
-rw-r--r-- | pango2/pango-markup.c | 14 | ||||
-rw-r--r-- | pango2/pango-types.h | 24 | ||||
-rw-r--r-- | pango2/serializer.c | 11 |
14 files changed, 603 insertions, 270 deletions
diff --git a/pango2/emoji_presentation_scanner.c b/pango2/emoji_presentation_scanner.c index e40b9d45..69c2ae9f 100644 --- a/pango2/emoji_presentation_scanner.c +++ b/pango2/emoji_presentation_scanner.c @@ -1,82 +1,84 @@ - -#line 1 "emoji_presentation_scanner.rl" +#line 1 "pango2/emoji_presentation_scanner.rl" // Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#line 9 "emoji_presentation_scanner.c" -static const char _emoji_presentation_actions[] = { - 0, 1, 0, 1, 1, 1, 5, 1, - 6, 1, 7, 1, 8, 1, 9, 1, - 10, 1, 11, 2, 2, 3, 2, 2, - 4 +#line 6 "pango2/emoji_presentation_scanner.c" +static const signed char _emoji_presentation_actions[] = { + 0, 1, 0, 1, 1, 1, 7, 1, + 8, 1, 9, 1, 10, 1, 11, 1, + 12, 1, 13, 1, 14, 2, 2, 3, + 2, 2, 4, 2, 2, 5, 2, 2, + 6, 0 }; -static const char _emoji_presentation_key_offsets[] = { - 0, 5, 7, 14, 18, 20, 21, 24, - 29, 30, 34, 36 +static const signed char _emoji_presentation_key_offsets[] = { + 0, 5, 7, 14, 18, 20, 21, 24, + 29, 30, 34, 36, 0 }; static const unsigned char _emoji_presentation_trans_keys[] = { - 3u, 7u, 13u, 0u, 2u, 14u, 15u, 2u, - 3u, 6u, 7u, 13u, 0u, 1u, 9u, 10u, - 11u, 12u, 10u, 12u, 10u, 4u, 10u, 12u, - 4u, 9u, 10u, 11u, 12u, 6u, 9u, 10u, - 11u, 12u, 8u, 10u, 9u, 10u, 11u, 12u, - 14u, 0 -}; - -static const char _emoji_presentation_single_lengths[] = { - 3, 2, 5, 4, 2, 1, 3, 5, - 1, 4, 2, 5 + 3u, 7u, 13u, 0u, 2u, 14u, 15u, 0u, + 1u, 2u, 3u, 6u, 7u, 13u, 9u, 10u, + 11u, 12u, 10u, 12u, 10u, 4u, 10u, 12u, + 4u, 9u, 10u, 11u, 12u, 6u, 9u, 10u, + 11u, 12u, 8u, 10u, 9u, 10u, 11u, 12u, + 14u, 0u }; -static const char _emoji_presentation_range_lengths[] = { - 1, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0 +static const signed char _emoji_presentation_single_lengths[] = { + 3, 2, 7, 4, 2, 1, 3, 5, + 1, 4, 2, 5, 0 }; -static const char _emoji_presentation_index_offsets[] = { - 0, 5, 8, 15, 20, 23, 25, 29, - 35, 37, 42, 45 +static const signed char _emoji_presentation_range_lengths[] = { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }; -static const char _emoji_presentation_indicies[] = { - 2, 1, 1, 1, 0, 4, 5, 3, - 7, 8, 10, 11, 12, 6, 9, 5, - 13, 14, 15, 0, 13, 15, 16, 13, - 16, 15, 13, 15, 16, 15, 5, 13, - 14, 15, 16, 5, 17, 5, 13, 14, - 18, 17, 5, 13, 16, 5, 13, 14, - 15, 4, 16, 0 +static const signed char _emoji_presentation_index_offsets[] = { + 0, 5, 8, 16, 21, 24, 26, 30, + 36, 38, 43, 46, 0 }; -static const char _emoji_presentation_trans_targs[] = { - 2, 4, 6, 2, 1, 2, 3, 3, - 7, 2, 8, 9, 11, 0, 2, 5, - 2, 2, 10 +static const signed char _emoji_presentation_cond_targs[] = { + 6, 4, 4, 4, 2, 1, 2, 2, + 3, 3, 3, 7, 8, 9, 11, 2, + 2, 0, 2, 5, 2, 0, 5, 2, + 0, 2, 5, 0, 5, 2, 5, 2, + 0, 2, 5, 2, 2, 2, 2, 0, + 2, 10, 2, 2, 0, 2, 2, 0, + 2, 5, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 0 }; -static const char _emoji_presentation_trans_actions[] = { - 17, 19, 19, 15, 0, 7, 22, 19, - 19, 9, 0, 22, 19, 0, 5, 19, - 11, 13, 19 +static const signed char _emoji_presentation_cond_actions[] = { + 27, 27, 27, 27, 19, 0, 7, 17, + 30, 21, 27, 27, 0, 30, 27, 9, + 7, 0, 5, 24, 19, 0, 27, 13, + 0, 19, 27, 0, 27, 13, 27, 7, + 0, 5, 24, 13, 7, 15, 7, 0, + 5, 24, 15, 7, 0, 11, 7, 0, + 5, 24, 0, 13, 19, 17, 0, 19, + 13, 19, 13, 13, 15, 15, 11, 13, + 0 }; -static const char _emoji_presentation_to_state_actions[] = { - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0 +static const signed char _emoji_presentation_to_state_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }; -static const char _emoji_presentation_from_state_actions[] = { - 0, 0, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0 +static const signed char _emoji_presentation_from_state_actions[] = { + 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }; -static const char _emoji_presentation_eof_trans[] = { - 1, 4, 0, 1, 17, 17, 17, 17, - 18, 18, 17, 17 +static const signed char _emoji_presentation_eof_trans[] = { + 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 0 }; static const int emoji_presentation_start = 2; @@ -84,197 +86,342 @@ static const int emoji_presentation_start = 2; static const int emoji_presentation_en_text_and_emoji_run = 2; -#line 9 "emoji_presentation_scanner.rl" +#line 9 "pango2/emoji_presentation_scanner.rl" -#line 78 "emoji_presentation_scanner.rl" +#line 81 "pango2/emoji_presentation_scanner.rl" static emoji_text_iter_t scan_emoji_presentation (emoji_text_iter_t p, - const emoji_text_iter_t pe, - bool* is_emoji) +const emoji_text_iter_t pe, +EmojiPresentation *state, +gboolean *explicit) { - emoji_text_iter_t te; - const emoji_text_iter_t eof = pe; - - unsigned act; - int cs; - - -#line 107 "emoji_presentation_scanner.c" - { - cs = emoji_presentation_start; - te = 0; - act = 0; - } - -#line 115 "emoji_presentation_scanner.c" - { - int _klen; - unsigned int _trans; - const char *_acts; - unsigned int _nacts; - const unsigned char *_keys; - - if ( p == pe ) - goto _test_eof; -_resume: - _acts = _emoji_presentation_actions + _emoji_presentation_from_state_actions[cs]; - _nacts = (unsigned int) *_acts++; - while ( _nacts-- > 0 ) { - switch ( *_acts++ ) { - case 1: + emoji_text_iter_t ts, te; + const emoji_text_iter_t eof = pe; + + unsigned act; + int cs; + + +#line 104 "pango2/emoji_presentation_scanner.c" + { + cs = (int)emoji_presentation_start; + ts = 0; + te = 0; + act = 0; + } + +#line 110 "pango2/emoji_presentation_scanner.c" + { + int _klen; + unsigned int _trans = 0; + const unsigned char * _keys; + const signed char * _acts; + unsigned int _nacts; + _resume: {} + if ( p == pe && p != eof ) + goto _out; + _acts = ( _emoji_presentation_actions + (_emoji_presentation_from_state_actions[cs])); + _nacts = (unsigned int)(*( _acts)); + _acts += 1; + while ( _nacts > 0 ) { + switch ( (*( _acts)) ) { + case 1: { + { #line 1 "NONE" - break; -#line 134 "emoji_presentation_scanner.c" - } - } - - _keys = _emoji_presentation_trans_keys + _emoji_presentation_key_offsets[cs]; - _trans = _emoji_presentation_index_offsets[cs]; - - _klen = _emoji_presentation_single_lengths[cs]; - if ( _klen > 0 ) { - const unsigned char *_lower = _keys; - const unsigned char *_mid; - const unsigned char *_upper = _keys + _klen - 1; - while (1) { - if ( _upper < _lower ) - break; - - _mid = _lower + ((_upper-_lower) >> 1); - if ( (*p) < *_mid ) - _upper = _mid - 1; - else if ( (*p) > *_mid ) - _lower = _mid + 1; - else { - _trans += (unsigned int)(_mid - _keys); - goto _match; - } - } - _keys += _klen; - _trans += _klen; - } - - _klen = _emoji_presentation_range_lengths[cs]; - if ( _klen > 0 ) { - const unsigned char *_lower = _keys; - const unsigned char *_mid; - const unsigned char *_upper = _keys + (_klen<<1) - 2; - while (1) { - if ( _upper < _lower ) - break; - - _mid = _lower + (((_upper-_lower) >> 1) & ~1); - if ( (*p) < _mid[0] ) - _upper = _mid - 2; - else if ( (*p) > _mid[1] ) - _lower = _mid + 2; - else { - _trans += (unsigned int)((_mid - _keys)>>1); - goto _match; - } - } - _trans += _klen; - } - -_match: - _trans = _emoji_presentation_indicies[_trans]; -_eof_trans: - cs = _emoji_presentation_trans_targs[_trans]; - - if ( _emoji_presentation_trans_actions[_trans] == 0 ) - goto _again; - - _acts = _emoji_presentation_actions + _emoji_presentation_trans_actions[_trans]; - _nacts = (unsigned int) *_acts++; - while ( _nacts-- > 0 ) - { - switch ( *_acts++ ) - { - case 2: + {ts = p;}} + +#line 129 "pango2/emoji_presentation_scanner.c" + + break; + } + } + _nacts -= 1; + _acts += 1; + } + + if ( p == eof ) { + if ( _emoji_presentation_eof_trans[cs] > 0 ) { + _trans = (unsigned int)_emoji_presentation_eof_trans[cs] - 1; + } + } + else { + _keys = ( _emoji_presentation_trans_keys + (_emoji_presentation_key_offsets[cs])); + _trans = (unsigned int)_emoji_presentation_index_offsets[cs]; + + _klen = (int)_emoji_presentation_single_lengths[cs]; + if ( _klen > 0 ) { + const unsigned char *_lower = _keys; + const unsigned char *_upper = _keys + _klen - 1; + const unsigned char *_mid; + while ( 1 ) { + if ( _upper < _lower ) { + _keys += _klen; + _trans += (unsigned int)_klen; + break; + } + + _mid = _lower + ((_upper-_lower) >> 1); + if ( ( (*( p))) < (*( _mid)) ) + _upper = _mid - 1; + else if ( ( (*( p))) > (*( _mid)) ) + _lower = _mid + 1; + else { + _trans += (unsigned int)(_mid - _keys); + goto _match; + } + } + } + + _klen = (int)_emoji_presentation_range_lengths[cs]; + if ( _klen > 0 ) { + const unsigned char *_lower = _keys; + const unsigned char *_upper = _keys + (_klen<<1) - 2; + const unsigned char *_mid; + while ( 1 ) { + if ( _upper < _lower ) { + _trans += (unsigned int)_klen; + break; + } + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( ( (*( p))) < (*( _mid)) ) + _upper = _mid - 2; + else if ( ( (*( p))) > (*( _mid + 1)) ) + _lower = _mid + 2; + else { + _trans += (unsigned int)((_mid - _keys)>>1); + break; + } + } + } + + _match: {} + } + cs = (int)_emoji_presentation_cond_targs[_trans]; + + if ( _emoji_presentation_cond_actions[_trans] != 0 ) { + + _acts = ( _emoji_presentation_actions + (_emoji_presentation_cond_actions[_trans])); + _nacts = (unsigned int)(*( _acts)); + _acts += 1; + while ( _nacts > 0 ) { + switch ( (*( _acts)) ) + { + case 2: { + { #line 1 "NONE" - {te = p+1;} - break; - case 3: -#line 74 "emoji_presentation_scanner.rl" - {act = 2;} - break; - case 4: -#line 75 "emoji_presentation_scanner.rl" - {act = 3;} - break; - case 5: -#line 73 "emoji_presentation_scanner.rl" - {te = p+1;{ *is_emoji = false; return te; }} - break; - case 6: -#line 74 "emoji_presentation_scanner.rl" - {te = p+1;{ *is_emoji = true; return te; }} - break; - case 7: -#line 75 "emoji_presentation_scanner.rl" - {te = p+1;{ *is_emoji = false; return te; }} - break; - case 8: -#line 74 "emoji_presentation_scanner.rl" - {te = p;p--;{ *is_emoji = true; return te; }} - break; - case 9: -#line 75 "emoji_presentation_scanner.rl" - {te = p;p--;{ *is_emoji = false; return te; }} - break; - case 10: -#line 74 "emoji_presentation_scanner.rl" - {{p = ((te))-1;}{ *is_emoji = true; return te; }} - break; - case 11: + {te = p+1;}} + +#line 210 "pango2/emoji_presentation_scanner.c" + + break; + } + case 3: { + { +#line 75 "pango2/emoji_presentation_scanner.rl" + {act = 2;}} + +#line 218 "pango2/emoji_presentation_scanner.c" + + break; + } + case 4: { + { +#line 76 "pango2/emoji_presentation_scanner.rl" + {act = 3;}} + +#line 226 "pango2/emoji_presentation_scanner.c" + + break; + } + case 5: { + { +#line 77 "pango2/emoji_presentation_scanner.rl" + {act = 4;}} + +#line 234 "pango2/emoji_presentation_scanner.c" + + break; + } + case 6: { + { +#line 78 "pango2/emoji_presentation_scanner.rl" + {act = 5;}} + +#line 242 "pango2/emoji_presentation_scanner.c" + + break; + } + case 7: { + { +#line 74 "pango2/emoji_presentation_scanner.rl" + {te = p+1;{ +#line 74 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_TEXT; *explicit = TRUE; return te; } + }} + +#line 253 "pango2/emoji_presentation_scanner.c" + + break; + } + case 8: { + { +#line 77 "pango2/emoji_presentation_scanner.rl" + {te = p+1;{ +#line 77 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = FALSE; return te; } + }} + +#line 264 "pango2/emoji_presentation_scanner.c" + + break; + } + case 9: { + { +#line 78 "pango2/emoji_presentation_scanner.rl" + {te = p+1;{ +#line 78 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_NONE; *explicit = TRUE; return te; } + }} + +#line 275 "pango2/emoji_presentation_scanner.c" + + break; + } + case 10: { + { +#line 76 "pango2/emoji_presentation_scanner.rl" + {te = p;p = p - 1;{ +#line 76 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = TRUE; return te; } + }} + +#line 286 "pango2/emoji_presentation_scanner.c" + + break; + } + case 11: { + { +#line 77 "pango2/emoji_presentation_scanner.rl" + {te = p;p = p - 1;{ +#line 77 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = FALSE; return te; } + }} + +#line 297 "pango2/emoji_presentation_scanner.c" + + break; + } + case 12: { + { +#line 78 "pango2/emoji_presentation_scanner.rl" + {te = p;p = p - 1;{ +#line 78 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_NONE; *explicit = TRUE; return te; } + }} + +#line 308 "pango2/emoji_presentation_scanner.c" + + break; + } + case 13: { + { +#line 77 "pango2/emoji_presentation_scanner.rl" + {p = ((te))-1; + { +#line 77 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = FALSE; return te; } + }} + +#line 320 "pango2/emoji_presentation_scanner.c" + + break; + } + case 14: { + { #line 1 "NONE" - { switch( act ) { - case 2: - {{p = ((te))-1;} *is_emoji = true; return te; } - break; - case 3: - {{p = ((te))-1;} *is_emoji = false; return te; } - break; - } - } - break; -#line 248 "emoji_presentation_scanner.c" - } - } - -_again: - _acts = _emoji_presentation_actions + _emoji_presentation_to_state_actions[cs]; - _nacts = (unsigned int) *_acts++; - while ( _nacts-- > 0 ) { - switch ( *_acts++ ) { - case 0: + {switch( act ) { + case 2: { + p = ((te))-1; + { +#line 75 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_TEXT; *explicit = FALSE; return te; } + break; + } + case 3: { + p = ((te))-1; + { +#line 76 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = TRUE; return te; } + break; + } + case 4: { + p = ((te))-1; + { +#line 77 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_EMOJI; *explicit = FALSE; return te; } + break; + } + case 5: { + p = ((te))-1; + { +#line 78 "pango2/emoji_presentation_scanner.rl" + *state = EMOJI_PRESENTATION_NONE; *explicit = TRUE; return te; } + break; + } + }} + } + +#line 358 "pango2/emoji_presentation_scanner.c" + + break; + } + } + _nacts -= 1; + _acts += 1; + } + + } + + if ( p == eof ) { + if ( cs >= 2 ) + goto _out; + } + else { + _acts = ( _emoji_presentation_actions + (_emoji_presentation_to_state_actions[cs])); + _nacts = (unsigned int)(*( _acts)); + _acts += 1; + while ( _nacts > 0 ) { + switch ( (*( _acts)) ) { + case 0: { + { #line 1 "NONE" - break; -#line 261 "emoji_presentation_scanner.c" - } - } - - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - if ( _emoji_presentation_eof_trans[cs] > 0 ) { - _trans = _emoji_presentation_eof_trans[cs] - 1; - goto _eof_trans; - } - } - - } - -#line 94 "emoji_presentation_scanner.rl" - - - /* Should not be reached. */ - *is_emoji = false; - return pe; + {ts = 0;}} + +#line 383 "pango2/emoji_presentation_scanner.c" + + break; + } + } + _nacts -= 1; + _acts += 1; + } + + p += 1; + goto _resume; + } + _out: {} + } + +#line 98 "pango2/emoji_presentation_scanner.rl" + + + /* Should not be reached. */ + *state = EMOJI_PRESENTATION_NONE; + *explicit = FALSE; + return pe; } diff --git a/pango2/emoji_presentation_scanner.rl b/pango2/emoji_presentation_scanner.rl index d9c26919..85892bf5 100644 --- a/pango2/emoji_presentation_scanner.rl +++ b/pango2/emoji_presentation_scanner.rl @@ -5,7 +5,7 @@ %%{ machine emoji_presentation; alphtype unsigned char; - write data noerror nofinal noentry; + write data noerror nofinal; }%% %%{ @@ -64,15 +64,18 @@ emoji_presentation = EMOJI_EMOJI_PRESENTATION | TAG_BASE | EMOJI_MODIFIER_BASE | emoji_run = emoji_presentation; +text_emoji = EMOJI_TEXT_PRESENTATION; text_presentation_emoji = any_emoji VS15; text_run = any; text_and_emoji_run := |* # In order to give the the VS15 sequences higher priority than detecting # emoji sequences they are listed first as scanner token here. -text_presentation_emoji => { *is_emoji = false; return te; }; -emoji_run => { *is_emoji = true; return te; }; -text_run => { *is_emoji = false; return te; }; +text_presentation_emoji => { *state = EMOJI_PRESENTATION_TEXT; *explicit = TRUE; return te; }; +text_emoji => { *state = EMOJI_PRESENTATION_TEXT; *explicit = FALSE; return te; }; +emoji_presentation_sequence => { *state = EMOJI_PRESENTATION_EMOJI; *explicit = TRUE; return te; }; +emoji_run => { *state = EMOJI_PRESENTATION_EMOJI; *explicit = FALSE; return te; }; +text_run => { *state = EMOJI_PRESENTATION_NONE; *explicit = TRUE; return te; }; *|; }%% @@ -80,7 +83,8 @@ text_run => { *is_emoji = false; return te; }; static emoji_text_iter_t scan_emoji_presentation (emoji_text_iter_t p, const emoji_text_iter_t pe, - bool* is_emoji) + EmojiPresentation *state, + gboolean *explicit) { emoji_text_iter_t ts, te; const emoji_text_iter_t eof = pe; @@ -94,6 +98,7 @@ scan_emoji_presentation (emoji_text_iter_t p, }%% /* Should not be reached. */ - *is_emoji = false; + *state = EMOJI_PRESENTATION_NONE; + *explicit = FALSE; return pe; } diff --git a/pango2/itemize.c b/pango2/itemize.c index 1cf4e6ea..537600d3 100644 --- a/pango2/itemize.c +++ b/pango2/itemize.c @@ -321,6 +321,7 @@ struct _ItemizeState Pango2WidthIter width_iter; Pango2EmojiIter emoji_iter; + Pango2EmojiPresentation preferred; Pango2Language *derived_lang; @@ -366,6 +367,7 @@ update_attr_iterator (ItemizeState *state) Pango2Language *old_lang; Pango2Attribute *attr; int end_index; + Pango2EmojiPresentation old_preferred; pango2_attr_iterator_range (state->attr_iter, NULL, &end_index); if (end_index < state->end - state->text) @@ -404,9 +406,15 @@ update_attr_iterator (ItemizeState *state) attr = find_attribute (state->extra_attrs, PANGO2_ATTR_GRAVITY_HINT); state->gravity_hint = attr == NULL ? state->context->gravity_hint : (Pango2GravityHint)attr->int_value; + old_preferred = state->preferred; + attr = find_attribute (state->extra_attrs, PANGO2_ATTR_EMOJI_PRESENTATION); + state->preferred = attr ? attr->int_value : state->context->presentation; + state->changed |= FONT_CHANGED; if (state->lang != old_lang) state->changed |= LANG_CHANGED; + if (state->preferred != old_preferred) + state->changed |= EMOJI_CHANGED; } static void @@ -460,6 +468,8 @@ itemize_state_init (ItemizeState *state, state->gravity_hint = state->context->gravity_hint; state->resolved_gravity = PANGO2_GRAVITY_AUTO; + state->preferred = context->presentation; + /* Initialize the attribute iterator */ if (cached_iter) @@ -505,11 +515,11 @@ itemize_state_init (ItemizeState *state, &state->script_end, &state->script); width_iter_init (&state->width_iter, text + start_index, length); - _pango2_emoji_iter_init (&state->emoji_iter, text + start_index, length); + pango2_emoji_iter_init (&state->emoji_iter, text + start_index, length); if (!PANGO2_GRAVITY_IS_VERTICAL (state->context->resolved_gravity)) state->width_iter.end = state->end; - else if (state->emoji_iter.is_emoji) + else if (pango2_emoji_iter_get (&state->emoji_iter, state->preferred) == EMOJI_PRESENTATION_EMOJI) state->width_iter.end = MAX (state->width_iter.end, state->emoji_iter.end); update_end (state); @@ -557,10 +567,10 @@ itemize_state_next (ItemizeState *state) } if (state->run_end == state->emoji_iter.end) { - _pango2_emoji_iter_next (&state->emoji_iter); + pango2_emoji_iter_next (&state->emoji_iter); state->changed |= EMOJI_CHANGED; - if (state->emoji_iter.is_emoji) + if (pango2_emoji_iter_get (&state->emoji_iter, state->preferred) == EMOJI_PRESENTATION_EMOJI) state->width_iter.end = MAX (state->width_iter.end, state->emoji_iter.end); } if (state->run_end == state->width_iter.end) @@ -874,16 +884,18 @@ itemize_state_update_for_new_run (ItemizeState *state) if (!state->current_fonts) { - gboolean is_emoji = state->emoji_iter.is_emoji; + gboolean is_emoji = pango2_emoji_iter_get (&state->emoji_iter, state->preferred) == EMOJI_PRESENTATION_EMOJI; + if (is_emoji && !state->emoji_font_desc) { state->emoji_font_desc = pango2_font_description_copy_static (state->font_desc); pango2_font_description_set_family_static (state->emoji_font_desc, "emoji"); } + state->current_fonts = pango2_font_map_load_fontset (state->context->font_map, - state->context, - is_emoji ? state->emoji_font_desc : state->font_desc, - state->derived_lang); + state->context, + is_emoji ? state->emoji_font_desc : state->font_desc, + state->derived_lang); state->cache = get_font_cache (state->current_fonts); } @@ -1022,7 +1034,7 @@ itemize_state_finish (ItemizeState *state) pango2_font_description_free (state->font_desc); pango2_font_description_free (state->emoji_font_desc); width_iter_fini (&state->width_iter); - _pango2_emoji_iter_fini (&state->emoji_iter); + pango2_emoji_iter_fini (&state->emoji_iter); if (state->current_fonts) g_object_unref (state->current_fonts); diff --git a/pango2/pango-attr-list.c b/pango2/pango-attr-list.c index eae66d87..a47b6814 100644 --- a/pango2/pango-attr-list.c +++ b/pango2/pango-attr-list.c @@ -1264,6 +1264,10 @@ pango2_attr_list_from_string (const char *text) if (!is_valid_end_char (*endp)) goto fail; break; + case PANGO2_ATTR_EMOJI_PRESENTATION: + ENUM_ATTR(emoji_presentation, Pango2EmojiPresentation, PANGO2_EMOJI_PRESENTATION_AUTO, PANGO2_EMOJI_PRESENTATION_EMOJI); + break; + case PANGO2_ATTR_SHAPE: default: g_assert_not_reached (); diff --git a/pango2/pango-attributes.c b/pango2/pango-attributes.c index 2c72c9c0..749af63f 100644 --- a/pango2/pango-attributes.c +++ b/pango2/pango-attributes.c @@ -864,6 +864,25 @@ pango2_attr_shape_new (Pango2Rectangle *ink_rect, return attr; } +/** + * pango2_attr_emoji_presentation_new: + * @presentation: a `Pango2EmojiPresentation` value + * + * Creates a new Emoji presentation attribute. + * + * Emoji presentation attributes override the preference for + * whether Emoji should be presented a text or as color Emoji. + * + * Return value: (transfer full): the newly allocated + * `Pango2Attribute`, which should be freed with + * [method@Pango2.Attribute.destroy] + */ +Pango2Attribute * +pango2_attr_emoji_presentation_new (Pango2EmojiPresentation presentation) +{ + return pango2_attr_int_new (PANGO2_ATTR_EMOJI_PRESENTATION, presentation); +} + /* }}} */ /* {{{ Private API */ diff --git a/pango2/pango-attributes.h b/pango2/pango-attributes.h index 990d90e1..6a960d73 100644 --- a/pango2/pango-attributes.h +++ b/pango2/pango-attributes.h @@ -73,6 +73,7 @@ G_BEGIN_DECLS * @PANGO2_ATTR_LINE_SPACING: space to add to the leading from the * font metrics (if not overridden by a line height attribute) * @PANGO2_ATTR_SHAPE: override glyph shapes (requires renderer support) + * @PANGO2_ATTR_EMOJI_PRESENTATION: override Emoji presentation * * `Pango2AttrType` contains predefined attribute types. * @@ -122,6 +123,7 @@ typedef enum PANGO2_ATTR_FONT_SCALE = PANGO2_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES), PANGO2_ATTR_LINE_SPACING = PANGO2_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES), PANGO2_ATTR_SHAPE = PANGO2_ATTR_TYPE (POINTER, ITEMIZATION, OVERRIDES), + PANGO2_ATTR_EMOJI_PRESENTATION = PANGO2_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES), } Pango2AttrType; #undef PANGO2_ATTR_TYPE @@ -319,4 +321,7 @@ Pango2Attribute * pango2_attr_shape_new (Pango2Rectang Pango2AttrDataCopyFunc copy, GDestroyNotify destroy); +PANGO2_AVAILABLE_IN_ALL +Pango2Attribute * pango2_attr_emoji_presentation_new (Pango2EmojiPresentation presentation); + G_END_DECLS diff --git a/pango2/pango-context-private.h b/pango2/pango-context-private.h index 4ec65da6..31c19d67 100644 --- a/pango2/pango-context-private.h +++ b/pango2/pango-context-private.h @@ -50,6 +50,8 @@ struct _Pango2Context GQuark palette; + Pango2EmojiPresentation presentation; + #ifdef HAVE_CAIRO gboolean set_options_explicit; diff --git a/pango2/pango-context.c b/pango2/pango-context.c index a6b6ee24..f6d28e11 100644 --- a/pango2/pango-context.c +++ b/pango2/pango-context.c @@ -70,6 +70,7 @@ enum { PROP_MATRIX, PROP_ROUND_GLYPH_POSITIONS, PROP_PALETTE, + PROP_EMOJI_PRESENTATION, N_PROPERTIES }; @@ -90,6 +91,7 @@ pango2_context_init (Pango2Context *context) context->font_map = NULL; context->round_glyph_positions = TRUE; context->palette = g_quark_from_static_string (PANGO2_COLOR_PALETTE_DEFAULT); + context->presentation = PANGO2_EMOJI_PRESENTATION_AUTO; context->font_desc = pango2_font_description_new (); pango2_font_description_set_family_static (context->font_desc, "serif"); @@ -146,6 +148,10 @@ pango2_context_set_property (GObject *object, pango2_context_set_palette (context, g_value_get_string (value)); break; + case PROP_EMOJI_PRESENTATION: + pango2_context_set_emoji_presentation (context, g_value_get_enum (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -197,6 +203,10 @@ pango2_context_get_property (GObject *object, g_value_set_string (value, pango2_context_get_palette (context)); break; + case PROP_EMOJI_PRESENTATION: + g_value_set_enum (value, pango2_context_get_emoji_presentation (context)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -326,6 +336,17 @@ pango2_context_class_init (Pango2ContextClass *klass) g_param_spec_string ("palette", NULL, NULL, "default", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * Pango2Context:emoji-presentation: (attributes org.gtk.Property.get=pango2_context_get_emoji_presentation org.gtk.Property.set=pango2_context_set_emoji_presentation) + * + * The preferred Emoji presentation style. + */ + properties[PROP_EMOJI_PRESENTATION] = + g_param_spec_enum ("emoji-presentation", NULL, NULL, + PANGO2_TYPE_EMOJI_PRESENTATION, + PANGO2_EMOJI_PRESENTATION_AUTO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPERTIES, properties); } @@ -1119,3 +1140,24 @@ pango2_context_get_palette (Pango2Context *context) return g_quark_to_string (context->palette); } + +void +pango2_context_set_emoji_presentation (Pango2Context *context, + Pango2EmojiPresentation presentation) +{ + g_return_if_fail (PANGO2_IS_CONTEXT (context)); + + if (context->presentation == presentation) + return; + + context->presentation = presentation; + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_EMOJI_PRESENTATION]); +} + +Pango2EmojiPresentation +pango2_context_get_emoji_presentation (Pango2Context *context) +{ + g_return_val_if_fail (PANGO2_IS_CONTEXT (context), PANGO2_EMOJI_PRESENTATION_AUTO); + + return context->presentation; +} diff --git a/pango2/pango-context.h b/pango2/pango-context.h index 21901498..6a2df34e 100644 --- a/pango2/pango-context.h +++ b/pango2/pango-context.h @@ -105,6 +105,11 @@ void pango2_context_set_palette (Pango2Context PANGO2_AVAILABLE_IN_ALL const char * pango2_context_get_palette (Pango2Context *context); +PANGO2_AVAILABLE_IN_ALL +void pango2_context_set_emoji_presentation (Pango2Context *context, + Pango2EmojiPresentation presentation); +PANGO2_AVAILABLE_IN_ALL +Pango2EmojiPresentation pango2_context_get_emoji_presentation (Pango2Context *context); G_END_DECLS diff --git a/pango2/pango-emoji-private.h b/pango2/pango-emoji-private.h index f7578bf4..30a2fa62 100644 --- a/pango2/pango-emoji-private.h +++ b/pango2/pango-emoji-private.h @@ -20,6 +20,7 @@ #pragma once #include <glib.h> +#include "pango-types.h" gboolean _pango2_Is_Emoji_Base_Character (gunichar ch); @@ -29,13 +30,21 @@ _pango2_Is_Emoji_Extended_Pictographic (gunichar ch); typedef struct _Pango2EmojiIter Pango2EmojiIter; +typedef enum { + EMOJI_PRESENTATION_NONE, + EMOJI_PRESENTATION_TEXT, + EMOJI_PRESENTATION_EMOJI, +} EmojiPresentation; + struct _Pango2EmojiIter { const char *text_start; const char *text_end; const char *start; const char *end; - gboolean is_emoji; + + gboolean explicit; + EmojiPresentation state; unsigned char *types; unsigned int n_chars; @@ -43,12 +52,17 @@ struct _Pango2EmojiIter }; Pango2EmojiIter * -_pango2_emoji_iter_init (Pango2EmojiIter *iter, - const char *text, - int length); +pango2_emoji_iter_init (Pango2EmojiIter *iter, + const char *text, + int length); gboolean -_pango2_emoji_iter_next (Pango2EmojiIter *iter); +pango2_emoji_iter_next (Pango2EmojiIter *iter); + void -_pango2_emoji_iter_fini (Pango2EmojiIter *iter); +pango2_emoji_iter_fini (Pango2EmojiIter *iter); + +EmojiPresentation +pango2_emoji_iter_get (Pango2EmojiIter *iter, + Pango2EmojiPresentation preferred); diff --git a/pango2/pango-emoji.c b/pango2/pango-emoji.c index aa82e9ec..15a7129e 100644 --- a/pango2/pango-emoji.c +++ b/pango2/pango-emoji.c @@ -49,6 +49,7 @@ #include <stdlib.h> #include <string.h> +#include "pango-types.h" #include "pango-emoji-private.h" #include "pango-emoji-table.h" @@ -200,21 +201,21 @@ _pango2_EmojiSegmentationCategory (gunichar codepoint) return kMaxEmojiScannerCategory; } - typedef gboolean bool; enum { false = FALSE, true = TRUE }; typedef unsigned char *emoji_text_iter_t; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-default" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #include "emoji_presentation_scanner.c" #pragma GCC diagnostic pop Pango2EmojiIter * -_pango2_emoji_iter_init (Pango2EmojiIter *iter, - const char *text, - int length) +pango2_emoji_iter_init (Pango2EmojiIter *iter, + const char *text, + int length) { unsigned int n_chars = g_utf8_strlen (text, length); unsigned char *types = g_malloc (n_chars); @@ -233,28 +234,29 @@ _pango2_emoji_iter_init (Pango2EmojiIter *iter, iter->text_end = text + length; else iter->text_end = text + strlen (text); - iter->is_emoji = FALSE; + iter->state = EMOJI_PRESENTATION_NONE; iter->types = types; iter->n_chars = n_chars; iter->cursor = 0; - _pango2_emoji_iter_next (iter); + pango2_emoji_iter_next (iter); return iter; } void -_pango2_emoji_iter_fini (Pango2EmojiIter *iter) +pango2_emoji_iter_fini (Pango2EmojiIter *iter) { g_free (iter->types); } gboolean -_pango2_emoji_iter_next (Pango2EmojiIter *iter) +pango2_emoji_iter_next (Pango2EmojiIter *iter) { unsigned int old_cursor, cursor; - gboolean is_emoji; + EmojiPresentation state; + gboolean explicit; if (iter->end >= iter->text_end) return FALSE; @@ -263,27 +265,54 @@ _pango2_emoji_iter_next (Pango2EmojiIter *iter) old_cursor = cursor = iter->cursor; cursor = scan_emoji_presentation (iter->types + cursor, - iter->types + iter->n_chars, - &is_emoji) - iter->types; + iter->types + iter->n_chars, + &state, &explicit) - iter->types; do { iter->cursor = cursor; - iter->is_emoji = is_emoji; + iter->state = state; + iter->explicit = explicit; if (cursor == iter->n_chars) break; cursor = scan_emoji_presentation (iter->types + cursor, - iter->types + iter->n_chars, - &is_emoji) - iter->types; + iter->types + iter->n_chars, + &state, &explicit) - iter->types; } - while (iter->is_emoji == is_emoji); + while (iter->state == state && iter->explicit == explicit); iter->end = g_utf8_offset_to_pointer (iter->start, iter->cursor - old_cursor); return TRUE; } +EmojiPresentation +pango2_emoji_iter_get (Pango2EmojiIter *iter, + Pango2EmojiPresentation preferred) +{ + gboolean explicit; + + if (preferred == PANGO2_EMOJI_PRESENTATION_AUTO) + explicit = TRUE; + else + explicit = iter->explicit; + + switch (iter->state) + { + case EMOJI_PRESENTATION_NONE: + return EMOJI_PRESENTATION_NONE; + break; + case EMOJI_PRESENTATION_TEXT: + return explicit ? EMOJI_PRESENTATION_TEXT : (EmojiPresentation)preferred; + break; + case EMOJI_PRESENTATION_EMOJI: + return explicit ? EMOJI_PRESENTATION_EMOJI : (EmojiPresentation)preferred; + break; + default: + g_assert_not_reached (); + } +} /********************************************************** * End of code from Chromium diff --git a/pango2/pango-markup.c b/pango2/pango-markup.c index 91c9aded..6b1d1075 100644 --- a/pango2/pango-markup.c +++ b/pango2/pango-markup.c @@ -1349,6 +1349,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *text_transform = NULL; const char *segment = NULL; const char *font_scale = NULL; + const char *emoji_presentation = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -1388,6 +1389,9 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, case 'c': CHECK_ATTRIBUTE2 (foreground, "color"); break; + case 'e': + CHECK_ATTRIBUTE (emoji_presentation); + break; case 'f': CHECK_ATTRIBUTE (fallback); CHECK_ATTRIBUTE2 (desc, "font"); @@ -1892,6 +1896,16 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, } } + if (G_UNLIKELY (emoji_presentation)) + { + Pango2EmojiPresentation ep; + + if (!span_parse_enum ("emoji_presentation", emoji_presentation, PANGO2_TYPE_EMOJI_PRESENTATION, (int*)(void*)&ep, line_number, error)) + goto error; + + add_attribute (tag, pango2_attr_emoji_presentation_new (ep)); + } + return TRUE; error: diff --git a/pango2/pango-types.h b/pango2/pango-types.h index fb045f14..7dfb9a99 100644 --- a/pango2/pango-types.h +++ b/pango2/pango-types.h @@ -342,6 +342,30 @@ typedef enum */ #define PANGO2_COLOR_PALETTE_DARK "dark" +/** + * Pango2EmojiPresentation: + * @PANGO2_EMOJI_PRESENTATION_AUTO: Present Emoji with to their + * default presentation according to Unicode + * @PANGO2_EMOJI_PRESENTATION_TEXT: Prefer text presentation + * @PANGO2_EMOJI_PRESENTATION_EMOJI: Prefer Emoji presentation + * + * `Pango2EmojiPresentation` describes a preference for Emoji + * presentation style. + * + * See [method@Pango2.Context.set_emoji_presentation] or + * [func@Pango2.attr_emoji_presentation_new] for ways to communicate + * Emoji presentation style preferences to Pango. + * + * Note that even with such a preference, Pango will respect + * Emoji presentation style if it has been explicitly selected + * with a Unicode [variation selector](https://unicode.org/reports/tr51/#Emoji_Variation_Sequences). + */ +typedef enum { + PANGO2_EMOJI_PRESENTATION_AUTO, + PANGO2_EMOJI_PRESENTATION_TEXT, + PANGO2_EMOJI_PRESENTATION_EMOJI +} Pango2EmojiPresentation; + /* * PANGO2_DECLARE_INTERNAL_TYPE: * @ModuleObjName: The name of the new type, in camel case (like GtkWidget) diff --git a/pango2/serializer.c b/pango2/serializer.c index 8f18f11b..cf0f57da 100644 --- a/pango2/serializer.c +++ b/pango2/serializer.c @@ -267,6 +267,13 @@ static const char *tab_unit_names[] = { NULL }; +static const char *emoji_presentation_names[] = { + "auto", + "text", + "emoji", + NULL +}; + /* }}} */ /* {{{ Serialization */ @@ -1124,6 +1131,10 @@ attr_for_type (GtkJsonParser *parser, attr = pango2_attr_paragraph_new (); break; + case PANGO2_ATTR_EMOJI_PRESENTATION: + attr = pango2_attr_emoji_presentation_new ((Pango2EmojiPresentation) parser_select_string (parser, emoji_presentation_names)); + break; + } attr->start_index = start; |