summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pango/emoji_presentation_scanner.c497
-rw-r--r--pango/emoji_presentation_scanner.rl96
-rw-r--r--pango/pango-emoji-private.h7
-rw-r--r--pango/pango-emoji.c256
4 files changed, 740 insertions, 116 deletions
diff --git a/pango/emoji_presentation_scanner.c b/pango/emoji_presentation_scanner.c
new file mode 100644
index 00000000..43872abb
--- /dev/null
+++ b/pango/emoji_presentation_scanner.c
@@ -0,0 +1,497 @@
+// 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.
+
+
+static const char _emoji_presentation_actions[] = {
+ 0, 1, 0, 1, 1, 1, 5, 1,
+ 6, 1, 7, 1, 8, 1, 9, 2,
+ 2, 3, 2, 2, 4, 0
+};
+
+static const char _emoji_presentation_key_offsets[] = {
+ 0, 3, 8, 9, 13, 15, 22, 26,
+ 33, 42, 52, 63, 71, 82, 92, 103,
+ 115, 116, 121, 0
+};
+
+static const unsigned char _emoji_presentation_trans_keys[] = {
+ 9u, 10u, 12u, 3u, 7u, 13u, 0u, 2u,
+ 6u, 10u, 12u, 8u, 9u, 14u, 15u, 2u,
+ 3u, 6u, 7u, 13u, 0u, 1u, 9u, 10u,
+ 11u, 12u, 2u, 3u, 6u, 7u, 13u, 0u,
+ 1u, 2u, 3u, 6u, 7u, 10u, 12u, 13u,
+ 0u, 1u, 2u, 3u, 6u, 7u, 9u, 10u,
+ 12u, 13u, 0u, 1u, 2u, 3u, 4u, 6u,
+ 7u, 9u, 10u, 12u, 13u, 0u, 1u, 2u,
+ 3u, 6u, 7u, 10u, 13u, 0u, 1u, 2u,
+ 3u, 6u, 7u, 9u, 10u, 12u, 13u, 14u,
+ 0u, 1u, 2u, 3u, 4u, 6u, 7u, 10u,
+ 12u, 13u, 0u, 1u, 2u, 3u, 6u, 7u,
+ 9u, 10u, 11u, 12u, 13u, 0u, 1u, 2u,
+ 3u, 4u, 6u, 7u, 9u, 10u, 11u, 12u,
+ 13u, 0u, 1u, 6u, 10u, 11u, 12u, 8u,
+ 9u, 2u, 3u, 6u, 7u, 9u, 10u, 11u,
+ 12u, 13u, 14u, 0u, 1u, 0u
+};
+
+static const char _emoji_presentation_single_lengths[] = {
+ 3, 3, 1, 2, 2, 5, 4, 5,
+ 7, 8, 9, 6, 9, 8, 9, 10,
+ 1, 3, 10, 0
+};
+
+static const char _emoji_presentation_range_lengths[] = {
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 1, 0
+};
+
+static const char _emoji_presentation_index_offsets[] = {
+ 0, 4, 9, 11, 15, 18, 25, 30,
+ 37, 46, 56, 67, 75, 86, 96, 107,
+ 119, 121, 126, 0
+};
+
+static const char _emoji_presentation_trans_cond_spaces[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0
+};
+
+static const short _emoji_presentation_trans_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 0
+};
+
+static const char _emoji_presentation_trans_lengths[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 0
+};
+
+static const char _emoji_presentation_cond_keys[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_cond_targs[] = {
+ 7, 1, 11, 5, 13, 8, 8, 8,
+ 5, 7, 5, 1, 11, 7, 5, 4,
+ 7, 5, 14, 15, 16, 17, 18, 6,
+ 5, 7, 1, 5, 11, 5, 9, 10,
+ 2, 3, 12, 0, 5, 9, 10, 2,
+ 3, 1, 11, 12, 0, 5, 9, 10,
+ 2, 3, 7, 1, 11, 12, 0, 5,
+ 9, 10, 11, 2, 3, 7, 1, 11,
+ 12, 0, 5, 9, 10, 2, 3, 1,
+ 12, 0, 5, 9, 10, 2, 3, 7,
+ 1, 11, 12, 4, 0, 5, 9, 10,
+ 11, 2, 3, 1, 11, 12, 0, 5,
+ 9, 10, 2, 3, 7, 1, 5, 11,
+ 12, 0, 5, 9, 10, 11, 2, 3,
+ 7, 1, 5, 11, 12, 0, 5, 7,
+ 5, 1, 5, 11, 7, 5, 9, 10,
+ 2, 3, 7, 1, 5, 11, 12, 4,
+ 0, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 0
+};
+
+static const char _emoji_presentation_cond_actions[] = {
+ 15, 0, 15, 11, 15, 15, 15, 15,
+ 13, 15, 11, 0, 15, 15, 11, 0,
+ 15, 11, 15, 15, 0, 18, 15, 18,
+ 5, 15, 0, 5, 15, 9, 15, 15,
+ 0, 0, 15, 0, 7, 15, 15, 0,
+ 0, 0, 15, 15, 0, 7, 15, 15,
+ 0, 0, 15, 0, 15, 15, 0, 7,
+ 15, 15, 15, 0, 0, 15, 0, 15,
+ 15, 0, 7, 15, 15, 0, 0, 0,
+ 15, 0, 7, 15, 15, 0, 0, 15,
+ 0, 15, 15, 0, 0, 7, 15, 15,
+ 15, 0, 0, 0, 15, 15, 0, 7,
+ 15, 15, 0, 0, 15, 0, 5, 15,
+ 15, 0, 7, 15, 15, 15, 0, 0,
+ 15, 0, 5, 15, 15, 0, 7, 15,
+ 9, 0, 5, 15, 15, 9, 15, 15,
+ 0, 0, 15, 0, 5, 15, 15, 0,
+ 0, 7, 11, 13, 11, 11, 11, 9,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 9, 9, 7, 0
+};
+
+static const char _emoji_presentation_to_state_actions[] = {
+ 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_from_state_actions[] = {
+ 0, 0, 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_eof_cond_spaces[] = {
+ -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0
+};
+
+static const char _emoji_presentation_eof_cond_key_offs[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_eof_cond_key_lens[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_eof_cond_keys[] = {
+ 0
+};
+
+static const short _emoji_presentation_eof_trans[] = {
+ 139, 140, 141, 142, 143, 0, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153,
+ 154, 155, 156, 0
+};
+
+static const char _emoji_presentation_nfa_targs[] = {
+ 0, 0
+};
+
+static const char _emoji_presentation_nfa_offsets[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char _emoji_presentation_nfa_push_actions[] = {
+ 0, 0
+};
+
+static const char _emoji_presentation_nfa_pop_trans[] = {
+ 0, 0
+};
+
+static const int emoji_presentation_start = 5;
+
+static const int emoji_presentation_en_text_and_emoji_run = 5;
+
+
+
+
+
+static gboolean
+scan_emoji_presentation (const unsigned char* buffer,
+unsigned buffer_size,
+unsigned cursor,
+unsigned* last,
+unsigned* end)
+{
+ const unsigned char *p = buffer + cursor;
+ const unsigned char *pe, *eof, *ts, *te;
+ unsigned act;
+ int cs;
+ pe = eof = buffer + buffer_size;
+
+
+ {
+ cs = (int)emoji_presentation_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+ {
+ int _cpc;
+ int _klen;const char * _cekeys;unsigned int _trans = 0;const unsigned char * _keys;const char * _acts;unsigned int _nacts; {
+ if ( p == pe )
+ goto _test_eof;
+ _resume: {
+ _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"
+ {ts = p;}}
+ break; }
+ }
+ _nacts -= 1;
+ _acts += 1;
+ }
+
+ _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 )
+ 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 += (unsigned int)_klen;
+ }
+
+ _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 )
+ 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);
+ goto _match;
+ }
+ }
+ _trans += (unsigned int)_klen;
+ }
+
+ _match: {
+ goto _match_cond;
+ }
+ }
+ _match_cond: {
+ cs = (int)_emoji_presentation_cond_targs[_trans];
+
+ if ( _emoji_presentation_cond_actions[_trans] == 0 )
+ goto _again;
+
+ _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 71 "emoji_presentation_scanner.rl"
+ {act = 1;}}
+ break; }
+ case 4: {
+ {
+ #line 72 "emoji_presentation_scanner.rl"
+ {act = 2;}}
+ break; }
+ case 5: {
+ {
+ #line 72 "emoji_presentation_scanner.rl"
+ {te = p+1;{
+ #line 72 "emoji_presentation_scanner.rl"
+ found_text_presentation_sequence }}}
+ break; }
+ case 6: {
+ {
+ #line 71 "emoji_presentation_scanner.rl"
+ {te = p;p = p - 1;{
+ #line 71 "emoji_presentation_scanner.rl"
+ found_emoji_presentation_sequence }}}
+ break; }
+ case 7: {
+ {
+ #line 72 "emoji_presentation_scanner.rl"
+ {te = p;p = p - 1;{
+ #line 72 "emoji_presentation_scanner.rl"
+ found_text_presentation_sequence }}}
+ break; }
+ case 8: {
+ {
+ #line 71 "emoji_presentation_scanner.rl"
+ {p = ((te))-1;
+ {
+ #line 71 "emoji_presentation_scanner.rl"
+ found_emoji_presentation_sequence }}}
+ break; }
+ case 9: {
+ {
+ #line 1 "NONE"
+ {switch( act ) {
+ case 1: {
+ p = ((te))-1;
+ {
+ #line 71 "emoji_presentation_scanner.rl"
+ found_emoji_presentation_sequence } break; }
+ case 2: {
+ p = ((te))-1;
+ {
+ #line 72 "emoji_presentation_scanner.rl"
+ found_text_presentation_sequence } break; }
+ }}
+ }
+ break; }
+ }
+ _nacts -= 1;
+ _acts += 1;
+ }
+
+
+ }
+ _again: {
+ _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"
+ {ts = 0;}}
+ break; }
+ }
+ _nacts -= 1;
+ _acts += 1;
+ }
+
+ p += 1;
+ if ( p != pe )
+ goto _resume;
+ }
+ _test_eof: { {}
+ if ( p == eof )
+ {
+ if ( _emoji_presentation_eof_cond_spaces[cs] != -1 ) {
+ _cekeys = ( _emoji_presentation_eof_cond_keys + (_emoji_presentation_eof_cond_key_offs[cs]));
+ _klen = (int)_emoji_presentation_eof_cond_key_lens[cs];
+ _cpc = 0;
+ {
+ const char *_lower = _cekeys;
+ const char *_upper = _cekeys + _klen - 1;
+ const char *_mid;
+ while ( 1 ) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( _cpc < (int)(*( _mid)) )
+ _upper = _mid - 1;
+ else if ( _cpc > (int)(*( _mid)) )
+ _lower = _mid + 1;
+ else {
+ goto _ok;
+ }
+ }
+ cs = -1;
+ goto _out;
+ }
+ _ok: {}
+ }
+ if ( _emoji_presentation_eof_trans[cs] > 0 ) {
+ _trans = (unsigned int)_emoji_presentation_eof_trans[cs] - 1;
+ goto _match_cond;
+ }
+ }
+
+ }
+ _out: { {}
+ }
+ }
+ }
+
+ return FALSE;
+}
+
diff --git a/pango/emoji_presentation_scanner.rl b/pango/emoji_presentation_scanner.rl
new file mode 100644
index 00000000..5eea495a
--- /dev/null
+++ b/pango/emoji_presentation_scanner.rl
@@ -0,0 +1,96 @@
+// 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.
+
+%%{
+ machine emoji_presentation;
+ alphtype unsigned char;
+ write data noerror nofinal noentry;
+}%%
+
+%%{
+
+EMOJI = 0;
+EMOJI_TEXT_PRESENTATION = 1;
+EMOJI_EMOJI_PRESENTATION = 2;
+EMOJI_MODIFIER_BASE = 3;
+EMOJI_MODIFIER = 4;
+EMOJI_VS_BASE = 5;
+REGIONAL_INDICATOR = 6;
+KEYCAP_BASE = 7;
+COMBINING_ENCLOSING_KEYCAP = 8;
+COMBINING_ENCLOSING_CIRCLE_BACKSLASH = 9;
+ZWJ = 10;
+VS15 = 11;
+VS16 = 12;
+TAG_BASE = 13;
+TAG_SEQUENCE = 14;
+TAG_TERM = 15;
+
+any_emoji = EMOJI_TEXT_PRESENTATION | EMOJI_EMOJI_PRESENTATION | KEYCAP_BASE |
+ EMOJI_MODIFIER_BASE | TAG_BASE | EMOJI;
+
+emoji_combining_encloding_circle_backslash_sequence = any_emoji
+ COMBINING_ENCLOSING_CIRCLE_BACKSLASH;
+
+# This could be sharper than any_emoji by restricting this only to valid
+# variation sequences:
+# https://www.unicode.org/Public/emoji/11.0/emoji-variation-sequences.txt
+# However, implementing
+# https://www.unicode.org/reports/tr51/#def_emoji_presentation_sequence is
+# sufficient for our purposes here.
+emoji_presentation_sequence = any_emoji VS16;
+
+emoji_modifier_sequence = EMOJI_MODIFIER_BASE EMOJI_MODIFIER;
+
+emoji_flag_sequence = REGIONAL_INDICATOR REGIONAL_INDICATOR;
+
+# Here we only allow the valid tag sequences
+# https://www.unicode.org/reports/tr51/#valid-emoji-tag-sequences, instead of
+# all well-formed ones defined in
+# https://www.unicode.org/reports/tr51/#def_emoji_tag_sequence
+emoji_tag_sequence = TAG_BASE TAG_SEQUENCE+ TAG_TERM;
+
+emoji_keycap_sequence = KEYCAP_BASE COMBINING_ENCLOSING_KEYCAP;
+
+emoji_zwj_element = emoji_presentation_sequence | emoji_modifier_sequence | any_emoji;
+
+emoji_zwj_sequence = emoji_zwj_element ( ZWJ emoji_zwj_element )+;
+
+emoji_presentation = EMOJI_EMOJI_PRESENTATION | TAG_BASE | EMOJI_MODIFIER_BASE |
+ emoji_presentation_sequence | emoji_modifier_sequence | emoji_flag_sequence |
+ emoji_tag_sequence | emoji_keycap_sequence | emoji_zwj_sequence |
+ emoji_combining_encloding_circle_backslash_sequence;
+
+emoji_run = emoji_presentation+;
+
+text_presentation_emoji = any_emoji VS15;
+text_run = text_presentation_emoji | any;
+
+text_and_emoji_run := |*
+emoji_run => { found_emoji_presentation_sequence };
+text_run => { found_text_presentation_sequence };
+*|;
+
+}%%
+
+static gboolean
+scan_emoji_presentation (const unsigned char* buffer,
+ unsigned buffer_size,
+ unsigned cursor,
+ unsigned* last,
+ unsigned* end)
+{
+ const unsigned char *p = buffer + cursor;
+ const unsigned char *pe, *eof, *ts, *te;
+ unsigned act;
+ int cs;
+ pe = eof = buffer + buffer_size;
+
+ %%{
+ write init;
+ write exec;
+ }%%
+ return FALSE;
+}
+
diff --git a/pango/pango-emoji-private.h b/pango/pango-emoji-private.h
index eb8a52a7..a360b37a 100644
--- a/pango/pango-emoji-private.h
+++ b/pango/pango-emoji-private.h
@@ -33,6 +33,13 @@ struct _PangoEmojiIter
const gchar *start;
const gchar *end;
gboolean is_emoji;
+
+ const gchar *token_start;
+ const gchar *token_end;
+
+ const unsigned char *types;
+ unsigned int n_chars;
+ unsigned int cursor;
};
PangoEmojiIter *
diff --git a/pango/pango-emoji.c b/pango/pango-emoji.c
index 29472452..46ab5b3f 100644
--- a/pango/pango-emoji.c
+++ b/pango/pango-emoji.c
@@ -18,11 +18,27 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
- * Implementation of pango_emoji_iter is derived from Chromium:
+ * Implementation of pango_emoji_iter is based on Chromium's Ragel-based
+ * parser:
*
- * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/fonts/FontFallbackPriority.h
- * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/text/CharacterEmoji.cpp
- * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/fonts/SymbolsIterator.cpp
+ * https://chromium-review.googlesource.com/c/chromium/src/+/1264577
+ *
+ * The grammar file emoji_presentation_scanner.rl was just modified to
+ * adapt the function signature and variables to our usecase. The
+ * grammar itself was NOT modified:
+ *
+ * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/emoji_presentation_scanner.rl
+ *
+ * The emoji_presentation_scanner.c is generated from .rl file by
+ * running ragel on it.
+ *
+ * The categorization is also based on:
+ *
+ * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
+ *
+ * The iterator next() is based on:
+ *
+ * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
*
* // Copyright 2015 The Chromium Authors. All rights reserved.
* // Use of this source code is governed by a BSD-style license that can be
@@ -105,62 +121,110 @@ _pango_Is_Regional_Indicator (gunichar ch)
const gunichar kCombiningEnclosingCircleBackslashCharacter = 0x20E0;
const gunichar kCombiningEnclosingKeycapCharacter = 0x20E3;
-const gunichar kEyeCharacter = 0x1F441;
-const gunichar kFemaleSignCharacter = 0x2640;
-const gunichar kLeftSpeechBubbleCharacter = 0x1F5E8;
-const gunichar kMaleSignCharacter = 0x2642;
-const gunichar kRainbowCharacter = 0x1F308;
-const gunichar kStaffOfAesculapiusCharacter = 0x2695;
const gunichar kVariationSelector15Character = 0xFE0E;
const gunichar kVariationSelector16Character = 0xFE0F;
-const gunichar kWavingWhiteFlagCharacter = 0x1F3F3;
const gunichar kZeroWidthJoinerCharacter = 0x200D;
-
-typedef enum {
- PANGO_EMOJI_TYPE_INVALID,
- PANGO_EMOJI_TYPE_TEXT, /* For regular non-symbols text */
- PANGO_EMOJI_TYPE_EMOJI_TEXT, /* For emoji in text presentaiton */
- PANGO_EMOJI_TYPE_EMOJI_EMOJI /* For emoji in emoji presentation */
-} PangoEmojiType;
-
-static PangoEmojiType
-_pango_get_emoji_type (gunichar codepoint)
+enum PangoEmojiScannerCategory {
+ EMOJI = 0,
+ EMOJI_TEXT_PRESENTATION = 1,
+ EMOJI_EMOJI_PRESENTATION = 2,
+ EMOJI_MODIFIER_BASE = 3,
+ EMOJI_MODIFIER = 4,
+ EMOJI_VS_BASE = 5,
+ REGIONAL_INDICATOR = 6,
+ KEYCAP_BASE = 7,
+ COMBINING_ENCLOSING_KEYCAP = 8,
+ COMBINING_ENCLOSING_CIRCLE_BACKSLASH = 9,
+ ZWJ = 10,
+ VS15 = 11,
+ VS16 = 12,
+ TAG_BASE = 13,
+ TAG_SEQUENCE = 14,
+ TAG_TERM = 15,
+ kMaxEmojiScannerCategory = 16
+};
+
+static unsigned char
+_pango_EmojiSegmentationCategory (gunichar codepoint)
{
- /* Those should only be Emoji presentation as combinations of two. */
- if (_pango_Is_Emoji_Keycap_Base (codepoint) ||
- _pango_Is_Regional_Indicator (codepoint))
- return PANGO_EMOJI_TYPE_TEXT;
-
+ /* Specific ones first. */
if (codepoint == kCombiningEnclosingKeycapCharacter)
- return PANGO_EMOJI_TYPE_EMOJI_EMOJI;
-
- if (_pango_Is_Emoji_Emoji_Default (codepoint) ||
- _pango_Is_Emoji_Modifier_Base (codepoint) ||
- _pango_Is_Emoji_Modifier (codepoint))
- return PANGO_EMOJI_TYPE_EMOJI_EMOJI;
-
+ return COMBINING_ENCLOSING_KEYCAP;
+ if (codepoint == kCombiningEnclosingCircleBackslashCharacter)
+ return COMBINING_ENCLOSING_CIRCLE_BACKSLASH;
+ if (codepoint == kZeroWidthJoinerCharacter)
+ return ZWJ;
+ if (codepoint == kVariationSelector15Character)
+ return VS15;
+ if (codepoint == kVariationSelector16Character)
+ return VS16;
+ if (codepoint == 0x1F3F4)
+ return TAG_BASE;
+ if ((codepoint >= 0xE0030 && codepoint <= 0xE0039) ||
+ (codepoint >= 0xE0061 && codepoint <= 0xE007A))
+ return TAG_SEQUENCE;
+ if (codepoint == 0xE007F)
+ return TAG_TERM;
+ if (_pango_Is_Emoji_Modifier_Base (codepoint))
+ return EMOJI_MODIFIER_BASE;
+ if (_pango_Is_Emoji_Modifier (codepoint))
+ return EMOJI_MODIFIER;
+ if (_pango_Is_Regional_Indicator (codepoint))
+ return REGIONAL_INDICATOR;
+ if (_pango_Is_Emoji_Keycap_Base (codepoint))
+ return KEYCAP_BASE;
+
+ if (_pango_Is_Emoji_Emoji_Default (codepoint))
+ return EMOJI_EMOJI_PRESENTATION;
if (_pango_Is_Emoji_Text_Default (codepoint))
- return PANGO_EMOJI_TYPE_EMOJI_TEXT;
+ return EMOJI_TEXT_PRESENTATION;
+ if (_pango_Is_Emoji (codepoint))
+ return EMOJI;
- return PANGO_EMOJI_TYPE_TEXT;
+ /* Ragel state machine will interpret unknown category as "any". */
+ return kMaxEmojiScannerCategory;
}
+#define found_text_presentation_sequence
+#define found_emoji_presentation_sequence \
+ { \
+ if (0) g_print ("emoji %ld..%ld\n", ts - buffer, te - buffer); \
+ *last = ts - buffer; \
+ *end = te - buffer; \
+ return TRUE; \
+ }
+
+#include "emoji_presentation_scanner.c"
+
PangoEmojiIter *
_pango_emoji_iter_init (PangoEmojiIter *iter,
const char *text,
int length)
{
- iter->text_start = text;
+ unsigned int n_chars = g_utf8_strlen (text, length);
+ unsigned char *types = g_malloc (n_chars);
+ unsigned int i;
+ const char *p;
+
+ p = text;
+ for (i = 0; i < n_chars; i++)
+ {
+ types[i] = _pango_EmojiSegmentationCategory (g_utf8_get_char (p));
+ p = g_utf8_next_char (p);
+ }
+
+ iter->text_start = iter->start = iter->end = iter->token_start = iter->token_end = text;
if (length >= 0)
iter->text_end = text + length;
else
iter->text_end = text + strlen (text);
+ iter->is_emoji = FALSE;
- iter->start = text;
- iter->end = text;
- iter->is_emoji = (gboolean) 2; /* HACK */
+ iter->types = types;
+ iter->n_chars = n_chars;
+ iter->cursor = 0;
_pango_emoji_iter_next (iter);
@@ -170,102 +234,62 @@ _pango_emoji_iter_init (PangoEmojiIter *iter,
void
_pango_emoji_iter_fini (PangoEmojiIter *iter)
{
+ g_free (iter->types);
}
-#define PANGO_EMOJI_TYPE_IS_EMOJI(typ) ((typ) == PANGO_EMOJI_TYPE_EMOJI_EMOJI)
-
gboolean
_pango_emoji_iter_next (PangoEmojiIter *iter)
{
- PangoEmojiType current_emoji_type = PANGO_EMOJI_TYPE_INVALID;
-
- if (iter->end == iter->text_end)
+ if (iter->end >= iter->text_end)
return FALSE;
iter->start = iter->end;
- for (; iter->end < iter->text_end; iter->end = g_utf8_next_char (iter->end))
+ /* The scan_emoji_presentation scanner function returns false when it reaches
+ * the end of the buffer and has not discovered any emoji runs in between. For
+ * Emoji runs, it returns true, and token_start_ and token_end_ are set to the
+ * start and end of the emoji sequence. This means, it may skip over text runs
+ * in between, see below. */
+ if (iter->start >= iter->token_end)
{
- gunichar ch = g_utf8_get_char (iter->end);
-
- /* Except at the beginning, ZWJ just carries over the emoji or neutral
- * text type, VS15 & VS16 we just carry over as well, since we already
- * resolved those through lookahead. Also, don't downgrade to text
- * presentation for emoji that are part of a ZWJ sequence, example
- * U+1F441 U+200D U+1F5E8, eye (text presentation) + ZWJ + left speech
- * bubble, see below. */
- if ((!(ch == kZeroWidthJoinerCharacter && !iter->is_emoji) &&
- ch != kVariationSelector15Character &&
- ch != kVariationSelector16Character &&
- ch != kCombiningEnclosingCircleBackslashCharacter &&
- !_pango_Is_Regional_Indicator(ch) &&
- !((ch == kLeftSpeechBubbleCharacter ||
- ch == kRainbowCharacter ||
- ch == kMaleSignCharacter ||
- ch == kFemaleSignCharacter ||
- ch == kStaffOfAesculapiusCharacter) &&
- !iter->is_emoji)) ||
- current_emoji_type == PANGO_EMOJI_TYPE_INVALID) {
- current_emoji_type = _pango_get_emoji_type (ch);
+ /* We need to scan furhter. */
+ unsigned int token_start, token_end;
+ if (!scan_emoji_presentation (iter->types, iter->n_chars, iter->cursor,
+ &token_start, &token_end))
+ {
+ /* The scanner returned false, which means it has reached the end of the
+ * buffer without discovering any emoji segments in between. */
+ iter->end = iter->text_end;
+ iter->is_emoji = FALSE;
+
+ return TRUE;
+ };
+ /* Ugly... */
+ g_assert (iter->cursor <= token_start && token_start < token_end && token_end <= iter->n_chars);
+ iter->token_start = g_utf8_offset_to_pointer (iter->token_end, token_start - iter->cursor);
+ iter->token_end = g_utf8_offset_to_pointer (iter->token_end, token_end - iter->cursor);
+ iter->cursor = token_end;
}
- if (g_utf8_next_char (iter->end) < iter->text_end) /* Optimize. */
+ if (iter->start < iter->token_start)
{
- gunichar peek_char = g_utf8_get_char (g_utf8_next_char (iter->end));
-
- /* Variation Selectors */
- if (current_emoji_type ==
- PANGO_EMOJI_TYPE_EMOJI_EMOJI &&
- peek_char == kVariationSelector15Character) {
- current_emoji_type = PANGO_EMOJI_TYPE_EMOJI_TEXT;
- }
-
- if ((current_emoji_type ==
- PANGO_EMOJI_TYPE_EMOJI_TEXT ||
- _pango_Is_Emoji_Keycap_Base(ch)) &&
- peek_char == kVariationSelector16Character) {
- current_emoji_type = PANGO_EMOJI_TYPE_EMOJI_EMOJI;
- }
-
- /* Combining characters Keycap... */
- if (_pango_Is_Emoji_Keycap_Base(ch) &&
- peek_char == kCombiningEnclosingKeycapCharacter) {
- current_emoji_type = PANGO_EMOJI_TYPE_EMOJI_EMOJI;
- };
-
- /* Regional indicators */
- if (_pango_Is_Regional_Indicator(ch) &&
- _pango_Is_Regional_Indicator(peek_char)) {
- current_emoji_type = PANGO_EMOJI_TYPE_EMOJI_EMOJI;
- }
-
- /* Upgrade text presentation emoji to emoji presentation when followed by
- * ZWJ, Example U+1F441 U+200D U+1F5E8, eye + ZWJ + left speech bubble. */
- if ((ch == kEyeCharacter ||
- ch == kWavingWhiteFlagCharacter) &&
- peek_char == kZeroWidthJoinerCharacter) {
- current_emoji_type = PANGO_EMOJI_TYPE_EMOJI_EMOJI;
- }
+ /* The scanner function has progressed to the next emoji segment, but we
+ * need to return the text segment over which it had skipped. */
+ iter->end = iter->token_start;
+ iter->is_emoji = FALSE;
+ return TRUE;
}
- if (iter->is_emoji == (gboolean) 2)
- iter->is_emoji = !PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type);
- if (iter->is_emoji == PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type))
+ if (iter->start >= iter->token_start && iter->start < iter->token_end)
{
- iter->is_emoji = !PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type);
-
- /* Make sure we make progress. Weird sequences, like a VC15 followed
- * by VC16, can trick us into stalling otherwise. */
- if (iter->start == iter->end)
- iter->end = g_utf8_next_char (iter->end);
-
+ /* Now our cursor has reached the emoji segment, and we can return it. */
+ iter->end = iter->token_end;
+ iter->is_emoji = TRUE;
return TRUE;
}
- }
-
- iter->is_emoji = PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type);
- return TRUE;
+ g_assert_not_reached ();
+ return FALSE;
}