5918 lines
460 KiB
Diff
5918 lines
460 KiB
Diff
From 16850709306589a2433c0038605d365a6b6bedad Mon Sep 17 00:00:00 2001
|
||
From: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
|
||
Date: Tue, 2 Apr 2024 13:20:34 +0200
|
||
Subject: Use emoji segmenter to apply emoji fonts automatically
|
||
|
||
Colorful emojis in Unicode are not isolated to specific ranges
|
||
of code points like other writing systems. Instead, there are
|
||
a set of rules defining whether a sequence of characters should
|
||
be displayed in color or black/white.
|
||
|
||
http://www.unicode.org/reports/tr51/
|
||
|
||
For instance, appending a variation selector to a character can
|
||
turn it into a color emoji, even if it is a code point that
|
||
predates the invention of emojis.
|
||
|
||
In addition, sequences of joined characters that are determined
|
||
to be a color emoji sequence should be parsed by a single emoji
|
||
font, so that it can apply things like skin color, etc.
|
||
|
||
In general, users expect emojis and emoji sequences to be shown
|
||
in the preferred color font of the system, even if a selected
|
||
font has black/white characters for the symbols.
|
||
|
||
This patch applies the emoji segmenter to strings to isolate
|
||
sequences that should be in color. As an implementation hack,
|
||
we mark this in the QScriptItems as a special "emoji" script.
|
||
Note that this is not a real Unicode script and only exists
|
||
internally for this reason, because the "emojiness" of the
|
||
resulting glyph overrides the original script of the
|
||
individual characters when selecting fonts. This way, we can
|
||
use a lot of the same logic for itemizing the strings and
|
||
looking up fonts, and we don't need to increase the size of
|
||
the QScriptItem. (It is just an implementation detail and
|
||
is not exposed to the user, so it can be replaced by other
|
||
approaches later if we need to.)
|
||
|
||
When matching an emoji sequence, we always try to apply a
|
||
color font and ignore all others. The exception is if there
|
||
is no color font at all on the system, then we will find a
|
||
black and white font which supports the characters instead
|
||
as a final failsafe.
|
||
|
||
In addition, each platform will put its default emoji font
|
||
at the top of the fallbacks list in order to make this the
|
||
preference in case there are more than one. This patch also
|
||
adds API to override this with an application-defined emoji
|
||
font, since this is a common use case.
|
||
|
||
Note: The font includes an environment variable to disable
|
||
the feature as a fail safe. A flag to disable it per QFont
|
||
will be added in a follow-up.
|
||
|
||
Fixes: QTBUG-111801
|
||
Change-Id: I9431ec34d56772ab8688814963073b83b23002ae
|
||
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
|
||
Reviewed-by: <carl@carlschwan.eu>
|
||
---
|
||
|
||
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm
|
||
index 2197c83f..fc551cdb 100644
|
||
--- a/src/gui/text/coretext/qcoretextfontdatabase.mm
|
||
+++ b/src/gui/text/coretext/qcoretextfontdatabase.mm
|
||
@@ -327,6 +327,7 @@ struct FontDescription {
|
||
QFont::Stretch stretch;
|
||
qreal pointSize;
|
||
bool fixedPitch;
|
||
+ bool colorFont;
|
||
QSupportedWritingSystems writingSystems;
|
||
};
|
||
|
||
@@ -359,6 +360,9 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
|
||
fd->style = QFont::StyleNormal;
|
||
fd->stretch = QFont::Unstretched;
|
||
fd->fixedPitch = false;
|
||
+ fd->colorFont = false;
|
||
+
|
||
+
|
||
|
||
if (QCFType<CTFontRef> tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) {
|
||
uint tag = QFont::Tag("OS/2").value();
|
||
@@ -393,6 +397,9 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
|
||
if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) {
|
||
int d;
|
||
if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) {
|
||
+ if (d & kCTFontColorGlyphsTrait)
|
||
+ fd->colorFont = true;
|
||
+
|
||
if (d & kCTFontMonoSpaceTrait)
|
||
fd->fixedPitch = true;
|
||
if (d & kCTFontExpandedTrait)
|
||
@@ -451,7 +458,7 @@ void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font, con
|
||
CFRetain(font);
|
||
QPlatformFontDatabase::registerFont(family, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch,
|
||
true /* antialiased */, true /* scalable */, 0 /* pixelSize, ignored as font is scalable */,
|
||
- fd.fixedPitch, fd.writingSystems, (void *)font);
|
||
+ fd.fixedPitch, fd.colorFont, fd.writingSystems, (void *)font);
|
||
}
|
||
|
||
static NSString * const kQtFontDataAttribute = @"QtFontDataAttribute";
|
||
@@ -629,7 +636,18 @@ CTFontDescriptorRef descriptorForStyle(QFont::StyleHint styleHint)
|
||
}
|
||
}
|
||
|
||
-QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QCoreTextFontDatabase::fallbacksForScript(QFontDatabasePrivate::ExtendedScript script) const
|
||
+{
|
||
+ if (script == QFontDatabasePrivate::Script_Emoji)
|
||
+ return QStringList{} << QStringLiteral(".Apple Color Emoji UI");
|
||
+ else
|
||
+ return QStringList{};
|
||
+}
|
||
+
|
||
+QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
Q_UNUSED(style);
|
||
|
||
@@ -639,7 +657,7 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
|
||
|
||
QMacAutoReleasePool pool;
|
||
|
||
- QStringList fallbackList;
|
||
+ QStringList fallbackList = fallbacksForScript(script);
|
||
|
||
QCFType<CFArrayRef> fallbackFonts = fallbacksForFamily(family);
|
||
if (!fallbackFonts || !CFArrayGetCount(fallbackFonts)) {
|
||
@@ -702,32 +720,34 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
|
||
fallbackList.append(QStringLiteral("Apple Symbols"));
|
||
// Some Noto* fonts are not automatically enumerated by system, despite being the main
|
||
// fonts for their writing system.
|
||
- QString hardcodedFont = m_hardcodedFallbackFonts.value(script);
|
||
- if (!hardcodedFont.isEmpty() && !fallbackList.contains(hardcodedFont)) {
|
||
- if (!isFamilyPopulated(hardcodedFont)) {
|
||
- if (!m_privateFamilies.contains(hardcodedFont)) {
|
||
- QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(hardcodedFont);
|
||
- QCFType<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(familyDescriptor, nullptr);
|
||
- if (matchingFonts) {
|
||
- const int numFonts = CFArrayGetCount(matchingFonts);
|
||
- for (int i = 0; i < numFonts; ++i)
|
||
- const_cast<QCoreTextFontDatabase *>(this)->populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)),
|
||
- hardcodedFont);
|
||
-
|
||
- fallbackList.append(hardcodedFont);
|
||
+ if (script < int(QChar::ScriptCount)) {
|
||
+ QString hardcodedFont = m_hardcodedFallbackFonts.value(QChar::Script(script));
|
||
+ if (!hardcodedFont.isEmpty() && !fallbackList.contains(hardcodedFont)) {
|
||
+ if (!isFamilyPopulated(hardcodedFont)) {
|
||
+ if (!m_privateFamilies.contains(hardcodedFont)) {
|
||
+ QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(hardcodedFont);
|
||
+ QCFType<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(familyDescriptor, nullptr);
|
||
+ if (matchingFonts) {
|
||
+ const int numFonts = CFArrayGetCount(matchingFonts);
|
||
+ for (int i = 0; i < numFonts; ++i)
|
||
+ const_cast<QCoreTextFontDatabase *>(this)->populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)),
|
||
+ hardcodedFont);
|
||
+
|
||
+ fallbackList.append(hardcodedFont);
|
||
+ }
|
||
+
|
||
+ // Register as private family even if the font is not found, in order to avoid
|
||
+ // redoing the check later. In later calls, the font will then just be ignored.
|
||
+ m_privateFamilies.insert(hardcodedFont);
|
||
}
|
||
-
|
||
- // Register as private family even if the font is not found, in order to avoid
|
||
- // redoing the check later. In later calls, the font will then just be ignored.
|
||
- m_privateFamilies.insert(hardcodedFont);
|
||
+ } else {
|
||
+ fallbackList.append(hardcodedFont);
|
||
}
|
||
- } else {
|
||
- fallbackList.append(hardcodedFont);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
- extern QStringList qt_sort_families_by_writing_system(QChar::Script, const QStringList &);
|
||
+ extern QStringList qt_sort_families_by_writing_system(QFontDatabasePrivate::ExtendedScript, const QStringList &);
|
||
fallbackList = qt_sort_families_by_writing_system(script, fallbackList);
|
||
|
||
qCDebug(lcQpaFonts).nospace() << "Fallback families ordered by script " << script << ": " << fallbackList;
|
||
diff --git a/src/gui/text/coretext/qcoretextfontdatabase_p.h b/src/gui/text/coretext/qcoretextfontdatabase_p.h
|
||
index eeea9ad6..5c8a6ad3 100644
|
||
--- a/src/gui/text/coretext/qcoretextfontdatabase_p.h
|
||
+++ b/src/gui/text/coretext/qcoretextfontdatabase_p.h
|
||
@@ -39,7 +39,7 @@ public:
|
||
void populateFamily(const QString &familyName) override;
|
||
void invalidate() override;
|
||
|
||
- QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
|
||
+ QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script) const override;
|
||
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
|
||
void releaseHandle(void *handle) override;
|
||
bool isPrivateFontFamily(const QString &family) const override;
|
||
@@ -55,6 +55,7 @@ private:
|
||
void populateThemeFonts();
|
||
void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString(), QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr);
|
||
static CFArrayRef fallbacksForFamily(const QString &family);
|
||
+ QStringList fallbacksForScript(QFontDatabasePrivate::ExtendedScript script) const;
|
||
|
||
QHash<QPlatformTheme::Font, QFont *> m_themeFonts;
|
||
QHash<QString, QList<QCFType<CTFontDescriptorRef>>> m_systemFontDescriptors;
|
||
diff --git a/src/gui/text/freetype/qfreetypefontdatabase.cpp b/src/gui/text/freetype/qfreetypefontdatabase.cpp
|
||
index 018e590a..8e0aec1f 100644
|
||
--- a/src/gui/text/freetype/qfreetypefontdatabase.cpp
|
||
+++ b/src/gui/text/freetype/qfreetypefontdatabase.cpp
|
||
@@ -103,6 +103,7 @@ void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
|
||
QFont::Stretch stretch,
|
||
QFont::Style style,
|
||
bool fixedPitch,
|
||
+ bool isColor,
|
||
const QSupportedWritingSystems &writingSystems,
|
||
const QByteArray &fileName,
|
||
const QByteArray &fontData)
|
||
@@ -183,6 +184,7 @@ void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
|
||
true,
|
||
0,
|
||
fixedPitch,
|
||
+ isColor,
|
||
writingSystems,
|
||
variantFontFile);
|
||
}
|
||
@@ -224,6 +226,12 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
|
||
}
|
||
numFaces = face->num_faces;
|
||
|
||
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20501
|
||
+ bool isColor = FT_HAS_COLOR(face);
|
||
+#else
|
||
+ bool isColor = false;
|
||
+#endif
|
||
+
|
||
QFont::Weight weight = QFont::Normal;
|
||
|
||
QFont::Style style = QFont::StyleNormal;
|
||
@@ -337,9 +345,9 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
|
||
applicationFont->properties.append(properties);
|
||
}
|
||
|
||
- registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, writingSystems, fontFile);
|
||
+ registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, isColor, writingSystems, fontFile);
|
||
|
||
- addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, writingSystems, file, fontData);
|
||
+ addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, isColor, writingSystems, file, fontData);
|
||
|
||
families.append(family);
|
||
|
||
diff --git a/src/gui/text/freetype/qfreetypefontdatabase_p.h b/src/gui/text/freetype/qfreetypefontdatabase_p.h
|
||
index 5fcec585..bc5cdbff 100644
|
||
--- a/src/gui/text/freetype/qfreetypefontdatabase_p.h
|
||
+++ b/src/gui/text/freetype/qfreetypefontdatabase_p.h
|
||
@@ -47,7 +47,7 @@ public:
|
||
static void addNamedInstancesForFace(void *face, int faceIndex,
|
||
const QString &family, const QString &styleName,
|
||
QFont::Weight weight, QFont::Stretch stretch,
|
||
- QFont::Style style, bool fixedPitch,
|
||
+ QFont::Style style, bool fixedPitch, bool isColor,
|
||
const QSupportedWritingSystems &writingSystems,
|
||
const QByteArray &fileName, const QByteArray &fontData);
|
||
|
||
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
|
||
index c8881a9b..94343854 100644
|
||
--- a/src/gui/text/qfont.cpp
|
||
+++ b/src/gui/text/qfont.cpp
|
||
@@ -383,13 +383,13 @@ void QFontPrivate::unsetFeature(QFont::Tag tag)
|
||
QFontEngineData::QFontEngineData()
|
||
: ref(0), fontCacheId(QFontCache::instance()->id())
|
||
{
|
||
- memset(engines, 0, QChar::ScriptCount * sizeof(QFontEngine *));
|
||
+ memset(engines, 0, QFontDatabasePrivate::ScriptCount * sizeof(QFontEngine *));
|
||
}
|
||
|
||
QFontEngineData::~QFontEngineData()
|
||
{
|
||
Q_ASSERT(ref.loadRelaxed() == 0);
|
||
- for (int i = 0; i < QChar::ScriptCount; ++i) {
|
||
+ for (int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
|
||
if (engines[i]) {
|
||
if (!engines[i]->ref.deref())
|
||
delete engines[i];
|
||
@@ -2683,8 +2683,10 @@ void QFont::clearFeatures()
|
||
d->features.clear();
|
||
}
|
||
|
||
-extern QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style,
|
||
- QFont::StyleHint styleHint, QChar::Script script);
|
||
+extern QStringList qt_fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script);
|
||
|
||
/*!
|
||
\fn QString QFont::defaultFamily() const
|
||
@@ -2696,8 +2698,10 @@ extern QStringList qt_fallbacksForFamily(const QString &family, QFont::Style sty
|
||
*/
|
||
QString QFont::defaultFamily() const
|
||
{
|
||
- const QStringList fallbacks = qt_fallbacksForFamily(QString(), QFont::StyleNormal
|
||
- , QFont::StyleHint(d->request.styleHint), QChar::Script_Common);
|
||
+ const QStringList fallbacks = qt_fallbacksForFamily(QString(),
|
||
+ QFont::StyleNormal,
|
||
+ QFont::StyleHint(d->request.styleHint),
|
||
+ QFontDatabasePrivate::Script_Common);
|
||
if (!fallbacks.isEmpty())
|
||
return fallbacks.first();
|
||
return QString();
|
||
@@ -3394,7 +3398,7 @@ void QFontCache::clear()
|
||
end = engineDataCache.end();
|
||
while (it != end) {
|
||
QFontEngineData *data = it.value();
|
||
- for (int i = 0; i < QChar::ScriptCount; ++i) {
|
||
+ for (int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
|
||
if (data->engines[i]) {
|
||
if (!data->engines[i]->ref.deref()) {
|
||
Q_ASSERT(engineCacheCount.value(data->engines[i]) == 0);
|
||
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
|
||
index b674e711..844ac5fc 100644
|
||
--- a/src/gui/text/qfont_p.h
|
||
+++ b/src/gui/text/qfont_p.h
|
||
@@ -23,6 +23,7 @@
|
||
#include "QtCore/qstringlist.h"
|
||
#include <QtGui/qfontdatabase.h>
|
||
#include "private/qfixed_p.h"
|
||
+#include "private/qfontdatabase_p.h"
|
||
|
||
QT_BEGIN_NAMESPACE
|
||
|
||
@@ -152,8 +153,7 @@ public:
|
||
QAtomicInt ref;
|
||
const int fontCacheId;
|
||
|
||
- QFontEngine *engines[QChar::ScriptCount];
|
||
-
|
||
+ QFontEngine *engines[QFontDatabasePrivate::ScriptCount];
|
||
private:
|
||
Q_DISABLE_COPY_MOVE(QFontEngineData)
|
||
};
|
||
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
|
||
index 28539977..96c2337e 100644
|
||
--- a/src/gui/text/qfontdatabase.cpp
|
||
+++ b/src/gui/text/qfontdatabase.cpp
|
||
@@ -413,6 +413,8 @@ static bool familySupportsWritingSystem(QtFontFamily *family, size_t writingSyst
|
||
|
||
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script)
|
||
{
|
||
+ if (script >= QChar::ScriptCount)
|
||
+ return QFontDatabase::Any;
|
||
return QFontDatabase::WritingSystem(std::find(scriptForWritingSystem,
|
||
scriptForWritingSystem + QFontDatabase::WritingSystemsCount,
|
||
script) - scriptForWritingSystem);
|
||
@@ -537,7 +539,7 @@ QFontDatabasePrivate *QFontDatabasePrivate::instance()
|
||
void qt_registerFont(const QString &familyName, const QString &stylename,
|
||
const QString &foundryname, int weight,
|
||
QFont::Style style, int stretch, bool antialiased,
|
||
- bool scalable, int pixelSize, bool fixedPitch,
|
||
+ bool scalable, int pixelSize, bool fixedPitch, bool colorFont,
|
||
const QSupportedWritingSystems &writingSystems, void *handle)
|
||
{
|
||
auto *d = QFontDatabasePrivate::instance();
|
||
@@ -549,6 +551,7 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
|
||
styleKey.stretch = stretch;
|
||
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::EnsureCreated);
|
||
f->fixedPitch = fixedPitch;
|
||
+ f->colorFont = colorFont;
|
||
|
||
for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
|
||
if (writingSystems.supported(QFontDatabase::WritingSystem(i)))
|
||
@@ -620,7 +623,10 @@ bool qt_isFontFamilyPopulated(const QString &familyName)
|
||
Default implementation returns a list of fonts for which \a style and \a script support
|
||
has been reported during the font database population.
|
||
*/
|
||
-QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
Q_UNUSED(family);
|
||
Q_UNUSED(styleHint);
|
||
@@ -659,7 +665,10 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo
|
||
return preferredFallbacks + otherFallbacks;
|
||
}
|
||
|
||
-static QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
|
||
+static QStringList fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script)
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
auto *db = QFontDatabasePrivate::ensureFontDatabase();
|
||
@@ -670,7 +679,7 @@ static QStringList fallbacksForFamily(const QString &family, QFont::Style style,
|
||
return *fallbacks;
|
||
|
||
// make sure that the db has all fallback families
|
||
- QStringList userFallbacks = db->applicationFallbackFontFamilies.value(script == QChar::Script_Latin ? QChar::Script_Common : script);
|
||
+ QStringList userFallbacks = db->applicationFallbackFontFamilies(script == QFontDatabasePrivate::Script_Latin ? QFontDatabasePrivate::Script_Common : script);
|
||
QStringList retList = userFallbacks + QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
|
||
|
||
QStringList::iterator i;
|
||
@@ -693,7 +702,7 @@ static QStringList fallbacksForFamily(const QString &family, QFont::Style style,
|
||
return retList;
|
||
}
|
||
|
||
-QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
|
||
+QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script)
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
return fallbacksForFamily(family, style, styleHint, script);
|
||
@@ -722,10 +731,10 @@ QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
|
||
QFontCache::Key key(def,script);
|
||
QFontEngine *engine = fontCache->findEngine(key);
|
||
if (!engine) {
|
||
- const bool cacheForCommonScript = script != QChar::Script_Common
|
||
+ const bool cacheForCommonScript = script != QFontDatabasePrivate::Script_Common
|
||
&& (family->writingSystems[QFontDatabase::Latin] & QtFontFamily::Supported) != 0;
|
||
|
||
- if (Q_LIKELY(cacheForCommonScript)) {
|
||
+ if (Q_LIKELY(cacheForCommonScript) && script < QChar::ScriptCount) {
|
||
// fast path: check if engine was loaded for another script
|
||
key.script = QChar::Script_Common;
|
||
engine = fontCache->findEngine(key);
|
||
@@ -757,7 +766,7 @@ QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
|
||
engine = pfdb->fontEngine(def, size->handle);
|
||
if (engine) {
|
||
// Also check for OpenType tables when using complex scripts
|
||
- if (!engine->supportsScript(QChar::Script(script))) {
|
||
+ if (script < QChar::ScriptCount && !engine->supportsScript(QChar::Script(script))) {
|
||
qCInfo(lcFontDb, "OpenType support missing for \"%ls\", script %d",
|
||
qUtf16Printable(def.families.constFirst()), script);
|
||
if (engine->ref.loadRelaxed() == 0)
|
||
@@ -789,7 +798,8 @@ QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &reques
|
||
Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize);
|
||
|
||
QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
|
||
- QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script));
|
||
+ QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine,
|
||
+ QFontDatabasePrivate::ExtendedScript(script));
|
||
if (!request.fallBackFamilies.isEmpty()) {
|
||
QStringList fallbacks = request.fallBackFamilies;
|
||
|
||
@@ -797,7 +807,10 @@ QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &reques
|
||
if (styleHint == QFont::AnyStyle && request.fixedPitch)
|
||
styleHint = QFont::TypeWriter;
|
||
|
||
- fallbacks += fallbacksForFamily(family->name, QFont::Style(style->key.style), styleHint, QChar::Script(script));
|
||
+ fallbacks += fallbacksForFamily(family->name,
|
||
+ QFont::Style(style->key.style),
|
||
+ styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript(script));
|
||
|
||
pfMultiEngine->setFallbackFamiliesList(fallbacks);
|
||
}
|
||
@@ -877,8 +890,10 @@ unsigned int QFontDatabasePrivate::bestFoundry(int script, unsigned int score, i
|
||
desc->style = nullptr;
|
||
desc->size = nullptr;
|
||
|
||
-
|
||
- qCDebug(lcFontMatch, " REMARK: looking for best foundry for family '%s' [%d]", family->name.toLatin1().constData(), family->count);
|
||
+ qCDebug(lcFontMatch, " REMARK: looking for best foundry for family '%s'%s [%d]",
|
||
+ family->name.toLatin1().constData(),
|
||
+ family->colorFont ? " (color font)" : "",
|
||
+ family->count);
|
||
|
||
for (int x = 0; x < family->count; ++x) {
|
||
QtFontFoundry *foundry = family->foundries[x];
|
||
@@ -1068,6 +1083,10 @@ int QFontDatabasePrivate::match(int script, const QFontDef &request, const QStri
|
||
if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(test.family, writingSystem))
|
||
continue;
|
||
|
||
+ // Check if we require a color font and check for match
|
||
+ if (script == QFontDatabasePrivate::Script_Emoji && !test.family->colorFont)
|
||
+ continue;
|
||
+
|
||
// as we know the script is supported, we can be sure
|
||
// to find a matching font here.
|
||
unsigned int newscore =
|
||
@@ -2202,6 +2221,48 @@ bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
|
||
return false;
|
||
}
|
||
|
||
+void QFontDatabasePrivate::setApplicationFallbackFontFamilies(ExtendedScript script, const QStringList &familyNames)
|
||
+{
|
||
+ applicationFallbackFontFamiliesHash[script] = familyNames;
|
||
+
|
||
+ QFontCache::instance()->clear();
|
||
+ fallbacksCache.clear();
|
||
+}
|
||
+
|
||
+QStringList QFontDatabasePrivate::applicationFallbackFontFamilies(ExtendedScript script)
|
||
+{
|
||
+ return applicationFallbackFontFamiliesHash.value(script);
|
||
+}
|
||
+
|
||
+bool QFontDatabasePrivate::removeApplicationFallbackFontFamily(ExtendedScript script, const QString &familyName)
|
||
+{
|
||
+ auto it = applicationFallbackFontFamiliesHash.find(script);
|
||
+ if (it != applicationFallbackFontFamiliesHash.end()) {
|
||
+ if (it->removeAll(familyName) > 0) {
|
||
+ if (it->isEmpty())
|
||
+ it = applicationFallbackFontFamiliesHash.erase(it);
|
||
+ QFontCache::instance()->clear();
|
||
+ fallbacksCache.clear();
|
||
+ return true;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return false;
|
||
+}
|
||
+
|
||
+void QFontDatabasePrivate::addApplicationFallbackFontFamily(ExtendedScript script, const QString &familyName)
|
||
+{
|
||
+ auto it = applicationFallbackFontFamiliesHash.find(script);
|
||
+ if (it == applicationFallbackFontFamiliesHash.end())
|
||
+ it = applicationFallbackFontFamiliesHash.insert(script, QStringList{});
|
||
+
|
||
+ it->prepend(familyName);
|
||
+
|
||
+ QFontCache::instance()->clear();
|
||
+ fallbacksCache.clear();
|
||
+}
|
||
+
|
||
+
|
||
/*!
|
||
\since 4.2
|
||
|
||
@@ -2398,7 +2459,7 @@ void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
|
||
- if (script < QChar::Script_Common) {
|
||
+ if (script < QChar::Script_Common || script >= QChar::ScriptCount) {
|
||
qCWarning(lcFontDb) << "Invalid script passed to addApplicationFallbackFontFamily:" << script;
|
||
return;
|
||
}
|
||
@@ -2407,14 +2468,7 @@ void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const
|
||
script = QChar::Script_Common;
|
||
|
||
auto *db = QFontDatabasePrivate::instance();
|
||
- auto it = db->applicationFallbackFontFamilies.find(script);
|
||
- if (it == db->applicationFallbackFontFamilies.end())
|
||
- it = db->applicationFallbackFontFamilies.insert(script, QStringList{});
|
||
-
|
||
- it->prepend(familyName);
|
||
-
|
||
- QFontCache::instance()->clear();
|
||
- db->fallbacksCache.clear();
|
||
+ db->addApplicationFallbackFontFamily(QFontDatabasePrivate::ExtendedScript(script), familyName);
|
||
}
|
||
|
||
/*!
|
||
@@ -2431,7 +2485,7 @@ bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, co
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
|
||
- if (script < QChar::Script_Common) {
|
||
+ if (script < QChar::Script_Common || script >= QChar::ScriptCount) {
|
||
qCWarning(lcFontDb) << "Invalid script passed to removeApplicationFallbackFontFamily:" << script;
|
||
return false;
|
||
}
|
||
@@ -2440,18 +2494,8 @@ bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, co
|
||
script = QChar::Script_Common;
|
||
|
||
auto *db = QFontDatabasePrivate::instance();
|
||
- auto it = db->applicationFallbackFontFamilies.find(script);
|
||
- if (it != db->applicationFallbackFontFamilies.end()) {
|
||
- if (it->removeAll(familyName) > 0) {
|
||
- if (it->isEmpty())
|
||
- it = db->applicationFallbackFontFamilies.erase(it);
|
||
- QFontCache::instance()->clear();
|
||
- db->fallbacksCache.clear();
|
||
- return true;
|
||
- }
|
||
- }
|
||
-
|
||
- return false;
|
||
+ return db->removeApplicationFallbackFontFamily(QFontDatabasePrivate::ExtendedScript(script),
|
||
+ familyName);
|
||
}
|
||
|
||
/*!
|
||
@@ -2471,7 +2515,7 @@ void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, con
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
|
||
- if (script < QChar::Script_Common) {
|
||
+ if (script < QChar::Script_Common || script >= QChar::ScriptCount) {
|
||
qCWarning(lcFontDb) << "Invalid script passed to setApplicationFallbackFontFamilies:" << script;
|
||
return;
|
||
}
|
||
@@ -2480,10 +2524,8 @@ void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, con
|
||
script = QChar::Script_Common;
|
||
|
||
auto *db = QFontDatabasePrivate::instance();
|
||
- db->applicationFallbackFontFamilies[script] = familyNames;
|
||
-
|
||
- QFontCache::instance()->clear();
|
||
- db->fallbacksCache.clear();
|
||
+ db->setApplicationFallbackFontFamilies(QFontDatabasePrivate::ExtendedScript(script),
|
||
+ familyNames);
|
||
}
|
||
|
||
/*!
|
||
@@ -2498,11 +2540,81 @@ QStringList QFontDatabase::applicationFallbackFontFamilies(QChar::Script script)
|
||
{
|
||
QMutexLocker locker(fontDatabaseMutex());
|
||
|
||
+ if (script >= QChar::ScriptCount) {
|
||
+ qCWarning(lcFontDb) << "Invalid script passed to applicationFallbackFontFamilies:" << script;
|
||
+ return QStringList{};
|
||
+ }
|
||
+
|
||
if (script == QChar::Script_Latin)
|
||
script = QChar::Script_Common;
|
||
|
||
auto *db = QFontDatabasePrivate::instance();
|
||
- return db->applicationFallbackFontFamilies.value(script);
|
||
+ return db->applicationFallbackFontFamilies(QFontDatabasePrivate::ExtendedScript(script));
|
||
+}
|
||
+
|
||
+/*!
|
||
+ \since 6.9
|
||
+
|
||
+ Adds \a familyName as an application-defined emoji font.
|
||
+
|
||
+ For displaying multi-color emojis or emoji sequences, Qt will by default prefer the system
|
||
+ default emoji font. Sometimes the application may want to override the default, either to
|
||
+ achieve a specific visual style or to show emojis that are not supported by the system.
|
||
+
|
||
+ \sa removeApplicationEmojiFontFamily, setApplicationEmojiFontFamilies(), applicationEmojiFontFamilies(), addApplicationFallbackFontFamily()
|
||
+*/
|
||
+void QFontDatabase::addApplicationEmojiFontFamily(const QString &familyName)
|
||
+{
|
||
+ QMutexLocker locker(fontDatabaseMutex());
|
||
+ auto *db = QFontDatabasePrivate::instance();
|
||
+ db->addApplicationFallbackFontFamily(QFontDatabasePrivate::Script_Emoji, familyName);
|
||
+}
|
||
+
|
||
+/*!
|
||
+ \since 6.9
|
||
+
|
||
+ Removes \a familyName from the list of application-defined emoji fonts,
|
||
+ provided that it has previously been added with \l{addApplicationEmojiFontFamily()}.
|
||
+
|
||
+ Returns true if the family name was in the list and false if it was not.
|
||
+
|
||
+ \sa addApplicationEmojiFontFamily(), setApplicationEmojiFontFamilies(), applicationEmojiFontFamilies(), removeApplicationFallbackFontFamily()
|
||
+*/
|
||
+bool QFontDatabase::removeApplicationEmojiFontFamily(const QString &familyName)
|
||
+{
|
||
+ QMutexLocker locker(fontDatabaseMutex());
|
||
+ auto *db = QFontDatabasePrivate::instance();
|
||
+ return db->removeApplicationFallbackFontFamily(QFontDatabasePrivate::Script_Emoji,
|
||
+ familyName);
|
||
+}
|
||
+
|
||
+/*!
|
||
+ \since 6.9
|
||
+
|
||
+ Sets the list of application-defined emoji fonts to \a familyNames.
|
||
+
|
||
+ \sa addApplicationEmojiFontFamily(), removeApplicationEmojiFontFamily(), applicationEmojiFontFamilies(), setApplicationFallbackFontFamilies()
|
||
+*/
|
||
+void QFontDatabase::setApplicationEmojiFontFamilies(const QStringList &familyNames)
|
||
+{
|
||
+ QMutexLocker locker(fontDatabaseMutex());
|
||
+ auto *db = QFontDatabasePrivate::instance();
|
||
+ db->setApplicationFallbackFontFamilies(QFontDatabasePrivate::Script_Emoji,
|
||
+ familyNames);
|
||
+}
|
||
+
|
||
+/*!
|
||
+ \since 6.9
|
||
+
|
||
+ Returns the list of application-defined emoji font families.
|
||
+
|
||
+ \sa addApplicationEmojiFontFamily(), removeApplicationEmojiFontFamily(), setApplicationEmojiFontFamilies(), applicationFallbackFontFamilies()
|
||
+*/
|
||
+QStringList QFontDatabase::applicationEmojiFontFamilies()
|
||
+{
|
||
+ QMutexLocker locker(fontDatabaseMutex());
|
||
+ auto *db = QFontDatabasePrivate::instance();
|
||
+ return db->applicationFallbackFontFamilies(QFontDatabasePrivate::Script_Emoji);
|
||
}
|
||
|
||
/*!
|
||
@@ -2559,12 +2671,27 @@ QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
|
||
QtFontDesc desc;
|
||
QList<int> blackListed;
|
||
unsigned int score = UINT_MAX;
|
||
- int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed, &score);
|
||
+
|
||
+ // 1.
|
||
+ // We start by looking up the family name and finding the best style/foundry. For multi fonts
|
||
+ // we always want the requested font to be on top, even if it does not support the selected
|
||
+ // script, since the fallback mechanism will handle this later. For NoFontMerging fonts, we pass
|
||
+ // in the script in order to prefer foundries that support the script. If none is found, we will
|
||
+ // retry with Script_Common later. Note that Script_Emoji is special. This means the Unicode
|
||
+ // algorithm has determined that we should use a color font. If the selected font is not
|
||
+ // a color font, we use the fall back mechanism to find one, since we want to prefer *any* color
|
||
+ // font over a non-color font in this case.
|
||
+ int index = match(multi && script != QFontDatabasePrivate::Script_Emoji ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed, &score);
|
||
+
|
||
+ // 2.
|
||
+ // If no font was found or it was not a perfect match, we let the database populate family
|
||
+ // aliases and try again.
|
||
if (score > 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) {
|
||
// We populated family aliases (e.g. localized families), so try again
|
||
- index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
|
||
+ index = match(multi && script != QFontDatabasePrivate::Script_Emoji ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
|
||
}
|
||
|
||
+ // 3.
|
||
// If we do not find a match and NoFontMerging is set, use the requested font even if it does
|
||
// not support the script.
|
||
//
|
||
@@ -2589,6 +2716,10 @@ QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
|
||
qCDebug(lcFontMatch, " NO MATCH FOUND\n");
|
||
}
|
||
|
||
+ // 4.
|
||
+ // If no font matching the script + family exists, we go via the fallback mechanism. This
|
||
+ // happens when the family does not exist or if we want a color font and the requested font
|
||
+ // is not.
|
||
if (!engine) {
|
||
if (!requestFamily.isEmpty()) {
|
||
QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint);
|
||
@@ -2599,32 +2730,49 @@ QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
|
||
+ fallbacksForFamily(requestFamily,
|
||
QFont::Style(request.style),
|
||
styleHint,
|
||
- QChar::Script(script));
|
||
+ QFontDatabasePrivate::ExtendedScript(script));
|
||
if (script > QChar::Script_Common)
|
||
fallbacks += QString(); // Find the first font matching the specified script.
|
||
|
||
- for (int i = 0; !engine && i < fallbacks.size(); i++) {
|
||
- QFontDef def = request;
|
||
- def.families = QStringList(fallbacks.at(i));
|
||
- QFontCache::Key key(def, script, multi ? 1 : 0);
|
||
- engine = fontCache->findEngine(key);
|
||
- if (!engine) {
|
||
- QtFontDesc desc;
|
||
- do {
|
||
- index = match(multi ? QChar::Script_Common : script, def, def.families.constFirst(), ""_L1, &desc, blackListed);
|
||
- if (index >= 0) {
|
||
- QFontDef loadDef = def;
|
||
- if (loadDef.families.isEmpty())
|
||
- loadDef.families = QStringList(desc.family->name);
|
||
- engine = loadEngine(script, loadDef, desc.family, desc.foundry, desc.style, desc.size);
|
||
- if (engine)
|
||
- initFontDef(desc, loadDef, &engine->fontDef, multi);
|
||
- else
|
||
- blackListed.append(index);
|
||
- }
|
||
- } while (index >= 0 && !engine);
|
||
+ auto findMatchingFallback = [&](int xscript) {
|
||
+ for (int i = 0; !engine && i < fallbacks.size(); i++) {
|
||
+ QFontDef def = request;
|
||
+
|
||
+ def.families = QStringList(fallbacks.at(i));
|
||
+ QFontCache::Key key(def, xscript, multi ? 1 : 0);
|
||
+ engine = fontCache->findEngine(key);
|
||
+ if (!engine) {
|
||
+ QtFontDesc desc;
|
||
+ do {
|
||
+ index = match(xscript,
|
||
+ def,
|
||
+ def.families.constFirst(),
|
||
+ ""_L1,
|
||
+ &desc,
|
||
+ blackListed);
|
||
+
|
||
+ if (index >= 0) {
|
||
+ QFontDef loadDef = def;
|
||
+ if (loadDef.families.isEmpty())
|
||
+ loadDef.families = QStringList(desc.family->name);
|
||
+ engine = loadEngine(xscript, loadDef, desc.family, desc.foundry, desc.style, desc.size);
|
||
+ if (engine)
|
||
+ initFontDef(desc, loadDef, &engine->fontDef, multi);
|
||
+ else
|
||
+ blackListed.append(index);
|
||
+ }
|
||
+ } while (index >= 0 && !engine);
|
||
+ }
|
||
}
|
||
- }
|
||
+ };
|
||
+
|
||
+ findMatchingFallback(multi && script != QFontDatabasePrivate::Script_Emoji ? QChar::Script_Common: script);
|
||
+
|
||
+ // If we are looking for a color font and there are no color fonts on the system,
|
||
+ // we will end up here, for one final pass. This is a rare occurrence so we accept
|
||
+ // and extra pass on the fallbacks for this.
|
||
+ if (!engine && script == QFontDatabasePrivate::Script_Emoji)
|
||
+ findMatchingFallback(QChar::Script_Common);
|
||
}
|
||
|
||
if (!engine)
|
||
@@ -2721,7 +2869,7 @@ void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
|
||
|
||
Q_ASSERT(fe);
|
||
if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
|
||
- for (int i = 0; i < QChar::ScriptCount; ++i) {
|
||
+ for (int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
|
||
if (!d->engineData->engines[i]) {
|
||
d->engineData->engines[i] = fe;
|
||
fe->ref.ref();
|
||
@@ -2738,11 +2886,13 @@ QString QFontDatabasePrivate::resolveFontFamilyAlias(const QString &family)
|
||
return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->resolveFontFamilyAlias(family);
|
||
}
|
||
|
||
-Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script, const QStringList &families)
|
||
+Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QFontDatabasePrivate::ExtendedScript script,
|
||
+ const QStringList &families)
|
||
{
|
||
size_t writingSystem = qt_writing_system_for_script(script);
|
||
- if (writingSystem == QFontDatabase::Any
|
||
- || writingSystem >= QFontDatabase::WritingSystemsCount) {
|
||
+ if (script != QFontDatabasePrivate::Script_Emoji
|
||
+ && (writingSystem == QFontDatabase::Any
|
||
+ || writingSystem >= QFontDatabase::WritingSystemsCount)) {
|
||
return families;
|
||
}
|
||
|
||
@@ -2762,7 +2912,8 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script
|
||
|
||
uint order = i;
|
||
if (testFamily == nullptr
|
||
- || !familySupportsWritingSystem(testFamily, writingSystem)) {
|
||
+ || (script == QFontDatabasePrivate::Script_Emoji && !testFamily->colorFont)
|
||
+ || (script != QFontDatabasePrivate::Script_Emoji && !familySupportsWritingSystem(testFamily, writingSystem))) {
|
||
order |= 1u << 31;
|
||
}
|
||
|
||
diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h
|
||
index 91a53426..cd5cc972 100644
|
||
--- a/src/gui/text/qfontdatabase.h
|
||
+++ b/src/gui/text/qfontdatabase.h
|
||
@@ -117,6 +117,11 @@ public:
|
||
static void setApplicationFallbackFontFamilies(QChar::Script, const QStringList &familyNames);
|
||
static QStringList applicationFallbackFontFamilies(QChar::Script script);
|
||
|
||
+ static void addApplicationEmojiFontFamily(const QString &familyName);
|
||
+ static bool removeApplicationEmojiFontFamily(const QString &familyName);
|
||
+ static void setApplicationEmojiFontFamilies(const QStringList &familyNames);
|
||
+ static QStringList applicationEmojiFontFamilies();
|
||
+
|
||
static QFont systemFont(SystemFont type);
|
||
};
|
||
|
||
diff --git a/src/gui/text/qfontdatabase_p.h b/src/gui/text/qfontdatabase_p.h
|
||
index 38e1b4ad..55fa1317 100644
|
||
--- a/src/gui/text/qfontdatabase_p.h
|
||
+++ b/src/gui/text/qfontdatabase_p.h
|
||
@@ -29,7 +29,7 @@ struct QtFontFallbacksCacheKey
|
||
QString family;
|
||
QFont::Style style;
|
||
QFont::StyleHint styleHint;
|
||
- QChar::Script script;
|
||
+ int script;
|
||
};
|
||
|
||
inline bool operator==(const QtFontFallbacksCacheKey &lhs, const QtFontFallbacksCacheKey &rhs) noexcept
|
||
@@ -151,6 +151,7 @@ struct Q_GUI_EXPORT QtFontFamily
|
||
:
|
||
populated(false),
|
||
fixedPitch(false),
|
||
+ colorFont(false),
|
||
name(n), count(0), foundries(nullptr)
|
||
{
|
||
memset(writingSystems, 0, sizeof(writingSystems));
|
||
@@ -163,6 +164,7 @@ struct Q_GUI_EXPORT QtFontFamily
|
||
|
||
bool populated : 1;
|
||
bool fixedPitch : 1;
|
||
+ bool colorFont : 1;
|
||
|
||
QString name;
|
||
QStringList aliases;
|
||
@@ -198,13 +200,21 @@ public:
|
||
EnsurePopulated
|
||
};
|
||
|
||
+ // Expands QChar::Script by adding a special "script" for emoji sequences
|
||
+ enum ExtendedScript {
|
||
+ Script_Common = QChar::Script_Common,
|
||
+ Script_Latin = QChar::Script_Latin,
|
||
+ Script_Emoji = QChar::ScriptCount,
|
||
+ ScriptCount
|
||
+ };
|
||
+
|
||
QtFontFamily *family(const QString &f, FamilyRequestFlags flags = EnsurePopulated);
|
||
|
||
int count;
|
||
QtFontFamily **families;
|
||
bool populated = false;
|
||
|
||
- QHash<QChar::Script, QStringList> applicationFallbackFontFamilies;
|
||
+ QHash<ExtendedScript, QStringList> applicationFallbackFontFamiliesHash;
|
||
|
||
QCache<QtFontFallbacksCacheKey, QStringList> fallbacksCache;
|
||
struct ApplicationFont {
|
||
@@ -232,21 +242,30 @@ public:
|
||
int addAppFont(const QByteArray &fontData, const QString &fileName);
|
||
bool isApplicationFont(const QString &fileName);
|
||
|
||
+ void setApplicationFallbackFontFamilies(ExtendedScript script, const QStringList &familyNames);
|
||
+ QStringList applicationFallbackFontFamilies(ExtendedScript script);
|
||
+ bool removeApplicationFallbackFontFamily(ExtendedScript script, const QString &familyName);
|
||
+ void addApplicationFallbackFontFamily(ExtendedScript script, const QString &familyName);
|
||
+
|
||
static QFontDatabasePrivate *instance();
|
||
|
||
static void parseFontName(const QString &name, QString &foundry, QString &family);
|
||
static QString resolveFontFamilyAlias(const QString &family);
|
||
static QFontEngine *findFont(const QFontDef &request,
|
||
- int script /* QChar::Script */,
|
||
+ int script /* QFontDatabasePrivate::ExtendedScript */,
|
||
bool preferScriptOverFamily = false);
|
||
- static void load(const QFontPrivate *d, int script /* QChar::Script */);
|
||
+ static void load(const QFontPrivate *d, int script /* QFontDatabasePrivate::ExtendedScript */);
|
||
static QFontDatabasePrivate *ensureFontDatabase();
|
||
|
||
void invalidate();
|
||
|
||
private:
|
||
- static int match(int script, const QFontDef &request, const QString &family_name,
|
||
- const QString &foundry_name, QtFontDesc *desc, const QList<int> &blacklistedFamilies,
|
||
+ static int match(int script,
|
||
+ const QFontDef &request,
|
||
+ const QString &family_name,
|
||
+ const QString &foundry_name,
|
||
+ QtFontDesc *desc,
|
||
+ const QList<int> &blacklistedFamilies,
|
||
unsigned int *resultingScore = nullptr);
|
||
|
||
static unsigned int bestFoundry(int script, unsigned int score, int styleStrategy,
|
||
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
|
||
index f5aec978..2e5c0624 100644
|
||
--- a/src/gui/text/qfontengine.cpp
|
||
+++ b/src/gui/text/qfontengine.cpp
|
||
@@ -1734,7 +1734,7 @@ QFontEngineMulti::~QFontEngineMulti()
|
||
}
|
||
}
|
||
|
||
-QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script);
|
||
+QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
|
||
|
||
void QFontEngineMulti::ensureFallbackFamiliesQueried()
|
||
{
|
||
@@ -1744,7 +1744,7 @@ void QFontEngineMulti::ensureFallbackFamiliesQueried()
|
||
|
||
setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.constFirst(),
|
||
QFont::Style(fontDef.style), styleHint,
|
||
- QChar::Script(m_script)));
|
||
+ QFontDatabasePrivate::ExtendedScript(m_script)));
|
||
}
|
||
|
||
void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
|
||
@@ -1792,7 +1792,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at)
|
||
// info about the actual script of the characters may have been discarded,
|
||
// so we do not check for writing system support, but instead just load
|
||
// the family indiscriminately.
|
||
- if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QChar::Script_Common)) {
|
||
+ if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
|
||
engine->fontDef.weight = request.weight;
|
||
if (request.style > QFont::StyleNormal)
|
||
engine->fontDef.style = request.style;
|
||
@@ -2363,7 +2363,7 @@ QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script
|
||
++it;
|
||
}
|
||
if (!engine) {
|
||
- engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QChar::Script(script));
|
||
+ engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
|
||
fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
|
||
}
|
||
Q_ASSERT(engine);
|
||
diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp
|
||
index a146254f..3d3f3c30 100644
|
||
--- a/src/gui/text/qplatformfontdatabase.cpp
|
||
+++ b/src/gui/text/qplatformfontdatabase.cpp
|
||
@@ -24,7 +24,7 @@ Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts")
|
||
void qt_registerFont(const QString &familyname, const QString &stylename,
|
||
const QString &foundryname, int weight,
|
||
QFont::Style style, int stretch, bool antialiased,
|
||
- bool scalable, int pixelSize, bool fixedPitch,
|
||
+ bool scalable, int pixelSize, bool fixedPitch, bool colorFont,
|
||
const QSupportedWritingSystems &writingSystems, void *hanlde);
|
||
|
||
void qt_registerFontFamily(const QString &familyName);
|
||
@@ -55,7 +55,7 @@ bool qt_isFontFamilyPopulated(const QString &familyName);
|
||
void QPlatformFontDatabase::registerFont(const QString &familyname, const QString &stylename,
|
||
const QString &foundryname, QFont::Weight weight,
|
||
QFont::Style style, QFont::Stretch stretch, bool antialiased,
|
||
- bool scalable, int pixelSize, bool fixedPitch,
|
||
+ bool scalable, int pixelSize, bool fixedPitch, bool colorFont,
|
||
const QSupportedWritingSystems &writingSystems, void *usrPtr)
|
||
{
|
||
if (scalable)
|
||
@@ -63,7 +63,7 @@ void QPlatformFontDatabase::registerFont(const QString &familyname, const QStrin
|
||
|
||
qt_registerFont(familyname, stylename, foundryname, weight, style,
|
||
stretch, antialiased, scalable, pixelSize,
|
||
- fixedPitch, writingSystems, usrPtr);
|
||
+ fixedPitch, colorFont, writingSystems, usrPtr);
|
||
}
|
||
|
||
/*!
|
||
@@ -271,7 +271,8 @@ void QPlatformFontDatabase::invalidate()
|
||
option to fall back to the fonts given by \a fallbacks if \a fontEngine does not support
|
||
a certain character.
|
||
*/
|
||
-QFontEngineMulti *QPlatformFontDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script)
|
||
+QFontEngineMulti *QPlatformFontDatabase::fontEngineMulti(QFontEngine *fontEngine,
|
||
+ QFontDatabasePrivate::ExtendedScript script)
|
||
{
|
||
return new QFontEngineMulti(fontEngine, script);
|
||
}
|
||
diff --git a/src/gui/text/qplatformfontdatabase.h b/src/gui/text/qplatformfontdatabase.h
|
||
index 3007a118..ba6dfac4 100644
|
||
--- a/src/gui/text/qplatformfontdatabase.h
|
||
+++ b/src/gui/text/qplatformfontdatabase.h
|
||
@@ -20,7 +20,6 @@
|
||
#include <QtCore/QList>
|
||
#include <QtGui/QFontDatabase>
|
||
#include <QtGui/private/qfontengine_p.h>
|
||
-#include <QtGui/private/qfont_p.h>
|
||
#include <QtGui/private/qfontdatabase_p.h>
|
||
|
||
QT_BEGIN_NAMESPACE
|
||
@@ -72,12 +71,12 @@ public:
|
||
virtual void populateFamily(const QString &familyName);
|
||
virtual void invalidate();
|
||
|
||
- virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const;
|
||
+ virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script) const;
|
||
virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr);
|
||
|
||
virtual QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
|
||
virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
|
||
- virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
|
||
+ virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QFontDatabasePrivate::ExtendedScript script);
|
||
virtual void releaseHandle(void *handle);
|
||
|
||
virtual QString fontDir() const;
|
||
@@ -99,7 +98,7 @@ public:
|
||
static void registerFont(const QString &familyname, const QString &stylename,
|
||
const QString &foundryname, QFont::Weight weight,
|
||
QFont::Style style, QFont::Stretch stretch, bool antialiased,
|
||
- bool scalable, int pixelSize, bool fixedPitch,
|
||
+ bool scalable, int pixelSize, bool fixedPitch, bool colorFont,
|
||
const QSupportedWritingSystems &writingSystems, void *handle);
|
||
|
||
static void registerFontFamily(const QString &familyName);
|
||
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
|
||
index 08512bea..0f10f4a7 100644
|
||
--- a/src/gui/text/qtextengine.cpp
|
||
+++ b/src/gui/text/qtextengine.cpp
|
||
@@ -1409,8 +1409,8 @@ void QTextEngine::shapeText(int item) const
|
||
QFont font = f.font();
|
||
# if QT_CONFIG(harfbuzz)
|
||
kerningEnabled = font.kerning();
|
||
- shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|
||
- || (font.styleStrategy() & QFont::PreferNoShaping) == 0;
|
||
+ shapingEnabled = (si.analysis.script < QChar::ScriptCount && QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script)))
|
||
+ || (font.styleStrategy() & QFont::PreferNoShaping) == 0;
|
||
# endif
|
||
wordSpacing = QFixed::fromReal(font.wordSpacing());
|
||
letterSpacing = QFixed::fromReal(font.letterSpacing());
|
||
@@ -1422,8 +1422,8 @@ void QTextEngine::shapeText(int item) const
|
||
QFont font = this->font(si);
|
||
#if QT_CONFIG(harfbuzz)
|
||
kerningEnabled = font.d->kerning;
|
||
- shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|
||
- || (font.d->request.styleStrategy & QFont::PreferNoShaping) == 0;
|
||
+ shapingEnabled = (si.analysis.script < QChar::ScriptCount && QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script)))
|
||
+ || (font.d->request.styleStrategy & QFont::PreferNoShaping) == 0;
|
||
#endif
|
||
letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
|
||
letterSpacing = font.d->letterSpacing;
|
||
@@ -1615,7 +1615,9 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
|
||
|
||
hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;
|
||
props.direction = si.analysis.bidiLevel % 2 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
|
||
- QChar::Script script = QChar::Script(si.analysis.script);
|
||
+ QChar::Script script = si.analysis.script < QChar::ScriptCount
|
||
+ ? QChar::Script(si.analysis.script)
|
||
+ : QChar::Script_Common;
|
||
props.script = hb_qt_script_to_script(script);
|
||
// ### TODO get_default_for_script?
|
||
props.language = hb_language_get_default(); // use default language from locale
|
||
@@ -1919,8 +1921,38 @@ void QTextEngine::validate() const
|
||
layoutData->string.insert(specialData->preeditPosition, specialData->preeditText);
|
||
}
|
||
|
||
+#if !defined(QT_NO_EMOJISEGMENTER)
|
||
+namespace {
|
||
+
|
||
+ enum CharacterCategory {
|
||
+ 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,
|
||
+ OTHER = 16
|
||
+ };
|
||
+
|
||
+ typedef CharacterCategory *emoji_text_iter_t;
|
||
+
|
||
+ #include "../../3rdparty/emoji-segmenter/emoji_presentation_scanner.c"
|
||
+}
|
||
+#endif
|
||
+
|
||
void QTextEngine::itemize() const
|
||
{
|
||
+ static bool disableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
|
||
validate();
|
||
if (layoutData->items.size())
|
||
return;
|
||
@@ -1950,9 +1982,76 @@ void QTextEngine::itemize() const
|
||
}
|
||
}
|
||
|
||
+#if !defined(QT_NO_EMOJISEGMENTER)
|
||
+ QVarLengthArray<CharacterCategory> categorizedString;
|
||
+ if (!disableEmojiSegmenter) {
|
||
+ // Parse emoji sequences
|
||
+ for (int i = 0; i < length; ++i) {
|
||
+ const QChar &c = string[i];
|
||
+ const bool isSurrogate = c.isHighSurrogate() && i < length - 1;
|
||
+ const char32_t ucs4 = isSurrogate
|
||
+ ? QChar::surrogateToUcs4(c, string[++i])
|
||
+ : c.unicode();
|
||
+ const QUnicodeTables::Properties *p = QUnicodeTables::properties(ucs4);
|
||
+
|
||
+ if (ucs4 == 0x20E3)
|
||
+ categorizedString.append(CharacterCategory::COMBINING_ENCLOSING_KEYCAP);
|
||
+ else if (ucs4 == 0x20E0)
|
||
+ categorizedString.append(CharacterCategory::COMBINING_ENCLOSING_CIRCLE_BACKSLASH);
|
||
+ else if (ucs4 == 0xFE0E)
|
||
+ categorizedString.append(CharacterCategory::VS15);
|
||
+ else if (ucs4 == 0xFE0F)
|
||
+ categorizedString.append(CharacterCategory::VS16);
|
||
+ else if (ucs4 == 0x200D)
|
||
+ categorizedString.append(CharacterCategory::ZWJ);
|
||
+ else if (ucs4 == 0x1F3F4)
|
||
+ categorizedString.append(CharacterCategory::TAG_BASE);
|
||
+ else if (ucs4 == 0xE007F)
|
||
+ categorizedString.append(CharacterCategory::TAG_TERM);
|
||
+ else if ((ucs4 >= 0xE0030 && ucs4 <= 0xE0039) || (ucs4 >= 0xE0061 && ucs4 <= 0xE007A))
|
||
+ categorizedString.append(CharacterCategory::TAG_SEQUENCE);
|
||
+ else if (ucs4 >= 0x1F1E6 && ucs4 <= 0x1F1FF)
|
||
+ categorizedString.append(CharacterCategory::REGIONAL_INDICATOR);
|
||
+ // emoji_keycap_sequence = [0-9#*] \x{FE0F 20E3}
|
||
+ else if ((ucs4 >= 0x0030 && ucs4 <= 0x0039) || ucs4 == 0x0023 || ucs4 == 0x002A)
|
||
+ categorizedString.append(CharacterCategory::KEYCAP_BASE);
|
||
+ else if (p->emojiFlags & uchar(QUnicodeTables::EmojiFlags::Emoji_Modifier_Base))
|
||
+ categorizedString.append(CharacterCategory::EMOJI_MODIFIER_BASE);
|
||
+ else if (p->emojiFlags & uchar(QUnicodeTables::EmojiFlags::Emoji_Modifier))
|
||
+ categorizedString.append(CharacterCategory::EMOJI_MODIFIER);
|
||
+ else if (p->emojiFlags & uchar(QUnicodeTables::EmojiFlags::Emoji_Presentation))
|
||
+ categorizedString.append(CharacterCategory::EMOJI_EMOJI_PRESENTATION);
|
||
+ // If it's in the emoji list and doesn't have the emoji presentation, it is text
|
||
+ // presentation.
|
||
+ else if (p->emojiFlags & uchar(QUnicodeTables::EmojiFlags::Emoji))
|
||
+ categorizedString.append(CharacterCategory::EMOJI_TEXT_PRESENTATION);
|
||
+ else
|
||
+ categorizedString.append(CharacterCategory::OTHER);
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
+
|
||
const ushort *uc = string;
|
||
const ushort *e = uc + length;
|
||
+
|
||
+#if !defined(QT_NO_EMOJISEGMENTER)
|
||
+ const emoji_text_iter_t categoriesStart = categorizedString.data();
|
||
+ const emoji_text_iter_t categoriesEnd = categoriesStart + categorizedString.size();
|
||
+
|
||
+ emoji_text_iter_t categoryIt = categoriesStart;
|
||
+
|
||
+ bool isEmoji = false;
|
||
+ bool hasVs = false;
|
||
+ emoji_text_iter_t nextIt = categoryIt;
|
||
+#endif
|
||
+
|
||
while (uc < e) {
|
||
+#if !defined(QT_NO_EMOJISEGMENTER)
|
||
+ // Find next emoji sequence
|
||
+ if (!disableEmojiSegmenter && categoryIt == nextIt)
|
||
+ nextIt = scan_emoji_presentation(categoryIt, categoriesEnd, &isEmoji, &hasVs);
|
||
+#endif
|
||
+
|
||
switch (*uc) {
|
||
case QChar::ObjectReplacementCharacter:
|
||
{
|
||
@@ -1992,7 +2091,27 @@ void QTextEngine::itemize() const
|
||
default:
|
||
analysis->flags = QScriptAnalysis::None;
|
||
break;
|
||
+ };
|
||
+
|
||
+#if !defined(QT_NO_EMOJISEGMENTER)
|
||
+ if (!disableEmojiSegmenter) {
|
||
+ if (isEmoji) {
|
||
+ static_assert(QChar::ScriptCount < USHRT_MAX);
|
||
+ analysis->script = QFontDatabasePrivate::Script_Emoji;
|
||
+ }
|
||
+
|
||
+ if (QChar::isHighSurrogate(*uc) && (uc + 1) < e && QChar::isLowSurrogate(*(uc + 1))) {
|
||
+ if (isEmoji)
|
||
+ (analysis + 1)->script = QFontDatabasePrivate::Script_Emoji;
|
||
+
|
||
+ ++uc;
|
||
+ ++analysis;
|
||
+ }
|
||
+
|
||
+ ++categoryIt;
|
||
}
|
||
+#endif
|
||
+
|
||
++uc;
|
||
++analysis;
|
||
}
|
||
diff --git a/src/gui/text/unix/qfontconfigdatabase.cpp b/src/gui/text/unix/qfontconfigdatabase.cpp
|
||
index f6c59380..7866e341 100644
|
||
--- a/src/gui/text/unix/qfontconfigdatabase.cpp
|
||
+++ b/src/gui/text/unix/qfontconfigdatabase.cpp
|
||
@@ -477,6 +477,12 @@ static void populateFromPattern(FcPattern *pattern,
|
||
FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &pixel_size);
|
||
|
||
bool fixedPitch = spacing_value >= FC_MONO;
|
||
+
|
||
+ FcBool colorFont = false;
|
||
+#ifdef FC_COLOR
|
||
+ FcPatternGetBool(pattern, FC_COLOR, 1, &colorFont);
|
||
+#endif
|
||
+
|
||
// Note: stretch should really be an int but registerFont incorrectly uses an enum
|
||
QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(width_value));
|
||
QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString();
|
||
@@ -492,7 +498,7 @@ static void populateFromPattern(FcPattern *pattern,
|
||
applicationFont->properties.append(properties);
|
||
}
|
||
|
||
- QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
|
||
+ QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,colorFont,writingSystems,fontFile);
|
||
if (applicationFont != nullptr && face != nullptr && db != nullptr) {
|
||
db->addNamedInstancesForFace(face,
|
||
indexValue,
|
||
@@ -502,6 +508,7 @@ static void populateFromPattern(FcPattern *pattern,
|
||
stretch,
|
||
style,
|
||
fixedPitch,
|
||
+ colorFont,
|
||
writingSystems,
|
||
QByteArray((const char*)file_value),
|
||
applicationFont->data);
|
||
@@ -538,7 +545,7 @@ static void populateFromPattern(FcPattern *pattern,
|
||
applicationFont->properties.append(properties);
|
||
}
|
||
FontFile *altFontFile = new FontFile(*fontFile);
|
||
- QPlatformFontDatabase::registerFont(altFamilyName, altStyleName, QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,altFontFile);
|
||
+ QPlatformFontDatabase::registerFont(altFamilyName, altStyleName, QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,colorFont,writingSystems,altFontFile);
|
||
} else {
|
||
QPlatformFontDatabase::registerAliasToFontFamily(familyName, altFamilyName);
|
||
}
|
||
@@ -615,9 +622,9 @@ void QFontconfigDatabase::populateFontDatabase()
|
||
|
||
while (f->qtname) {
|
||
QString familyQtName = QString::fromLatin1(f->qtname);
|
||
- registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,f->fixed,ws,nullptr);
|
||
- registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,ws,nullptr);
|
||
- registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,ws,nullptr);
|
||
+ registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,false,0,f->fixed,ws,nullptr);
|
||
+ registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,false,0,f->fixed,ws,nullptr);
|
||
+ registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,false,0,f->fixed,ws,nullptr);
|
||
++f;
|
||
}
|
||
|
||
@@ -636,7 +643,7 @@ void QFontconfigDatabase::invalidate()
|
||
FcConfigAppFontClear(nullptr);
|
||
}
|
||
|
||
-QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script)
|
||
+QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QFontDatabasePrivate::ExtendedScript script)
|
||
{
|
||
return new QFontEngineMultiFontConfig(fontEngine, script);
|
||
}
|
||
@@ -760,7 +767,10 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal p
|
||
return engine;
|
||
}
|
||
|
||
-QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList fallbackFamilies;
|
||
FcPattern *pattern = FcPatternCreate();
|
||
@@ -773,6 +783,14 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
|
||
value.u.s = (const FcChar8 *)cs.data();
|
||
FcPatternAdd(pattern,FC_FAMILY,value,true);
|
||
|
||
+#ifdef FC_COLOR
|
||
+ if (script == QFontDatabasePrivate::Script_Emoji) {
|
||
+ FcPatternAddBool(pattern, FC_COLOR, true);
|
||
+ value.u.s = (const FcChar8 *)"emoji";
|
||
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
|
||
+ }
|
||
+#endif
|
||
+
|
||
int slant_value = FC_SLANT_ROMAN;
|
||
if (style == QFont::StyleItalic)
|
||
slant_value = FC_SLANT_ITALIC;
|
||
@@ -780,8 +798,8 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
|
||
slant_value = FC_SLANT_OBLIQUE;
|
||
FcPatternAddInteger(pattern, FC_SLANT, slant_value);
|
||
|
||
- Q_ASSERT(uint(script) < QChar::ScriptCount);
|
||
- if (*specialLanguages[script] != '\0') {
|
||
+ Q_ASSERT(uint(script) < QFontDatabasePrivate::ScriptCount);
|
||
+ if (uint(script) < QChar::ScriptCount && *specialLanguages[script] != '\0') {
|
||
FcLangSet *ls = FcLangSetCreate();
|
||
FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
|
||
FcPatternAddLangSet(pattern, FC_LANG, ls);
|
||
diff --git a/src/gui/text/unix/qfontconfigdatabase_p.h b/src/gui/text/unix/qfontconfigdatabase_p.h
|
||
index dd7a70a3..40de8919 100644
|
||
--- a/src/gui/text/unix/qfontconfigdatabase_p.h
|
||
+++ b/src/gui/text/unix/qfontconfigdatabase_p.h
|
||
@@ -29,10 +29,14 @@ public:
|
||
void populateFontDatabase() override;
|
||
void invalidate() override;
|
||
bool supportsVariableApplicationFonts() const override;
|
||
- QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) override;
|
||
+ QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine,
|
||
+ QFontDatabasePrivate::ExtendedScript script) override;
|
||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
|
||
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
|
||
- QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
|
||
+ QStringList fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const override;
|
||
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
|
||
QString resolveFontFamilyAlias(const QString &family) const override;
|
||
QFont defaultFont() const override;
|
||
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
|
||
index 610cd1d6..f2671b6c 100644
|
||
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
|
||
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
|
||
@@ -138,9 +138,9 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
|
||
for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
|
||
DirectWriteScope<IDWriteFont> font;
|
||
if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
|
||
- DirectWriteScope<IDWriteFont1> font1;
|
||
- if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
|
||
- reinterpret_cast<void **>(&font1)))) {
|
||
+ DirectWriteScope<IDWriteFont2> font2;
|
||
+ if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont2),
|
||
+ reinterpret_cast<void **>(&font2)))) {
|
||
qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
|
||
continue;
|
||
}
|
||
@@ -149,7 +149,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
|
||
QString englishLocaleFamilyName;
|
||
|
||
DirectWriteScope<IDWriteFontFamily> fontFamily2;
|
||
- if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) {
|
||
+ if (SUCCEEDED(font2->GetFontFamily(&fontFamily2))) {
|
||
DirectWriteScope<IDWriteLocalizedStrings> names;
|
||
if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
|
||
defaultLocaleFamilyName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
|
||
@@ -162,14 +162,15 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
|
||
|
||
{
|
||
DirectWriteScope<IDWriteLocalizedStrings> names;
|
||
- if (SUCCEEDED(font1->GetFaceNames(&names))) {
|
||
+ if (SUCCEEDED(font2->GetFaceNames(&names))) {
|
||
QString defaultLocaleStyleName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
|
||
QString englishLocaleStyleName = localeString(*names, englishLocale);
|
||
|
||
- QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
|
||
- QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
|
||
- QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
|
||
- bool fixed = font1->IsMonospacedFont();
|
||
+ QFont::Stretch stretch = fromDirectWriteStretch(font2->GetStretch());
|
||
+ QFont::Style style = fromDirectWriteStyle(font2->GetStyle());
|
||
+ QFont::Weight weight = fromDirectWriteWeight(font2->GetWeight());
|
||
+ bool fixed = font2->IsMonospacedFont();
|
||
+ bool color = font2->IsColorFont();
|
||
|
||
qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed;
|
||
|
||
@@ -190,6 +191,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(*face, englishLocaleFamilyName));
|
||
}
|
||
@@ -205,6 +207,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(*face, defaultLocaleFamilyName));
|
||
}
|
||
@@ -290,18 +293,20 @@ bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missi
|
||
DirectWriteScope<IDWriteFont> font;
|
||
if (SUCCEEDED(fontCollection->GetFontFromFontFace(*directWriteFontFace, &font))) {
|
||
|
||
- DirectWriteScope<IDWriteFont1> font1;
|
||
- if (SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
|
||
- reinterpret_cast<void **>(&font1)))) {
|
||
+ DirectWriteScope<IDWriteFont2> font2;
|
||
+ if (SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont2),
|
||
+ reinterpret_cast<void **>(&font2)))) {
|
||
DirectWriteScope<IDWriteLocalizedStrings> names;
|
||
- if (SUCCEEDED(font1->GetFaceNames(&names))) {
|
||
+ if (SUCCEEDED(font2->GetFaceNames(&names))) {
|
||
wchar_t englishLocale[] = L"en-us";
|
||
QString englishLocaleStyleName = localeString(*names, englishLocale);
|
||
|
||
- QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
|
||
- QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
|
||
- QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
|
||
- bool fixed = font1->IsMonospacedFont();
|
||
+ QFont::Stretch stretch = fromDirectWriteStretch(font2->GetStretch());
|
||
+ QFont::Style style = fromDirectWriteStyle(font2->GetStyle());
|
||
+ QFont::Weight weight = fromDirectWriteWeight(font2->GetWeight());
|
||
+ bool fixed = font2->IsMonospacedFont();
|
||
+ bool isColorFont = font2->IsColorFont();
|
||
+
|
||
|
||
QSupportedWritingSystems writingSystems = supportedWritingSystems(*directWriteFontFace);
|
||
|
||
@@ -316,6 +321,7 @@ bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missi
|
||
true,
|
||
0xffff,
|
||
fixed,
|
||
+ isColorFont,
|
||
writingSystems,
|
||
new FontHandle(*directWriteFontFace, missingFamily));
|
||
|
||
@@ -407,9 +413,13 @@ QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef
|
||
return fontEngine;
|
||
}
|
||
|
||
-QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList result;
|
||
+ result.append(QWindowsFontDatabaseBase::familiesForScript(script));
|
||
result.append(familyForStyleHint(styleHint));
|
||
result.append(extraTryFontsForFamily(family));
|
||
result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script));
|
||
@@ -516,6 +526,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
|
||
QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
|
||
bool fixed = face3->IsMonospacedFont();
|
||
+ bool color = face3->IsColorFont();
|
||
|
||
qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
|
||
<< ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
|
||
@@ -545,6 +556,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, englishLocaleFamilyName));
|
||
}
|
||
@@ -570,6 +582,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, defaultLocaleFamilyName));
|
||
}
|
||
@@ -595,6 +608,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, englishLocaleGdiCompatibleFamilyName));
|
||
}
|
||
@@ -620,6 +634,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, defaultLocaleGdiCompatibleFamilyName));
|
||
}
|
||
@@ -645,6 +660,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, englishLocaleTypographicFamilyName));
|
||
}
|
||
@@ -670,6 +686,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
|
||
scalable,
|
||
size,
|
||
fixed,
|
||
+ color,
|
||
writingSystems,
|
||
new FontHandle(face, defaultLocaleTypographicFamilyName));
|
||
}
|
||
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
|
||
index 3e5e9663..1ea01cbd 100644
|
||
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
|
||
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
|
||
@@ -43,7 +43,10 @@ public:
|
||
bool populateFamilyAliases(const QString &missingFamily) override;
|
||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
|
||
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
|
||
- QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
|
||
+ QStringList fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const override;
|
||
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr) override;
|
||
|
||
bool isPrivateFontFamily(const QString &family) const override;
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp
|
||
index 772620d6..73814c6a 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabase.cpp
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabase.cpp
|
||
@@ -541,19 +541,19 @@ static bool addFontToDatabase(QString familyName,
|
||
|
||
const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName);
|
||
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight,
|
||
- style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
+ style, stretch, antialias, scalable, size, fixed, false, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
|
||
|
||
// add fonts windows can generate for us:
|
||
if (weight <= QFont::DemiBold && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
|
||
- style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
+ style, stretch, antialias, scalable, size, fixed, false, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
if (style != QFont::StyleItalic && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight,
|
||
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, false, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
|
||
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, false, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
|
||
// We came here from populating a different font family, so we have
|
||
// to ensure the entire typographic family is populated before we
|
||
@@ -567,7 +567,7 @@ static bool addFontToDatabase(QString familyName,
|
||
|
||
if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
|
||
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
|
||
- style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
+ style, stretch, antialias, scalable, size, fixed, false, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
|
||
}
|
||
|
||
if (!englishName.isEmpty() && englishName != familyName)
|
||
@@ -1162,9 +1162,13 @@ void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont)
|
||
++it->refCount;
|
||
}
|
||
|
||
-QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList result;
|
||
+ result.append(QWindowsFontDatabaseBase::familiesForScript(script));
|
||
result.append(familyForStyleHint(styleHint));
|
||
result.append(m_eudcFonts);
|
||
result.append(extraTryFontsForFamily(family));
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
|
||
index 0604a85e..18d90261 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
|
||
@@ -243,24 +243,24 @@ static bool addFontToDatabase(QString familyName,
|
||
value.prepend(QFile::decodeName(qgetenv("windir") + "\\Fonts\\"));
|
||
|
||
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch,
|
||
- antialias, scalable, size, fixed, writingSystems, createFontFile(value, index));
|
||
+ antialias, scalable, size, fixed, false, writingSystems, createFontFile(value, index));
|
||
|
||
// add fonts windows can generate for us:
|
||
if (weight <= QFont::DemiBold && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, style, stretch,
|
||
- antialias, scalable, size, fixed, writingSystems, createFontFile(value, index));
|
||
+ antialias, scalable, size, fixed, false, writingSystems, createFontFile(value, index));
|
||
|
||
if (style != QFont::StyleItalic && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, QFont::StyleItalic, stretch,
|
||
- antialias, scalable, size, fixed, writingSystems, createFontFile(value, index));
|
||
+ antialias, scalable, size, fixed, false, writingSystems, createFontFile(value, index));
|
||
|
||
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
|
||
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, QFont::StyleItalic, stretch,
|
||
- antialias, scalable, size, fixed, writingSystems, createFontFile(value, index));
|
||
+ antialias, scalable, size, fixed, false, writingSystems, createFontFile(value, index));
|
||
|
||
if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
|
||
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
|
||
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index));
|
||
+ style, stretch, antialias, scalable, size, fixed, false, writingSystems, createFontFile(value, index));
|
||
}
|
||
|
||
if (!englishName.isEmpty() && englishName != familyName)
|
||
@@ -397,9 +397,13 @@ QFontEngine *QWindowsFontDatabaseFT::fontEngine(const QByteArray &fontData, qrea
|
||
return fe;
|
||
}
|
||
|
||
-QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||
+QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family,
|
||
+ QFont::Style style,
|
||
+ QFont::StyleHint styleHint,
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList result;
|
||
+ result.append(QWindowsFontDatabaseBase::familiesForScript(script));
|
||
result.append(QWindowsFontDatabaseBase::familyForStyleHint(styleHint));
|
||
result.append(QWindowsFontDatabaseBase::extraTryFontsForFamily(family));
|
||
result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script));
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h b/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
|
||
index 381a7be4..1b8fb9dd 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
|
||
@@ -33,7 +33,7 @@ public:
|
||
|
||
QStringList fallbacksForFamily(const QString &family, QFont::Style style,
|
||
QFont::StyleHint styleHint,
|
||
- QChar::Script script) const override;
|
||
+ QFontDatabasePrivate::ExtendedScript script) const override;
|
||
|
||
QString fontDir() const override;
|
||
QFont defaultFont() const override;
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabase_p.h b/src/gui/text/windows/qwindowsfontdatabase_p.h
|
||
index 0c99c91f..92628726 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabase_p.h
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabase_p.h
|
||
@@ -51,7 +51,7 @@ public:
|
||
bool populateFamilyAliases(const QString &missingFamily) override;
|
||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
|
||
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
|
||
- QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
|
||
+ QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script) const override;
|
||
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
|
||
void releaseHandle(void *handle) override;
|
||
QString fontDir() const override;
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
|
||
index 84e619b0..fdaf9240 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
|
||
@@ -873,6 +873,14 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr
|
||
return fontEngine;
|
||
}
|
||
|
||
+QStringList QWindowsFontDatabaseBase::familiesForScript(QFontDatabasePrivate::ExtendedScript script)
|
||
+{
|
||
+ if (script == QFontDatabasePrivate::Script_Emoji)
|
||
+ return QStringList{} << QStringLiteral("Segoe UI Emoji");
|
||
+ else
|
||
+ return QStringList{};
|
||
+}
|
||
+
|
||
QString QWindowsFontDatabaseBase::familyForStyleHint(QFont::StyleHint styleHint)
|
||
{
|
||
switch (styleHint) {
|
||
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase_p.h b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
|
||
index 55a33635..beb9b52f 100644
|
||
--- a/src/gui/text/windows/qwindowsfontdatabasebase_p.h
|
||
+++ b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
|
||
@@ -75,6 +75,7 @@ public:
|
||
|
||
static QString familyForStyleHint(QFont::StyleHint styleHint);
|
||
static QStringList extraTryFontsForFamily(const QString &family);
|
||
+ static QStringList familiesForScript(QFontDatabasePrivate::ExtendedScript script);
|
||
|
||
class FontTable{};
|
||
class EmbeddedFont
|
||
diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
|
||
index 82a10dac..3c174f04 100644
|
||
--- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
|
||
+++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
|
||
@@ -45,10 +45,15 @@ void QAndroidPlatformFontDatabase::populateFontDatabase()
|
||
QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &family,
|
||
QFont::Style style,
|
||
QFont::StyleHint styleHint,
|
||
- QChar::Script script) const
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList result;
|
||
|
||
+ if (script == QFontDatabasePrivate::Script_Emoji) {
|
||
+ result.append(QStringLiteral("Noto Color Emoji"));
|
||
+ result.append(QStringLiteral("Noto Color Emoji Flags"));
|
||
+ }
|
||
+
|
||
// Prepend CJK fonts by the locale.
|
||
QLocale locale = QLocale::system();
|
||
switch (locale.language()) {
|
||
diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/qandroidplatformfontdatabase.h
|
||
index 23561e22..1150a298 100644
|
||
--- a/src/plugins/platforms/android/qandroidplatformfontdatabase.h
|
||
+++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.h
|
||
@@ -17,7 +17,7 @@ public:
|
||
QStringList fallbacksForFamily(const QString &family,
|
||
QFont::Style style,
|
||
QFont::StyleHint styleHint,
|
||
- QChar::Script script) const override;
|
||
+ QFontDatabasePrivate::ExtendedScript script) const override;
|
||
};
|
||
|
||
QT_END_NAMESPACE
|
||
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
|
||
index 3f3dc10f..e1882dd1 100644
|
||
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
|
||
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
|
||
@@ -319,7 +319,7 @@ QFontEngine *QWasmFontDatabase::fontEngine(const QFontDef &fontDef, void *handle
|
||
|
||
QStringList QWasmFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style,
|
||
QFont::StyleHint styleHint,
|
||
- QChar::Script script) const
|
||
+ QFontDatabasePrivate::ExtendedScript script) const
|
||
{
|
||
QStringList fallbacks
|
||
= QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);
|
||
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h
|
||
index a1c8f1ff..aea3b955 100644
|
||
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.h
|
||
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h
|
||
@@ -20,7 +20,7 @@ public:
|
||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
|
||
QStringList fallbacksForFamily(const QString &family, QFont::Style style,
|
||
QFont::StyleHint styleHint,
|
||
- QChar::Script script) const override;
|
||
+ QFontDatabasePrivate::ExtendedScript script) const override;
|
||
void releaseHandle(void *handle) override;
|
||
QFont defaultFont() const override;
|
||
|
||
diff --git a/tests/auto/gui/text/qfontdatabase/CMakeLists.txt b/tests/auto/gui/text/qfontdatabase/CMakeLists.txt
|
||
index 0cb6e8d7..03e4bdce 100644
|
||
--- a/tests/auto/gui/text/qfontdatabase/CMakeLists.txt
|
||
+++ b/tests/auto/gui/text/qfontdatabase/CMakeLists.txt
|
||
@@ -49,6 +49,7 @@ set(testdata_resource_files
|
||
"LED_REAL.TTF"
|
||
"QtTestLimitedFont-Regular.ttf"
|
||
"QtTestFallbackFont-Regular.ttf"
|
||
+ "QtEmojiTestFont-Regular.ttf"
|
||
)
|
||
|
||
qt_internal_add_resource(tst_qfontdatabase "testdata"
|
||
diff --git a/tests/auto/gui/text/qfontdatabase/QtEmojiTestFont-Regular.ttf b/tests/auto/gui/text/qfontdatabase/QtEmojiTestFont-Regular.ttf
|
||
new file mode 100644
|
||
index 00000000..e69de29b
|
||
diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
|
||
index 8733f64d..5773363d 100644
|
||
--- a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
|
||
+++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
|
||
@@ -71,6 +71,7 @@ private slots:
|
||
#endif
|
||
|
||
void addApplicationFontFallback();
|
||
+ void addApplicationEmojiFontFamily();
|
||
|
||
private:
|
||
QString m_ledFont;
|
||
@@ -80,6 +81,7 @@ private:
|
||
QString m_testFontVariable;
|
||
QString m_limitedFont;
|
||
QString m_fallbackFont;
|
||
+ QString m_emojiFont;
|
||
};
|
||
|
||
tst_QFontDatabase::tst_QFontDatabase()
|
||
@@ -95,6 +97,7 @@ void tst_QFontDatabase::initTestCase()
|
||
m_testFontVariable = QFINDTESTDATA("testfont_variable.ttf");
|
||
m_limitedFont = QFINDTESTDATA("QtTestLimitedFont-Regular.ttf");
|
||
m_fallbackFont = QFINDTESTDATA("QtTestFallbackFont-Regular.ttf");
|
||
+ m_emojiFont = QFINDTESTDATA("QtEmojiTestFont-Regular.ttf");
|
||
QVERIFY(!m_ledFont.isEmpty());
|
||
QVERIFY(!m_testFont.isEmpty());
|
||
QVERIFY(!m_testFontCondensed.isEmpty());
|
||
@@ -102,6 +105,7 @@ void tst_QFontDatabase::initTestCase()
|
||
QVERIFY(!m_testFontVariable.isEmpty());
|
||
QVERIFY(!m_limitedFont.isEmpty());
|
||
QVERIFY(!m_fallbackFont.isEmpty());
|
||
+ QVERIFY(!m_emojiFont.isEmpty());
|
||
}
|
||
|
||
void tst_QFontDatabase::styles_data()
|
||
@@ -760,5 +764,65 @@ void tst_QFontDatabase::addApplicationFontFallback()
|
||
QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Latin, u"QtTestFallbackFont"_s));
|
||
}
|
||
|
||
+void tst_QFontDatabase::addApplicationEmojiFontFamily()
|
||
+{
|
||
+ int id = -1;
|
||
+ auto cleanup = qScopeGuard([&id] {
|
||
+ if (id >= 0)
|
||
+ QFontDatabase::removeApplicationFont(id);
|
||
+ });
|
||
+
|
||
+ id = QFontDatabase::addApplicationFont(m_emojiFont);
|
||
+ QVERIFY(id >= 0);
|
||
+
|
||
+ QStringList families = QFontDatabase::applicationFontFamilies(id);
|
||
+ QVERIFY(families.size() > 0);
|
||
+
|
||
+ const QChar airplane(0x2708);
|
||
+ const QChar vs16(0xfe0f);
|
||
+
|
||
+ QFontDatabase::addApplicationEmojiFontFamily(families.first());
|
||
+
|
||
+ // Get emoji version of regular airplane symbol
|
||
+ {
|
||
+ QTextLayout layout;
|
||
+ layout.setText(QString(airplane) + vs16);
|
||
+ layout.beginLayout();
|
||
+ layout.createLine();
|
||
+ layout.endLayout();
|
||
+
|
||
+ QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||
+ QCOMPARE(glyphRuns.size(), 1);
|
||
+
|
||
+ QGlyphRun glyphRun = glyphRuns.first();
|
||
+ QList<quint32> glyphIndexes = glyphRun.glyphIndexes();
|
||
+
|
||
+ QCOMPARE(glyphIndexes.size(), 1);
|
||
+ QCOMPARE(glyphIndexes.at(0), 237);
|
||
+ }
|
||
+
|
||
+ const QChar asterisk('*');
|
||
+ const QChar enclosingKeyCap(0x20e3);
|
||
+
|
||
+ // Get emoji keycap ligature (vs16 should be ignored when evaluating ligature substitution)
|
||
+ {
|
||
+ QTextLayout layout;
|
||
+ layout.setText(QString(asterisk) + vs16 + enclosingKeyCap);
|
||
+ layout.beginLayout();
|
||
+ layout.createLine();
|
||
+ layout.endLayout();
|
||
+
|
||
+ QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||
+ QCOMPARE(glyphRuns.size(), 1);
|
||
+
|
||
+ QGlyphRun glyphRun = glyphRuns.first();
|
||
+ QList<quint32> glyphIndexes = glyphRun.glyphIndexes();
|
||
+
|
||
+ QCOMPARE(glyphIndexes.size(), 1);
|
||
+ QCOMPARE(glyphIndexes.at(0), 238);
|
||
+ }
|
||
+
|
||
+}
|
||
+
|
||
QTEST_MAIN(tst_QFontDatabase)
|
||
#include "tst_qfontdatabase.moc"
|
||
diff --git a/tests/manual/emojisequences/CMakeLists.txt b/tests/manual/emojisequences/CMakeLists.txt
|
||
new file mode 100644
|
||
index 00000000..c367bb12
|
||
--- /dev/null
|
||
+++ b/tests/manual/emojisequences/CMakeLists.txt
|
||
@@ -0,0 +1,34 @@
|
||
+# Copyright (C) 2024 The Qt Company Ltd.
|
||
+# SPDX-License-Identifier: BSD-3-Clause
|
||
+
|
||
+cmake_minimum_required(VERSION 3.16)
|
||
+project(emojisequences LANGUAGES CXX)
|
||
+
|
||
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||
+find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
|
||
+
|
||
+qt_standard_project_setup()
|
||
+
|
||
+qt_internal_add_manual_test(emojisequences
|
||
+ GUI
|
||
+ SOURCES
|
||
+ main.cpp
|
||
+ mainwindow.h mainwindow.cpp
|
||
+ mainwindow.ui
|
||
+ LIBRARIES
|
||
+ Qt::Gui
|
||
+ Qt::Widgets
|
||
+ ENABLE_AUTOGEN_TOOLS
|
||
+ uic
|
||
+)
|
||
+
|
||
+set(emojisequences_resource_files
|
||
+ "emoji-test.txt"
|
||
+)
|
||
+
|
||
+qt_internal_add_resource(emojisequences "emojisequences"
|
||
+ PREFIX
|
||
+ "/"
|
||
+ FILES
|
||
+ ${emojisequences_resource_files}
|
||
+)
|
||
diff --git a/tests/manual/emojisequences/emoji-test.txt b/tests/manual/emojisequences/emoji-test.txt
|
||
new file mode 100644
|
||
index 00000000..3357ccf8
|
||
--- /dev/null
|
||
+++ b/tests/manual/emojisequences/emoji-test.txt
|
||
@@ -0,0 +1,3768 @@
|
||
+1F600 ; fully-qualified # 😀 grinning face
|
||
+1F601 ; fully-qualified # 😁 beaming face with smiling eyes
|
||
+1F602 ; fully-qualified # 😂 face with tears of joy
|
||
+1F923 ; fully-qualified # 🤣 rolling on the floor laughing
|
||
+1F603 ; fully-qualified # 😃 grinning face with big eyes
|
||
+1F604 ; fully-qualified # 😄 grinning face with smiling eyes
|
||
+1F605 ; fully-qualified # 😅 grinning face with sweat
|
||
+1F606 ; fully-qualified # 😆 grinning squinting face
|
||
+1F609 ; fully-qualified # 😉 winking face
|
||
+1F60A ; fully-qualified # 😊 smiling face with smiling eyes
|
||
+1F60B ; fully-qualified # 😋 face savoring food
|
||
+1F60E ; fully-qualified # 😎 smiling face with sunglasses
|
||
+1F60D ; fully-qualified # 😍 smiling face with heart-eyes
|
||
+1F618 ; fully-qualified # 😘 face blowing a kiss
|
||
+1F970 ; fully-qualified # 🥰 smiling face with 3 hearts
|
||
+1F617 ; fully-qualified # 😗 kissing face
|
||
+1F619 ; fully-qualified # 😙 kissing face with smiling eyes
|
||
+1F61A ; fully-qualified # 😚 kissing face with closed eyes
|
||
+263A FE0F ; fully-qualified # ☺️ smiling face
|
||
+263A ; non-fully-qualified # ☺ smiling face
|
||
+1F642 ; fully-qualified # 🙂 slightly smiling face
|
||
+1F917 ; fully-qualified # 🤗 hugging face
|
||
+1F929 ; fully-qualified # 🤩 star-struck
|
||
+
|
||
+# subgroup: face-neutral
|
||
+1F914 ; fully-qualified # 🤔 thinking face
|
||
+1F928 ; fully-qualified # 🤨 face with raised eyebrow
|
||
+1F610 ; fully-qualified # 😐 neutral face
|
||
+1F611 ; fully-qualified # 😑 expressionless face
|
||
+1F636 ; fully-qualified # 😶 face without mouth
|
||
+1F644 ; fully-qualified # 🙄 face with rolling eyes
|
||
+1F60F ; fully-qualified # 😏 smirking face
|
||
+1F623 ; fully-qualified # 😣 persevering face
|
||
+1F625 ; fully-qualified # 😥 sad but relieved face
|
||
+1F62E ; fully-qualified # 😮 face with open mouth
|
||
+1F910 ; fully-qualified # 🤐 zipper-mouth face
|
||
+1F62F ; fully-qualified # 😯 hushed face
|
||
+1F62A ; fully-qualified # 😪 sleepy face
|
||
+1F62B ; fully-qualified # 😫 tired face
|
||
+1F634 ; fully-qualified # 😴 sleeping face
|
||
+1F60C ; fully-qualified # 😌 relieved face
|
||
+1F61B ; fully-qualified # 😛 face with tongue
|
||
+1F61C ; fully-qualified # 😜 winking face with tongue
|
||
+1F61D ; fully-qualified # 😝 squinting face with tongue
|
||
+1F924 ; fully-qualified # 🤤 drooling face
|
||
+1F612 ; fully-qualified # 😒 unamused face
|
||
+1F613 ; fully-qualified # 😓 downcast face with sweat
|
||
+1F614 ; fully-qualified # 😔 pensive face
|
||
+1F615 ; fully-qualified # 😕 confused face
|
||
+1F643 ; fully-qualified # 🙃 upside-down face
|
||
+1F911 ; fully-qualified # 🤑 money-mouth face
|
||
+1F632 ; fully-qualified # 😲 astonished face
|
||
+
|
||
+# subgroup: face-negative
|
||
+2639 FE0F ; fully-qualified # ☹️ frowning face
|
||
+2639 ; non-fully-qualified # ☹ frowning face
|
||
+1F641 ; fully-qualified # 🙁 slightly frowning face
|
||
+1F616 ; fully-qualified # 😖 confounded face
|
||
+1F61E ; fully-qualified # 😞 disappointed face
|
||
+1F61F ; fully-qualified # 😟 worried face
|
||
+1F624 ; fully-qualified # 😤 face with steam from nose
|
||
+1F622 ; fully-qualified # 😢 crying face
|
||
+1F62D ; fully-qualified # 😭 loudly crying face
|
||
+1F626 ; fully-qualified # 😦 frowning face with open mouth
|
||
+1F627 ; fully-qualified # 😧 anguished face
|
||
+1F628 ; fully-qualified # 😨 fearful face
|
||
+1F629 ; fully-qualified # 😩 weary face
|
||
+1F92F ; fully-qualified # 🤯 exploding head
|
||
+1F62C ; fully-qualified # 😬 grimacing face
|
||
+1F630 ; fully-qualified # 😰 anxious face with sweat
|
||
+1F631 ; fully-qualified # 😱 face screaming in fear
|
||
+1F975 ; fully-qualified # 🥵 hot face
|
||
+1F976 ; fully-qualified # 🥶 cold face
|
||
+1F633 ; fully-qualified # 😳 flushed face
|
||
+1F92A ; fully-qualified # 🤪 zany face
|
||
+1F635 ; fully-qualified # 😵 dizzy face
|
||
+1F621 ; fully-qualified # 😡 pouting face
|
||
+1F620 ; fully-qualified # 😠 angry face
|
||
+1F92C ; fully-qualified # 🤬 face with symbols on mouth
|
||
+
|
||
+# subgroup: face-sick
|
||
+1F637 ; fully-qualified # 😷 face with medical mask
|
||
+1F912 ; fully-qualified # 🤒 face with thermometer
|
||
+1F915 ; fully-qualified # 🤕 face with head-bandage
|
||
+1F922 ; fully-qualified # 🤢 nauseated face
|
||
+1F92E ; fully-qualified # 🤮 face vomiting
|
||
+1F927 ; fully-qualified # 🤧 sneezing face
|
||
+
|
||
+# subgroup: face-role
|
||
+1F607 ; fully-qualified # 😇 smiling face with halo
|
||
+1F920 ; fully-qualified # 🤠 cowboy hat face
|
||
+1F973 ; fully-qualified # 🥳 partying face
|
||
+1F974 ; fully-qualified # 🥴 woozy face
|
||
+1F97A ; fully-qualified # 🥺 pleading face
|
||
+1F925 ; fully-qualified # 🤥 lying face
|
||
+1F92B ; fully-qualified # 🤫 shushing face
|
||
+1F92D ; fully-qualified # 🤭 face with hand over mouth
|
||
+1F9D0 ; fully-qualified # 🧐 face with monocle
|
||
+1F913 ; fully-qualified # 🤓 nerd face
|
||
+
|
||
+# subgroup: face-fantasy
|
||
+1F608 ; fully-qualified # 😈 smiling face with horns
|
||
+1F47F ; fully-qualified # 👿 angry face with horns
|
||
+1F921 ; fully-qualified # 🤡 clown face
|
||
+1F479 ; fully-qualified # 👹 ogre
|
||
+1F47A ; fully-qualified # 👺 goblin
|
||
+1F480 ; fully-qualified # 💀 skull
|
||
+2620 FE0F ; fully-qualified # ☠️ skull and crossbones
|
||
+2620 ; non-fully-qualified # ☠ skull and crossbones
|
||
+1F47B ; fully-qualified # 👻 ghost
|
||
+1F47D ; fully-qualified # 👽 alien
|
||
+1F47E ; fully-qualified # 👾 alien monster
|
||
+1F916 ; fully-qualified # 🤖 robot face
|
||
+1F4A9 ; fully-qualified # 💩 pile of poo
|
||
+
|
||
+# subgroup: cat-face
|
||
+1F63A ; fully-qualified # 😺 grinning cat face
|
||
+1F638 ; fully-qualified # 😸 grinning cat face with smiling eyes
|
||
+1F639 ; fully-qualified # 😹 cat face with tears of joy
|
||
+1F63B ; fully-qualified # 😻 smiling cat face with heart-eyes
|
||
+1F63C ; fully-qualified # 😼 cat face with wry smile
|
||
+1F63D ; fully-qualified # 😽 kissing cat face
|
||
+1F640 ; fully-qualified # 🙀 weary cat face
|
||
+1F63F ; fully-qualified # 😿 crying cat face
|
||
+1F63E ; fully-qualified # 😾 pouting cat face
|
||
+
|
||
+# subgroup: monkey-face
|
||
+1F648 ; fully-qualified # 🙈 see-no-evil monkey
|
||
+1F649 ; fully-qualified # 🙉 hear-no-evil monkey
|
||
+1F64A ; fully-qualified # 🙊 speak-no-evil monkey
|
||
+
|
||
+# subgroup: skin-tone
|
||
+1F3FB ; fully-qualified # 🏻 light skin tone
|
||
+1F3FC ; fully-qualified # 🏼 medium-light skin tone
|
||
+1F3FD ; fully-qualified # 🏽 medium skin tone
|
||
+1F3FE ; fully-qualified # 🏾 medium-dark skin tone
|
||
+1F3FF ; fully-qualified # 🏿 dark skin tone
|
||
+
|
||
+# subgroup: person
|
||
+1F476 ; fully-qualified # 👶 baby
|
||
+1F476 1F3FB ; fully-qualified # 👶🏻 baby: light skin tone
|
||
+1F476 1F3FC ; fully-qualified # 👶🏼 baby: medium-light skin tone
|
||
+1F476 1F3FD ; fully-qualified # 👶🏽 baby: medium skin tone
|
||
+1F476 1F3FE ; fully-qualified # 👶🏾 baby: medium-dark skin tone
|
||
+1F476 1F3FF ; fully-qualified # 👶🏿 baby: dark skin tone
|
||
+1F9D2 ; fully-qualified # 🧒 child
|
||
+1F9D2 1F3FB ; fully-qualified # 🧒🏻 child: light skin tone
|
||
+1F9D2 1F3FC ; fully-qualified # 🧒🏼 child: medium-light skin tone
|
||
+1F9D2 1F3FD ; fully-qualified # 🧒🏽 child: medium skin tone
|
||
+1F9D2 1F3FE ; fully-qualified # 🧒🏾 child: medium-dark skin tone
|
||
+1F9D2 1F3FF ; fully-qualified # 🧒🏿 child: dark skin tone
|
||
+1F466 ; fully-qualified # 👦 boy
|
||
+1F466 1F3FB ; fully-qualified # 👦🏻 boy: light skin tone
|
||
+1F466 1F3FC ; fully-qualified # 👦🏼 boy: medium-light skin tone
|
||
+1F466 1F3FD ; fully-qualified # 👦🏽 boy: medium skin tone
|
||
+1F466 1F3FE ; fully-qualified # 👦🏾 boy: medium-dark skin tone
|
||
+1F466 1F3FF ; fully-qualified # 👦🏿 boy: dark skin tone
|
||
+1F467 ; fully-qualified # 👧 girl
|
||
+1F467 1F3FB ; fully-qualified # 👧🏻 girl: light skin tone
|
||
+1F467 1F3FC ; fully-qualified # 👧🏼 girl: medium-light skin tone
|
||
+1F467 1F3FD ; fully-qualified # 👧🏽 girl: medium skin tone
|
||
+1F467 1F3FE ; fully-qualified # 👧🏾 girl: medium-dark skin tone
|
||
+1F467 1F3FF ; fully-qualified # 👧🏿 girl: dark skin tone
|
||
+1F9D1 ; fully-qualified # 🧑 adult
|
||
+1F9D1 1F3FB ; fully-qualified # 🧑🏻 adult: light skin tone
|
||
+1F9D1 1F3FC ; fully-qualified # 🧑🏼 adult: medium-light skin tone
|
||
+1F9D1 1F3FD ; fully-qualified # 🧑🏽 adult: medium skin tone
|
||
+1F9D1 1F3FE ; fully-qualified # 🧑🏾 adult: medium-dark skin tone
|
||
+1F9D1 1F3FF ; fully-qualified # 🧑🏿 adult: dark skin tone
|
||
+1F468 ; fully-qualified # 👨 man
|
||
+1F468 1F3FB ; fully-qualified # 👨🏻 man: light skin tone
|
||
+1F468 1F3FC ; fully-qualified # 👨🏼 man: medium-light skin tone
|
||
+1F468 1F3FD ; fully-qualified # 👨🏽 man: medium skin tone
|
||
+1F468 1F3FE ; fully-qualified # 👨🏾 man: medium-dark skin tone
|
||
+1F468 1F3FF ; fully-qualified # 👨🏿 man: dark skin tone
|
||
+1F469 ; fully-qualified # 👩 woman
|
||
+1F469 1F3FB ; fully-qualified # 👩🏻 woman: light skin tone
|
||
+1F469 1F3FC ; fully-qualified # 👩🏼 woman: medium-light skin tone
|
||
+1F469 1F3FD ; fully-qualified # 👩🏽 woman: medium skin tone
|
||
+1F469 1F3FE ; fully-qualified # 👩🏾 woman: medium-dark skin tone
|
||
+1F469 1F3FF ; fully-qualified # 👩🏿 woman: dark skin tone
|
||
+1F9D3 ; fully-qualified # 🧓 older adult
|
||
+1F9D3 1F3FB ; fully-qualified # 🧓🏻 older adult: light skin tone
|
||
+1F9D3 1F3FC ; fully-qualified # 🧓🏼 older adult: medium-light skin tone
|
||
+1F9D3 1F3FD ; fully-qualified # 🧓🏽 older adult: medium skin tone
|
||
+1F9D3 1F3FE ; fully-qualified # 🧓🏾 older adult: medium-dark skin tone
|
||
+1F9D3 1F3FF ; fully-qualified # 🧓🏿 older adult: dark skin tone
|
||
+1F474 ; fully-qualified # 👴 old man
|
||
+1F474 1F3FB ; fully-qualified # 👴🏻 old man: light skin tone
|
||
+1F474 1F3FC ; fully-qualified # 👴🏼 old man: medium-light skin tone
|
||
+1F474 1F3FD ; fully-qualified # 👴🏽 old man: medium skin tone
|
||
+1F474 1F3FE ; fully-qualified # 👴🏾 old man: medium-dark skin tone
|
||
+1F474 1F3FF ; fully-qualified # 👴🏿 old man: dark skin tone
|
||
+1F475 ; fully-qualified # 👵 old woman
|
||
+1F475 1F3FB ; fully-qualified # 👵🏻 old woman: light skin tone
|
||
+1F475 1F3FC ; fully-qualified # 👵🏼 old woman: medium-light skin tone
|
||
+1F475 1F3FD ; fully-qualified # 👵🏽 old woman: medium skin tone
|
||
+1F475 1F3FE ; fully-qualified # 👵🏾 old woman: medium-dark skin tone
|
||
+1F475 1F3FF ; fully-qualified # 👵🏿 old woman: dark skin tone
|
||
+
|
||
+# subgroup: person-role
|
||
+1F468 200D 2695 FE0F ; fully-qualified # 👨⚕️ man health worker
|
||
+1F468 200D 2695 ; non-fully-qualified # 👨⚕ man health worker
|
||
+1F468 1F3FB 200D 2695 FE0F ; fully-qualified # 👨🏻⚕️ man health worker: light skin tone
|
||
+1F468 1F3FB 200D 2695 ; non-fully-qualified # 👨🏻⚕ man health worker: light skin tone
|
||
+1F468 1F3FC 200D 2695 FE0F ; fully-qualified # 👨🏼⚕️ man health worker: medium-light skin tone
|
||
+1F468 1F3FC 200D 2695 ; non-fully-qualified # 👨🏼⚕ man health worker: medium-light skin tone
|
||
+1F468 1F3FD 200D 2695 FE0F ; fully-qualified # 👨🏽⚕️ man health worker: medium skin tone
|
||
+1F468 1F3FD 200D 2695 ; non-fully-qualified # 👨🏽⚕ man health worker: medium skin tone
|
||
+1F468 1F3FE 200D 2695 FE0F ; fully-qualified # 👨🏾⚕️ man health worker: medium-dark skin tone
|
||
+1F468 1F3FE 200D 2695 ; non-fully-qualified # 👨🏾⚕ man health worker: medium-dark skin tone
|
||
+1F468 1F3FF 200D 2695 FE0F ; fully-qualified # 👨🏿⚕️ man health worker: dark skin tone
|
||
+1F468 1F3FF 200D 2695 ; non-fully-qualified # 👨🏿⚕ man health worker: dark skin tone
|
||
+1F469 200D 2695 FE0F ; fully-qualified # 👩⚕️ woman health worker
|
||
+1F469 200D 2695 ; non-fully-qualified # 👩⚕ woman health worker
|
||
+1F469 1F3FB 200D 2695 FE0F ; fully-qualified # 👩🏻⚕️ woman health worker: light skin tone
|
||
+1F469 1F3FB 200D 2695 ; non-fully-qualified # 👩🏻⚕ woman health worker: light skin tone
|
||
+1F469 1F3FC 200D 2695 FE0F ; fully-qualified # 👩🏼⚕️ woman health worker: medium-light skin tone
|
||
+1F469 1F3FC 200D 2695 ; non-fully-qualified # 👩🏼⚕ woman health worker: medium-light skin tone
|
||
+1F469 1F3FD 200D 2695 FE0F ; fully-qualified # 👩🏽⚕️ woman health worker: medium skin tone
|
||
+1F469 1F3FD 200D 2695 ; non-fully-qualified # 👩🏽⚕ woman health worker: medium skin tone
|
||
+1F469 1F3FE 200D 2695 FE0F ; fully-qualified # 👩🏾⚕️ woman health worker: medium-dark skin tone
|
||
+1F469 1F3FE 200D 2695 ; non-fully-qualified # 👩🏾⚕ woman health worker: medium-dark skin tone
|
||
+1F469 1F3FF 200D 2695 FE0F ; fully-qualified # 👩🏿⚕️ woman health worker: dark skin tone
|
||
+1F469 1F3FF 200D 2695 ; non-fully-qualified # 👩🏿⚕ woman health worker: dark skin tone
|
||
+1F468 200D 1F393 ; fully-qualified # 👨🎓 man student
|
||
+1F468 1F3FB 200D 1F393 ; fully-qualified # 👨🏻🎓 man student: light skin tone
|
||
+1F468 1F3FC 200D 1F393 ; fully-qualified # 👨🏼🎓 man student: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F393 ; fully-qualified # 👨🏽🎓 man student: medium skin tone
|
||
+1F468 1F3FE 200D 1F393 ; fully-qualified # 👨🏾🎓 man student: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F393 ; fully-qualified # 👨🏿🎓 man student: dark skin tone
|
||
+1F469 200D 1F393 ; fully-qualified # 👩🎓 woman student
|
||
+1F469 1F3FB 200D 1F393 ; fully-qualified # 👩🏻🎓 woman student: light skin tone
|
||
+1F469 1F3FC 200D 1F393 ; fully-qualified # 👩🏼🎓 woman student: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F393 ; fully-qualified # 👩🏽🎓 woman student: medium skin tone
|
||
+1F469 1F3FE 200D 1F393 ; fully-qualified # 👩🏾🎓 woman student: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F393 ; fully-qualified # 👩🏿🎓 woman student: dark skin tone
|
||
+1F468 200D 1F3EB ; fully-qualified # 👨🏫 man teacher
|
||
+1F468 1F3FB 200D 1F3EB ; fully-qualified # 👨🏻🏫 man teacher: light skin tone
|
||
+1F468 1F3FC 200D 1F3EB ; fully-qualified # 👨🏼🏫 man teacher: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F3EB ; fully-qualified # 👨🏽🏫 man teacher: medium skin tone
|
||
+1F468 1F3FE 200D 1F3EB ; fully-qualified # 👨🏾🏫 man teacher: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F3EB ; fully-qualified # 👨🏿🏫 man teacher: dark skin tone
|
||
+1F469 200D 1F3EB ; fully-qualified # 👩🏫 woman teacher
|
||
+1F469 1F3FB 200D 1F3EB ; fully-qualified # 👩🏻🏫 woman teacher: light skin tone
|
||
+1F469 1F3FC 200D 1F3EB ; fully-qualified # 👩🏼🏫 woman teacher: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F3EB ; fully-qualified # 👩🏽🏫 woman teacher: medium skin tone
|
||
+1F469 1F3FE 200D 1F3EB ; fully-qualified # 👩🏾🏫 woman teacher: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F3EB ; fully-qualified # 👩🏿🏫 woman teacher: dark skin tone
|
||
+1F468 200D 2696 FE0F ; fully-qualified # 👨⚖️ man judge
|
||
+1F468 200D 2696 ; non-fully-qualified # 👨⚖ man judge
|
||
+1F468 1F3FB 200D 2696 FE0F ; fully-qualified # 👨🏻⚖️ man judge: light skin tone
|
||
+1F468 1F3FB 200D 2696 ; non-fully-qualified # 👨🏻⚖ man judge: light skin tone
|
||
+1F468 1F3FC 200D 2696 FE0F ; fully-qualified # 👨🏼⚖️ man judge: medium-light skin tone
|
||
+1F468 1F3FC 200D 2696 ; non-fully-qualified # 👨🏼⚖ man judge: medium-light skin tone
|
||
+1F468 1F3FD 200D 2696 FE0F ; fully-qualified # 👨🏽⚖️ man judge: medium skin tone
|
||
+1F468 1F3FD 200D 2696 ; non-fully-qualified # 👨🏽⚖ man judge: medium skin tone
|
||
+1F468 1F3FE 200D 2696 FE0F ; fully-qualified # 👨🏾⚖️ man judge: medium-dark skin tone
|
||
+1F468 1F3FE 200D 2696 ; non-fully-qualified # 👨🏾⚖ man judge: medium-dark skin tone
|
||
+1F468 1F3FF 200D 2696 FE0F ; fully-qualified # 👨🏿⚖️ man judge: dark skin tone
|
||
+1F468 1F3FF 200D 2696 ; non-fully-qualified # 👨🏿⚖ man judge: dark skin tone
|
||
+1F469 200D 2696 FE0F ; fully-qualified # 👩⚖️ woman judge
|
||
+1F469 200D 2696 ; non-fully-qualified # 👩⚖ woman judge
|
||
+1F469 1F3FB 200D 2696 FE0F ; fully-qualified # 👩🏻⚖️ woman judge: light skin tone
|
||
+1F469 1F3FB 200D 2696 ; non-fully-qualified # 👩🏻⚖ woman judge: light skin tone
|
||
+1F469 1F3FC 200D 2696 FE0F ; fully-qualified # 👩🏼⚖️ woman judge: medium-light skin tone
|
||
+1F469 1F3FC 200D 2696 ; non-fully-qualified # 👩🏼⚖ woman judge: medium-light skin tone
|
||
+1F469 1F3FD 200D 2696 FE0F ; fully-qualified # 👩🏽⚖️ woman judge: medium skin tone
|
||
+1F469 1F3FD 200D 2696 ; non-fully-qualified # 👩🏽⚖ woman judge: medium skin tone
|
||
+1F469 1F3FE 200D 2696 FE0F ; fully-qualified # 👩🏾⚖️ woman judge: medium-dark skin tone
|
||
+1F469 1F3FE 200D 2696 ; non-fully-qualified # 👩🏾⚖ woman judge: medium-dark skin tone
|
||
+1F469 1F3FF 200D 2696 FE0F ; fully-qualified # 👩🏿⚖️ woman judge: dark skin tone
|
||
+1F469 1F3FF 200D 2696 ; non-fully-qualified # 👩🏿⚖ woman judge: dark skin tone
|
||
+1F468 200D 1F33E ; fully-qualified # 👨🌾 man farmer
|
||
+1F468 1F3FB 200D 1F33E ; fully-qualified # 👨🏻🌾 man farmer: light skin tone
|
||
+1F468 1F3FC 200D 1F33E ; fully-qualified # 👨🏼🌾 man farmer: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F33E ; fully-qualified # 👨🏽🌾 man farmer: medium skin tone
|
||
+1F468 1F3FE 200D 1F33E ; fully-qualified # 👨🏾🌾 man farmer: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F33E ; fully-qualified # 👨🏿🌾 man farmer: dark skin tone
|
||
+1F469 200D 1F33E ; fully-qualified # 👩🌾 woman farmer
|
||
+1F469 1F3FB 200D 1F33E ; fully-qualified # 👩🏻🌾 woman farmer: light skin tone
|
||
+1F469 1F3FC 200D 1F33E ; fully-qualified # 👩🏼🌾 woman farmer: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F33E ; fully-qualified # 👩🏽🌾 woman farmer: medium skin tone
|
||
+1F469 1F3FE 200D 1F33E ; fully-qualified # 👩🏾🌾 woman farmer: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F33E ; fully-qualified # 👩🏿🌾 woman farmer: dark skin tone
|
||
+1F468 200D 1F373 ; fully-qualified # 👨🍳 man cook
|
||
+1F468 1F3FB 200D 1F373 ; fully-qualified # 👨🏻🍳 man cook: light skin tone
|
||
+1F468 1F3FC 200D 1F373 ; fully-qualified # 👨🏼🍳 man cook: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F373 ; fully-qualified # 👨🏽🍳 man cook: medium skin tone
|
||
+1F468 1F3FE 200D 1F373 ; fully-qualified # 👨🏾🍳 man cook: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F373 ; fully-qualified # 👨🏿🍳 man cook: dark skin tone
|
||
+1F469 200D 1F373 ; fully-qualified # 👩🍳 woman cook
|
||
+1F469 1F3FB 200D 1F373 ; fully-qualified # 👩🏻🍳 woman cook: light skin tone
|
||
+1F469 1F3FC 200D 1F373 ; fully-qualified # 👩🏼🍳 woman cook: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F373 ; fully-qualified # 👩🏽🍳 woman cook: medium skin tone
|
||
+1F469 1F3FE 200D 1F373 ; fully-qualified # 👩🏾🍳 woman cook: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F373 ; fully-qualified # 👩🏿🍳 woman cook: dark skin tone
|
||
+1F468 200D 1F527 ; fully-qualified # 👨🔧 man mechanic
|
||
+1F468 1F3FB 200D 1F527 ; fully-qualified # 👨🏻🔧 man mechanic: light skin tone
|
||
+1F468 1F3FC 200D 1F527 ; fully-qualified # 👨🏼🔧 man mechanic: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F527 ; fully-qualified # 👨🏽🔧 man mechanic: medium skin tone
|
||
+1F468 1F3FE 200D 1F527 ; fully-qualified # 👨🏾🔧 man mechanic: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F527 ; fully-qualified # 👨🏿🔧 man mechanic: dark skin tone
|
||
+1F469 200D 1F527 ; fully-qualified # 👩🔧 woman mechanic
|
||
+1F469 1F3FB 200D 1F527 ; fully-qualified # 👩🏻🔧 woman mechanic: light skin tone
|
||
+1F469 1F3FC 200D 1F527 ; fully-qualified # 👩🏼🔧 woman mechanic: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F527 ; fully-qualified # 👩🏽🔧 woman mechanic: medium skin tone
|
||
+1F469 1F3FE 200D 1F527 ; fully-qualified # 👩🏾🔧 woman mechanic: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F527 ; fully-qualified # 👩🏿🔧 woman mechanic: dark skin tone
|
||
+1F468 200D 1F3ED ; fully-qualified # 👨🏭 man factory worker
|
||
+1F468 1F3FB 200D 1F3ED ; fully-qualified # 👨🏻🏭 man factory worker: light skin tone
|
||
+1F468 1F3FC 200D 1F3ED ; fully-qualified # 👨🏼🏭 man factory worker: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F3ED ; fully-qualified # 👨🏽🏭 man factory worker: medium skin tone
|
||
+1F468 1F3FE 200D 1F3ED ; fully-qualified # 👨🏾🏭 man factory worker: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F3ED ; fully-qualified # 👨🏿🏭 man factory worker: dark skin tone
|
||
+1F469 200D 1F3ED ; fully-qualified # 👩🏭 woman factory worker
|
||
+1F469 1F3FB 200D 1F3ED ; fully-qualified # 👩🏻🏭 woman factory worker: light skin tone
|
||
+1F469 1F3FC 200D 1F3ED ; fully-qualified # 👩🏼🏭 woman factory worker: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F3ED ; fully-qualified # 👩🏽🏭 woman factory worker: medium skin tone
|
||
+1F469 1F3FE 200D 1F3ED ; fully-qualified # 👩🏾🏭 woman factory worker: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F3ED ; fully-qualified # 👩🏿🏭 woman factory worker: dark skin tone
|
||
+1F468 200D 1F4BC ; fully-qualified # 👨💼 man office worker
|
||
+1F468 1F3FB 200D 1F4BC ; fully-qualified # 👨🏻💼 man office worker: light skin tone
|
||
+1F468 1F3FC 200D 1F4BC ; fully-qualified # 👨🏼💼 man office worker: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F4BC ; fully-qualified # 👨🏽💼 man office worker: medium skin tone
|
||
+1F468 1F3FE 200D 1F4BC ; fully-qualified # 👨🏾💼 man office worker: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F4BC ; fully-qualified # 👨🏿💼 man office worker: dark skin tone
|
||
+1F469 200D 1F4BC ; fully-qualified # 👩💼 woman office worker
|
||
+1F469 1F3FB 200D 1F4BC ; fully-qualified # 👩🏻💼 woman office worker: light skin tone
|
||
+1F469 1F3FC 200D 1F4BC ; fully-qualified # 👩🏼💼 woman office worker: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F4BC ; fully-qualified # 👩🏽💼 woman office worker: medium skin tone
|
||
+1F469 1F3FE 200D 1F4BC ; fully-qualified # 👩🏾💼 woman office worker: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F4BC ; fully-qualified # 👩🏿💼 woman office worker: dark skin tone
|
||
+1F468 200D 1F52C ; fully-qualified # 👨🔬 man scientist
|
||
+1F468 1F3FB 200D 1F52C ; fully-qualified # 👨🏻🔬 man scientist: light skin tone
|
||
+1F468 1F3FC 200D 1F52C ; fully-qualified # 👨🏼🔬 man scientist: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F52C ; fully-qualified # 👨🏽🔬 man scientist: medium skin tone
|
||
+1F468 1F3FE 200D 1F52C ; fully-qualified # 👨🏾🔬 man scientist: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F52C ; fully-qualified # 👨🏿🔬 man scientist: dark skin tone
|
||
+1F469 200D 1F52C ; fully-qualified # 👩🔬 woman scientist
|
||
+1F469 1F3FB 200D 1F52C ; fully-qualified # 👩🏻🔬 woman scientist: light skin tone
|
||
+1F469 1F3FC 200D 1F52C ; fully-qualified # 👩🏼🔬 woman scientist: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F52C ; fully-qualified # 👩🏽🔬 woman scientist: medium skin tone
|
||
+1F469 1F3FE 200D 1F52C ; fully-qualified # 👩🏾🔬 woman scientist: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F52C ; fully-qualified # 👩🏿🔬 woman scientist: dark skin tone
|
||
+1F468 200D 1F4BB ; fully-qualified # 👨💻 man technologist
|
||
+1F468 1F3FB 200D 1F4BB ; fully-qualified # 👨🏻💻 man technologist: light skin tone
|
||
+1F468 1F3FC 200D 1F4BB ; fully-qualified # 👨🏼💻 man technologist: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F4BB ; fully-qualified # 👨🏽💻 man technologist: medium skin tone
|
||
+1F468 1F3FE 200D 1F4BB ; fully-qualified # 👨🏾💻 man technologist: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F4BB ; fully-qualified # 👨🏿💻 man technologist: dark skin tone
|
||
+1F469 200D 1F4BB ; fully-qualified # 👩💻 woman technologist
|
||
+1F469 1F3FB 200D 1F4BB ; fully-qualified # 👩🏻💻 woman technologist: light skin tone
|
||
+1F469 1F3FC 200D 1F4BB ; fully-qualified # 👩🏼💻 woman technologist: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F4BB ; fully-qualified # 👩🏽💻 woman technologist: medium skin tone
|
||
+1F469 1F3FE 200D 1F4BB ; fully-qualified # 👩🏾💻 woman technologist: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F4BB ; fully-qualified # 👩🏿💻 woman technologist: dark skin tone
|
||
+1F468 200D 1F3A4 ; fully-qualified # 👨🎤 man singer
|
||
+1F468 1F3FB 200D 1F3A4 ; fully-qualified # 👨🏻🎤 man singer: light skin tone
|
||
+1F468 1F3FC 200D 1F3A4 ; fully-qualified # 👨🏼🎤 man singer: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F3A4 ; fully-qualified # 👨🏽🎤 man singer: medium skin tone
|
||
+1F468 1F3FE 200D 1F3A4 ; fully-qualified # 👨🏾🎤 man singer: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F3A4 ; fully-qualified # 👨🏿🎤 man singer: dark skin tone
|
||
+1F469 200D 1F3A4 ; fully-qualified # 👩🎤 woman singer
|
||
+1F469 1F3FB 200D 1F3A4 ; fully-qualified # 👩🏻🎤 woman singer: light skin tone
|
||
+1F469 1F3FC 200D 1F3A4 ; fully-qualified # 👩🏼🎤 woman singer: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F3A4 ; fully-qualified # 👩🏽🎤 woman singer: medium skin tone
|
||
+1F469 1F3FE 200D 1F3A4 ; fully-qualified # 👩🏾🎤 woman singer: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F3A4 ; fully-qualified # 👩🏿🎤 woman singer: dark skin tone
|
||
+1F468 200D 1F3A8 ; fully-qualified # 👨🎨 man artist
|
||
+1F468 1F3FB 200D 1F3A8 ; fully-qualified # 👨🏻🎨 man artist: light skin tone
|
||
+1F468 1F3FC 200D 1F3A8 ; fully-qualified # 👨🏼🎨 man artist: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F3A8 ; fully-qualified # 👨🏽🎨 man artist: medium skin tone
|
||
+1F468 1F3FE 200D 1F3A8 ; fully-qualified # 👨🏾🎨 man artist: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F3A8 ; fully-qualified # 👨🏿🎨 man artist: dark skin tone
|
||
+1F469 200D 1F3A8 ; fully-qualified # 👩🎨 woman artist
|
||
+1F469 1F3FB 200D 1F3A8 ; fully-qualified # 👩🏻🎨 woman artist: light skin tone
|
||
+1F469 1F3FC 200D 1F3A8 ; fully-qualified # 👩🏼🎨 woman artist: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F3A8 ; fully-qualified # 👩🏽🎨 woman artist: medium skin tone
|
||
+1F469 1F3FE 200D 1F3A8 ; fully-qualified # 👩🏾🎨 woman artist: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F3A8 ; fully-qualified # 👩🏿🎨 woman artist: dark skin tone
|
||
+1F468 200D 2708 FE0F ; fully-qualified # 👨✈️ man pilot
|
||
+1F468 200D 2708 ; non-fully-qualified # 👨✈ man pilot
|
||
+1F468 1F3FB 200D 2708 FE0F ; fully-qualified # 👨🏻✈️ man pilot: light skin tone
|
||
+1F468 1F3FB 200D 2708 ; non-fully-qualified # 👨🏻✈ man pilot: light skin tone
|
||
+1F468 1F3FC 200D 2708 FE0F ; fully-qualified # 👨🏼✈️ man pilot: medium-light skin tone
|
||
+1F468 1F3FC 200D 2708 ; non-fully-qualified # 👨🏼✈ man pilot: medium-light skin tone
|
||
+1F468 1F3FD 200D 2708 FE0F ; fully-qualified # 👨🏽✈️ man pilot: medium skin tone
|
||
+1F468 1F3FD 200D 2708 ; non-fully-qualified # 👨🏽✈ man pilot: medium skin tone
|
||
+1F468 1F3FE 200D 2708 FE0F ; fully-qualified # 👨🏾✈️ man pilot: medium-dark skin tone
|
||
+1F468 1F3FE 200D 2708 ; non-fully-qualified # 👨🏾✈ man pilot: medium-dark skin tone
|
||
+1F468 1F3FF 200D 2708 FE0F ; fully-qualified # 👨🏿✈️ man pilot: dark skin tone
|
||
+1F468 1F3FF 200D 2708 ; non-fully-qualified # 👨🏿✈ man pilot: dark skin tone
|
||
+1F469 200D 2708 FE0F ; fully-qualified # 👩✈️ woman pilot
|
||
+1F469 200D 2708 ; non-fully-qualified # 👩✈ woman pilot
|
||
+1F469 1F3FB 200D 2708 FE0F ; fully-qualified # 👩🏻✈️ woman pilot: light skin tone
|
||
+1F469 1F3FB 200D 2708 ; non-fully-qualified # 👩🏻✈ woman pilot: light skin tone
|
||
+1F469 1F3FC 200D 2708 FE0F ; fully-qualified # 👩🏼✈️ woman pilot: medium-light skin tone
|
||
+1F469 1F3FC 200D 2708 ; non-fully-qualified # 👩🏼✈ woman pilot: medium-light skin tone
|
||
+1F469 1F3FD 200D 2708 FE0F ; fully-qualified # 👩🏽✈️ woman pilot: medium skin tone
|
||
+1F469 1F3FD 200D 2708 ; non-fully-qualified # 👩🏽✈ woman pilot: medium skin tone
|
||
+1F469 1F3FE 200D 2708 FE0F ; fully-qualified # 👩🏾✈️ woman pilot: medium-dark skin tone
|
||
+1F469 1F3FE 200D 2708 ; non-fully-qualified # 👩🏾✈ woman pilot: medium-dark skin tone
|
||
+1F469 1F3FF 200D 2708 FE0F ; fully-qualified # 👩🏿✈️ woman pilot: dark skin tone
|
||
+1F469 1F3FF 200D 2708 ; non-fully-qualified # 👩🏿✈ woman pilot: dark skin tone
|
||
+1F468 200D 1F680 ; fully-qualified # 👨🚀 man astronaut
|
||
+1F468 1F3FB 200D 1F680 ; fully-qualified # 👨🏻🚀 man astronaut: light skin tone
|
||
+1F468 1F3FC 200D 1F680 ; fully-qualified # 👨🏼🚀 man astronaut: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F680 ; fully-qualified # 👨🏽🚀 man astronaut: medium skin tone
|
||
+1F468 1F3FE 200D 1F680 ; fully-qualified # 👨🏾🚀 man astronaut: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F680 ; fully-qualified # 👨🏿🚀 man astronaut: dark skin tone
|
||
+1F469 200D 1F680 ; fully-qualified # 👩🚀 woman astronaut
|
||
+1F469 1F3FB 200D 1F680 ; fully-qualified # 👩🏻🚀 woman astronaut: light skin tone
|
||
+1F469 1F3FC 200D 1F680 ; fully-qualified # 👩🏼🚀 woman astronaut: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F680 ; fully-qualified # 👩🏽🚀 woman astronaut: medium skin tone
|
||
+1F469 1F3FE 200D 1F680 ; fully-qualified # 👩🏾🚀 woman astronaut: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F680 ; fully-qualified # 👩🏿🚀 woman astronaut: dark skin tone
|
||
+1F468 200D 1F692 ; fully-qualified # 👨🚒 man firefighter
|
||
+1F468 1F3FB 200D 1F692 ; fully-qualified # 👨🏻🚒 man firefighter: light skin tone
|
||
+1F468 1F3FC 200D 1F692 ; fully-qualified # 👨🏼🚒 man firefighter: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F692 ; fully-qualified # 👨🏽🚒 man firefighter: medium skin tone
|
||
+1F468 1F3FE 200D 1F692 ; fully-qualified # 👨🏾🚒 man firefighter: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F692 ; fully-qualified # 👨🏿🚒 man firefighter: dark skin tone
|
||
+1F469 200D 1F692 ; fully-qualified # 👩🚒 woman firefighter
|
||
+1F469 1F3FB 200D 1F692 ; fully-qualified # 👩🏻🚒 woman firefighter: light skin tone
|
||
+1F469 1F3FC 200D 1F692 ; fully-qualified # 👩🏼🚒 woman firefighter: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F692 ; fully-qualified # 👩🏽🚒 woman firefighter: medium skin tone
|
||
+1F469 1F3FE 200D 1F692 ; fully-qualified # 👩🏾🚒 woman firefighter: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F692 ; fully-qualified # 👩🏿🚒 woman firefighter: dark skin tone
|
||
+1F46E ; fully-qualified # 👮 police officer
|
||
+1F46E 1F3FB ; fully-qualified # 👮🏻 police officer: light skin tone
|
||
+1F46E 1F3FC ; fully-qualified # 👮🏼 police officer: medium-light skin tone
|
||
+1F46E 1F3FD ; fully-qualified # 👮🏽 police officer: medium skin tone
|
||
+1F46E 1F3FE ; fully-qualified # 👮🏾 police officer: medium-dark skin tone
|
||
+1F46E 1F3FF ; fully-qualified # 👮🏿 police officer: dark skin tone
|
||
+1F46E 200D 2642 FE0F ; fully-qualified # 👮♂️ man police officer
|
||
+1F46E 200D 2642 ; non-fully-qualified # 👮♂ man police officer
|
||
+1F46E 1F3FB 200D 2642 FE0F ; fully-qualified # 👮🏻♂️ man police officer: light skin tone
|
||
+1F46E 1F3FB 200D 2642 ; non-fully-qualified # 👮🏻♂ man police officer: light skin tone
|
||
+1F46E 1F3FC 200D 2642 FE0F ; fully-qualified # 👮🏼♂️ man police officer: medium-light skin tone
|
||
+1F46E 1F3FC 200D 2642 ; non-fully-qualified # 👮🏼♂ man police officer: medium-light skin tone
|
||
+1F46E 1F3FD 200D 2642 FE0F ; fully-qualified # 👮🏽♂️ man police officer: medium skin tone
|
||
+1F46E 1F3FD 200D 2642 ; non-fully-qualified # 👮🏽♂ man police officer: medium skin tone
|
||
+1F46E 1F3FE 200D 2642 FE0F ; fully-qualified # 👮🏾♂️ man police officer: medium-dark skin tone
|
||
+1F46E 1F3FE 200D 2642 ; non-fully-qualified # 👮🏾♂ man police officer: medium-dark skin tone
|
||
+1F46E 1F3FF 200D 2642 FE0F ; fully-qualified # 👮🏿♂️ man police officer: dark skin tone
|
||
+1F46E 1F3FF 200D 2642 ; non-fully-qualified # 👮🏿♂ man police officer: dark skin tone
|
||
+1F46E 200D 2640 FE0F ; fully-qualified # 👮♀️ woman police officer
|
||
+1F46E 200D 2640 ; non-fully-qualified # 👮♀ woman police officer
|
||
+1F46E 1F3FB 200D 2640 FE0F ; fully-qualified # 👮🏻♀️ woman police officer: light skin tone
|
||
+1F46E 1F3FB 200D 2640 ; non-fully-qualified # 👮🏻♀ woman police officer: light skin tone
|
||
+1F46E 1F3FC 200D 2640 FE0F ; fully-qualified # 👮🏼♀️ woman police officer: medium-light skin tone
|
||
+1F46E 1F3FC 200D 2640 ; non-fully-qualified # 👮🏼♀ woman police officer: medium-light skin tone
|
||
+1F46E 1F3FD 200D 2640 FE0F ; fully-qualified # 👮🏽♀️ woman police officer: medium skin tone
|
||
+1F46E 1F3FD 200D 2640 ; non-fully-qualified # 👮🏽♀ woman police officer: medium skin tone
|
||
+1F46E 1F3FE 200D 2640 FE0F ; fully-qualified # 👮🏾♀️ woman police officer: medium-dark skin tone
|
||
+1F46E 1F3FE 200D 2640 ; non-fully-qualified # 👮🏾♀ woman police officer: medium-dark skin tone
|
||
+1F46E 1F3FF 200D 2640 FE0F ; fully-qualified # 👮🏿♀️ woman police officer: dark skin tone
|
||
+1F46E 1F3FF 200D 2640 ; non-fully-qualified # 👮🏿♀ woman police officer: dark skin tone
|
||
+1F575 FE0F ; fully-qualified # 🕵️ detective
|
||
+1F575 ; non-fully-qualified # 🕵 detective
|
||
+1F575 1F3FB ; fully-qualified # 🕵🏻 detective: light skin tone
|
||
+1F575 1F3FC ; fully-qualified # 🕵🏼 detective: medium-light skin tone
|
||
+1F575 1F3FD ; fully-qualified # 🕵🏽 detective: medium skin tone
|
||
+1F575 1F3FE ; fully-qualified # 🕵🏾 detective: medium-dark skin tone
|
||
+1F575 1F3FF ; fully-qualified # 🕵🏿 detective: dark skin tone
|
||
+1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️♂️ man detective
|
||
+1F575 200D 2642 FE0F ; non-fully-qualified # 🕵♂️ man detective
|
||
+1F575 FE0F 200D 2642 ; non-fully-qualified # 🕵️♂ man detective
|
||
+1F575 200D 2642 ; non-fully-qualified # 🕵♂ man detective
|
||
+1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻♂️ man detective: light skin tone
|
||
+1F575 1F3FB 200D 2642 ; non-fully-qualified # 🕵🏻♂ man detective: light skin tone
|
||
+1F575 1F3FC 200D 2642 FE0F ; fully-qualified # 🕵🏼♂️ man detective: medium-light skin tone
|
||
+1F575 1F3FC 200D 2642 ; non-fully-qualified # 🕵🏼♂ man detective: medium-light skin tone
|
||
+1F575 1F3FD 200D 2642 FE0F ; fully-qualified # 🕵🏽♂️ man detective: medium skin tone
|
||
+1F575 1F3FD 200D 2642 ; non-fully-qualified # 🕵🏽♂ man detective: medium skin tone
|
||
+1F575 1F3FE 200D 2642 FE0F ; fully-qualified # 🕵🏾♂️ man detective: medium-dark skin tone
|
||
+1F575 1F3FE 200D 2642 ; non-fully-qualified # 🕵🏾♂ man detective: medium-dark skin tone
|
||
+1F575 1F3FF 200D 2642 FE0F ; fully-qualified # 🕵🏿♂️ man detective: dark skin tone
|
||
+1F575 1F3FF 200D 2642 ; non-fully-qualified # 🕵🏿♂ man detective: dark skin tone
|
||
+1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️♀️ woman detective
|
||
+1F575 200D 2640 FE0F ; non-fully-qualified # 🕵♀️ woman detective
|
||
+1F575 FE0F 200D 2640 ; non-fully-qualified # 🕵️♀ woman detective
|
||
+1F575 200D 2640 ; non-fully-qualified # 🕵♀ woman detective
|
||
+1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻♀️ woman detective: light skin tone
|
||
+1F575 1F3FB 200D 2640 ; non-fully-qualified # 🕵🏻♀ woman detective: light skin tone
|
||
+1F575 1F3FC 200D 2640 FE0F ; fully-qualified # 🕵🏼♀️ woman detective: medium-light skin tone
|
||
+1F575 1F3FC 200D 2640 ; non-fully-qualified # 🕵🏼♀ woman detective: medium-light skin tone
|
||
+1F575 1F3FD 200D 2640 FE0F ; fully-qualified # 🕵🏽♀️ woman detective: medium skin tone
|
||
+1F575 1F3FD 200D 2640 ; non-fully-qualified # 🕵🏽♀ woman detective: medium skin tone
|
||
+1F575 1F3FE 200D 2640 FE0F ; fully-qualified # 🕵🏾♀️ woman detective: medium-dark skin tone
|
||
+1F575 1F3FE 200D 2640 ; non-fully-qualified # 🕵🏾♀ woman detective: medium-dark skin tone
|
||
+1F575 1F3FF 200D 2640 FE0F ; fully-qualified # 🕵🏿♀️ woman detective: dark skin tone
|
||
+1F575 1F3FF 200D 2640 ; non-fully-qualified # 🕵🏿♀ woman detective: dark skin tone
|
||
+1F482 ; fully-qualified # 💂 guard
|
||
+1F482 1F3FB ; fully-qualified # 💂🏻 guard: light skin tone
|
||
+1F482 1F3FC ; fully-qualified # 💂🏼 guard: medium-light skin tone
|
||
+1F482 1F3FD ; fully-qualified # 💂🏽 guard: medium skin tone
|
||
+1F482 1F3FE ; fully-qualified # 💂🏾 guard: medium-dark skin tone
|
||
+1F482 1F3FF ; fully-qualified # 💂🏿 guard: dark skin tone
|
||
+1F482 200D 2642 FE0F ; fully-qualified # 💂♂️ man guard
|
||
+1F482 200D 2642 ; non-fully-qualified # 💂♂ man guard
|
||
+1F482 1F3FB 200D 2642 FE0F ; fully-qualified # 💂🏻♂️ man guard: light skin tone
|
||
+1F482 1F3FB 200D 2642 ; non-fully-qualified # 💂🏻♂ man guard: light skin tone
|
||
+1F482 1F3FC 200D 2642 FE0F ; fully-qualified # 💂🏼♂️ man guard: medium-light skin tone
|
||
+1F482 1F3FC 200D 2642 ; non-fully-qualified # 💂🏼♂ man guard: medium-light skin tone
|
||
+1F482 1F3FD 200D 2642 FE0F ; fully-qualified # 💂🏽♂️ man guard: medium skin tone
|
||
+1F482 1F3FD 200D 2642 ; non-fully-qualified # 💂🏽♂ man guard: medium skin tone
|
||
+1F482 1F3FE 200D 2642 FE0F ; fully-qualified # 💂🏾♂️ man guard: medium-dark skin tone
|
||
+1F482 1F3FE 200D 2642 ; non-fully-qualified # 💂🏾♂ man guard: medium-dark skin tone
|
||
+1F482 1F3FF 200D 2642 FE0F ; fully-qualified # 💂🏿♂️ man guard: dark skin tone
|
||
+1F482 1F3FF 200D 2642 ; non-fully-qualified # 💂🏿♂ man guard: dark skin tone
|
||
+1F482 200D 2640 FE0F ; fully-qualified # 💂♀️ woman guard
|
||
+1F482 200D 2640 ; non-fully-qualified # 💂♀ woman guard
|
||
+1F482 1F3FB 200D 2640 FE0F ; fully-qualified # 💂🏻♀️ woman guard: light skin tone
|
||
+1F482 1F3FB 200D 2640 ; non-fully-qualified # 💂🏻♀ woman guard: light skin tone
|
||
+1F482 1F3FC 200D 2640 FE0F ; fully-qualified # 💂🏼♀️ woman guard: medium-light skin tone
|
||
+1F482 1F3FC 200D 2640 ; non-fully-qualified # 💂🏼♀ woman guard: medium-light skin tone
|
||
+1F482 1F3FD 200D 2640 FE0F ; fully-qualified # 💂🏽♀️ woman guard: medium skin tone
|
||
+1F482 1F3FD 200D 2640 ; non-fully-qualified # 💂🏽♀ woman guard: medium skin tone
|
||
+1F482 1F3FE 200D 2640 FE0F ; fully-qualified # 💂🏾♀️ woman guard: medium-dark skin tone
|
||
+1F482 1F3FE 200D 2640 ; non-fully-qualified # 💂🏾♀ woman guard: medium-dark skin tone
|
||
+1F482 1F3FF 200D 2640 FE0F ; fully-qualified # 💂🏿♀️ woman guard: dark skin tone
|
||
+1F482 1F3FF 200D 2640 ; non-fully-qualified # 💂🏿♀ woman guard: dark skin tone
|
||
+1F477 ; fully-qualified # 👷 construction worker
|
||
+1F477 1F3FB ; fully-qualified # 👷🏻 construction worker: light skin tone
|
||
+1F477 1F3FC ; fully-qualified # 👷🏼 construction worker: medium-light skin tone
|
||
+1F477 1F3FD ; fully-qualified # 👷🏽 construction worker: medium skin tone
|
||
+1F477 1F3FE ; fully-qualified # 👷🏾 construction worker: medium-dark skin tone
|
||
+1F477 1F3FF ; fully-qualified # 👷🏿 construction worker: dark skin tone
|
||
+1F477 200D 2642 FE0F ; fully-qualified # 👷♂️ man construction worker
|
||
+1F477 200D 2642 ; non-fully-qualified # 👷♂ man construction worker
|
||
+1F477 1F3FB 200D 2642 FE0F ; fully-qualified # 👷🏻♂️ man construction worker: light skin tone
|
||
+1F477 1F3FB 200D 2642 ; non-fully-qualified # 👷🏻♂ man construction worker: light skin tone
|
||
+1F477 1F3FC 200D 2642 FE0F ; fully-qualified # 👷🏼♂️ man construction worker: medium-light skin tone
|
||
+1F477 1F3FC 200D 2642 ; non-fully-qualified # 👷🏼♂ man construction worker: medium-light skin tone
|
||
+1F477 1F3FD 200D 2642 FE0F ; fully-qualified # 👷🏽♂️ man construction worker: medium skin tone
|
||
+1F477 1F3FD 200D 2642 ; non-fully-qualified # 👷🏽♂ man construction worker: medium skin tone
|
||
+1F477 1F3FE 200D 2642 FE0F ; fully-qualified # 👷🏾♂️ man construction worker: medium-dark skin tone
|
||
+1F477 1F3FE 200D 2642 ; non-fully-qualified # 👷🏾♂ man construction worker: medium-dark skin tone
|
||
+1F477 1F3FF 200D 2642 FE0F ; fully-qualified # 👷🏿♂️ man construction worker: dark skin tone
|
||
+1F477 1F3FF 200D 2642 ; non-fully-qualified # 👷🏿♂ man construction worker: dark skin tone
|
||
+1F477 200D 2640 FE0F ; fully-qualified # 👷♀️ woman construction worker
|
||
+1F477 200D 2640 ; non-fully-qualified # 👷♀ woman construction worker
|
||
+1F477 1F3FB 200D 2640 FE0F ; fully-qualified # 👷🏻♀️ woman construction worker: light skin tone
|
||
+1F477 1F3FB 200D 2640 ; non-fully-qualified # 👷🏻♀ woman construction worker: light skin tone
|
||
+1F477 1F3FC 200D 2640 FE0F ; fully-qualified # 👷🏼♀️ woman construction worker: medium-light skin tone
|
||
+1F477 1F3FC 200D 2640 ; non-fully-qualified # 👷🏼♀ woman construction worker: medium-light skin tone
|
||
+1F477 1F3FD 200D 2640 FE0F ; fully-qualified # 👷🏽♀️ woman construction worker: medium skin tone
|
||
+1F477 1F3FD 200D 2640 ; non-fully-qualified # 👷🏽♀ woman construction worker: medium skin tone
|
||
+1F477 1F3FE 200D 2640 FE0F ; fully-qualified # 👷🏾♀️ woman construction worker: medium-dark skin tone
|
||
+1F477 1F3FE 200D 2640 ; non-fully-qualified # 👷🏾♀ woman construction worker: medium-dark skin tone
|
||
+1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿♀️ woman construction worker: dark skin tone
|
||
+1F477 1F3FF 200D 2640 ; non-fully-qualified # 👷🏿♀ woman construction worker: dark skin tone
|
||
+1F934 ; fully-qualified # 🤴 prince
|
||
+1F934 1F3FB ; fully-qualified # 🤴🏻 prince: light skin tone
|
||
+1F934 1F3FC ; fully-qualified # 🤴🏼 prince: medium-light skin tone
|
||
+1F934 1F3FD ; fully-qualified # 🤴🏽 prince: medium skin tone
|
||
+1F934 1F3FE ; fully-qualified # 🤴🏾 prince: medium-dark skin tone
|
||
+1F934 1F3FF ; fully-qualified # 🤴🏿 prince: dark skin tone
|
||
+1F478 ; fully-qualified # 👸 princess
|
||
+1F478 1F3FB ; fully-qualified # 👸🏻 princess: light skin tone
|
||
+1F478 1F3FC ; fully-qualified # 👸🏼 princess: medium-light skin tone
|
||
+1F478 1F3FD ; fully-qualified # 👸🏽 princess: medium skin tone
|
||
+1F478 1F3FE ; fully-qualified # 👸🏾 princess: medium-dark skin tone
|
||
+1F478 1F3FF ; fully-qualified # 👸🏿 princess: dark skin tone
|
||
+1F473 ; fully-qualified # 👳 person wearing turban
|
||
+1F473 1F3FB ; fully-qualified # 👳🏻 person wearing turban: light skin tone
|
||
+1F473 1F3FC ; fully-qualified # 👳🏼 person wearing turban: medium-light skin tone
|
||
+1F473 1F3FD ; fully-qualified # 👳🏽 person wearing turban: medium skin tone
|
||
+1F473 1F3FE ; fully-qualified # 👳🏾 person wearing turban: medium-dark skin tone
|
||
+1F473 1F3FF ; fully-qualified # 👳🏿 person wearing turban: dark skin tone
|
||
+1F473 200D 2642 FE0F ; fully-qualified # 👳♂️ man wearing turban
|
||
+1F473 200D 2642 ; non-fully-qualified # 👳♂ man wearing turban
|
||
+1F473 1F3FB 200D 2642 FE0F ; fully-qualified # 👳🏻♂️ man wearing turban: light skin tone
|
||
+1F473 1F3FB 200D 2642 ; non-fully-qualified # 👳🏻♂ man wearing turban: light skin tone
|
||
+1F473 1F3FC 200D 2642 FE0F ; fully-qualified # 👳🏼♂️ man wearing turban: medium-light skin tone
|
||
+1F473 1F3FC 200D 2642 ; non-fully-qualified # 👳🏼♂ man wearing turban: medium-light skin tone
|
||
+1F473 1F3FD 200D 2642 FE0F ; fully-qualified # 👳🏽♂️ man wearing turban: medium skin tone
|
||
+1F473 1F3FD 200D 2642 ; non-fully-qualified # 👳🏽♂ man wearing turban: medium skin tone
|
||
+1F473 1F3FE 200D 2642 FE0F ; fully-qualified # 👳🏾♂️ man wearing turban: medium-dark skin tone
|
||
+1F473 1F3FE 200D 2642 ; non-fully-qualified # 👳🏾♂ man wearing turban: medium-dark skin tone
|
||
+1F473 1F3FF 200D 2642 FE0F ; fully-qualified # 👳🏿♂️ man wearing turban: dark skin tone
|
||
+1F473 1F3FF 200D 2642 ; non-fully-qualified # 👳🏿♂ man wearing turban: dark skin tone
|
||
+1F473 200D 2640 FE0F ; fully-qualified # 👳♀️ woman wearing turban
|
||
+1F473 200D 2640 ; non-fully-qualified # 👳♀ woman wearing turban
|
||
+1F473 1F3FB 200D 2640 FE0F ; fully-qualified # 👳🏻♀️ woman wearing turban: light skin tone
|
||
+1F473 1F3FB 200D 2640 ; non-fully-qualified # 👳🏻♀ woman wearing turban: light skin tone
|
||
+1F473 1F3FC 200D 2640 FE0F ; fully-qualified # 👳🏼♀️ woman wearing turban: medium-light skin tone
|
||
+1F473 1F3FC 200D 2640 ; non-fully-qualified # 👳🏼♀ woman wearing turban: medium-light skin tone
|
||
+1F473 1F3FD 200D 2640 FE0F ; fully-qualified # 👳🏽♀️ woman wearing turban: medium skin tone
|
||
+1F473 1F3FD 200D 2640 ; non-fully-qualified # 👳🏽♀ woman wearing turban: medium skin tone
|
||
+1F473 1F3FE 200D 2640 FE0F ; fully-qualified # 👳🏾♀️ woman wearing turban: medium-dark skin tone
|
||
+1F473 1F3FE 200D 2640 ; non-fully-qualified # 👳🏾♀ woman wearing turban: medium-dark skin tone
|
||
+1F473 1F3FF 200D 2640 FE0F ; fully-qualified # 👳🏿♀️ woman wearing turban: dark skin tone
|
||
+1F473 1F3FF 200D 2640 ; non-fully-qualified # 👳🏿♀ woman wearing turban: dark skin tone
|
||
+1F472 ; fully-qualified # 👲 man with Chinese cap
|
||
+1F472 1F3FB ; fully-qualified # 👲🏻 man with Chinese cap: light skin tone
|
||
+1F472 1F3FC ; fully-qualified # 👲🏼 man with Chinese cap: medium-light skin tone
|
||
+1F472 1F3FD ; fully-qualified # 👲🏽 man with Chinese cap: medium skin tone
|
||
+1F472 1F3FE ; fully-qualified # 👲🏾 man with Chinese cap: medium-dark skin tone
|
||
+1F472 1F3FF ; fully-qualified # 👲🏿 man with Chinese cap: dark skin tone
|
||
+1F9D5 ; fully-qualified # 🧕 woman with headscarf
|
||
+1F9D5 1F3FB ; fully-qualified # 🧕🏻 woman with headscarf: light skin tone
|
||
+1F9D5 1F3FC ; fully-qualified # 🧕🏼 woman with headscarf: medium-light skin tone
|
||
+1F9D5 1F3FD ; fully-qualified # 🧕🏽 woman with headscarf: medium skin tone
|
||
+1F9D5 1F3FE ; fully-qualified # 🧕🏾 woman with headscarf: medium-dark skin tone
|
||
+1F9D5 1F3FF ; fully-qualified # 🧕🏿 woman with headscarf: dark skin tone
|
||
+1F9D4 ; fully-qualified # 🧔 bearded person
|
||
+1F9D4 1F3FB ; fully-qualified # 🧔🏻 bearded person: light skin tone
|
||
+1F9D4 1F3FC ; fully-qualified # 🧔🏼 bearded person: medium-light skin tone
|
||
+1F9D4 1F3FD ; fully-qualified # 🧔🏽 bearded person: medium skin tone
|
||
+1F9D4 1F3FE ; fully-qualified # 🧔🏾 bearded person: medium-dark skin tone
|
||
+1F9D4 1F3FF ; fully-qualified # 🧔🏿 bearded person: dark skin tone
|
||
+1F471 ; fully-qualified # 👱 blond-haired person
|
||
+1F471 1F3FB ; fully-qualified # 👱🏻 blond-haired person: light skin tone
|
||
+1F471 1F3FC ; fully-qualified # 👱🏼 blond-haired person: medium-light skin tone
|
||
+1F471 1F3FD ; fully-qualified # 👱🏽 blond-haired person: medium skin tone
|
||
+1F471 1F3FE ; fully-qualified # 👱🏾 blond-haired person: medium-dark skin tone
|
||
+1F471 1F3FF ; fully-qualified # 👱🏿 blond-haired person: dark skin tone
|
||
+1F471 200D 2642 FE0F ; fully-qualified # 👱♂️ blond-haired man
|
||
+1F471 200D 2642 ; non-fully-qualified # 👱♂ blond-haired man
|
||
+1F471 1F3FB 200D 2642 FE0F ; fully-qualified # 👱🏻♂️ blond-haired man: light skin tone
|
||
+1F471 1F3FB 200D 2642 ; non-fully-qualified # 👱🏻♂ blond-haired man: light skin tone
|
||
+1F471 1F3FC 200D 2642 FE0F ; fully-qualified # 👱🏼♂️ blond-haired man: medium-light skin tone
|
||
+1F471 1F3FC 200D 2642 ; non-fully-qualified # 👱🏼♂ blond-haired man: medium-light skin tone
|
||
+1F471 1F3FD 200D 2642 FE0F ; fully-qualified # 👱🏽♂️ blond-haired man: medium skin tone
|
||
+1F471 1F3FD 200D 2642 ; non-fully-qualified # 👱🏽♂ blond-haired man: medium skin tone
|
||
+1F471 1F3FE 200D 2642 FE0F ; fully-qualified # 👱🏾♂️ blond-haired man: medium-dark skin tone
|
||
+1F471 1F3FE 200D 2642 ; non-fully-qualified # 👱🏾♂ blond-haired man: medium-dark skin tone
|
||
+1F471 1F3FF 200D 2642 FE0F ; fully-qualified # 👱🏿♂️ blond-haired man: dark skin tone
|
||
+1F471 1F3FF 200D 2642 ; non-fully-qualified # 👱🏿♂ blond-haired man: dark skin tone
|
||
+1F471 200D 2640 FE0F ; fully-qualified # 👱♀️ blond-haired woman
|
||
+1F471 200D 2640 ; non-fully-qualified # 👱♀ blond-haired woman
|
||
+1F471 1F3FB 200D 2640 FE0F ; fully-qualified # 👱🏻♀️ blond-haired woman: light skin tone
|
||
+1F471 1F3FB 200D 2640 ; non-fully-qualified # 👱🏻♀ blond-haired woman: light skin tone
|
||
+1F471 1F3FC 200D 2640 FE0F ; fully-qualified # 👱🏼♀️ blond-haired woman: medium-light skin tone
|
||
+1F471 1F3FC 200D 2640 ; non-fully-qualified # 👱🏼♀ blond-haired woman: medium-light skin tone
|
||
+1F471 1F3FD 200D 2640 FE0F ; fully-qualified # 👱🏽♀️ blond-haired woman: medium skin tone
|
||
+1F471 1F3FD 200D 2640 ; non-fully-qualified # 👱🏽♀ blond-haired woman: medium skin tone
|
||
+1F471 1F3FE 200D 2640 FE0F ; fully-qualified # 👱🏾♀️ blond-haired woman: medium-dark skin tone
|
||
+1F471 1F3FE 200D 2640 ; non-fully-qualified # 👱🏾♀ blond-haired woman: medium-dark skin tone
|
||
+1F471 1F3FF 200D 2640 FE0F ; fully-qualified # 👱🏿♀️ blond-haired woman: dark skin tone
|
||
+1F471 1F3FF 200D 2640 ; non-fully-qualified # 👱🏿♀ blond-haired woman: dark skin tone
|
||
+1F468 200D 1F9B0 ; fully-qualified # 👨🦰 man, red haired
|
||
+1F468 1F3FB 200D 1F9B0 ; fully-qualified # 👨🏻🦰 man, red haired: light skin tone
|
||
+1F468 1F3FC 200D 1F9B0 ; fully-qualified # 👨🏼🦰 man, red haired: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F9B0 ; fully-qualified # 👨🏽🦰 man, red haired: medium skin tone
|
||
+1F468 1F3FE 200D 1F9B0 ; fully-qualified # 👨🏾🦰 man, red haired: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F9B0 ; fully-qualified # 👨🏿🦰 man, red haired: dark skin tone
|
||
+1F469 200D 1F9B0 ; fully-qualified # 👩🦰 woman, red haired
|
||
+1F469 1F3FB 200D 1F9B0 ; fully-qualified # 👩🏻🦰 woman, red haired: light skin tone
|
||
+1F469 1F3FC 200D 1F9B0 ; fully-qualified # 👩🏼🦰 woman, red haired: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F9B0 ; fully-qualified # 👩🏽🦰 woman, red haired: medium skin tone
|
||
+1F469 1F3FE 200D 1F9B0 ; fully-qualified # 👩🏾🦰 woman, red haired: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F9B0 ; fully-qualified # 👩🏿🦰 woman, red haired: dark skin tone
|
||
+1F468 200D 1F9B1 ; fully-qualified # 👨🦱 man, curly haired
|
||
+1F468 1F3FB 200D 1F9B1 ; fully-qualified # 👨🏻🦱 man, curly haired: light skin tone
|
||
+1F468 1F3FC 200D 1F9B1 ; fully-qualified # 👨🏼🦱 man, curly haired: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F9B1 ; fully-qualified # 👨🏽🦱 man, curly haired: medium skin tone
|
||
+1F468 1F3FE 200D 1F9B1 ; fully-qualified # 👨🏾🦱 man, curly haired: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F9B1 ; fully-qualified # 👨🏿🦱 man, curly haired: dark skin tone
|
||
+1F469 200D 1F9B1 ; fully-qualified # 👩🦱 woman, curly haired
|
||
+1F469 1F3FB 200D 1F9B1 ; fully-qualified # 👩🏻🦱 woman, curly haired: light skin tone
|
||
+1F469 1F3FC 200D 1F9B1 ; fully-qualified # 👩🏼🦱 woman, curly haired: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F9B1 ; fully-qualified # 👩🏽🦱 woman, curly haired: medium skin tone
|
||
+1F469 1F3FE 200D 1F9B1 ; fully-qualified # 👩🏾🦱 woman, curly haired: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F9B1 ; fully-qualified # 👩🏿🦱 woman, curly haired: dark skin tone
|
||
+1F468 200D 1F9B2 ; fully-qualified # 👨🦲 man, bald
|
||
+1F468 1F3FB 200D 1F9B2 ; fully-qualified # 👨🏻🦲 man, bald: light skin tone
|
||
+1F468 1F3FC 200D 1F9B2 ; fully-qualified # 👨🏼🦲 man, bald: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F9B2 ; fully-qualified # 👨🏽🦲 man, bald: medium skin tone
|
||
+1F468 1F3FE 200D 1F9B2 ; fully-qualified # 👨🏾🦲 man, bald: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F9B2 ; fully-qualified # 👨🏿🦲 man, bald: dark skin tone
|
||
+1F469 200D 1F9B2 ; fully-qualified # 👩🦲 woman, bald
|
||
+1F469 1F3FB 200D 1F9B2 ; fully-qualified # 👩🏻🦲 woman, bald: light skin tone
|
||
+1F469 1F3FC 200D 1F9B2 ; fully-qualified # 👩🏼🦲 woman, bald: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F9B2 ; fully-qualified # 👩🏽🦲 woman, bald: medium skin tone
|
||
+1F469 1F3FE 200D 1F9B2 ; fully-qualified # 👩🏾🦲 woman, bald: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F9B2 ; fully-qualified # 👩🏿🦲 woman, bald: dark skin tone
|
||
+1F468 200D 1F9B3 ; fully-qualified # 👨🦳 man, white haired
|
||
+1F468 1F3FB 200D 1F9B3 ; fully-qualified # 👨🏻🦳 man, white haired: light skin tone
|
||
+1F468 1F3FC 200D 1F9B3 ; fully-qualified # 👨🏼🦳 man, white haired: medium-light skin tone
|
||
+1F468 1F3FD 200D 1F9B3 ; fully-qualified # 👨🏽🦳 man, white haired: medium skin tone
|
||
+1F468 1F3FE 200D 1F9B3 ; fully-qualified # 👨🏾🦳 man, white haired: medium-dark skin tone
|
||
+1F468 1F3FF 200D 1F9B3 ; fully-qualified # 👨🏿🦳 man, white haired: dark skin tone
|
||
+1F469 200D 1F9B3 ; fully-qualified # 👩🦳 woman, white haired
|
||
+1F469 1F3FB 200D 1F9B3 ; fully-qualified # 👩🏻🦳 woman, white haired: light skin tone
|
||
+1F469 1F3FC 200D 1F9B3 ; fully-qualified # 👩🏼🦳 woman, white haired: medium-light skin tone
|
||
+1F469 1F3FD 200D 1F9B3 ; fully-qualified # 👩🏽🦳 woman, white haired: medium skin tone
|
||
+1F469 1F3FE 200D 1F9B3 ; fully-qualified # 👩🏾🦳 woman, white haired: medium-dark skin tone
|
||
+1F469 1F3FF 200D 1F9B3 ; fully-qualified # 👩🏿🦳 woman, white haired: dark skin tone
|
||
+1F935 ; fully-qualified # 🤵 man in tuxedo
|
||
+1F935 1F3FB ; fully-qualified # 🤵🏻 man in tuxedo: light skin tone
|
||
+1F935 1F3FC ; fully-qualified # 🤵🏼 man in tuxedo: medium-light skin tone
|
||
+1F935 1F3FD ; fully-qualified # 🤵🏽 man in tuxedo: medium skin tone
|
||
+1F935 1F3FE ; fully-qualified # 🤵🏾 man in tuxedo: medium-dark skin tone
|
||
+1F935 1F3FF ; fully-qualified # 🤵🏿 man in tuxedo: dark skin tone
|
||
+1F470 ; fully-qualified # 👰 bride with veil
|
||
+1F470 1F3FB ; fully-qualified # 👰🏻 bride with veil: light skin tone
|
||
+1F470 1F3FC ; fully-qualified # 👰🏼 bride with veil: medium-light skin tone
|
||
+1F470 1F3FD ; fully-qualified # 👰🏽 bride with veil: medium skin tone
|
||
+1F470 1F3FE ; fully-qualified # 👰🏾 bride with veil: medium-dark skin tone
|
||
+1F470 1F3FF ; fully-qualified # 👰🏿 bride with veil: dark skin tone
|
||
+1F930 ; fully-qualified # 🤰 pregnant woman
|
||
+1F930 1F3FB ; fully-qualified # 🤰🏻 pregnant woman: light skin tone
|
||
+1F930 1F3FC ; fully-qualified # 🤰🏼 pregnant woman: medium-light skin tone
|
||
+1F930 1F3FD ; fully-qualified # 🤰🏽 pregnant woman: medium skin tone
|
||
+1F930 1F3FE ; fully-qualified # 🤰🏾 pregnant woman: medium-dark skin tone
|
||
+1F930 1F3FF ; fully-qualified # 🤰🏿 pregnant woman: dark skin tone
|
||
+1F931 ; fully-qualified # 🤱 breast-feeding
|
||
+1F931 1F3FB ; fully-qualified # 🤱🏻 breast-feeding: light skin tone
|
||
+1F931 1F3FC ; fully-qualified # 🤱🏼 breast-feeding: medium-light skin tone
|
||
+1F931 1F3FD ; fully-qualified # 🤱🏽 breast-feeding: medium skin tone
|
||
+1F931 1F3FE ; fully-qualified # 🤱🏾 breast-feeding: medium-dark skin tone
|
||
+1F931 1F3FF ; fully-qualified # 🤱🏿 breast-feeding: dark skin tone
|
||
+
|
||
+# subgroup: person-fantasy
|
||
+1F47C ; fully-qualified # 👼 baby angel
|
||
+1F47C 1F3FB ; fully-qualified # 👼🏻 baby angel: light skin tone
|
||
+1F47C 1F3FC ; fully-qualified # 👼🏼 baby angel: medium-light skin tone
|
||
+1F47C 1F3FD ; fully-qualified # 👼🏽 baby angel: medium skin tone
|
||
+1F47C 1F3FE ; fully-qualified # 👼🏾 baby angel: medium-dark skin tone
|
||
+1F47C 1F3FF ; fully-qualified # 👼🏿 baby angel: dark skin tone
|
||
+1F385 ; fully-qualified # 🎅 Santa Claus
|
||
+1F385 1F3FB ; fully-qualified # 🎅🏻 Santa Claus: light skin tone
|
||
+1F385 1F3FC ; fully-qualified # 🎅🏼 Santa Claus: medium-light skin tone
|
||
+1F385 1F3FD ; fully-qualified # 🎅🏽 Santa Claus: medium skin tone
|
||
+1F385 1F3FE ; fully-qualified # 🎅🏾 Santa Claus: medium-dark skin tone
|
||
+1F385 1F3FF ; fully-qualified # 🎅🏿 Santa Claus: dark skin tone
|
||
+1F936 ; fully-qualified # 🤶 Mrs. Claus
|
||
+1F936 1F3FB ; fully-qualified # 🤶🏻 Mrs. Claus: light skin tone
|
||
+1F936 1F3FC ; fully-qualified # 🤶🏼 Mrs. Claus: medium-light skin tone
|
||
+1F936 1F3FD ; fully-qualified # 🤶🏽 Mrs. Claus: medium skin tone
|
||
+1F936 1F3FE ; fully-qualified # 🤶🏾 Mrs. Claus: medium-dark skin tone
|
||
+1F936 1F3FF ; fully-qualified # 🤶🏿 Mrs. Claus: dark skin tone
|
||
+1F9B8 ; fully-qualified # 🦸 superhero
|
||
+1F9B8 1F3FB ; fully-qualified # 🦸🏻 superhero: light skin tone
|
||
+1F9B8 1F3FC ; fully-qualified # 🦸🏼 superhero: medium-light skin tone
|
||
+1F9B8 1F3FD ; fully-qualified # 🦸🏽 superhero: medium skin tone
|
||
+1F9B8 1F3FE ; fully-qualified # 🦸🏾 superhero: medium-dark skin tone
|
||
+1F9B8 1F3FF ; fully-qualified # 🦸🏿 superhero: dark skin tone
|
||
+1F9B8 200D 2640 FE0F ; fully-qualified # 🦸♀️ woman superhero
|
||
+1F9B8 200D 2640 ; non-fully-qualified # 🦸♀ woman superhero
|
||
+1F9B8 1F3FB 200D 2640 FE0F ; fully-qualified # 🦸🏻♀️ woman superhero: light skin tone
|
||
+1F9B8 1F3FB 200D 2640 ; non-fully-qualified # 🦸🏻♀ woman superhero: light skin tone
|
||
+1F9B8 1F3FC 200D 2640 FE0F ; fully-qualified # 🦸🏼♀️ woman superhero: medium-light skin tone
|
||
+1F9B8 1F3FC 200D 2640 ; non-fully-qualified # 🦸🏼♀ woman superhero: medium-light skin tone
|
||
+1F9B8 1F3FD 200D 2640 FE0F ; fully-qualified # 🦸🏽♀️ woman superhero: medium skin tone
|
||
+1F9B8 1F3FD 200D 2640 ; non-fully-qualified # 🦸🏽♀ woman superhero: medium skin tone
|
||
+1F9B8 1F3FE 200D 2640 FE0F ; fully-qualified # 🦸🏾♀️ woman superhero: medium-dark skin tone
|
||
+1F9B8 1F3FE 200D 2640 ; non-fully-qualified # 🦸🏾♀ woman superhero: medium-dark skin tone
|
||
+1F9B8 1F3FF 200D 2640 FE0F ; fully-qualified # 🦸🏿♀️ woman superhero: dark skin tone
|
||
+1F9B8 1F3FF 200D 2640 ; non-fully-qualified # 🦸🏿♀ woman superhero: dark skin tone
|
||
+1F9B8 200D 2642 FE0F ; fully-qualified # 🦸♂️ man superhero
|
||
+1F9B8 200D 2642 ; non-fully-qualified # 🦸♂ man superhero
|
||
+1F9B8 1F3FB 200D 2642 FE0F ; fully-qualified # 🦸🏻♂️ man superhero: light skin tone
|
||
+1F9B8 1F3FB 200D 2642 ; non-fully-qualified # 🦸🏻♂ man superhero: light skin tone
|
||
+1F9B8 1F3FC 200D 2642 FE0F ; fully-qualified # 🦸🏼♂️ man superhero: medium-light skin tone
|
||
+1F9B8 1F3FC 200D 2642 ; non-fully-qualified # 🦸🏼♂ man superhero: medium-light skin tone
|
||
+1F9B8 1F3FD 200D 2642 FE0F ; fully-qualified # 🦸🏽♂️ man superhero: medium skin tone
|
||
+1F9B8 1F3FD 200D 2642 ; non-fully-qualified # 🦸🏽♂ man superhero: medium skin tone
|
||
+1F9B8 1F3FE 200D 2642 FE0F ; fully-qualified # 🦸🏾♂️ man superhero: medium-dark skin tone
|
||
+1F9B8 1F3FE 200D 2642 ; non-fully-qualified # 🦸🏾♂ man superhero: medium-dark skin tone
|
||
+1F9B8 1F3FF 200D 2642 FE0F ; fully-qualified # 🦸🏿♂️ man superhero: dark skin tone
|
||
+1F9B8 1F3FF 200D 2642 ; non-fully-qualified # 🦸🏿♂ man superhero: dark skin tone
|
||
+1F9B9 ; fully-qualified # 🦹 supervillain
|
||
+1F9B9 1F3FB ; fully-qualified # 🦹🏻 supervillain: light skin tone
|
||
+1F9B9 1F3FC ; fully-qualified # 🦹🏼 supervillain: medium-light skin tone
|
||
+1F9B9 1F3FD ; fully-qualified # 🦹🏽 supervillain: medium skin tone
|
||
+1F9B9 1F3FE ; fully-qualified # 🦹🏾 supervillain: medium-dark skin tone
|
||
+1F9B9 1F3FF ; fully-qualified # 🦹🏿 supervillain: dark skin tone
|
||
+1F9B9 200D 2640 FE0F ; fully-qualified # 🦹♀️ woman supervillain
|
||
+1F9B9 200D 2640 ; non-fully-qualified # 🦹♀ woman supervillain
|
||
+1F9B9 1F3FB 200D 2640 FE0F ; fully-qualified # 🦹🏻♀️ woman supervillain: light skin tone
|
||
+1F9B9 1F3FB 200D 2640 ; non-fully-qualified # 🦹🏻♀ woman supervillain: light skin tone
|
||
+1F9B9 1F3FC 200D 2640 FE0F ; fully-qualified # 🦹🏼♀️ woman supervillain: medium-light skin tone
|
||
+1F9B9 1F3FC 200D 2640 ; non-fully-qualified # 🦹🏼♀ woman supervillain: medium-light skin tone
|
||
+1F9B9 1F3FD 200D 2640 FE0F ; fully-qualified # 🦹🏽♀️ woman supervillain: medium skin tone
|
||
+1F9B9 1F3FD 200D 2640 ; non-fully-qualified # 🦹🏽♀ woman supervillain: medium skin tone
|
||
+1F9B9 1F3FE 200D 2640 FE0F ; fully-qualified # 🦹🏾♀️ woman supervillain: medium-dark skin tone
|
||
+1F9B9 1F3FE 200D 2640 ; non-fully-qualified # 🦹🏾♀ woman supervillain: medium-dark skin tone
|
||
+1F9B9 1F3FF 200D 2640 FE0F ; fully-qualified # 🦹🏿♀️ woman supervillain: dark skin tone
|
||
+1F9B9 1F3FF 200D 2640 ; non-fully-qualified # 🦹🏿♀ woman supervillain: dark skin tone
|
||
+1F9B9 200D 2642 FE0F ; fully-qualified # 🦹♂️ man supervillain
|
||
+1F9B9 200D 2642 ; non-fully-qualified # 🦹♂ man supervillain
|
||
+1F9B9 1F3FB 200D 2642 FE0F ; fully-qualified # 🦹🏻♂️ man supervillain: light skin tone
|
||
+1F9B9 1F3FB 200D 2642 ; non-fully-qualified # 🦹🏻♂ man supervillain: light skin tone
|
||
+1F9B9 1F3FC 200D 2642 FE0F ; fully-qualified # 🦹🏼♂️ man supervillain: medium-light skin tone
|
||
+1F9B9 1F3FC 200D 2642 ; non-fully-qualified # 🦹🏼♂ man supervillain: medium-light skin tone
|
||
+1F9B9 1F3FD 200D 2642 FE0F ; fully-qualified # 🦹🏽♂️ man supervillain: medium skin tone
|
||
+1F9B9 1F3FD 200D 2642 ; non-fully-qualified # 🦹🏽♂ man supervillain: medium skin tone
|
||
+1F9B9 1F3FE 200D 2642 FE0F ; fully-qualified # 🦹🏾♂️ man supervillain: medium-dark skin tone
|
||
+1F9B9 1F3FE 200D 2642 ; non-fully-qualified # 🦹🏾♂ man supervillain: medium-dark skin tone
|
||
+1F9B9 1F3FF 200D 2642 FE0F ; fully-qualified # 🦹🏿♂️ man supervillain: dark skin tone
|
||
+1F9B9 1F3FF 200D 2642 ; non-fully-qualified # 🦹🏿♂ man supervillain: dark skin tone
|
||
+1F9D9 ; fully-qualified # 🧙 mage
|
||
+1F9D9 1F3FB ; fully-qualified # 🧙🏻 mage: light skin tone
|
||
+1F9D9 1F3FC ; fully-qualified # 🧙🏼 mage: medium-light skin tone
|
||
+1F9D9 1F3FD ; fully-qualified # 🧙🏽 mage: medium skin tone
|
||
+1F9D9 1F3FE ; fully-qualified # 🧙🏾 mage: medium-dark skin tone
|
||
+1F9D9 1F3FF ; fully-qualified # 🧙🏿 mage: dark skin tone
|
||
+1F9D9 200D 2640 FE0F ; fully-qualified # 🧙♀️ woman mage
|
||
+1F9D9 200D 2640 ; non-fully-qualified # 🧙♀ woman mage
|
||
+1F9D9 1F3FB 200D 2640 FE0F ; fully-qualified # 🧙🏻♀️ woman mage: light skin tone
|
||
+1F9D9 1F3FB 200D 2640 ; non-fully-qualified # 🧙🏻♀ woman mage: light skin tone
|
||
+1F9D9 1F3FC 200D 2640 FE0F ; fully-qualified # 🧙🏼♀️ woman mage: medium-light skin tone
|
||
|