Home > Article > Backend Development > Unicode, Emojis, and a bit of Golang
Dernièrement, j'ai eu un problème avec mon installation Fedora Linux affichant des emojis dans l'interface utilisateur du système d'exploitation et dans les navigateurs. Ce problème m'a amené à enquêter un peu sur le projet de configuration des polices, mais pour tester mes configurations et mes polices, j'avais besoin de produire des emojis à partir de toutes les versions Unicode, ce qui m'a finalement amené à écrire un "script" Golang pour imprimer tous les emojis et certains informations sur leurs composants internes.
Tout au long de ce voyage, j'ai plongé en profondeur dans les composants internes des emojis, leurs représentations binaires et certaines des décisions étranges/mignonnes prises par la norme Unicode concernant les emojis.
Mais d'abord, prenons un peu de recul et résumons quelques glossaires.
On pourrait décrire l'encodage comme le « mappage » ou la « traduction » entre une lettre d'une langue et la représentation binaire de cette lettre. Par exemple, le codage ASCII traditionnel mappe la lettre a en 0x61 hex (0b01100001 binaire). Des exemples de codages sont les pages de codes Microsoft (Windows 125x) ou ISO (ISO/IEC 8859) à 8 bits.
Dans ces pages de codes fixes de 8 bits, la « quantité » minimale d'informations utilisée est de 8 bits (1 octet), ce qui signifie qu'elles peuvent contenir 256 lettres/caractères différents. Différentes pages de codes ont été créées en réutilisant les 256 codes binaires pour prendre en charge de nombreuses langues. Ainsi, avoir un fichier texte avec ces 3 octets écrits dessus [0xD0, 0xE5, 0xF2] se lit comme "Πες" en utilisant l'ISO grec 8859-7, ou "Ðåò" en utilisant l'ISO 8859-7 occidental (mêmes octets, interprétés différemment basé sur la page de codes).
À un moment donné, le fait d'avoir de nombreuses pages de codes différentes n'a pas bien évolué à mesure que la technologie progressait. Nous avions donc besoin de quelque chose qui puisse s'adapter à toutes les langues (et plus encore) et être unifié entre les systèmes.
[ avance rapide, laissant de côté beaucoup d'histoire et de normes, jusqu'au présent ]
La norme Unicode a été conçue pour prendre en charge tous les systèmes d'écriture du monde pouvant être numérisés. Ainsi, en reprenant l'exemple ci-dessus, dans les standards Unicode, la lettre grecque "Π" a le code 0x03A0 tandis que la lettre majuscule latine eth "Ð" a le code 0x00D0 et n'entrent plus en collision. Unicode Standard a des versions, et au moment de la rédaction, la dernière version est 16.0 (spéc.).
Mais attendez une minute, c'est quoi ce « point de code » ?
Dans la norme Unicode, chaque « lettre », caractère de contrôle, emoji et chaque élément défini en général a une valeur binaire unique appelée « point de code ». La norme définit tous les points de code, et chaque point de code contient des informations pures de code/binaire. Le format hexadécimal de chaque point de code est généralement écrit avec un préfixe U. Par exemple, le point de code de la lettre minuscule grecque Omega (ω) est U 03C9.
Alors, qui codons-nous réellement ces points de code ?
La première partie de l'encodage des points de code en octets est l'encodage des formulaires. Selon la norme :
les formes de codage spécifient comment chaque entier (point de code) d'un caractère Unicode doit être exprimé sous la forme d'une séquence d'une ou plusieurs unités de code.
Les formulaires de codage utilisent le terme « unité de code » pour désigner la plus petite unité de données utilisée pour représenter un point de code Unicode dans un codage particulier.
La norme Unicode définit trois formes d'encodage différentes :
Cela signifie qu'un seul point de code ou une séquence de points de code peut être codé différemment selon la forme de codage utilisée.
La couche qui s'occupe de la sérialisation binaire réelle dans Unicode est appelée Encoding Schemes et s'occupe de tous les détails de bas niveau (tels que l'endianité). Tableau 2-4 de la spécification Unicode :
|Encoding Scheme| Endian Order | BOM Allowed? | | ------------- | ----------------------------| ------------ | | UTF-8 | N/A | yes | | UTF-16 | Big-endian or little-endian | yes | | UTF-16BE | Big-endian | no | | UTF-16LE | Little-endian | no | | UTF-32 | Big-endian or little-endian | yes | | UTF-32BE | Big-endian | no | | UTF-32LE | Little-endian | no |
Remarque : presque tous les langages de programmation, systèmes d'exploitation et systèmes de fichiers modernes utilisent Unicode (avec l'un de ses schémas de codage) comme codage natif. Java et .NET utilisent UTF-16, tandis que Golang utilise UTF-8 comme codage de chaîne interne (cela signifie que lorsque nous créons une chaîne en mémoire, elle est codée en Unicode avec le formulaire d'encodage mentionné)
Le standard Unicode définit également des points de code pour les emojis (beaucoup d'entre eux), et (après quelques confusions avec le numéro de version), la version du "standard" Emoji progresse en parallèle avec le standard Unicode. Au moment de la rédaction, nous avons Emoji "16.0" et Unicode Standard "16.0".
Exemples :
⛄Bonhomme de neige sans neige (U 26C4)
? Visage souriant aux yeux souriants et trois cœurs (U 1F970)
Unicode defines modifiers that could follow an emoji's base code point, such as variation and skin tone (we will not explore the variation part).
We have six skin tone modifiers (following the Fitzpatrick scale) called EMOJI MODIFIER FITZPATRICK TYPE-X (where x is 1 to 6), and they affect all human emojis.
Light Skin Tone (Fitzpatrick Type-1-2) (U+1F3FB)
Medium-Light Skin Tone (Fitzpatrick Type-3) (U+1F3FC)
Medium Skin Tone (Fitzpatrick Type-4) (U+1F3FD)
Medium-Dark Skin Tone (Fitzpatrick Type-5) (U+1F3FE)
Dark Skin Tone (Fitzpatrick Type-6) (U+1F3FF)
So, for example, like all human emojis, the baby emoji ? (U+1F476), when not followed by a skin modifier, appears in a neutral yellow color. In contrast, when a skin color modifier follows it, it changes accordingly.
? U+1F476
?? U+1F476 U+1F3FF
?? U+1F476 U+1F3FE
?? U+1F476 U+1F3FD
?? U+1F476 U+1F3FC
?? U+1F476 U+1F3FB
The most strange but cute decision of the Emoji/Unicode Standard is that some emojis have been defined by joining others together using the Zero Width Joiner without a standalone code point.
So, for example, when we combine:
White Flag ?️ (U+1F3F3 U+FE0F) +
Zero Width Joiner (U+200D) +
Rainbow ? (U+1F308)
It appears as Rainbow Flag ?️? (U+1F3F3 U+FE0F U+200D U+1F308)
Or, ?? + ? => ???
Or even, ?? + ❤️ + ? + ?? => ??❤️???
It's like squeezing emojis together, and then, poof ?, a new emoji appears. How cute is that?
I wanted to create a Markdown table with all emojis, and the Unicode emoji sequence tables are the source of truth for that.
https://unicode.org/Public/emoji/16.0/emoji-sequences.txt
https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt
So I created a Golang parser (here) that fetches and parses those sequence files, generates each emoji when a range is described in the sequence file, and prints a markdown table with some internal information for each one (like the parts in case it joined, or the base + skin tone, etc.).
You can find the markdown table here.
The last column of this table is in this format
str := "⌚" len([]rune(str)) // 1 len([]byte(str)) // 3
As we discussed, Golang internal string encoding is UTF-8, which means that, for example, for clock emoji ⌚ the byte length is 3 (because the UTF-8 produces 3 bytes to "write" this code point), and the code point length is 1.
Golang rune == Unicode Code Point
But in the case of joined emoji -even if it "appears" as one- we have many code points (runes) and even more bytes.
str := "??❤️???" len([]rune(str)) // 10 len([]byte(str)) // 35
And the reason is that:
??❤️??? : ?? + ZWJ + ❤️ + ZWJ + ? + ZWJ + ?? ?? : 1F469 1F3FC // ? + skin tone modifier [2 code points] ZWJ : 200D // [1 code points] * 3 ❤️ : 2764 FE0F // ❤ + VS16 for emoji-style [2 code points] ? : 1F48B // [1 code point] ?? : 1F468 1F3FE // ? + skin tone modifier [2 code points]
?
It is worth mentioning that how we see emojis depends on our system font and which versions of emoji this font supports.
I don't know the exact internals of font rendering and how it can render the joined fonts correctly. Perhaps it will be a future post.
Til then, cheers ?
The above is the detailed content of Unicode, Emojis, and a bit of Golang. For more information, please follow other related articles on the PHP Chinese website!