summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornicm <nicm>2023-09-01 14:29:11 +0000
committernicm <nicm>2023-09-01 14:29:11 +0000
commit9456258ccc03a1a959cfb7d020011d751b39bb1b (patch)
tree6f2331046cfde77a7125d3cc54082161f2fc9303
parentc41d59f232a7243bfff34ec3f02adc76fddc91b2 (diff)
Rewrite combined character handling to be more consistent and to support
newer Unicode combined characters (which we have to "know" are combined since they are not width zero). GitHub issue 3600.
-rw-r--r--Makefile1
-rw-r--r--screen-write.c64
-rw-r--r--server.c1
-rw-r--r--tmux.h18
-rw-r--r--utf8-combined.c1135
-rw-r--r--utf8.c8
-rw-r--r--window-copy.c6
7 files changed, 1197 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index e3e2fd9c..6cd1b1b5 100644
--- a/Makefile
+++ b/Makefile
@@ -121,6 +121,7 @@ SRCS= alerts.c \
tty-term.c \
tty.c \
utf8.c \
+ utf8-combined.c \
window-buffer.c \
window-client.c \
window-clock.c \
diff --git a/screen-write.c b/screen-write.c
index ee3a31a1..88ca77ae 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -1840,46 +1840,37 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
- const struct utf8_data *ud = &gc->data;
- const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 };
+ struct grid_cell copy;
+ const struct utf8_data *ud = &gc->data, *previous = NULL, *combine;
struct grid_line *gl;
struct grid_cell_entry *gce;
struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
- u_int width = gc->data.width, xx, last, cx, cy;
+ u_int width = ud->width, xx, last, cx, cy;
int selected, skip = 1;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
return;
- /*
- * If this is a zero width joiner, set the flag so the next character
- * will be treated as zero width and appended. Note that we assume a
- * ZWJ will not change the width - the width of the first character is
- * used.
- */
- if (ud->size == 3 && memcmp(ud->data, "\342\200\215", 3) == 0) {
- log_debug("zero width joiner at %u,%u", s->cx, s->cy);
- ctx->flags |= SCREEN_WRITE_ZWJ;
+ /* Check if this cell needs to be combined with the previous cell. */
+ if (ctx->flags & SCREEN_WRITE_COMBINE)
+ previous = &ctx->previous;
+ switch (utf8_try_combined(ud, previous, &combine, &width)) {
+ case UTF8_DISCARD_NOW:
+ log_debug("%s: UTF8_DISCARD_NOW (width %u)", __func__, width);
+ ctx->flags &= ~SCREEN_WRITE_COMBINE;
return;
- }
-
- /*
- * If the width is zero, combine onto the previous character. We always
- * combine with the cell to the left of the cursor position. In theory,
- * the application could have moved the cursor somewhere else, but if
- * they are silly enough to do that, who cares?
- */
- if (ctx->flags & SCREEN_WRITE_ZWJ) {
- screen_write_collect_flush(ctx, 0, __func__);
- screen_write_combine(ctx, &zwj, &xx, &cx);
- }
- if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) {
- ctx->flags &= ~SCREEN_WRITE_ZWJ;
+ case UTF8_WRITE_NOW:
+ log_debug("%s: UTF8_WRITE_NOW (width %u)", __func__, width);
+ ctx->flags &= ~SCREEN_WRITE_COMBINE;
+ break;
+ case UTF8_COMBINE_NOW:
+ log_debug("%s: UTF8_COMBINE_NOW (width %u)", __func__, width);
screen_write_collect_flush(ctx, 0, __func__);
- if ((gc = screen_write_combine(ctx, ud, &xx, &cx)) != NULL) {
+ gc = screen_write_combine(ctx, combine, &xx, &cx);
+ if (gc != NULL) {
cy = s->cy;
screen_write_set_cursor(ctx, xx, s->cy);
screen_write_initctx(ctx, &ttyctx, 0);
@@ -1887,8 +1878,27 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
tty_write(tty_cmd_cell, &ttyctx);
s->cx = cx; s->cy = cy;
}
+ ctx->flags &= ~SCREEN_WRITE_COMBINE;
return;
+ case UTF8_WRITE_MAYBE_COMBINE:
+ log_debug("%s: UTF8_WRITE_MAYBE_COMBINE (width %u)", __func__,
+ width);
+ utf8_copy(&ctx->previous, ud);
+ ctx->flags |= SCREEN_WRITE_COMBINE;
+ break;
+ case UTF8_DISCARD_MAYBE_COMBINE:
+ log_debug("%s: UTF8_DISCARD_MAYBE_COMBINE (width %u)", __func__,
+ width);
+ utf8_copy(&ctx->previous, ud);
+ ctx->flags |= SCREEN_WRITE_COMBINE;
+ return;
+ }
+ if (width != ud->width) {
+ memcpy(&copy, gc, sizeof copy);
+ copy.data.width = width;
+ gc = &copy;
}
+ ud = NULL;
/* Flush any existing scrolling. */
screen_write_collect_flush(ctx, 1, __func__);
diff --git a/server.c b/server.c
index f1df1aa4..3709945d 100644
--- a/server.c
+++ b/server.c
@@ -205,6 +205,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
fatal("pledge failed");
input_key_build();
+ utf8_build_combined();
RB_INIT(&windows);
RB_INIT(&all_window_panes);
TAILQ_INIT(&clients);
diff --git a/tmux.h b/tmux.h
index d95ba597..758713e5 100644
--- a/tmux.h
+++ b/tmux.h
@@ -618,6 +618,15 @@ enum utf8_state {
UTF8_ERROR
};
+/* UTF-8 combine state. */
+enum utf8_combine_state {
+ UTF8_DISCARD_NOW, /* discard immediately */
+ UTF8_WRITE_NOW, /* do not combine, write immediately */
+ UTF8_COMBINE_NOW, /* combine immediately */
+ UTF8_WRITE_MAYBE_COMBINE, /* write but try to combine the next */
+ UTF8_DISCARD_MAYBE_COMBINE /* discard but try to combine the next */
+};
+
/* Colour flags. */
#define COLOUR_FLAG_256 0x01000000
#define COLOUR_FLAG_RGB 0x02000000
@@ -890,7 +899,7 @@ struct screen_write_ctx {
int flags;
#define SCREEN_WRITE_SYNC 0x1
-#define SCREEN_WRITE_ZWJ 0x2
+#define SCREEN_WRITE_COMBINE 0x2
screen_write_init_ctx_cb init_ctx_cb;
void *arg;
@@ -898,6 +907,7 @@ struct screen_write_ctx {
struct screen_write_citem *item;
u_int scrolled;
u_int bg;
+ struct utf8_data previous;
};
/* Box border lines option. */
@@ -3285,6 +3295,12 @@ char *utf8_padcstr(const char *, u_int);
char *utf8_rpadcstr(const char *, u_int);
int utf8_cstrhas(const char *, const struct utf8_data *);
+/* utf8-combined.c */
+void utf8_build_combined(void);
+int utf8_try_combined(const struct utf8_data *,
+ const struct utf8_data *, const struct utf8_data **,
+ u_int *width);
+
/* procname.c */
char *get_proc_name(int, char *);
char *get_proc_cwd(int);
diff --git a/utf8-combined.c b/utf8-combined.c
new file mode 100644
index 00000000..e1b7934f
--- /dev/null
+++ b/utf8-combined.c
@@ -0,0 +1,1135 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2023 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "tmux.h"
+
+static const struct {
+ wchar_t first;
+ wchar_t second;
+} utf8_combined_table[] = {
+ { 0x1F1E6, 0x1F1E8 }, /* flag: Ascension Island */
+ { 0x1F1E6, 0x1F1E9 }, /* flag: Andorra */
+ { 0x1F1E6, 0x1F1EA }, /* flag: United Arab Emirates */
+ { 0x1F1E6, 0x1F1EB }, /* flag: Afghanistan */
+ { 0x1F1E6, 0x1F1EC }, /* flag: Antigua & Barbuda */
+ { 0x1F1E6, 0x1F1EE }, /* flag: Anguilla */
+ { 0x1F1E6, 0x1F1F1 }, /* flag: Albania */
+ { 0x1F1E6, 0x1F1F2 }, /* flag: Armenia */
+ { 0x1F1E6, 0x1F1F4 }, /* flag: Angola */
+ { 0x1F1E6, 0x1F1F6 }, /* flag: Antarctica */
+ { 0x1F1E6, 0x1F1F7 }, /* flag: Argentina */
+ { 0x1F1E6, 0x1F1F8 }, /* flag: American Samoa */
+ { 0x1F1E6, 0x1F1F9 }, /* flag: Austria */
+ { 0x1F1E6, 0x1F1FA }, /* flag: Australia */
+ { 0x1F1E6, 0x1F1FC }, /* flag: Aruba */
+ { 0x1F1E6, 0x1F1FD }, /* flag: Aland Islands */
+ { 0x1F1E6, 0x1F1FF }, /* flag: Azerbaijan */
+ { 0x1F1E7, 0x1F1E6 }, /* flag: Bosnia & Herzegovina */
+ { 0x1F1E7, 0x1F1E7 }, /* flag: Barbados */
+ { 0x1F1E7, 0x1F1E9 }, /* flag: Bangladesh */
+ { 0x1F1E7, 0x1F1EA }, /* flag: Belgium */
+ { 0x1F1E7, 0x1F1EB }, /* flag: Burkina Faso */
+ { 0x1F1E7, 0x1F1EC }, /* flag: Bulgaria */
+ { 0x1F1E7, 0x1F1ED }, /* flag: Bahrain */
+ { 0x1F1E7, 0x1F1EE }, /* flag: Burundi */
+ { 0x1F1E7, 0x1F1EF }, /* flag: Benin */
+ { 0x1F1E7, 0x1F1F1 }, /* flag: St. Barthelemy */
+ { 0x1F1E7, 0x1F1F2 }, /* flag: Bermuda */
+ { 0x1F1E7, 0x1F1F3 }, /* flag: Brunei */
+ { 0x1F1E7, 0x1F1F4 }, /* flag: Bolivia */
+ { 0x1F1E7, 0x1F1F6 }, /* flag: Caribbean Netherlands */
+ { 0x1F1E7, 0x1F1F7 }, /* flag: Brazil */
+ { 0x1F1E7, 0x1F1F8 }, /* flag: Bahamas */
+ { 0x1F1E7, 0x1F1F9 }, /* flag: Bhutan */
+ { 0x1F1E7, 0x1F1FB }, /* flag: Bouvet Island */
+ { 0x1F1E7, 0x1F1FC }, /* flag: Botswana */
+ { 0x1F1E7, 0x1F1FE }, /* flag: Belarus */
+ { 0x1F1E7, 0x1F1FF }, /* flag: Belize */
+ { 0x1F1E8, 0x1F1E6 }, /* flag: Canada */
+ { 0x1F1E8, 0x1F1E8 }, /* flag: Cocos (Keeling) Islands */
+ { 0x1F1E8, 0x1F1E9 }, /* flag: Congo - Kinshasa */
+ { 0x1F1E8, 0x1F1EB }, /* flag: Central African Republic */
+ { 0x1F1E8, 0x1F1EC }, /* flag: Congo - Brazzaville */
+ { 0x1F1E8, 0x1F1ED }, /* flag: Switzerland */
+ { 0x1F1E8, 0x1F1EE }, /* flag: Cote d'Ivoire */
+ { 0x1F1E8, 0x1F1F0 }, /* flag: Cook Islands */
+ { 0x1F1E8, 0x1F1F1 }, /* flag: Chile */
+ { 0x1F1E8, 0x1F1F2 }, /* flag: Cameroon */
+ { 0x1F1E8, 0x1F1F3 }, /* flag: China */
+ { 0x1F1E8, 0x1F1F4 }, /* flag: Colombia */
+ { 0x1F1E8, 0x1F1F5 }, /* flag: Clipperton Island */
+ { 0x1F1E8, 0x1F1F7 }, /* flag: Costa Rica */
+ { 0x1F1E8, 0x1F1FA }, /* flag: Cuba */
+ { 0x1F1E8, 0x1F1FB }, /* flag: Cape Verde */
+ { 0x1F1E8, 0x1F1FC }, /* flag: Curacao */
+ { 0x1F1E8, 0x1F1FD }, /* flag: Christmas Island */
+ { 0x1F1E8, 0x1F1FE }, /* flag: Cyprus */
+ { 0x1F1E8, 0x1F1FF }, /* flag: Czechia */
+ { 0x1F1E9, 0x1F1EA }, /* flag: Germany */
+ { 0x1F1E9, 0x1F1EC }, /* flag: Diego Garcia */
+ { 0x1F1E9, 0x1F1EF }, /* flag: Djibouti */
+ { 0x1F1E9, 0x1F1F0 }, /* flag: Denmark */
+ { 0x1F1E9, 0x1F1F2 }, /* flag: Dominica */
+ { 0x1F1E9, 0x1F1F4 }, /* flag: Dominican Republic */
+ { 0x1F1E9, 0x1F1FF }, /* flag: Algeria */
+ { 0x1F1EA, 0x1F1E6 }, /* flag: Ceuta & Melilla */
+ { 0x1F1EA, 0x1F1E8 }, /* flag: Ecuador */
+ { 0x1F1EA, 0x1F1EA }, /* flag: Estonia */
+ { 0x1F1EA, 0x1F1EC }, /* flag: Egypt */
+ { 0x1F1EA, 0x1F1ED }, /* flag: Western Sahara */
+ { 0x1F1EA, 0x1F1F7 }, /* flag: Eritrea */
+ { 0x1F1EA, 0x1F1F8 }, /* flag: Spain */
+ { 0x1F1EA, 0x1F1F9 }, /* flag: Ethiopia */
+ { 0x1F1EA, 0x1F1FA }, /* flag: European Union */
+ { 0x1F1EB, 0x1F1EE }, /* flag: Finland */
+ { 0x1F1EB, 0x1F1EF }, /* flag: Fiji */
+ { 0x1F1EB, 0x1F1F0 }, /* flag: Falkland Islands */
+ { 0x1F1EB, 0x1F1F2 }, /* flag: Micronesia */
+ { 0x1F1EB, 0x1F1F4 }, /* flag: Faroe Islands */
+ { 0x1F1EB, 0x1F1F7 }, /* flag: France */
+ { 0x1F1EC, 0x1F1E6 }, /* flag: Gabon */
+ { 0x1F1EC, 0x1F1E7 }, /* flag: United Kingdom */
+ { 0x1F1EC, 0x1F1E9 }, /* flag: Grenada */
+ { 0x1F1EC, 0x1F1EA }, /* flag: Georgia */
+ { 0x1F1EC, 0x1F1EB }, /* flag: French Guiana */
+ { 0x1F1EC, 0x1F1EC }, /* flag: Guernsey */
+ { 0x1F1EC, 0x1F1ED }, /* flag: Ghana */
+ { 0x1F1EC, 0x1F1EE }, /* flag: Gibraltar */
+ { 0x1F1EC, 0x1F1F1 }, /* flag: Greenland */
+ { 0x1F1EC, 0x1F1F2 }, /* flag: Gambia */
+ { 0x1F1EC, 0x1F1F3 }, /* flag: Guinea */
+ { 0x1F1EC, 0x1F1F5 }, /* flag: Guadeloupe */
+ { 0x1F1EC, 0x1F1F6 }, /* flag: Equatorial Guinea */
+ { 0x1F1EC, 0x1F1F7 }, /* flag: Greece */
+ { 0x1F1EC, 0x1F1F8 }, /* flag: South Georgia & South Sandwich Islands */
+ { 0x1F1EC, 0x1F1F9 }, /* flag: Guatemala */
+ { 0x1F1EC, 0x1F1FA }, /* flag: Guam */
+ { 0x1F1EC, 0x1F1FC }, /* flag: Guinea-Bissau */
+ { 0x1F1EC, 0x1F1FE }, /* flag: Guyana */
+ { 0x1F1ED, 0x1F1F0 }, /* flag: Hong Kong SAR China */
+ { 0x1F1ED, 0x1F1F2 }, /* flag: Heard & McDonald Islands */
+ { 0x1F1ED, 0x1F1F3 }, /* flag: Honduras */
+ { 0x1F1ED, 0x1F1F7 }, /* flag: Croatia */
+ { 0x1F1ED, 0x1F1F9 }, /* flag: Haiti */
+ { 0x1F1ED, 0x1F1FA }, /* flag: Hungary */
+ { 0x1F1EE, 0x1F1E8 }, /* flag: Canary Islands */
+ { 0x1F1EE, 0x1F1E9 }, /* flag: Indonesia */
+ { 0x1F1EE, 0x1F1EA }, /* flag: Ireland */
+ { 0x1F1EE, 0x1F1F1 }, /* flag: Israel */
+ { 0x1F1EE, 0x1F1F2 }, /* flag: Isle of Man */
+ { 0x1F1EE, 0x1F1F3 }, /* flag: India */
+ { 0x1F1EE, 0x1F1F4 }, /* flag: British Indian Ocean Territory */
+ { 0x1F1EE, 0x1F1F6 }, /* flag: Iraq */
+ { 0x1F1EE, 0x1F1F7 }, /* flag: Iran */
+ { 0x1F1EE, 0x1F1F8 }, /* flag: Iceland */
+ { 0x1F1EE, 0x1F1F9 }, /* flag: Italy */
+ { 0x1F1EF, 0x1F1EA }, /* flag: Jersey */
+ { 0x1F1EF, 0x1F1F2 }, /* flag: Jamaica */
+ { 0x1F1EF, 0x1F1F4 }, /* flag: Jordan */
+ { 0x1F1EF, 0x1F1F5 }, /* flag: Japan */
+ { 0x1F1F0, 0x1F1EA }, /* flag: Kenya */
+ { 0x1F1F0, 0x1F1EC }, /* flag: Kyrgyzstan */
+ { 0x1F1F0, 0x1F1ED }, /* flag: Cambodia */
+ { 0x1F1F0, 0x1F1EE }, /* flag: Kiribati */
+ { 0x1F1F0, 0x1F1F2 }, /* flag: Comoros */
+ { 0x1F1F0, 0x1F1F3 }, /* flag: St. Kitts & Nevis */
+ { 0x1F1F0, 0x1F1F5 }, /* flag: North Korea */
+ { 0x1F1F0, 0x1F1F7 }, /* flag: South Korea */
+ { 0x1F1F0, 0x1F1FC }, /* flag: Kuwait */
+ { 0x1F1F0, 0x1F1FE }, /* flag: Cayman Islands */
+ { 0x1F1F0, 0x1F1FF }, /* flag: Kazakhstan */
+ { 0x1F1F1, 0x1F1E6 }, /* flag: Laos */
+ { 0x1F1F1, 0x1F1E7 }, /* flag: Lebanon */
+ { 0x1F1F1, 0x1F1E8 }, /* flag: St. Lucia */
+ { 0x1F1F1, 0x1F1EE }, /* flag: Liechtenstein */
+ { 0x1F1F1, 0x1F1F0 }, /* flag: Sri Lanka */
+ { 0x1F1F1, 0x1F1F7 }, /* flag: Liberia */
+ { 0x1F1F1, 0x1F1F8 }, /* flag: Lesotho */
+ { 0x1F1F1, 0x1F1F9 }, /* flag: Lithuania */
+ { 0x1F1F1, 0x1F1FA }, /* flag: Luxembourg */
+ { 0x1F1F1, 0x1F1FB }, /* flag: Latvia */
+ { 0x1F1F1, 0x1F1FE }, /* flag: Libya */
+ { 0x1F1F2, 0x1F1E6 }, /* flag: Morocco */
+ { 0x1F1F2, 0x1F1E8 }, /* flag: Monaco */
+ { 0x1F1F2, 0x1F1E9 }, /* flag: Moldova */
+ { 0x1F1F2, 0x1F1EA }, /* flag: Montenegro */
+ { 0x1F1F2, 0x1F1EB }, /* flag: St. Martin */
+ { 0x1F1F2, 0x1F1EC }, /* flag: Madagascar */
+ { 0x1F1F2, 0x1F1ED }, /* flag: Marshall Islands */
+ { 0x1F1F2, 0x1F1F0 }, /* flag: North Macedonia */
+ { 0x1F1F2, 0x1F1F1 }, /* flag: Mali */
+ { 0x1F1F2, 0x1F1F2 }, /* flag: Myanmar (Burma */
+ { 0x1F1F2, 0x1F1F3 }, /* flag: Mongolia */
+ { 0x1F1F2, 0x1F1F4 }, /* flag: Macao SAR China */
+ { 0x1F1F2, 0x1F1F5 }, /* flag: Northern Mariana Islands */
+ { 0x1F1F2, 0x1F1F6 }, /* flag: Martinique */
+ { 0x1F1F2, 0x1F1F7 }, /* flag: Mauritania */
+ { 0x1F1F2, 0x1F1F8 }, /* flag: Montserrat */
+ { 0x1F1F2, 0x1F1F9 }, /* flag: Malta */
+ { 0x1F1F2, 0x1F1FA }, /* flag: Mauritius */
+ { 0x1F1F2, 0x1F1FB }, /* flag: Maldives */
+ { 0x1F1F2, 0x1F1FC }, /* flag: Malawi */
+ { 0x1F1F2, 0x1F1FD }, /* flag: Mexico */
+ { 0x1F1F2, 0x1F1FE }, /* flag: Malaysia */
+ { 0x1F1F2, 0x1F1FF }, /* flag: Mozambique */
+ { 0x1F1F3, 0x1F1E6 }, /* flag: Namibia */
+ { 0x1F1F3, 0x1F1E8 }, /* flag: New Caledonia */
+ { 0x1F1F3, 0x1F1EA }, /* flag: Niger */
+ { 0x1F1F3, 0x1F1EB }, /* flag: Norfolk Island */
+ { 0x1F1F3, 0x1F1EC }, /* flag: Nigeria */
+ { 0x1F1F3, 0x1F1EE }, /* flag: Nicaragua */
+ { 0x1F1F3, 0x1F1F1 }, /* flag: Netherlands */
+ { 0x1F1F3, 0x1F1F4 }, /* flag: Norway */
+ { 0x1F1F3, 0x1F1F5 }, /* flag: Nepal */
+ { 0x1F1F3, 0x1F1F7 }, /* flag: Nauru */
+ { 0x1F1F3, 0x1F1FA }, /* flag: Niue */
+ { 0x1F1F3, 0x1F1FF }, /* flag: New Zealand */
+ { 0x1F1F4, 0x1F1F2 }, /* flag: Oman */
+ { 0x1F1F5, 0x1F1E6 }, /* flag: Panama */
+ { 0x1F1F5, 0x1F1EA }, /* flag: Peru */
+ { 0x1F1F5, 0x1F1EB }, /* flag: French Polynesia */
+ { 0x1F1F5, 0x1F1EC }, /* flag: Papua New Guinea */
+ { 0x1F1F5, 0x1F1ED }, /* flag: Philippines */
+ { 0x1F1F5, 0x1F1F0 }, /* flag: Pakistan */
+ { 0x1F1F5, 0x1F1F1 }, /* flag: Poland */
+ { 0x1F1F5, 0x1F1F2 }, /* flag: St. Pierre & Miquelon */
+ { 0x1F1F5, 0x1F1F3 }, /* flag: Pitcairn Islands */
+ { 0x1F1F5, 0x1F1F7 }, /* flag: Puerto Rico */
+ { 0x1F1F5, 0x1F1F8 }, /* flag: Palestinian Territories */
+ { 0x1F1F5, 0x1F1F9 }, /* flag: Portugal */
+ { 0x1F1F5, 0x1F1FC }, /* flag: Palau */
+ { 0x1F1F5, 0x1F1FE }, /* flag: Paraguay */
+ { 0x1F1F6, 0x1F1E6 }, /* flag: Qatar */
+ { 0x1F1F7, 0x1F1EA }, /* flag: Reunion */
+ { 0x1F1F7, 0x1F1F4 }, /* flag: Romania */
+ { 0x1F1F7, 0x1F1F8 }, /* flag: Serbia */
+ { 0x1F1F7, 0x1F1FA }, /* flag: Russia */
+ { 0x1F1F7, 0x1F1FC }, /* flag: Rwanda */
+ { 0x1F1F8, 0x1F1E6 }, /* flag: Saudi Arabia */
+ { 0x1F1F8, 0x1F1E7 }, /* flag: Solomon Islands */
+ { 0x1F1F8, 0x1F1E8 }, /* flag: Seychelles */
+ { 0x1F1F8, 0x1F1E9 }, /* flag: Sudan */
+ { 0x1F1F8, 0x1F1EA }, /* flag: Sweden */
+ { 0x1F1F8, 0x1F1EC }, /* flag: Singapore */
+ { 0x1F1F8, 0x1F1ED }, /* flag: St. Helena */
+ { 0x1F1F8, 0x1F1EE }, /* flag: Slovenia */
+ { 0x1F1F8, 0x1F1EF }, /* flag: Svalbard & Jan Mayen */
+ { 0x1F1F8, 0x1F1F0 }, /* flag: Slovakia */
+ { 0x1F1F8, 0x1F1F1 }, /* flag: Sierra Leone */
+ { 0x1F1F8, 0x1F1F2 }, /* flag: San Marino */
+ { 0x1F1F8, 0x1F1F3 }, /* flag: Senegal */
+ { 0x1F1F8, 0x1F1F4 }, /* flag: Somalia */
+ { 0x1F1F8, 0x1F1F7 }, /* flag: Suriname */
+ { 0x1F1F8, 0x1F1F8 }, /* flag: South Sudan */
+ { 0x1F1F8, 0x1F1F9 }, /* flag: Sao Tome & Principe */
+ { 0x1F1F8, 0x1F1FB }, /* flag: El Salvador */
+ { 0x1F1F8, 0x1F1FD }, /* flag: Sint Maarten */
+ { 0x1F1F8, 0x1F1FE }, /* flag: Syria */
+ { 0x1F1F8, 0x1F1FF }, /* flag: Eswatini */
+ { 0x1F1F9, 0x1F1E6 }, /* flag: Tristan da Cunha */
+ { 0x1F1F9, 0x1F1E8 }, /* flag: Turks & Caicos Islands */
+ { 0x1F1F9, 0x1F1E9 }, /* flag: Chad */
+ { 0x1F1F9, 0x1F1EB }, /* flag: French Southern Territories */
+ { 0x1F1F9, 0x1F1EC }, /* flag: Togo */
+ { 0x1F1F9, 0x1F1ED }, /* flag: Thailand */
+ { 0x1F1F9, 0x1F1EF }, /* flag: Tajikistan */
+ { 0x1F1F9, 0x1F1F0 }, /* flag: Tokelau */
+ { 0x1F1F9, 0x1F1F1 }, /* flag: Timor-Leste */
+ { 0x1F1F9, 0x1F1F2 }, /* flag: Turkmenistan */
+ { 0x1F1F9, 0x1F1F3 }, /* flag: Tunisia */
+ { 0x1F1F9, 0x1F1F4 }, /* flag: Tonga */
+ { 0x1F1F9, 0x1F1F7 }, /* flag: Turkey */
+ { 0x1F1F9, 0x1F1F9 }, /* flag: Trinidad & Tobago */
+ { 0x1F1F9, 0x1F1FB }, /* flag: Tuvalu */
+ { 0x1F1F9, 0x1F1FC }, /* flag: Taiwan */
+ { 0x1F1F9, 0x1F1FF }, /* flag: Tanzania */
+ { 0x1F1FA, 0x1F1E6 }, /* flag: Ukraine */
+ { 0x1F1FA, 0x1F1EC }, /* flag: Uganda */
+ { 0x1F1FA, 0x1F1F2 }, /* flag: U.S. Outlying Islands */
+ { 0x1F1FA, 0x1F1F3 }, /* flag: United Nations */
+ { 0x1F1FA, 0x1F1F8 }, /* flag: United States */
+ { 0x1F1FA, 0x1F1FE }, /* flag: Uruguay */
+ { 0x1F1FA, 0x1F1FF }, /* flag: Uzbekistan */
+ { 0x1F1FB, 0x1F1E6 }, /* flag: Vatican City */
+ { 0x1F1FB, 0x1F1E8 }, /* flag: St. Vincent & Grenadines */
+ { 0x1F1FB, 0x1F1EA }, /* flag: Venezuela */
+ { 0x1F1FB, 0x1F1EC }, /* flag: British Virgin Islands */
+ { 0x1F1FB, 0x1F1EE }, /* flag: U.S. Virgin Islands */
+ { 0x1F1FB, 0x1F1F3 }, /* flag: Vietnam */
+ { 0x1F1FB, 0x1F1FA }, /* flag: Vanuatu */
+ { 0x1F1FC, 0x1F1EB }, /* flag: Wallis & Futuna */
+ { 0x1F1FC, 0x1F1F8 }, /* flag: Samoa */
+ { 0x1F1FD, 0x1F1F0 }, /* flag: Kosovo */
+ { 0x1F1FE, 0x1F1EA }, /* flag: Yemen */
+ { 0x1F1FE, 0x1F1F9 }, /* flag: Mayotte */
+ { 0x1F1FF, 0x1F1E6 }, /* flag: South Africa */
+ { 0x1F1FF, 0x1F1F2 }, /* flag: Zambia */
+ { 0x1F1FF, 0x1F1FC }, /* flag: Zimbabwe */
+ { 0x0261D, 0x1F3FB }, /* index pointing up: light skin tone */
+ { 0x0261D, 0x1F3FC }, /* index pointing up: medium-light skin tone */
+ { 0x0261D, 0x1F3FD }, /* index pointing up: medium skin tone */
+ { 0x0261D, 0x1F3FE }, /* index pointing up: medium-dark skin tone */
+ { 0x0261D, 0x1F3FF }, /* index pointing up: dark skin tone */
+ { 0x026F9, 0x1F3FB }, /* person bouncing ball: light skin tone */
+ { 0x026F9, 0x1F3FC }, /* person bouncing ball: medium-light skin tone */
+ { 0x026F9, 0x1F3FD }, /* person bouncing ball: medium skin tone */
+ { 0x026F9, 0x1F3FE }, /* person bouncing ball: medium-dark skin tone */
+ { 0x026F9, 0x1F3FF }, /* person bouncing ball: dark skin tone */
+ { 0x0270A, 0x1F3FB }, /* raised fist: light skin tone */
+ { 0x0270A, 0x1F3FC }, /* raised fist: medium-light skin tone */
+ { 0x0270A, 0x1F3FD }, /* raised fist: medium skin tone */
+ { 0x0270A, 0x1F3FE }, /* raised fist: medium-dark skin tone */
+ { 0x0270A, 0x1F3FF }, /* raised fist: dark skin tone */
+ { 0x0270B, 0x1F3FB }, /* raised hand: light skin tone */
+ { 0x0270B, 0x1F3FC }, /* raised hand: medium-light skin tone */
+ { 0x0270B, 0x1F3FD }, /* raised hand: medium skin tone */
+ { 0x0270B, 0x1F3FE }, /* raised hand: medium-dark skin tone */
+ { 0x0270B, 0x1F3FF }, /* raised hand: dark skin tone */
+ { 0x0270C, 0x1F3FB }, /* victory hand: light skin tone */
+ { 0x0270C, 0x1F3FC }, /* victory hand: medium-light skin tone */
+ { 0x0270C, 0x1F3FD }, /* victory hand: medium skin tone */
+ { 0x0270C, 0x1F3FE }, /* victory hand: medium-dark skin tone */
+ { 0x0270C, 0x1F3FF }, /* victory hand: dark skin tone */
+ { 0x0270D, 0x1F3FB }, /* writing hand: light skin tone */
+ { 0x0270D, 0x1F3FC }, /* writing hand: medium-light skin tone */
+ { 0x0270D, 0x1F3FD }, /* writing hand: medium skin tone */
+ { 0x0270D, 0x1F3FE }, /* writing hand: medium-dark skin tone */
+ { 0x0270D, 0x1F3FF }, /* writing hand: dark skin tone */
+ { 0x1F385, 0x1F3FB }, /* Santa Claus: light skin tone */
+ { 0x1F385, 0x1F3FC }, /* Santa Claus: medium-light skin tone */
+ { 0x1F385, 0x1F3FD }, /* Santa Claus: medium skin tone */
+ { 0x1F385, 0x1F3FE }, /* Santa Claus: medium-dark skin tone */
+ { 0x1F385, 0x1F3FF }, /* Santa Claus: dark skin tone */
+ { 0x1F3C2, 0x1F3FB }, /* snowboarder: light skin tone */
+ { 0x1F3C2, 0x1F3FC }, /* snowboarder: medium-light skin tone */
+ { 0x1F3C2, 0x1F3FD }, /* snowboarder: medium skin tone */
+ { 0x1F3C2, 0x1F3FE }, /* snowboarder: medium-dark skin tone */
+ { 0x1F3C2, 0x1F3FF }, /* snowboarder: dark skin tone */
+ { 0x1F3C3, 0x1F3FB }, /* person running: light skin tone */
+ { 0x1F3C3, 0x1F3FC }, /* person running: medium-light skin tone */
+ { 0x1F3C3, 0x1F3FD }, /* person running: medium skin tone */
+ { 0x1F3C3, 0x1F3FE }, /* person running: medium-dark skin tone */
+ { 0x1F3C3, 0x1F3FF }, /* person running: dark skin tone */
+ { 0x1F3C4, 0x1F3FB }, /* person surfing: light skin tone */
+ { 0x1F3C4, 0x1F3FC }, /* person surfing: medium-light skin tone */
+ { 0x1F3C4, 0x1F3FD }, /* person surfing: medium skin tone */
+ { 0x1F3C4, 0x1F3FE }, /* person surfing: medium-dark skin tone */
+ { 0x1F3C4, 0x1F3FF }, /* person surfing: dark skin tone */
+ { 0x1F3C7, 0x1F3FB }, /* horse racing: light skin tone */
+ { 0x1F3C7, 0x1F3FC }, /* horse racing: medium-light skin tone */
+ { 0x1F3C7, 0x1F3FD }, /* horse racing: medium skin tone */
+ { 0x1F3C7, 0x1F3FE }, /* horse racing: medium-dark skin tone */
+ { 0x1F3C7, 0x1F3FF }, /* horse racing: dark skin tone */
+ { 0x1F3CA, 0x1F3FB }, /* person swimming: light skin tone */
+ { 0x1F3CA, 0x1F3FC }, /* person swimming: medium-light skin tone */
+ { 0x1F3CA, 0x1F3FD }, /* person swimming: medium skin tone */
+ { 0x1F3CA, 0x1F3FE }, /* person swimming: medium-dark skin tone */
+ { 0x1F3CA, 0x1F3FF }, /* person swimming: dark skin tone */
+ { 0x1F3CB, 0x1F3FB }, /* person lifting weights: light skin tone */
+ { 0x1F3CB, 0x1F3FC }, /* person lifting weights: medium-light skin tone */
+ { 0x1F3CB, 0x1F3FD }, /* person lifting weights: medium skin tone */
+ { 0x1F3CB, 0x1F3FE }, /* person lifting weights: medium-dark skin tone */
+ { 0x1F3CB, 0x1F3FF }, /* person lifting weights: dark skin tone */
+ { 0x1F3CC, 0x1F3FB }, /* person golfing: light skin tone */
+ { 0x1F3CC, 0x1F3FC }, /* person golfing: medium-light skin tone */
+ { 0x1F3CC, 0x1F3FD }, /* person golfing: medium skin tone */
+ { 0x1F3CC, 0x1F3FE }, /* person golfing: medium-dark skin tone */
+ { 0x1F3CC, 0x1F3FF }, /* person golfing: dark skin tone */
+ { 0x1F442, 0x1F3FB }, /* ear: light skin tone */
+ { 0x1F442, 0x1F3FC }, /* ear: medium-light skin tone */
+ { 0x1F442, 0x1F3FD }, /* ear: medium skin tone */
+ { 0x1F442, 0x1F3FE }, /* ear: medium-dark skin tone */
+ { 0x1F442, 0x1F3FF }, /* ear: dark skin tone */
+ { 0x1F443, 0x1F3FB }, /* nose: light skin tone */
+ { 0x1F443, 0x1F3FC }, /* nose: medium-light skin tone */
+ { 0x1F443, 0x1F3FD }, /* nose: medium skin tone */
+ { 0x1F443, 0x1F3FE }, /* nose: medium-dark skin tone */
+ { 0x1F443, 0x1F3FF }, /* nose: dark skin tone */
+ { 0x1F446, 0x1F3FB }, /* backhand index pointing up: light skin tone */
+ { 0x1F446, 0x1F3FC }, /* backhand index pointing up: medium-light skin tone */
+ { 0x1F446, 0x1F3FD }, /* backhand index pointing up: medium skin tone */
+ { 0x1F446, 0x1F3FE }, /* backhand index pointing up: medium-dark skin tone */
+ { 0x1F446, 0x1F3FF }, /* backhand index pointing up: dark skin tone */
+ { 0x1F447, 0x1F3FB }, /* backhand index pointing down: light skin tone */
+ { 0x1F447, 0x1F3FC }, /* backhand index pointing down: medium-light skin tone */
+ { 0x1F447, 0x1F3FD }, /* backhand index pointing down: medium skin tone */
+ { 0x1F447, 0x1F3FE }, /* backhand index pointing down: medium-dark skin tone */
+ { 0x1F447, 0x1F3FF }, /* backhand index pointing down: dark skin tone */
+ { 0x1F448, 0x1F3FB }, /* backhand index pointing left: light skin tone */
+ { 0x1F448, 0x1F3FC }, /* backhand index pointing left: medium-light skin tone */
+ { 0x1F448, 0x1F3FD }, /* backhand index pointing left: medium skin tone */
+ { 0x1F448, 0x1F3FE }, /* backhand index pointing left: medium-dark skin tone */
+ { 0x1F448, 0x1F3FF }, /* backhand index pointing left: dark skin tone */
+ { 0x1F449, 0x1F3FB }, /* backhand index pointing right: light skin tone */
+ { 0x1F449, 0x1F3FC }, /* backhand index pointing right: medium-light skin tone */
+ { 0x1F449, 0x1F3FD }, /* backhand index pointing right: medium skin tone */
+ { 0x1F449, 0x1F3FE }, /* backhand index pointing right: medium-dark skin tone */
+ { 0x1F449, 0x1F3FF }, /* backhand index pointing right: dark skin tone */
+ { 0x1F44A, 0x1F3FB }, /* oncoming fist: light skin tone */
+ { 0x1F44A, 0x1F3FC }, /* oncoming fist: medium-light skin tone */
+ { 0x1F44A, 0x1F3FD }, /* oncoming fist: medium skin tone */
+ { 0x1F44A, 0x1F3FE }, /* oncoming fist: medium-dark skin tone */
+ { 0x1F44A, 0x1F3FF }, /* oncoming fist: dark skin tone */
+ { 0x1F44B, 0x1F3FB }, /* waving hand: light skin tone */
+ { 0x1F44B, 0x1F3FC }, /* waving hand: medium-light skin tone */
+ { 0x1F44B, 0x1F3FD }, /* waving hand: medium skin tone */
+ { 0x1F44B, 0x1F3FE }, /* waving hand: medium-dark skin tone */
+ { 0x1F44B, 0x1F3FF }, /* waving hand: dark skin tone */
+ { 0x1F44C, 0x1F3FB }, /* OK hand: light skin tone */
+ { 0x1F44C, 0x1F3FC }, /* OK hand: medium-light skin tone */
+ { 0x1F44C, 0x1F3FD }, /* OK hand: medium skin tone */
+ { 0x1F44C, 0x1F3FE }, /* OK hand: medium-dark skin tone */
+ { 0x1F44C, 0x1F3FF }, /* OK hand: dark skin tone */
+ { 0x1F44D, 0x1F3FB }, /* thumbs up: light skin tone */
+ { 0x1F44D, 0x1F3FC }, /* thumbs up: medium-light skin tone */
+ { 0x1F44D, 0x1F3FD }, /* thumbs up: medium skin tone */
+ { 0x1F44D, 0x1F3FE }, /* thumbs up: medium-dark skin tone */
+ { 0x1F44D, 0x1F3FF }, /* thumbs up: dark skin tone */
+ { 0x1F44E, 0x1F3FB }, /* thumbs down: light skin tone */
+ { 0x1F44E, 0x1F3FC }, /* thumbs down: medium-light skin tone */
+ { 0x1F44E, 0x1F3FD }, /* thumbs down: medium skin tone */
+ { 0x1F44E, 0x1F3FE }, /* thumbs down: medium-dark skin tone */
+ { 0x1F44E, 0x1F3FF }, /* thumbs down: dark skin tone */
+ { 0x1F44F, 0x1F3FB }, /* clapping hands: light skin tone */
+ { 0x1F44F, 0x1F3FC }, /* clapping hands: medium-light skin tone */
+ { 0x1F44F, 0x1F3FD }, /* clapping hands: medium skin tone */
+ { 0x1F44F, 0x1F3FE }, /* clapping hands: medium-dark skin tone */
+ { 0x1F44F, 0x1F3FF }, /* clapping hands: dark skin tone */
+ { 0x1F450, 0x1F3FB }, /* open hands: light skin tone */
+ { 0x1F450, 0x1F3FC }, /* open hands: medium-light skin tone */
+ { 0x1F450, 0x1F3FD }, /* open hands: medium skin tone */
+ { 0x1F450, 0x1F3FE }, /* open hands: medium-dark skin tone */
+ { 0x1F450, 0x1F3FF }, /* open hands: dark skin tone */
+ { 0x1F466, 0x1F3FB }, /* boy: light skin tone */
+ { 0x1F466, 0x1F3FC }, /* boy: medium-light skin tone */
+ { 0x1F466, 0x1F3FD }, /* boy: medium skin tone */
+ { 0x1F466, 0x1F3FE }, /* boy: medium-dark skin tone */
+ { 0x1F466, 0x1F3FF }, /* boy: dark skin tone */
+ { 0x1F467, 0x1F3FB }, /* girl: light skin tone */
+ { 0x1F467, 0x1F3FC }, /* girl: medium-light skin tone */
+ { 0x1F467, 0x1F3FD }, /* girl: medium skin tone */
+ { 0x1F467, 0x1F3FE }, /* girl: medium-dark skin tone */
+ { 0x1F467, 0x1F3FF }, /* girl: dark skin tone */
+ { 0x1F468, 0x1F3FB }, /* man: light skin tone */
+ { 0x1F468, 0x1F3FC }, /* man: medium-light skin tone */
+ { 0x1F468, 0x1F3FD }, /* man: medium skin tone */
+ { 0x1F468, 0x1F3FE }, /* man: medium-dark skin tone */
+ { 0x1F468, 0x1F3FF }, /* man: dark skin tone */
+ { 0x1F469, 0x1F3FB }, /* woman: light skin tone */
+ { 0x1F469, 0x1F3FC }, /* woman: medium-light skin tone */
+ { 0x1F469, 0x1F3FD }, /* woman: medium skin tone */
+ { 0x1F469, 0x1F3FE }, /* woman: medium-dark skin tone */
+ { 0x1F469, 0x1F3FF }, /* woman: dark skin tone */
+ { 0x1F46B, 0x1F3FB }, /* woman and man holding hands: light skin tone */
+ { 0x1F46B, 0x1F3FC }, /* woman and man holding hands: medium-light skin tone */
+ { 0x1F46B, 0x1F3FD }, /* woman and man holding hands: medium skin tone */
+ { 0x1F46B, 0x1F3FE }, /* woman and man holding hands: medium-dark skin tone */
+ { 0x1F46B, 0x1F3FF }, /* woman and man holding hands: dark skin tone */
+ { 0x1F46C, 0x1F3FB }, /* men holding hands: light skin tone */
+ { 0x1F46C, 0x1F3FC }, /* men holding hands: medium-light skin tone */
+ { 0x1F46C, 0x1F3FD }, /* men holding hands: medium skin tone */
+ { 0x1F46C, 0x1F3FE }, /* men holding hands: medium-dark skin tone */
+ { 0x1F46C, 0x1F3FF }, /* men holding hands: dark skin tone */
+ { 0x1F46D, 0x1F3FB }, /* women holding hands: light skin tone */
+ { 0x1F46D, 0x1F3FC }, /* women holding hands: medium-light skin tone */
+ { 0x1F46D, 0x1F3FD }, /* women holding hands: medium skin tone */
+ { 0x1F46D, 0x1F3FE }, /* women holding hands: medium-dark skin tone */
+ { 0x1F46D, 0x1F3FF }, /* women holding hands: dark skin tone */
+ { 0x1F46E, 0x1F3FB }, /* police officer: light skin tone */
+ { 0x1F46E, 0x1F3FC }, /* police officer: medium-light skin tone */
+ { 0x1F46E, 0x1F3FD }, /* police officer: medium skin tone */
+ { 0x1F46E, 0x1F3FE }, /* police officer: medium-dark skin tone */
+ { 0x1F46E, 0x1F3FF }, /* police officer: dark skin tone */
+ { 0x1F470, 0x1F3FB }, /* person with veil: light skin tone */
+ { 0x1F470, 0x1F3FC }, /* person with veil: medium-light skin tone */
+ { 0x1F470, 0x1F3FD }, /* person with veil: medium skin tone */
+ { 0x1F470, 0x1F3FE }, /* person with veil: medium-dark skin tone */
+ { 0x1F470, 0x1F3FF }, /* person with veil: dark skin tone */
+ { 0x1F471, 0x1F3FB }, /* person: light skin tone, blond hair */
+ { 0x1F471, 0x1F3FC }, /* person: medium-light skin tone, blond hair */
+ { 0x1F471, 0x1F3FD }, /* person: medium skin tone, blond hair */
+ { 0x1F471, 0x1F3FE }, /* person: medium-dark skin tone, blond hair */
+ { 0x1F471, 0x1F3FF }, /* person: dark skin tone, blond hair */
+ { 0x1F472, 0x1F3FB }, /* person with skullcap: light skin tone */
+ { 0x1F472, 0x1F3FC }, /* person with skullcap: medium-light skin tone */
+ { 0x1F472, 0x1F3FD }, /* person with skullcap: medium skin tone */
+ { 0x1F472, 0x1F3FE }, /* person with skullcap: medium-dark skin tone */
+ { 0x1F472, 0x1F3FF }, /* person with skullcap: dark skin tone */
+ { 0x1F473, 0x1F3FB }, /* person wearing turban: light skin tone */
+ { 0x1F473, 0x1F3FC }, /* person wearing turban: medium-light skin tone */
+ { 0x1F473, 0x1F3FD }, /* person wearing turban: medium skin tone */
+ { 0x1F473, 0x1F3FE }, /* person wearing turban: medium-dark skin tone */
+ { 0x1F473, 0x1F3FF }, /* person wearing turban: dark skin tone */
+ { 0x1F474, 0x1F3FB }, /* old man: light skin tone */
+ { 0x1F474, 0x1F3FC }, /* old man: medium-light skin tone */
+ { 0x1F474, 0x1F3FD }, /* old man: medium skin tone */
+ { 0x1F474, 0x1F3FE }, /* old man: medium-dark skin tone */
+ { 0x1F474, 0x1F3FF }, /* old man: dark skin tone */
+ { 0x1F475, 0x1F3FB }, /* old woman: light skin tone */
+ { 0x1F475, 0x1F3FC }, /* old woman: medium-light skin tone */
+ { 0x1F475, 0x1F3FD }, /* old woman: medium skin tone */
+ { 0x1F475, 0x1F3FE }, /* old woman: medium-dark skin tone */
+ { 0x1F475, 0x1F3FF }, /* old woman: dark skin tone */
+ { 0x1F476, 0x1F3FB }, /* baby: light skin tone */
+ { 0x1F476, 0x1F3FC }, /* baby: medium-light skin tone */
+ { 0x1F476, 0x1F3FD }, /* baby: medium skin tone */
+ { 0x1F476, 0x1F3FE }, /* baby: medium-dark skin tone */
+ { 0x1F476, 0x1F3FF }, /* baby: dark skin tone */
+ { 0x1F477, 0x1F3FB }, /* construction worker: light skin tone */
+ { 0x1F477, 0x1F3FC }, /* construction worker: medium-light skin tone */
+ { 0x1F477, 0x1F3FD }, /* construction worker: medium skin tone */
+ { 0x1F477, 0x1F3FE }, /* construction worker: medium-dark skin tone */
+ { 0x1F477, 0x1F3FF }, /* construction worker: dark skin tone */
+ { 0x1F478, 0x1F3FB }, /* princess: light skin tone */
+ { 0x1F478, 0x1F3FC }, /* princess: medium-light skin tone */
+ { 0x1F478, 0x1F3FD }, /* princess: medium skin tone */
+ { 0x1F478, 0x1F3FE }, /* princess: medium-dark skin tone */
+ { 0x1F478, 0x1F3FF }, /* princess: dark skin tone */
+ { 0x1F47C, 0x1F3FB }, /* baby angel: light skin tone */
+ { 0x1F47C, 0x1F3FC }, /* baby angel: medium-light skin tone */
+ { 0x1F47C, 0x1F3FD }, /* baby angel: medium skin tone */
+ { 0x1F47C, 0x1F3FE }, /* baby angel: medium-dark skin tone */
+ { 0x1F47C, 0x1F3FF }, /* baby angel: dark skin tone */
+ { 0x1F481, 0x1F3FB }, /* person tipping hand: light skin tone */
+ { 0x1F481, 0x1F3FC }, /* person tipping hand: medium-light skin tone */
+ { 0x1F481, 0x1F3FD }, /* person tipping hand: medium skin tone */
+ { 0x1F481, 0x1F3FE }, /* person tipping hand: medium-dark skin tone */
+ { 0x1F481, 0x1F3FF }, /* person tipping hand: dark skin tone */
+ { 0x1F482, 0x1F3FB }, /* guard: light skin tone */
+ { 0x1F482, 0x1F3FC }, /* guard: medium-light skin tone */
+ { 0x1F482, 0x1F3FD }, /* guard: medium skin tone */
+ { 0x1F482, 0x1F3FE }, /* guard: medium-dark skin tone */
+ { 0x1F482, 0x1F3FF }, /* guard: dark skin tone */
+ { 0x1F483, 0x1F3FB }, /* woman dancing: light skin tone */
+ { 0x1F483, 0x1F3FC }, /* woman dancing: medium-light skin tone */
+ { 0x1F483, 0x1F3FD }, /* woman dancing: medium skin tone */
+ { 0x1F483, 0x1F3FE }, /* woman dancing: medium-dark skin tone */
+ { 0x1F483, 0x1F3FF }, /* woman dancing: dark skin tone */
+ { 0x1F485, 0x1F3FB }, /* nail polish: light skin tone */
+ { 0x1F485, 0x1F3FC }, /* nail polish: medium-light skin tone */
+ { 0x1F485, 0x1F3FD }, /* nail polish: medium skin tone */
+ { 0x1F485, 0x1F3FE }, /* nail polish: medium-dark skin tone */
+ { 0x1F485, 0x1F3FF }, /* nail polish: dark skin tone */
+ { 0x1F486, 0x1F3FB }, /* person getting massage: light skin tone */
+ { 0x1F486, 0x1F3FC }, /* person getting massage: medium-light skin tone */
+ { 0x1F486, 0x1F3FD }, /* person getting massage: medium skin tone */
+ { 0x1F486, 0x1F3FE }, /* person getting massage: medium-dark skin tone */
+ { 0x1F486, 0x1F3FF }, /* person getting massage: dark skin tone */
+ { 0x1F487, 0x1F3FB }, /* person getting haircut: light skin tone */
+ { 0x1F487, 0x1F3FC }, /* person getting haircut: medium-light skin tone */
+ { 0x1F487, 0x1F3FD }, /* person getting haircut: medium skin tone */
+ { 0x1F487, 0x1F3FE }, /* person getting haircut: medium-dark skin tone */
+ { 0x1F487, 0x1F3FF }, /* person getting haircut: dark skin tone */
+ { 0x1F48F, 0x1F3FB }, /* kiss: light skin tone */
+ { 0x1F48F, 0x1F3FC }, /* kiss: medium-light skin tone */
+ { 0x1F48F, 0x1F3FD }, /* kiss: medium skin tone */
+ { 0x1F48F, 0x1F3FE }, /* kiss: medium-dark skin tone */
+ { 0x1F48F, 0x1F3FF }, /* kiss: dark skin tone */
+ { 0x1F491, 0x1F3FB }, /* couple with heart: light skin tone */
+ { 0x1F491, 0x1F3FC }, /* couple with heart: medium-light skin tone */
+ { 0x1F491, 0x1F3FD }, /* couple with heart: medium skin tone */
+ { 0x1F491, 0x1F3FE }, /* couple with heart: medium-dark skin tone */
+ { 0x1F491, 0x1F3FF }, /* couple with heart: dark skin tone */
+ { 0x1F4AA, 0x1F3FB }, /* flexed biceps: light skin tone */
+ { 0x1F4AA, 0x1F3FC }, /* flexed biceps: medium-light skin tone */
+ { 0x1F4AA, 0x1F3FD }, /* flexed biceps: medium skin tone */
+ { 0x1F4AA, 0x1F3FE }, /* flexed biceps: medium-dark skin tone */
+ { 0x1F4AA, 0x1F3FF }, /* flexed biceps: dark skin tone */
+ { 0x1F574, 0x1F3FB }, /* person in suit levitating: light skin tone */
+ { 0x1F574, 0x1F3FC }, /* person in suit levitating: medium-light skin tone */
+ { 0x1F574, 0x1F3FD }, /* person in suit levitating: medium skin tone */
+ { 0x1F574, 0x1F3FE }, /* person in suit levitating: medium-dark skin tone */
+ { 0x1F574, 0x1F3FF }, /* person in suit levitating: dark skin tone */
+ { 0x1F575, 0x1F3FB }, /* detective: light skin tone */
+ { 0x1F575, 0x1F3FC }, /* detective: medium-light skin tone */
+ { 0x1F575, 0x1F3FD }, /* detective: medium skin tone */
+ { 0x1F575, 0x1F3FE }, /* detective: medium-dark skin tone */
+ { 0x1F575, 0x1F3FF }, /* detective: dark skin tone */
+ { 0x1F57A, 0x1F3FB }, /* man dancing: light skin tone */
+ { 0x1F57A, 0x1F3FC }, /* man dancing: medium-light skin tone */
+ { 0x1F57A, 0x1F3FD }, /* man dancing: medium skin tone */
+ { 0x1F57A, 0x1F3FE }, /* man dancing: medium-dark skin tone */
+ { 0x1F57A, 0x1F3FF }, /* man dancing: dark skin tone */
+ { 0x1F590, 0x1F3FB }, /* hand with fingers splayed: light skin tone */
+ { 0x1F590, 0x1F3FC }, /* hand with fingers splayed: medium-light skin tone */
+ { 0x1F590, 0x1F3FD }, /* hand with fingers splayed: medium skin tone */
+ { 0x1F590, 0x1F3FE }, /* hand with fingers splayed: medium-dark skin tone */
+ { 0x1F590, 0x1F3FF }, /* hand with fingers splayed: dark skin tone */
+ { 0x1F595, 0x1F3FB }, /* middle finger: light skin tone */
+ { 0x1F595, 0x1F3FC }, /* middle finger: medium-light skin tone */
+ { 0x1F595, 0x1F3FD }, /* middle finger: medium skin tone */
+ { 0x1F595, 0x1F3FE }, /* middle finger: medium-dark skin tone */
+ { 0x1F595, 0x1F3FF }, /* middle finger: dark skin tone */
+ { 0x1F596, 0x1F3FB }, /* vulcan salute: light skin tone */
+ { 0x1F596, 0x1F3FC }, /* vulcan salute: medium-light skin tone */
+ { 0x1F596, 0x1F3FD }, /* vulcan salute: medium skin tone */
+ { 0x1F596, 0x1F3FE }, /* vulcan salute: medium-dark skin tone */
+ { 0x1F596, 0x1F3FF }, /* vulcan salute: dark skin tone */
+ { 0x1F645, 0x1F3FB }, /* person gesturing NO: light skin tone */
+ { 0x1F645, 0x1F3FC }, /* person gesturing NO: medium-light skin tone */
+ { 0x1F645, 0x1F3FD }, /* person gesturing NO: medium skin tone */
+ { 0x1F645, 0x1F3FE }, /* person gesturing NO: medium-dark skin tone */
+ { 0x1F645, 0x1F3FF }, /* person gesturing NO: dark skin tone */
+ { 0x1F646, 0x1F3FB }, /* person gesturing OK: light skin tone */
+ { 0x1F646, 0x1F3FC }, /* person gesturing OK: medium-light skin tone */
+ { 0x1F646, 0x1F3FD }, /* person gesturing OK: medium skin tone */
+ { 0x1F646, 0x1F3FE }, /* person gesturing OK: medium-dark skin tone */
+ { 0x1F646, 0x1F3FF }, /* person gesturing OK: dark skin tone */
+ { 0x1F647, 0x1F3FB }, /* person bowing: light skin tone */
+ { 0x1F647, 0x1F3FC }, /* person bowing: medium-light skin tone */
+ { 0x1F647, 0x1F3FD }, /* person bowing: medium skin tone */
+ { 0x1F647, 0x1F3FE }, /* person bowing: medium-dark skin tone */
+ { 0x1F647, 0x1F3FF }, /* person bowing: dark skin tone */
+ { 0x1F64B, 0x1F3FB }, /* person raising hand: light skin tone */
+ { 0x1F64B, 0x1F3FC }, /* person raising hand: medium-light skin tone */
+ { 0x1F64B, 0x1F3FD }, /* person raising hand: medium skin tone */
+ { 0x1F64B, 0x1F3FE }, /* person raising hand: medium-dark skin tone */
+ { 0x1F64B, 0x1F3FF }, /* person ra