Shape the font for selecting multiple emoji fonts. am: 31aebbf1bc

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/minikin/+/14485567

Change-Id: Ifd24883631fc595e456cf1191339acfa7600a61e
diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h
index de18ab5..98df571 100644
--- a/include/minikin/FontCollection.h
+++ b/include/minikin/FontCollection.h
@@ -75,12 +75,99 @@
         }
     }
 
+    // Helper class for representing font family match result in packed bits.
+    struct FamilyMatchResult {
+    public:
+        struct Builder {
+        public:
+            Builder() : mSize(0), mBits(0) {}
+
+            Builder& add(uint8_t x) {
+                if (mSize >= 7) [[unlikely]] {
+                        return *this;
+                    }
+                mBits = mBits | (static_cast<uint64_t>(x) << (8 * mSize));
+                mSize++;
+                return *this;
+            }
+
+            Builder& reset() {
+                mSize = 0;
+                mBits = 0;
+                return *this;
+            }
+
+            uint8_t size() const { return mSize; }
+
+            bool empty() const { return size() == 0; }
+
+            FamilyMatchResult build() {
+                return FamilyMatchResult(mBits | (static_cast<uint64_t>(mSize) << 56));
+            }
+
+        private:
+            uint8_t mSize;
+            uint64_t mBits;
+        };
+
+        // Helper class for iterating FamilyMatchResult
+        class iterator {
+        public:
+            inline bool operator==(const iterator& o) const {
+                return mOffset == o.mOffset && mResult == o.mResult;
+            }
+
+            inline bool operator!=(const iterator& o) const { return !(*this == o); }
+            inline uint8_t operator*() const { return mResult[mOffset]; }
+            inline iterator& operator++() {
+                mOffset++;
+                return *this;
+            }
+
+        private:
+            friend struct FamilyMatchResult;
+            iterator(const FamilyMatchResult& result, uint32_t offset)
+                    : mResult(result), mOffset(offset) {}
+            const FamilyMatchResult& mResult;
+            uint32_t mOffset;
+        };
+
+        // Create empty FamilyMatchResult.
+        FamilyMatchResult() : mBits(0) {}
+
+        inline uint8_t size() const { return static_cast<uint8_t>(mBits >> 56); }
+
+        inline uint8_t operator[](uint32_t pos) const {
+            return static_cast<uint8_t>(mBits >> (pos * 8));
+        }
+
+        inline bool empty() const { return size() == 0; }
+
+        inline bool operator==(const FamilyMatchResult& o) const { return mBits == o.mBits; }
+
+        // Returns the common family indices between l and r.
+        static FamilyMatchResult intersect(FamilyMatchResult l, FamilyMatchResult r);
+
+        // Iterator
+        inline iterator begin() const { return iterator(*this, 0); }
+        inline iterator end() const { return iterator(*this, size()); }
+
+        FamilyMatchResult(const FamilyMatchResult& o) = default;
+        FamilyMatchResult& operator=(const FamilyMatchResult& o) = default;
+
+    private:
+        explicit FamilyMatchResult(uint64_t bits) : mBits(bits) {}
+        uint64_t mBits;
+    };
+
     struct Run {
-        FakedFont fakedFont;
+        FamilyMatchResult familyMatch;
         int start;
         int end;
     };
 
+    FakedFont getBestFont(U16StringPiece textBuf, const Run& run, FontStyle style);
+
     // Perform the itemization until given max runs.
     std::vector<Run> itemize(U16StringPiece text, FontStyle style, uint32_t localeListId,
                              FamilyVariant familyVariant, uint32_t runMax) const;
@@ -141,9 +228,8 @@
     // Initialize the FontCollection.
     void init(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
 
-    const std::shared_ptr<FontFamily>& getFamilyForChar(uint32_t ch, uint32_t vs,
-                                                        uint32_t localeListId,
-                                                        FamilyVariant variant) const;
+    FamilyMatchResult getFamilyForChar(uint32_t ch, uint32_t vs, uint32_t localeListId,
+                                       FamilyVariant variant) const;
 
     uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, FamilyVariant variant, uint32_t localeListId,
                              const std::shared_ptr<FontFamily>& fontFamily) const;
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index a8b96c3..9e4dd3b 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -24,6 +24,7 @@
 #include <unicode/unorm2.h>
 
 #include "minikin/Emoji.h"
+#include "minikin/FontFileParser.h"
 
 #include "Locale.h"
 #include "LocaleListCache.h"
@@ -43,6 +44,23 @@
 
 static std::atomic<uint32_t> gNextCollectionId = {0};
 
+namespace {
+
+uint32_t getGlyphCount(U16StringPiece text, uint32_t start, uint32_t end,
+                       const HbFontUniquePtr& font) {
+    HbBufferUniquePtr buffer(hb_buffer_create());
+    hb_buffer_set_direction(buffer.get(), HB_DIRECTION_LTR);
+    hb_buffer_add_utf16(buffer.get(), text.data() + start, end - start, 0, end - start);
+    hb_buffer_guess_segment_properties(buffer.get());
+
+    unsigned int numGlyphs = -1;
+    hb_shape(font.get(), buffer.get(), nullptr, 0);
+    hb_buffer_get_glyph_infos(buffer.get(), &numGlyphs);
+    return numGlyphs;
+}
+
+}  // namespace
+
 FontCollection::FontCollection(std::shared_ptr<FontFamily>&& typeface) : mMaxChar(0) {
     std::vector<std::shared_ptr<FontFamily>> typefaces;
     typefaces.push_back(typeface);
@@ -316,11 +334,11 @@
 // 2. Calculate a score for the font family. See comments in calcFamilyScore for the detail.
 // 3. Highest score wins, with ties resolved to the first font.
 // This method never returns nullptr.
-const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
-                                                                    uint32_t localeListId,
-                                                                    FamilyVariant variant) const {
+FontCollection::FamilyMatchResult FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
+                                                                   uint32_t localeListId,
+                                                                   FamilyVariant variant) const {
     if (ch >= mMaxChar) {
-        return mFamilies[0];
+        return FamilyMatchResult::Builder().add(0).build();
     }
 
     Range range = mRanges[ch >> kLogCharsPerPage];
@@ -329,23 +347,27 @@
         range = {0, static_cast<uint16_t>(mFamilies.size())};
     }
 
-    int bestFamilyIndex = -1;
     uint32_t bestScore = kUnsupportedFontScore;
+    FamilyMatchResult::Builder builder;
+
     for (size_t i = range.start; i < range.end; i++) {
-        const std::shared_ptr<FontFamily>& family =
-                vs == 0 ? mFamilies[mFamilyVec[i]] : mFamilies[i];
+        const uint8_t familyIndex = vs == 0 ? mFamilyVec[i] : i;
+        const std::shared_ptr<FontFamily>& family = mFamilies[familyIndex];
         const uint32_t score = calcFamilyScore(ch, vs, variant, localeListId, family);
         if (score == kFirstFontScore) {
             // If the first font family supports the given character or variation sequence, always
             // use it.
-            return family;
+            return builder.add(familyIndex).build();
         }
-        if (score > bestScore) {
-            bestScore = score;
-            bestFamilyIndex = i;
+        if (score != kUnsupportedFontScore && score >= bestScore) {
+            if (score > bestScore) {
+                builder.reset();
+                bestScore = score;
+            }
+            builder.add(familyIndex);
         }
     }
-    if (bestFamilyIndex == -1) {
+    if (builder.empty()) {
         UErrorCode errorCode = U_ZERO_ERROR;
         const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
         if (U_SUCCESS(errorCode)) {
@@ -357,9 +379,9 @@
                 return getFamilyForChar(ch, vs, localeListId, variant);
             }
         }
-        return mFamilies[0];
+        return FamilyMatchResult::Builder().add(0).build();
     }
-    return vs == 0 ? mFamilies[mFamilyVec[bestFamilyIndex]] : mFamilies[bestFamilyIndex];
+    return builder.build();
 }
 
 // Characters where we want to continue using existing font run for (or stick to the next run if
@@ -379,7 +401,7 @@
 
 // Characters where we want to continue using existing font run instead of
 // recomputing the best match in the fallback list.
-static const uint32_t stickyWhitelist[] = {
+static const uint32_t stickyAllowlist[] = {
         '!',    ',', '-', '.', ':', ';', '?',
         0x00A0,  // NBSP
         0x2010,  // HYPHEN
@@ -390,9 +412,9 @@
         0x2695,  // STAFF_OF_AESCULAPIUS
 };
 
-static bool isStickyWhitelisted(uint32_t c) {
-    for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) {
-        if (stickyWhitelist[i] == c) return true;
+static bool isStickyAllowlisted(uint32_t c) {
+    for (size_t i = 0; i < sizeof(stickyAllowlist) / sizeof(stickyAllowlist[0]); i++) {
+        if (stickyAllowlist[i] == c) return true;
     }
     return false;
 }
@@ -435,22 +457,45 @@
 
 constexpr uint32_t REPLACEMENT_CHARACTER = 0xFFFD;
 
-std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, FontStyle style,
+FontCollection::FamilyMatchResult FontCollection::FamilyMatchResult::intersect(
+        FontCollection::FamilyMatchResult l, FontCollection::FamilyMatchResult r) {
+    if (l == r) {
+        return l;
+    }
+
+    uint32_t li = 0;
+    uint32_t ri = 0;
+    FamilyMatchResult::Builder b;
+    while (li < l.size() && ri < r.size()) {
+        if (l[li] < r[ri]) {
+            li++;
+        } else if (l[li] > r[ri]) {
+            ri++;
+        } else {  // l[li] == r[ri]
+            b.add(l[li]);
+            li++;
+            ri++;
+        }
+    }
+    return b.build();
+}
+
+std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, FontStyle,
                                                          uint32_t localeListId,
                                                          FamilyVariant familyVariant,
                                                          uint32_t runMax) const {
     const uint16_t* string = text.data();
     const uint32_t string_size = text.size();
-    std::vector<Run> result;
 
-    const FontFamily* lastFamily = nullptr;
-    Run* run = nullptr;
+    FamilyMatchResult lastFamilyIndices = FamilyMatchResult();
 
     if (string_size == 0) {
-        return result;
+        return std::vector<Run>();
     }
 
     const uint32_t kEndOfString = 0xFFFFFFFF;
+    std::vector<Run> result;
+    Run* run = nullptr;
 
     uint32_t nextCh = 0;
     uint32_t prevCh = 0;
@@ -478,15 +523,44 @@
         if (doesNotNeedFontSupport(ch)) {
             // Always continue if the character is a format character not needed to be in the font.
             shouldContinueRun = true;
-        } else if (lastFamily != nullptr && (isStickyWhitelisted(ch) || isCombining(ch))) {
+        } else if (!lastFamilyIndices.empty() && (isStickyAllowlisted(ch) || isCombining(ch))) {
             // Continue using existing font as long as it has coverage and is whitelisted.
-            shouldContinueRun = lastFamily->getCoverage().get(ch);
+
+            const std::shared_ptr<FontFamily>& lastFamily = mFamilies[lastFamilyIndices[0]];
+            if (lastFamily->isColorEmojiFamily()) {
+                // If the last family is color emoji font, find the longest family.
+                shouldContinueRun = false;
+                for (uint8_t ix : lastFamilyIndices) {
+                    shouldContinueRun |= mFamilies[ix]->getCoverage().get(ch);
+                }
+            } else {
+                shouldContinueRun = lastFamily->getCoverage().get(ch);
+            }
         }
 
         if (!shouldContinueRun) {
-            const std::shared_ptr<FontFamily>& family = getFamilyForChar(
+            FamilyMatchResult familyIndices = getFamilyForChar(
                     ch, isVariationSelector(nextCh) ? nextCh : 0, localeListId, familyVariant);
-            if (utf16Pos == 0 || family.get() != lastFamily) {
+            bool breakRun;
+            if (utf16Pos == 0 || lastFamilyIndices.empty()) {
+                breakRun = true;
+            } else {
+                const std::shared_ptr<FontFamily>& lastFamily = mFamilies[lastFamilyIndices[0]];
+                if (lastFamily->isColorEmojiFamily()) {
+                    FamilyMatchResult intersection =
+                            FamilyMatchResult::intersect(familyIndices, lastFamilyIndices);
+                    if (intersection.empty()) {
+                        breakRun = true;  // None of last family can draw the given char.
+                    } else {
+                        lastFamilyIndices = intersection;
+                        breakRun = false;
+                    }
+                } else {
+                    breakRun = familyIndices[0] != lastFamilyIndices[0];
+                }
+            }
+
+            if (breakRun) {
                 size_t start = utf16Pos;
                 // Workaround for combining marks and emoji modifiers until we implement
                 // per-cluster font selection: if a combining mark or an emoji modifier is found in
@@ -494,27 +568,31 @@
                 // character to the new run. U+20E3 COMBINING ENCLOSING KEYCAP, used in emoji, is
                 // handled properly by this since it's a combining mark too.
                 if (utf16Pos != 0 &&
-                    (isCombining(ch) || (isEmojiModifier(ch) && isEmojiBase(prevCh))) &&
-                    family != nullptr && family->getCoverage().get(prevCh)) {
-                    const size_t prevChLength = U16_LENGTH(prevCh);
-                    if (run != nullptr) {
-                        run->end -= prevChLength;
-                        if (run->start == run->end) {
-                            result.pop_back();
+                    (isCombining(ch) || (isEmojiModifier(ch) && isEmojiBase(prevCh)))) {
+                    for (uint8_t ix : familyIndices) {
+                        if (mFamilies[ix]->getCoverage().get(prevCh)) {
+                            const size_t prevChLength = U16_LENGTH(prevCh);
+                            if (run != nullptr) {
+                                run->end -= prevChLength;
+                                if (run->start == run->end) {
+                                    result.pop_back();
+                                }
+                            }
+                            start -= prevChLength;
+                            break;
                         }
                     }
-                    start -= prevChLength;
                 }
-                if (lastFamily == nullptr) {
+                if (lastFamilyIndices.empty()) {
                     // This is the first family ever assigned. We are either seeing the very first
                     // character (which means start would already be zero), or we have only seen
                     // characters that don't need any font support (which means we need to adjust
                     // start to be 0 to include those characters).
                     start = 0;
                 }
-                result.push_back({family->getClosestMatch(style), static_cast<int>(start), 0});
+                result.push_back({familyIndices, static_cast<int>(start), 0});
                 run = &result.back();
-                lastFamily = family.get();
+                lastFamilyIndices = run->familyMatch;
             }
         }
         prevCh = ch;
@@ -532,19 +610,42 @@
         }
     } while (nextCh != kEndOfString);
 
-    if (lastFamily == nullptr) {
+    if (lastFamilyIndices.empty()) {
         // No character needed any font support, so it doesn't really matter which font they end up
         // getting displayed in. We put the whole string in one run, using the first font.
-        result.push_back({mFamilies[0]->getClosestMatch(style), 0, static_cast<int>(string_size)});
+        result.push_back(
+                {FamilyMatchResult::Builder().add(0).build(), 0, static_cast<int>(string_size)});
     }
 
     if (result.size() > runMax) {
         // The itemization has terminated since it reaches the runMax. Remove last unfinalized runs.
         return std::vector<Run>(result.begin(), result.begin() + runMax);
     }
+
     return result;
 }
 
+FakedFont FontCollection::getBestFont(U16StringPiece text, const Run& run, FontStyle style) {
+    uint8_t bestIndex = 0;
+    uint32_t bestGlyphCount = 0xFFFFFFFF;
+
+    const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[0]];
+    if (family->isColorEmojiFamily() && run.familyMatch.size() > 1) {
+        for (size_t i = 0; i < run.familyMatch.size(); ++i) {
+            const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[i]];
+            const HbFontUniquePtr& font = family->getFont(0)->baseFont();
+            uint32_t glyphCount = getGlyphCount(text, run.start, run.end, font);
+            if (glyphCount < bestGlyphCount) {
+                bestIndex = run.familyMatch[i];
+                bestGlyphCount = glyphCount;
+            }
+        }
+    } else {
+        bestIndex = run.familyMatch[0];
+    }
+    return mFamilies[bestIndex]->getClosestMatch(style);
+}
+
 FakedFont FontCollection::baseFontFaked(FontStyle style) {
     return mFamilies[0]->getClosestMatch(style);
 }
diff --git a/libs/minikin/LayoutCore.cpp b/libs/minikin/LayoutCore.cpp
index 03c72b2..f1d0de8 100644
--- a/libs/minikin/LayoutCore.cpp
+++ b/libs/minikin/LayoutCore.cpp
@@ -355,8 +355,9 @@
     mPoints.reserve(count);
 
     HbBufferUniquePtr buffer(hb_buffer_create());
-    std::vector<FontCollection::Run> items = paint.font->itemize(
-            textBuf.substr(range), paint.fontStyle, paint.localeListId, paint.familyVariant);
+    U16StringPiece substr = textBuf.substr(range);
+    std::vector<FontCollection::Run> items =
+            paint.font->itemize(substr, paint.fontStyle, paint.localeListId, paint.familyVariant);
 
     std::vector<hb_feature_t> features;
     // Disable default-on non-required ligature features if letter-spacing
@@ -384,7 +385,7 @@
          isRtl ? run_ix >= 0 : run_ix < static_cast<int>(items.size());
          isRtl ? --run_ix : ++run_ix) {
         FontCollection::Run& run = items[run_ix];
-        const FakedFont& fakedFont = run.fakedFont;
+        FakedFont fakedFont = paint.font->getBestFont(substr, run, paint.fontStyle);
         auto it = fontMap.find(fakedFont.font.get());
         uint8_t font_ix;
         if (it == fontMap.end()) {
diff --git a/tests/Android.bp b/tests/Android.bp
index cb05901..3088ca4 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -15,6 +15,7 @@
         "data/ColorTextMixedEmojiFont.ttf",
         "data/CustomExtent.ttf",
         "data/Emoji.ttf",
+        "data/EmojiBase.ttf",
         "data/Hiragana.ttf",
         "data/Italic.ttf",
         "data/Ja.ttf",
@@ -24,6 +25,7 @@
         "data/MultiAxis.ttf",
         "data/NoCmapFormat14.ttf",
         "data/NoGlyphFont.ttf",
+        "data/OverrideEmoji.ttf",
         "data/Regular.ttf",
         "data/TextEmojiFont.ttf",
         "data/UnicodeBMPOnly.ttf",
@@ -33,6 +35,7 @@
         "data/ZhHans.ttf",
         "data/ZhHant.ttf",
         "data/emoji.xml",
+        "data/emoji_itemization.xml",
         "data/itemize.xml",
     ],
 }
diff --git a/tests/data/EmojiBase.ttf b/tests/data/EmojiBase.ttf
new file mode 100644
index 0000000..2e464f7
--- /dev/null
+++ b/tests/data/EmojiBase.ttf
Binary files differ
diff --git a/tests/data/EmojiBase.ttx b/tests/data/EmojiBase.ttx
new file mode 100644
index 0000000..d33a2c0
--- /dev/null
+++ b/tests/data/EmojiBase.ttx
@@ -0,0 +1,485 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+
+    <!-- Compbining characters for Emoji -->
+    <GlyphID id="1" name="U+200D"/>
+    <GlyphID id="2" name="U+1F3FB"/>
+    <GlyphID id="3" name="U+1F3FC"/>
+
+    <!-- Random Emoji -->
+    <GlyphID id="4" name="U+1F9B0"/>
+    <GlyphID id="5" name="U+1F9B1"/>
+    <GlyphID id="6" name="U+1F9B2"/>
+    <GlyphID id="7" name="U+1F9B3"/>
+    <GlyphID id="8" name="U+1F9B4"/>
+    <GlyphID id="9" name="U+1F9B5"/>
+    <GlyphID id="10" name="U+1F9B6"/>
+    <GlyphID id="11" name="U+1F9B7"/>
+    <GlyphID id="12" name="U+1F9B8"/>
+    <GlyphID id="13" name="U+1F9B9"/>
+
+    <!-- Unassigned Code Points -->
+    <GlyphID id="14" name="U+E0000"/>
+    <GlyphID id="15" name="U+E0001"/>
+
+    <!-- Ligature Form. Not in cmap -->
+    <GlyphID id="16" name="LigaForm1"/>
+    <GlyphID id="17" name="LigaForm2"/>
+    <GlyphID id="18" name="LigaForm3"/>
+    <GlyphID id="19" name="LigaForm4"/>
+    <GlyphID id="20" name="LigaForm5"/>
+
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep  9 08:01:17 2015"/>
+    <modified value="Wed Sep  9 08:48:07 2015"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="12"/>
+    <maxStorage value="28"/>
+    <maxFunctionDefs value="119"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="61"/>
+    <maxSizeOfInstructions value="2967"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="U+200D" width="500" lsb="93"/>
+    <mtx name="U+1F3FB" width="500" lsb="93"/>
+    <mtx name="U+1F3FC" width="500" lsb="93"/>
+    <mtx name="U+1F9B0" width="500" lsb="93"/>
+    <mtx name="U+1F9B1" width="500" lsb="93"/>
+    <mtx name="U+1F9B2" width="500" lsb="93"/>
+    <mtx name="U+1F9B3" width="500" lsb="93"/>
+    <mtx name="U+1F9B4" width="500" lsb="93"/>
+    <mtx name="U+1F9B5" width="500" lsb="93"/>
+    <mtx name="U+1F9B6" width="500" lsb="93"/>
+    <mtx name="U+1F9B7" width="500" lsb="93"/>
+    <mtx name="U+1F9B8" width="500" lsb="93"/>
+    <mtx name="U+1F9B9" width="500" lsb="93"/>
+    <mtx name="U+E0000" width="500" lsb="93"/>
+    <mtx name="U+E0001" width="500" lsb="93"/>
+    <mtx name="LigaForm1" width="500" lsb="93"/>
+    <mtx name="LigaForm2" width="500" lsb="93"/>
+    <mtx name="LigaForm3" width="500" lsb="93"/>
+    <mtx name="LigaForm4" width="500" lsb="93"/>
+    <mtx name="LigaForm5" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="3" nGroups="6" platformID="3" platEncID="1" language="0">
+      <map code="0x200D" name="U+200D" />
+      <map code="0x1F3FB" name="U+1F3FB" />
+      <map code="0x1F3FC" name="U+1F3FC" />
+      <map code="0x1F9B0" name="U+1F9B0" />
+      <map code="0x1F9B1" name="U+1F9B1" />
+      <map code="0x1F9B2" name="U+1F9B2" />
+      <map code="0x1F9B3" name="U+1F9B3" />
+      <map code="0x1F9B4" name="U+1F9B4" />
+      <map code="0x1F9B5" name="U+1F9B5" />
+      <map code="0x1F9B6" name="U+1F9B6" />
+      <map code="0x1F9B7" name="U+1F9B7" />
+      <map code="0x1F9B8" name="U+1F9B8" />
+      <map code="0x1F9B9" name="U+1F9B9" />
+      <map code="0xE0000" name="U+E0000" />
+      <map code="0xE0001" name="U+E0001" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+200D" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F3FB" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F3FC" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B0" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B1" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B2" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B3" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B4" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B5" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B6" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B7" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B8" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B9" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+E0000" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+E0001" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm1" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm2" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm3" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm4" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm5" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      EmojiBaseFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      EmojiBaseFont
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      EmojiBaseFont
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <FeatureRecord index="0">
+        <FeatureTag value="ccmp"/>
+        <Feature>
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="U+1F9B0">
+            <!-- U+1F9B0 U+1F3FB -> LigaForm1 -->
+            <Ligature components="U+1F3FB" glyph="LigaForm1"/>
+
+            <!-- U+1F9B0 U+1F3FC -> LigaForm2 -->
+            <Ligature components="U+1F3FC" glyph="LigaForm2"/>
+          </LigatureSet>
+          <LigatureSet glyph="U+1F9B1">
+            <!-- U+1F9B1 U+1F3FB -> LigaForm3 -->
+            <Ligature components="U+1F3FB" glyph="LigaForm3"/>
+          </LigatureSet>
+          <LigatureSet glyph="U+1F9B2">
+            <Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm4"/>
+          </LigatureSet>
+          <LigatureSet glyph="U+1F9B6">
+            <!-- U+1F9B6 U+1F3FB -> LigaForm3 -->
+            <Ligature components="U+1F3FB" glyph="LigaForm3"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+</ttFont>
diff --git a/tests/data/OverrideEmoji.ttf b/tests/data/OverrideEmoji.ttf
new file mode 100644
index 0000000..06bb8ca
--- /dev/null
+++ b/tests/data/OverrideEmoji.ttf
Binary files differ
diff --git a/tests/data/OverrideEmoji.ttx b/tests/data/OverrideEmoji.ttx
new file mode 100644
index 0000000..b484369
--- /dev/null
+++ b/tests/data/OverrideEmoji.ttx
@@ -0,0 +1,470 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+
+    <!-- Compbining characters for Emoji -->
+    <GlyphID id="1" name="U+200D"/>
+    <GlyphID id="2" name="U+1F3FB"/>
+    <GlyphID id="3" name="U+1F3FC"/>
+
+    <!-- Random Emoji -->
+    <GlyphID id="4" name="U+1F9B0"/>
+    <GlyphID id="5" name="U+1F9B1"/>
+    <GlyphID id="6" name="U+1F9B2"/>
+    <GlyphID id="7" name="U+1F9B3"/>
+    <GlyphID id="8" name="U+1F9B4"/>
+    <GlyphID id="9" name="U+1F9B5"/>
+    <!--
+      Following four glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <GlyphID id="10" name="U+1F9B6"/>
+    <GlyphID id="11" name="U+1F9B7"/>
+    <GlyphID id="12" name="U+1F9B8"/>
+    <GlyphID id="13" name="U+1F9B9"/>
+    -->
+
+    <!-- Unassigned Code Points -->
+    <GlyphID id="14" name="U+E0000"/>
+    <!--
+      Following glyph is removed from EmojiBase.ttf for verifying fallback.
+    <GlyphID id="15" name="U+E0001"/>
+    -->
+
+    <!-- Ligature Form. Not in cmap -->
+    <GlyphID id="16" name="LigaForm1"/>
+    <GlyphID id="17" name="LigaForm2"/>
+    <GlyphID id="18" name="LigaForm3"/>
+    <!--
+      Following glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <GlyphID id="19" name="LigaForm4"/>
+    <GlyphID id="20" name="LigaForm5"/>
+    -->
+
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep  9 08:01:17 2015"/>
+    <modified value="Wed Sep  9 08:48:07 2015"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="12"/>
+    <maxStorage value="28"/>
+    <maxFunctionDefs value="119"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="61"/>
+    <maxSizeOfInstructions value="2967"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="U+200D" width="500" lsb="93"/>
+    <mtx name="U+1F3FB" width="500" lsb="93"/>
+    <mtx name="U+1F3FC" width="500" lsb="93"/>
+    <mtx name="U+1F9B0" width="500" lsb="93"/>
+    <mtx name="U+1F9B1" width="500" lsb="93"/>
+    <mtx name="U+1F9B2" width="500" lsb="93"/>
+    <mtx name="U+1F9B3" width="500" lsb="93"/>
+    <mtx name="U+1F9B4" width="500" lsb="93"/>
+    <mtx name="U+1F9B5" width="500" lsb="93"/>
+    <!--
+      Following four glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <mtx name="U+1F9B6" width="500" lsb="93"/>
+    <mtx name="U+1F9B7" width="500" lsb="93"/>
+    <mtx name="U+1F9B8" width="500" lsb="93"/>
+    <mtx name="U+1F9B9" width="500" lsb="93"/>
+    -->
+    <mtx name="U+E0000" width="500" lsb="93"/>
+    <!--
+      Following glyph is removed from EmojiBase.ttf for verifying fallback.
+    <mtx name="U+1F9B6" width="500" lsb="93"/>
+    -->
+    <mtx name="U+E0001" width="500" lsb="93"/>
+    <mtx name="LigaForm1" width="500" lsb="93"/>
+    <mtx name="LigaForm2" width="500" lsb="93"/>
+    <mtx name="LigaForm3" width="500" lsb="93"/>
+    <!--
+      Following two glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <mtx name="LigaForm4" width="500" lsb="93"/>
+    <mtx name="LigaForm5" width="500" lsb="93"/>
+    -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="3" nGroups="6" platformID="3" platEncID="1" language="0">
+      <map code="0x200D" name="U+200D" />
+      <map code="0x1F3FB" name="U+1F3FB" />
+      <map code="0x1F3FC" name="U+1F3FC" />
+      <map code="0x1F9B0" name="U+1F9B0" />
+      <map code="0x1F9B1" name="U+1F9B1" />
+      <map code="0x1F9B2" name="U+1F9B2" />
+      <map code="0x1F9B3" name="U+1F9B3" />
+      <map code="0x1F9B4" name="U+1F9B4" />
+      <map code="0x1F9B5" name="U+1F9B5" />
+      <!--
+        Following four glyphs are removed from EmojiBase.ttf for verifying fallback.
+      <map code="0x1F9B6" name="U+1F9B6" />
+      <map code="0x1F9B7" name="U+1F9B7" />
+      <map code="0x1F9B8" name="U+1F9B8" />
+      <map code="0x1F9B9" name="U+1F9B9" />
+      -->
+      <map code="0xE0000" name="U+E0000" />
+      <!--
+        Following glyph is removed from EmojiBase.ttf for verifying fallback.
+      <map code="0xE0001" name="U+E0001" />
+      -->
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+200D" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F3FB" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F3FC" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B0" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B1" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B2" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B3" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B4" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="U+1F9B5" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <!--
+      Following four glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <TTGlyph name="U+1F9B6">
+    <TTGlyph name="U+1F9B7">
+    <TTGlyph name="U+1F9B8">
+    <TTGlyph name="U+1F9B9">
+    -->
+    <TTGlyph name="U+E0000" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <!--
+      Following glyph is removed from EmojiBase.ttf for verifying fallback.
+    <TTGlyph name="U+E0001"/>
+    -->
+    <TTGlyph name="LigaForm1" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm2" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <TTGlyph name="LigaForm3" xMin="0" yMin="0" xMax="100" yMax="100">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="100" y="0" on="1" />
+        <pt x="100" y="100" on="1" />
+        <pt x="0" y="100" on="1" />
+      </contour>
+      <instructions><assembly></assembly></instructions>
+    </TTGlyph>
+    <!--
+      Following two glyphs are removed from EmojiBase.ttf for verifying fallback.
+    <TTGlyph name="LigaForm4">
+    <TTGlyph name="LigaForm5">
+    -->
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      OverrideEmojiFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      OverrideEmojiFont
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      OverrideEmojiFont
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <FeatureRecord index="0">
+        <FeatureTag value="ccmp"/>
+        <Feature>
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="U+1F9B0">
+            <!-- U+1F9B0 U+1F3FB -> LigaForm0 -->
+            <Ligature components="U+1F3FB" glyph="LigaForm1"/>
+
+            <!-- U+1F9B0 U+1F3FC -> LigaForm1 -->
+            <!--
+              Following ligature is removed from the EmojiBase.ttf for verifying fallback
+            <Ligature components="U+1F3FC" glyph="LigaForm2"/>
+            -->
+          </LigatureSet>
+          <!--
+              Following ligature is removed from the EmojiBase.ttf for verifying fallback
+          <LigatureSet glyph="U+1F9B1">
+            <Ligature components="U+1F3FB" glyph="LigaForm3"/>
+          </LigatureSet>
+          -->
+          <LigatureSet glyph="U+1F9B2">
+            <Ligature components="U+200D,U+1F9B3" glyph="LigaForm3"/>
+          </LigatureSet>
+          <!--
+            Following ligature is removed from the EmojIBase.ttf for verifying fallback.
+            <LigatureSet glyph="U+1F9B6">
+              <Ligature components="U+1F3FB" glyph="LigaForm3"/>
+            </LigatureSet>
+          -->
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+</ttFont>
diff --git a/tests/data/emoji_itemization.xml b/tests/data/emoji_itemization.xml
new file mode 100644
index 0000000..d4213f9
--- /dev/null
+++ b/tests/data/emoji_itemization.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<familyset version="22">
+    <!-- Place NoGlyphFont here since the first font can be chosen if no font is
+         available for the code point. -->
+    <family>
+        <font weight="400" style="normal">Ascii.ttf</font>
+    </family>
+    <family lang="und-Zsye">
+        <font weight="400" style="normal">EmojiSubset.ttf</font>
+    </family>
+    <family lang="und-Zsye">
+        <font weight="400" style="normal">EmojiBase.ttf</font>
+    </family>
+</familyset>
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index 35745c7..96589c9 100644
--- a/tests/unittest/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -21,6 +21,7 @@
 #include <gtest/gtest.h>
 
 #include "minikin/FontFamily.h"
+#include "minikin/FontFileParser.h"
 #include "minikin/LocaleList.h"
 #include "minikin/MinikinPaint.h"
 
@@ -55,10 +56,15 @@
 const char kHasCmapFormat14Font[] = "NoCmapFormat14.ttf";
 const char kNoCmapFormat14Font[] = "VariationSelectorTest-Regular.ttf";
 
+struct Run {
+    FakedFont fakedFont;
+    int start;
+    int end;
+};
+
 // Utility functions for calling itemize function.
-std::vector<FontCollection::Run> itemize(const std::shared_ptr<FontCollection>& collection,
-                                         const char* str, FontStyle style,
-                                         const std::string& localeList) {
+std::vector<Run> itemize(const std::shared_ptr<FontCollection>& collection, const char* str,
+                         FontStyle style, const std::string& localeList) {
     const size_t BUF_SIZE = 256;
     uint16_t buf[BUF_SIZE];
     size_t len;
@@ -76,32 +82,36 @@
         for (uint32_t i = 0; i < runMax; ++i) {
             EXPECT_EQ(result[i].start, resultWithRunMax[i].start);
             EXPECT_EQ(result[i].end, resultWithRunMax[i].end);
-            EXPECT_EQ(result[i].fakedFont, resultWithRunMax[i].fakedFont);
+            EXPECT_EQ(result[i].familyMatch, resultWithRunMax[i].familyMatch);
         }
     }
-    return result;
+    std::vector<Run> runs;
+    for (const auto& r : result) {
+        runs.push_back(
+                {collection->getBestFont(U16StringPiece(buf, len), r, style), r.start, r.end});
+    }
+    return runs;
 }
 
 // Overloaded version for default font style.
-std::vector<FontCollection::Run> itemize(const std::shared_ptr<FontCollection>& collection,
-                                         const char* str, const std::string& localeList) {
+std::vector<Run> itemize(const std::shared_ptr<FontCollection>& collection, const char* str,
+                         const std::string& localeList) {
     return itemize(collection, str, FontStyle(), localeList);
 }
 
 // Overloaded version for empty locale list id.
-std::vector<FontCollection::Run> itemize(const std::shared_ptr<FontCollection>& collection,
-                                         const char* str, FontStyle style) {
+std::vector<Run> itemize(const std::shared_ptr<FontCollection>& collection, const char* str,
+                         FontStyle style) {
     return itemize(collection, str, style, "");
 }
 
 // Overloaded version for default font style and empty locale list id.
-std::vector<FontCollection::Run> itemize(const std::shared_ptr<FontCollection>& collection,
-                                         const char* str) {
+std::vector<Run> itemize(const std::shared_ptr<FontCollection>& collection, const char* str) {
     return itemize(collection, str, FontStyle(), "");
 }
 
 // Utility function to obtain font path associated with run.
-std::string getFontName(const FontCollection::Run& run) {
+std::string getFontName(const Run& run) {
     EXPECT_NE(nullptr, run.fakedFont.font.get());
     return getBasename(run.fakedFont.font.get()->typeface()->GetFontPath());
 }
@@ -1619,4 +1629,45 @@
     EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font.get());
 }
 
+std::string itemizeEmojiAndFontPostScriptName(const std::string& txt) {
+    auto firstFamily = buildFontFamily(kAsciiFont);
+    auto OverrideEmojiFamily = buildFontFamily("OverrideEmoji.ttf", "und-Zsye");
+    auto emojiBaseFamily = buildFontFamily("EmojiBase.ttf", "und-Zsye");
+
+    std::vector<std::shared_ptr<FontFamily>> families = {firstFamily, OverrideEmojiFamily,
+                                                         emojiBaseFamily};
+
+    auto collection = std::make_shared<FontCollection>(families);
+    auto runs = itemize(collection, txt.c_str());
+
+    EXPECT_EQ(1u, runs.size());
+    return FontFileParser(runs[0].fakedFont.font->baseFont()).getPostScriptName().value();
+}
+
+TEST(FontCollectionItemizeTest, emojiFallback) {
+    // OverrideEmojiFont supports U+1F9B0, U+E0000, U+1F3FB and U+1F9B0 U+1F3FB sequence.
+    // Use Override font.
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0"));
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+E0000"));
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0 U+1F3FB"));
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0 U+FE0F U+1F3FB"));
+
+    // OverrideEmojiFont doesn't suppot U+1F9B6 U+E0001 and U+1F3FC.
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6"));
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+E0001"));
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6 U+1F3FC"));
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6 U+FE0F U+1F3FC"));
+
+    // OverrideEmojiFont support U+1F9B1, U+1F3FB but doesn't support the sequence U+1F9B1 U+1F3FB.
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B1"));
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F3FB"));
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B1 U+1F3FB"));
+    EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B1 U+FE0F U+1F3FB"));
+
+    // Find the longest sequence if two sequences are supported.
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B2 U+200D U+1F9B3"));
+    EXPECT_EQ("EmojiBaseFont",
+              itemizeEmojiAndFontPostScriptName("U+1F9B2 U+200D U+1F9B3 U+200D U+1F9B4"));
+}
+
 }  // namespace minikin
diff --git a/tests/unittest/FontCollectionTest.cpp b/tests/unittest/FontCollectionTest.cpp
index 40b6bf2..aa9d4a8 100644
--- a/tests/unittest/FontCollectionTest.cpp
+++ b/tests/unittest/FontCollectionTest.cpp
@@ -234,4 +234,87 @@
     }
 }
 
+TEST(FontCollectionTest, FamilyMatchResultBuilderTest) {
+    using Builder = FontCollection::FamilyMatchResult::Builder;
+    EXPECT_TRUE(Builder().empty());
+    EXPECT_EQ(0u, Builder().size());
+    EXPECT_EQ(1u, Builder().add(5).size());
+    EXPECT_EQ(2u, Builder().add(5).add(4).size());
+
+    // Reset
+    EXPECT_TRUE(Builder().add(5).reset().empty());
+    EXPECT_EQ(0u, Builder().add(5).reset().size());
+}
+
+TEST(FontCollectionTest, FamilyMatchResultTest) {
+    using Builder = FontCollection::FamilyMatchResult::Builder;
+
+    auto r = Builder().build();
+    EXPECT_EQ(0u, r.size());
+    EXPECT_TRUE(r.empty());
+
+    r = Builder().add(1).build();
+    EXPECT_EQ(1u, r.size());
+    EXPECT_FALSE(r.empty());
+    EXPECT_EQ(1u, r[0]);
+
+    r = Builder().add(1).add(2).build();
+    EXPECT_EQ(2u, r.size());
+    EXPECT_FALSE(r.empty());
+    EXPECT_EQ(1u, r[0]);
+    EXPECT_EQ(2u, r[1]);
+}
+
+TEST(FontCollectionTest, FamilyMatchResultTest_BuilderHoldeFirst7) {
+    auto b = FontCollection::FamilyMatchResult::Builder();
+    for (uint8_t i = 0; i < 128; ++i) {
+        b.add(i);
+    }
+    auto r = b.build();
+    EXPECT_EQ(7u, r.size());
+    EXPECT_FALSE(r.empty());
+    EXPECT_EQ(0u, r[0]);
+    EXPECT_EQ(1u, r[1]);
+    EXPECT_EQ(2u, r[2]);
+    EXPECT_EQ(3u, r[3]);
+    EXPECT_EQ(4u, r[4]);
+    EXPECT_EQ(5u, r[5]);
+    EXPECT_EQ(6u, r[6]);
+}
+
+TEST(FontCollectionTest, FamilyMatchResultTest_iterator) {
+    auto b = FontCollection::FamilyMatchResult::Builder();
+    for (uint8_t i = 0; i < 7; ++i) {
+        b.add(i);
+    }
+    auto r = b.build();
+    EXPECT_EQ(7u, r.size());
+    EXPECT_FALSE(r.empty());
+    int i = 0;
+    for (auto v : r) {
+        EXPECT_EQ(i, v);
+        i++;
+    }
+}
+
+TEST(FontCollectionTest, FamilyMatchResultTest_intersect) {
+    using Builder = FontCollection::FamilyMatchResult::Builder;
+
+    EXPECT_EQ(Builder().add(1).add(2).add(3).build(),
+              FontCollection::FamilyMatchResult::intersect(Builder().add(1).add(2).add(3).build(),
+                                                           Builder().add(1).add(2).add(3).build()));
+
+    EXPECT_EQ(Builder().build(),
+              FontCollection::FamilyMatchResult::intersect(Builder().add(1).add(2).add(3).build(),
+                                                           Builder().build()));
+
+    EXPECT_EQ(Builder().build(),
+              FontCollection::FamilyMatchResult::intersect(Builder().add(2).add(4).add(6).build(),
+                                                           Builder().add(1).add(3).add(5).build()));
+
+    EXPECT_EQ(Builder().add(1).add(3).build(),
+              FontCollection::FamilyMatchResult::intersect(Builder().add(1).add(2).add(3).build(),
+                                                           Builder().add(1).add(3).add(5).build()));
+}
+
 }  // namespace minikin