[Перевод] Под капотом у Emoji

image

В течение последних нескольких недель Nikita Prokopov внедрял поддержку эмодзи для Skija. Он решил поделиться несколькими мелкими деталями того, как это «самое большое новшество в человеческом общении со времен изобретения буквы image» работает под капотом.

Примечание переводчика: Хабр не поддерживает эмодзи, поэтому пришлось выкручиваться и заменить эмодзи картинками.

Unicode


Каждый символ на компьютере кодируется числом. Самая популярная кодировка — Unicode, а две самые распространенные подвариации — UTF-8 и UTF-16.

Unicode выделяет 221 (2 млн) символов, назывемых «codepoints». Из этих двух миллионов сейчас определены только ~150k символов. В эти 150 000 символов впихнули все языки, мёртвые и живые и прочие украшательства. Можно использовать различные шрифты, писать задом наперед и кверх ногами: image, а так же отобразить «GHz» как один глиф: image.

Направленная вправо двуглавая стрела с оперением и двумя вертикальными штрихами: image или семиглазый монстр: image. И утка:

image

Обратите внимание и на блок с египетскими иероглифами (U+13000–U+1342F), там много интересного:
image

Базовые emoji


Эмодзи — это просто символы Unicode, которые располагаются тут U+1F300–1F6FF и тут U+1F900–1FAFF:
image

Эмодзи ведут себя как обычные буквы, с ними можно делать все операции, что и с буквами (прим. пер.: только не на Хабре! ). Когда вы печатаете «A», компьютер видит U+0041. Когда вы печатаете image компьютер видит U+1F335.

Эмодзи это шрифты


Почему же они отображаются как картинки? Растровые шрифты. вы можете создать веселые png для глифов вместо скучных черно-белых векторов.

image

Каждая ОС поставлятся с преустановленным шрифтом для эмодзи. В macOS/iOS это Apple Color Emoji. Windows — Segoe UI Emoji, Android — Noto Color Emoji.

На разных устройствах эмодзи, как и шрифты, выглядят по-разному. Некоторые приложения имеют свои эмодзи: WhatsApp, Twitter, Facebook.

image

Резервные шрифты


Вы пишите текст каким-то шрифтом, как туда вписывается эмодзи? И почему русский текст выглядит убого в Clubhouse или на Medium?
image

Вот вы печатаете символ U+1F419, а ваш шрифт, например, San Francisco. Но в шрифте San Francisco нет глифа для U+1F419, поэтому ваша ОС начинает искать другой шрифт, где такой глиф есть.

U+1F419 есть только в Apple Color Emoji. Поэтому вы видите это: image.
Какой бы шрифт вы не использовали, эмодзи выглядят одинаково.

image

Variation selector-16


Некоторые эмодзи зародились в виде пиктограмм ещё в 1993 году, в разделах Miscellaneous Symbols U+2600–26FF или Dingbats U+2700–27FF:
image

Эти глифы совсем как буквы, черно-белые. Многие шрифты имеют свои image (U+2702 BLACK SCISSORS):

image

У Apple Color Emoji есть своя версия:

image

Как ОС понимает что отображать image» alt=«image»/> или image если у них одинаковый код U+2702?

Познакомьтесь с U+FE0F, так же известным как VARIATION SELECTOR-16. Это подсказка текстовому рендеру переключиться на эмодзи.

image

Просто, элегантно и не надо выделять новые codepoints. image имеют одно и тоже значение, но немного разный стиль изображения.

Кластеры графем


Здесь мы сталкиваемся с другой проблемой — наши эмодзи теперь представляют собой не одну codepoint, а две. Это означает, что нам нужен способ определить границы символа.

Нам поможет кластер графем. Кластер графем — это последовательность codepoints, которая рассматривается как единый воспринимаемый человеком глиф.

Графемные кластеры были изобретены не только для эмодзи, они применимы и к обычным алфавитам. image — это единый кластер графем, даже если он состоит из двух codepoints: U+0055 UPPER-CASE Uза которым следует U+0308 COMBINING DIAERESIS.

Графемные кластеры создают много сложностей для программистов. Вы не можете просто сделать substring(0, 10), чтобы взять первые 10 символов — вы можете разделить эмодзи пополам.

Реверс строки нужно делать хитро. U+263A U+FE0F имеет смысл, а U+FE0F U+263A — нет.

image

Наконец, вы не можете просто вызвать .length для string. Ну, можно, но результат вас удивит. Если вы разработчик, попробуйте выполнить image в консоли вашего браузера.

Подсказка для программистов: если вы работаете с текстом, приобретите библиотеку, ориентированную на графемные кластеры. Для C, C++m и JVM это может быть ICU, Swift делает все правильно по умолчанию, для других — ищите сами.

image

Длина этой штуки 65, и её нельзя расщеплять. Живите теперь с этим.

Модификатор оттенка кожи


Большинство человеческих эмодзи изображают абстрактного желтого человека. Когда в 2015 году был добавлен оттенок кожи, вместо добавления новой codepoint для каждой комбинации эмодзи и оттенка кожи было добавлено только пять новых codepoints: U+1F3FB…U+1F3FF

Они не должны использоваться сами по себе, но должны быть добавлены к существующим эмодзи. Вместе они образуют лигатуру: image (U+1F44B WAVING HAND SIGN), а затем (U+1F3FD MEDIUM SKIN TONE MODIFIER), то получим image

image не имеет своей собственной codepoint (это последовательность из двух: U + 1F44B U + 1F3FD), но имеет свой собственный уникальный внешний вид. Всего с помощью пяти модификаторов ~280 человеческих эмодзи превратились в 1680 вариаций. Вот несколько танцоров:

image

Zero-width Joiner


Допустим, ваша подруга только что прислала вам фотографию яблока, которое она выращивает в своем саду. Вам нужно ответить—как? Вы можете отправить image WOMAN EMOJI (U+1F469), с прикрепленным колоском риса image SHEAF OF RICE (U+1F33E). В итоге получится image, но если вы между ними влепите U+200D, то получится фермерка: image

U+200D называется Zero-width Joiner, или сокращенно ZWJ. Он работает аналогично тому, что мы видели с оттенком кожи, но на этот раз вы можете объединить два самодостаточных эмодзи в один. Не все комбинации работают, но многие работают, иногда удивительным образом!

Некоторые примеры:

image

Одна странная несогласованность, которую я заметил, заключается в том, что цвет волос делается через ZWJ, в то время как оттенок кожи — это просто модификатор emoji без ZWJ. Почему? Понятия не имею.

image

К сожалению, некоторые эмодзи не реализованы в виде комбинаций с ZWJ. Я считаю эти упущенные возможности:

image

Как напечатать ZWJ? Никак. Но вы можете скопировать его отсюда:». Примечание: это особый символ, поэтому ожидайте, что он будет вести себя странно. Вы его не видите, а он есть. (прим пер.: в оригинальной статье есть, а Хабр не позволяет)

Еще одна большая область, где ZWJ на коне — это конфигурация семей и отношений. Вот короткий сюжет для иллюстрации:

image

Флаги


Флаги стран являются частью стандарта Unicode, но по какой-то причине не реализованы в Windows. Если вы читаете это в браузере из Windows — Извините!

Флаги не имеют выделенных codepoints. Вместо этого они представляют собой двухбуквенные лигатуры.

image

Слева — Windows, справа — Mac

Правда, они не используют настоящие буквы. Вместо этого используется алфавит «regional indicator symbol letter» (U+1F1E6…1F1FF). Эти буквы не используются ни для чего, кроме составления флагов.

Что произойдет, если вы сложите вместе две случайные буквы? Не так уж много: image (за исключением того, что редактирование текста начинает вести себя странно).

Если вы хотите поэкспериментировать, не стесняйтесь копировать и комбинировать из этого алфавита: image

Существует 258 допустимых двухбуквенных комбинаций. Вы можете найти их все?

Забавный побочный эффект двухбуквенной лигатуры: image

Последовательности тэгов


Двухбуквенные лигатуры — это круто, но разве вы не хотите быть круче? Как насчет 32-буквенных лигатур? Вот вам последовательностями тегов.

Последовательность тегов — это последовательность обычных эмодзи, за которой следует другая разновидность латинских букв (U+E0020…E007E), заканчивающаяся U+E007F CANCEL TAG.

В настоящее время они используются только для этих трех флагов: Англии, Шотландии и Уэльса:

image

Keycaps


Не супер-захватывающе, но необходимо для полноты: последовательности Keycaps используют еще одно соглашение.

Это выглядит так: возьмите цифру * или #, превратите ее в эмодзи с U+FE0F, оберните в квадрат с U+20E3 COMBINING ENCLOSING KEYCAP

image

Всего их 12:

image

Unicode updates


Unicode обновляется каждый год, и эмодзи являются основной частью каждого выпуска. Например, в Unicode 13 (март 2020 года) было добавлено 55 новых эмодзи.

На момент написания статьи ни последняя версия Mac OS (11.2.3), ни iOS (14.4.1) не поддерживают эмодзи из Unicode 13 типа: image

Вот что я вижу в марте 2021 года: image

Но, благодаря магии ZWJ, я все еще могу понять, что происходит, просто не самым оптимальным способом.

Заключение


Подводя итог, можно сказать, что это семь способов кодировки эмодзи:
  1. Одиночный codepoint image
  2. Одиночный codepoint + variation selector-16 image
  3. Модификатор оттенка кожи image
  4. Последовательность с zero-width joiner image
  5. Флаги image
  6. Последовательность тэгов image
  7. Последовательность Keycap image

Методы из 1–4 могут быть объединены для построения довольно сложного сообщения:
image

Если вы программист, не забывайте всегда использовать библиотеку ICU для:

  • извлечения подстроки
  • измерения длину строки
  • реверс строки

Ключевое слово для гугления — «Grapheme Cluster». Это относится к эмодзи, к диакритическим знакам в западных языках, к индуцированным и корейским шрифтам, поэтому, пожалуйста, будьте внимательны.
image

© Habrahabr.ru