summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2022-07-08 21:03:08 -0400
committerMatthias Clasen <mclasen@redhat.com>2022-07-09 12:07:38 -0400
commitfe4fc164f9c2a7d6d5c95cd532025fc75a59fc93 (patch)
tree626112d298388b85e788a0341b042c6fe3e9340c
parent02a06423e31bcfb65ef314f01096a1de2d780654 (diff)
downloadpango-fe4fc164f9c2a7d6d5c95cd532025fc75a59fc93.tar.gz
Support Emoji presentation
Add a context property and attribute for influencing the presentation of Emoji. Emoji whose presentation is explicitly set in the text via variation selectors are not changed. Related: #298
-rw-r--r--pango2/emoji_presentation_scanner.c615
-rw-r--r--pango2/emoji_presentation_scanner.rl17
-rw-r--r--pango2/itemize.c30
-rw-r--r--pango2/pango-attr-list.c4
-rw-r--r--pango2/pango-attributes.c19
-rw-r--r--pango2/pango-attributes.h5
-rw-r--r--pango2/pango-context-private.h2
-rw-r--r--pango2/pango-context.c42
-rw-r--r--pango2/pango-context.h5
-rw-r--r--pango2/pango-emoji-private.h26
-rw-r--r--pango2/pango-emoji.c59
-rw-r--r--pango2/pango-markup.c14
-rw-r--r--pango2/pango-types.h24
-rw-r--r--pango2/serializer.c11
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;