diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2017-07-31 21:45:16 +0100 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2017-07-31 23:04:15 +0100 |
commit | f39ad9bab1f327062958b2750bf1f5609a9ee991 (patch) | |
tree | 2f4170c32d9101881c86b6d685533a215ab13b6a /pango/pango-emoji.c | |
parent | 284d357e3d6e29c1437ca18bab347c1af8330908 (diff) | |
download | pango-f39ad9bab1f327062958b2750bf1f5609a9ee991.tar.gz |
Add data files and routines for emoji itemization
Ported from Chromium.
Not hooked yet.
Diffstat (limited to 'pango/pango-emoji.c')
-rw-r--r-- | pango/pango-emoji.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/pango/pango-emoji.c b/pango/pango-emoji.c new file mode 100644 index 00000000..630a0e7f --- /dev/null +++ b/pango/pango-emoji.c @@ -0,0 +1,266 @@ +/* Pango + * pango-emoji.c: Emoji handling + * + * Copyright (C) 2017 Google, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Implementation of pango_emoji_iter is derived from Chromium: + * + * 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 + * + * // Copyright 2015 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. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> + +#include "pango-emoji-private.h" +#include "pango-emoji-table.h" + + +static int +interval_compare (const void *key, const void *elt) +{ + gunichar c = GPOINTER_TO_UINT (key); + struct Interval *interval = (struct Interval *)elt; + + if (c < interval->start) + return -1; + if (c > interval->end) + return +1; + + return 0; +} + +#define DEFINE_pango_Is_(name) \ +static gboolean \ +_pango_Is_##name (gunichar ch) \ +{ \ + /* bsearch() is declared attribute(nonnull(1)) so we can't validly search \ + * for a NULL key */ \ + /* \ + if (G_UNLIKELY (ch == 0)) \ + return FALSE; \ + */ \ + \ + if (bsearch (GUINT_TO_POINTER (ch), \ + _pango_##name##_table, \ + G_N_ELEMENTS (_pango_##name##_table), \ + sizeof _pango_##name##_table[0], \ + interval_compare)) \ + return TRUE; \ + \ + return FALSE; \ +} + +DEFINE_pango_Is_(Emoji) +DEFINE_pango_Is_(Emoji_Presentation) +DEFINE_pango_Is_(Emoji_Modifier) +DEFINE_pango_Is_(Emoji_Modifier_Base) + + +static gboolean +_pango_Is_Emoji_Text_Default (gunichar ch) +{ + return _pango_Is_Emoji (ch) && !_pango_Is_Emoji_Presentation (ch); +} + +static gboolean +_pango_Is_Emoji_Emoji_Default (gunichar ch) +{ + return _pango_Is_Emoji_Presentation (ch); +} + +static gboolean +_pango_Is_Emoji_Keycap_Base (gunichar ch) +{ + return (ch >= '0' && ch <= '9') || ch == '#' || ch == '*'; +} + +static gboolean +_pango_Is_Regional_Indicator (gunichar ch) +{ + return (ch >= 0x1F1E6 && ch <= 0x1F1FF); +} + + +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) +{ + /* 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; + + 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; + + if (_pango_Is_Emoji_Text_Default (codepoint)) + return PANGO_EMOJI_TYPE_EMOJI_TEXT; + + return PANGO_EMOJI_TYPE_TEXT; +} + + +PangoEmojiIter * +_pango_emoji_iter_init (PangoEmojiIter *iter, + const char *text, + int length) +{ + iter->text_start = text; + if (length >= 0) + iter->text_end = text + length; + else + iter->text_end = text + strlen (text); + + iter->start = text; + iter->end = text; + iter->is_emoji = FALSE; + + _pango_emoji_iter_next (iter); + + return iter; +} + +void +_pango_emoji_iter_fini (PangoEmojiIter *iter) +{ +} + +#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) + return FALSE; + + iter->start = iter->end; + + for (; iter->end < iter->text_end; iter->end = g_utf8_next_char (iter->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); + } + + if (iter->end < iter->text_end) + { + gunichar peek_char = g_utf8_get_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; + } + } + + if (iter->is_emoji != PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type) && iter->start != iter->text_start) + { + iter->is_emoji = PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type); + return TRUE; + } + } + + iter->is_emoji = PANGO_EMOJI_TYPE_IS_EMOJI (current_emoji_type); + + return TRUE; +} + + +/********************************************************** + * End of code from Chromium + **********************************************************/ |