Merge Android 12

Bug: 202323961
Merged-In: Ibca7e25968decd95cdc4c54b12239976d6762e49
Change-Id: I94734acf08f2b62e695d4771dcaa0afabec7a33b
diff --git a/include/minikin/BoundsCache.h b/include/minikin/BoundsCache.h
new file mode 100644
index 0000000..64f2b49
--- /dev/null
+++ b/include/minikin/BoundsCache.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef MINIKIN_BOUNDS_CACHE_H
+#define MINIKIN_BOUNDS_CACHE_H
+
+#include "minikin/LayoutCache.h"
+
+#include <mutex>
+
+#include <utils/LruCache.h>
+
+#include "minikin/BoundsCache.h"
+#include "minikin/FontCollection.h"
+#include "minikin/Hasher.h"
+#include "minikin/MinikinPaint.h"
+
+namespace minikin {
+
+// Cache entry
+struct BoundsValue {
+    MinikinRect rect;
+    float advance;
+};
+
+// Used for callback for LayoutCache.
+struct ValueExtractor {
+    void operator()(const LayoutPiece& layoutPiece, const MinikinPaint& paint);
+    std::unique_ptr<BoundsValue> value;
+};
+
+class BoundsCache : private android::OnEntryRemoved<LayoutCacheKey, BoundsValue*> {
+public:
+    void clear() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mCache.clear();
+    }
+
+    // Do not use BoundsCache inside the callback function, otherwise dead-lock may happen.
+    template <typename F>
+    void getOrCreate(const U16StringPiece& text, const Range& range, const MinikinPaint& paint,
+                     bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, F& f) {
+        LayoutCacheKey key(text, range, paint, dir, startHyphen, endHyphen);
+        if (paint.skipCache() || range.getLength() >= LENGTH_LIMIT_CACHE) {
+            LayoutPiece piece = LayoutPiece(text, range, dir, paint, startHyphen, endHyphen);
+            f(getBounds(piece, paint), piece.advance());
+            return;
+        }
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            BoundsValue* value = mCache.get(key);
+            if (value != nullptr) {
+                f(value->rect, value->advance);
+                return;
+            }
+        }
+        // Doing text layout takes long time, so releases the mutex during doing layout.
+        // Don't care even if we do the same layout in other thread.
+        key.copyText();
+        ValueExtractor ve;
+        LayoutCache::getInstance().getOrCreate(text, range, paint, dir, startHyphen, endHyphen, ve);
+        f(ve.value->rect, ve.value->advance);
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mCache.put(key, ve.value.release());
+        }
+    }
+
+    static BoundsCache& getInstance() {
+        static BoundsCache cache(kMaxEntries);
+        return cache;
+    }
+
+    // Compute new bounding box for the layout piece.
+    static MinikinRect getBounds(const LayoutPiece& layoutPiece, const MinikinPaint& paint);
+
+protected:
+    BoundsCache(uint32_t maxEntries) : mCache(maxEntries) {
+        mCache.setOnEntryRemovedListener(this);
+    }
+
+private:
+    // callback for OnEntryRemoved
+    void operator()(LayoutCacheKey& key, BoundsValue*& value) {
+        key.freeText();
+        delete value;
+    }
+
+    std::mutex mMutex;
+    android::LruCache<LayoutCacheKey, BoundsValue*> mCache GUARDED_BY(mMutex) GUARDED_BY(mMutex);
+    // LRU cache capacity. Should be fine to be less than LayoutCache#kMaxEntries since bbox
+    // calculation happens less than layout calculation.
+    static const size_t kMaxEntries = 500;
+};
+
+}  // namespace minikin
+#endif  // MINIKIN_BOUNDS_CACHE_H
diff --git a/include/minikin/Buffer.h b/include/minikin/Buffer.h
new file mode 100644
index 0000000..87ba3fd
--- /dev/null
+++ b/include/minikin/Buffer.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef MINIKIN_BUFFER_H
+#define MINIKIN_BUFFER_H
+
+#include <cstring>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+namespace minikin {
+
+// This is a helper class to read data from a memory buffer.
+// This class does not copy memory, and may return pointers to parts of the memory buffer.
+// Thus the memory buffer should outlive objects created using this class.
+class BufferReader {
+public:
+    BufferReader(const void* buffer) : BufferReader(buffer, 0) {}
+    BufferReader(const void* buffer, uint32_t pos)
+            : mData(reinterpret_cast<const uint8_t*>(buffer)), mPos(pos) {}
+
+    template <typename T>
+    static uint32_t align(uint32_t pos) {
+        // This should be true for all types, unless custom alignment attributes are set.
+        static_assert(sizeof(T) % alignof(T) == 0, "sizeof(T) must be a multiple of alignof(T)");
+        // We align to sizeof(T) instead of alignof(T), because the buffer may be shared between
+        // 32-bit processes and 64-bit processes. alignof(T) may change between the two.
+        // We assume that T is a type whose size is fixed (e.g. uint32_t).
+        return (pos + sizeof(T) - 1) / sizeof(T) * sizeof(T);
+    }
+
+    template <typename T>
+    const T& read() {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        mPos = BufferReader::align<T>(mPos);
+        const T* data = reinterpret_cast<const T*>(mData + mPos);
+        mPos += sizeof(T);
+        return *data;
+    }
+
+    template <typename T>
+    void skip() {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        mPos = BufferReader::align<T>(mPos);
+        mPos += sizeof(T);
+    }
+
+    // Return a pointer to an array and its number of elements.
+    template <typename T>
+    std::pair<const T*, uint32_t> readArray() {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        uint32_t size = read<uint32_t>();
+        mPos = BufferReader::align<T>(mPos);
+        const T* data = reinterpret_cast<const T*>(mData + mPos);
+        mPos += size * sizeof(T);
+        return std::make_pair(data, size);
+    }
+
+    template <typename T>
+    void skipArray() {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        uint32_t size = read<uint32_t>();
+        mPos = BufferReader::align<T>(mPos);
+        mPos += size * sizeof(T);
+    }
+
+    std::string_view readString() {
+        auto [data, size] = readArray<char>();
+        return std::string_view(data, size);
+    }
+
+    void skipString() { skipArray<char>(); }
+
+    const void* data() const { return mData; }
+    size_t pos() const { return mPos; }
+
+private:
+    const uint8_t* mData;
+    size_t mPos;
+};
+
+// This is a helper class to write data to a memory buffer.
+class BufferWriter {
+public:
+    // Create a buffer writer. Passing nullptr creates a fake writer,
+    // which can be used to measure the buffer size needed.
+    BufferWriter(void* buffer) : mData(reinterpret_cast<uint8_t*>(buffer)), mPos(0) {}
+
+    BufferWriter(BufferWriter&&) = default;
+    BufferWriter& operator=(BufferWriter&&) = default;
+
+    // Write a single data of type T.
+    // Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
+    // disables template argument deduction.
+    // TODO: use std::type_identity_t when C++20 is available.
+    template <typename T>
+    void write(const std::common_type_t<T>& data) {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        mPos = BufferReader::align<T>(mPos);
+        if (mData != nullptr) {
+            memcpy(mData + mPos, &data, sizeof(T));
+        }
+        mPos += sizeof(T);
+    }
+
+    // Write an array of type T.
+    // Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
+    // disables template argument deduction.
+    // TODO: use std::type_identity_t when C++20 is available.
+    template <typename T>
+    void writeArray(const std::common_type_t<T>* data, uint32_t size) {
+        static_assert(std::is_pod<T>::value, "T must be a POD");
+        write<uint32_t>(size);
+        mPos = BufferReader::align<T>(mPos);
+        if (mData != nullptr) {
+            memcpy(mData + mPos, data, size * sizeof(T));
+        }
+        mPos += size * sizeof(T);
+    }
+
+    void writeString(std::string_view string) { writeArray<char>(string.data(), string.size()); }
+
+    // Return the number of bytes written.
+    size_t size() const { return mPos; }
+
+private:
+    uint8_t* mData;
+    size_t mPos;
+
+    // Forbid copy and assign.
+    BufferWriter(const BufferWriter&) = delete;
+    void operator=(const BufferWriter&) = delete;
+};
+
+}  // namespace minikin
+
+#endif  // MINIKIN_BUFFER_H
diff --git a/include/minikin/Font.h b/include/minikin/Font.h
index eeb074e..67feecf 100644
--- a/include/minikin/Font.h
+++ b/include/minikin/Font.h
@@ -18,11 +18,14 @@
 #define MINIKIN_FONT_H
 
 #include <memory>
+#include <mutex>
 #include <unordered_set>
 
+#include "minikin/Buffer.h"
 #include "minikin/FontStyle.h"
 #include "minikin/FontVariation.h"
 #include "minikin/HbUtils.h"
+#include "minikin/LocaleList.h"
 #include "minikin/Macros.h"
 #include "minikin/MinikinFont.h"
 
@@ -55,7 +58,10 @@
     inline bool operator!=(const FakedFont& o) const { return !(*this == o); }
 
     // ownership is the enclosing FontCollection
-    const Font* font;
+    // FakedFont will be stored in the LayoutCache. It is not a good idea too keep font instance
+    // even if the enclosing FontCollection, i.e. Typeface is GC-ed. The layout cache is only
+    // purged when it is overflown, thus intentionally keep only reference.
+    const std::shared_ptr<Font>& font;
     FontFakery fakery;
 };
 
@@ -88,44 +94,96 @@
             return *this;
         }
 
-        Font build();
+        Builder& setLocaleListId(uint32_t id) {
+            mLocaleListId = id;
+            return *this;
+        }
+
+        std::shared_ptr<Font> build();
 
     private:
         std::shared_ptr<MinikinFont> mTypeface;
         uint16_t mWeight = static_cast<uint16_t>(FontStyle::Weight::NORMAL);
         FontStyle::Slant mSlant = FontStyle::Slant::UPRIGHT;
+        uint32_t mLocaleListId = kEmptyLocaleListId;
         bool mIsWeightSet = false;
         bool mIsSlantSet = false;
     };
 
-    Font(Font&& o) = default;
-    Font& operator=(Font&& o) = default;
+    // Type for functions to load MinikinFont lazily.
+    using TypefaceLoader = std::shared_ptr<MinikinFont>(BufferReader reader);
+    // Type for functions to read MinikinFont metadata and return
+    // TypefaceLoader.
+    using TypefaceReader = TypefaceLoader*(BufferReader* reader);
+    // Type for functions to write MinikinFont metadata.
+    using TypefaceWriter = void(BufferWriter* writer, const MinikinFont* typeface);
 
-    Font& operator=(const Font& o) {
-        mTypeface = o.mTypeface;
-        mStyle = o.mStyle;
-        mBaseFont = HbFontUniquePtr(hb_font_reference(o.mBaseFont.get()));
-        return *this;
+    template <TypefaceReader typefaceReader>
+    static std::shared_ptr<Font> readFrom(BufferReader* reader, uint32_t localeListId) {
+        FontStyle style = FontStyle(reader);
+        BufferReader typefaceMetadataReader = *reader;
+        TypefaceLoader* typefaceLoader = typefaceReader(reader);
+        return std::shared_ptr<Font>(
+                new Font(style, typefaceMetadataReader, typefaceLoader, localeListId));
     }
-    Font(const Font& o) { *this = o; }
 
-    inline const std::shared_ptr<MinikinFont>& typeface() const { return mTypeface; }
+    template <TypefaceWriter typefaceWriter>
+    void writeTo(BufferWriter* writer) const {
+        mStyle.writeTo(writer);
+        typefaceWriter(writer, typeface().get());
+    }
+
+    // This locale list is just for API compatibility. This is not used in font selection or family
+    // fallback.
+    uint32_t getLocaleListId() const { return mLocaleListId; }
+    const std::shared_ptr<MinikinFont>& typeface() const;
     inline FontStyle style() const { return mStyle; }
-    inline const HbFontUniquePtr& baseFont() const { return mBaseFont; }
+    const HbFontUniquePtr& baseFont() const;
+    BufferReader typefaceMetadataReader() const { return mTypefaceMetadataReader; }
 
     std::unordered_set<AxisTag> getSupportedAxes() const;
 
 private:
     // Use Builder instead.
-    Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style, HbFontUniquePtr&& baseFont)
-            : mTypeface(std::move(typeface)), mStyle(style), mBaseFont(std::move(baseFont)) {}
+    Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style, HbFontUniquePtr&& baseFont,
+         uint32_t localeListId)
+            : mTypeface(std::move(typeface)),
+              mStyle(style),
+              mBaseFont(std::move(baseFont)),
+              mTypefaceLoader(nullptr),
+              mTypefaceMetadataReader(nullptr),
+              mLocaleListId(localeListId) {}
+    Font(FontStyle style, BufferReader typefaceMetadataReader, TypefaceLoader* typefaceLoader,
+         uint32_t localeListId)
+            : mStyle(style),
+              mTypefaceLoader(typefaceLoader),
+              mTypefaceMetadataReader(typefaceMetadataReader),
+              mLocaleListId(localeListId) {}
+
+    void initTypefaceLocked() const EXCLUSIVE_LOCKS_REQUIRED(mTypefaceMutex);
 
     static HbFontUniquePtr prepareFont(const std::shared_ptr<MinikinFont>& typeface);
     static FontStyle analyzeStyle(const HbFontUniquePtr& font);
 
-    std::shared_ptr<MinikinFont> mTypeface;
+    // Lazy-initialized if created by readFrom().
+    mutable std::shared_ptr<MinikinFont> mTypeface GUARDED_BY(mTypefaceMutex);
     FontStyle mStyle;
-    HbFontUniquePtr mBaseFont;
+    // Lazy-initialized if created by readFrom().
+    mutable HbFontUniquePtr mBaseFont GUARDED_BY(mTypefaceMutex);
+
+    mutable std::mutex mTypefaceMutex;
+    // Non-null if created by readFrom().
+    TypefaceLoader* mTypefaceLoader;
+    // Non-null if created by readFrom().
+    BufferReader mTypefaceMetadataReader;
+
+    uint32_t mLocaleListId;
+
+    // Stop copying and moving
+    Font(Font&& o) = delete;
+    Font& operator=(Font&& o) = delete;
+    Font(const Font& o) = delete;
+    Font& operator=(const Font& o) = delete;
 };
 
 }  // namespace minikin
diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h
index f136384..98df571 100644
--- a/include/minikin/FontCollection.h
+++ b/include/minikin/FontCollection.h
@@ -18,9 +18,14 @@
 #define MINIKIN_FONT_COLLECTION_H
 
 #include <memory>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
+#include <gtest/gtest_prod.h>
+
+#include "minikin/Buffer.h"
+#include "minikin/Font.h"
 #include "minikin/FontFamily.h"
 #include "minikin/MinikinFont.h"
 #include "minikin/U16StringPiece.h"
@@ -35,12 +40,134 @@
     explicit FontCollection(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
     explicit FontCollection(std::shared_ptr<FontFamily>&& typeface);
 
+    template <Font::TypefaceReader typefaceReader>
+    static std::vector<std::shared_ptr<FontCollection>> readVector(BufferReader* reader) {
+        uint32_t allFontFamiliesCount = reader->read<uint32_t>();
+        std::vector<std::shared_ptr<FontFamily>> allFontFamilies;
+        allFontFamilies.reserve(allFontFamiliesCount);
+        for (uint32_t i = 0; i < allFontFamiliesCount; i++) {
+            allFontFamilies.push_back(FontFamily::readFrom<typefaceReader>(reader));
+        }
+        uint32_t fontCollectionsCount = reader->read<uint32_t>();
+        std::vector<std::shared_ptr<FontCollection>> fontCollections;
+        fontCollections.reserve(fontCollectionsCount);
+        for (uint32_t i = 0; i < fontCollectionsCount; i++) {
+            fontCollections.emplace_back(new FontCollection(reader, allFontFamilies));
+        }
+        return fontCollections;
+    }
+
+    template <Font::TypefaceWriter typefaceWriter>
+    static void writeVector(BufferWriter* writer,
+                            const std::vector<std::shared_ptr<FontCollection>>& fontCollections) {
+        std::vector<std::shared_ptr<FontFamily>> allFontFamilies;
+        // Note: operator== for shared_ptr compares raw pointer values.
+        std::unordered_map<std::shared_ptr<FontFamily>, uint32_t> fontFamilyToIndexMap;
+        collectAllFontFamilies(fontCollections, &allFontFamilies, &fontFamilyToIndexMap);
+
+        writer->write<uint32_t>(allFontFamilies.size());
+        for (const auto& fontFamily : allFontFamilies) {
+            fontFamily->writeTo<typefaceWriter>(writer);
+        }
+        writer->write<uint32_t>(fontCollections.size());
+        for (const auto& fontCollection : fontCollections) {
+            fontCollection->writeTo(writer, fontFamilyToIndexMap);
+        }
+    }
+
+    // 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;
@@ -68,7 +195,23 @@
 
     uint32_t getId() const;
 
+    const std::vector<std::shared_ptr<FontFamily>>& getFamilies() const { return mFamilies; }
+
 private:
+    FRIEND_TEST(FontCollectionTest, bufferTest);
+
+    FontCollection(BufferReader* reader,
+                   const std::vector<std::shared_ptr<FontFamily>>& allFontFamilies);
+    // Write fields of the instance, using fontFamilyToIndexMap for finding
+    // indices for FontFamily.
+    void writeTo(BufferWriter* writer,
+                 const std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>&
+                         fontFamilyToIndexMap) const;
+    static void collectAllFontFamilies(
+            const std::vector<std::shared_ptr<FontCollection>>& fontCollections,
+            std::vector<std::shared_ptr<FontFamily>>* outAllFontFamilies,
+            std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>* outFontFamilyToIndexMap);
+
     static const int kLogCharsPerPage = 8;
     static const int kPageMask = (1 << kLogCharsPerPage) - 1;
 
@@ -85,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;
@@ -116,14 +258,21 @@
     // mFamilyVec[mRange[0xXXYY].end] instead of whole mFamilies.
     // This vector contains indices into mFamilies.
     // This vector can't be empty.
-    std::vector<Range> mRanges;
-    std::vector<uint8_t> mFamilyVec;
+    uint32_t mRangesCount;
+    const Range* mRanges;
+    uint32_t mFamilyVecCount;
+    const uint8_t* mFamilyVec;
 
     // This vector has pointers to the font family instances which have cmap 14 subtables.
     std::vector<std::shared_ptr<FontFamily>> mVSFamilyVec;
 
     // Set of supported axes in this collection.
     std::unordered_set<AxisTag> mSupportedAxes;
+
+    // Owns allocated memory if this class is created from font families, otherwise these are
+    // nullptr.
+    std::unique_ptr<Range[]> mOwnedRanges;
+    std::vector<uint8_t> mOwnedFamilyVec;
 };
 
 }  // namespace minikin
diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h
index 4aafaa0..6169193 100644
--- a/include/minikin/FontFamily.h
+++ b/include/minikin/FontFamily.h
@@ -33,10 +33,32 @@
 
 class FontFamily {
 public:
-    explicit FontFamily(std::vector<Font>&& fonts);
-    FontFamily(FamilyVariant variant, std::vector<Font>&& fonts);
-    FontFamily(uint32_t localeListId, FamilyVariant variant, std::vector<Font>&& fonts,
-               bool isCustomFallback);
+    explicit FontFamily(std::vector<std::shared_ptr<Font>>&& fonts);
+    FontFamily(FamilyVariant variant, std::vector<std::shared_ptr<Font>>&& fonts);
+    FontFamily(uint32_t localeListId, FamilyVariant variant,
+               std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback);
+
+    template <Font::TypefaceReader typefaceReader>
+    static std::shared_ptr<FontFamily> readFrom(BufferReader* reader) {
+        uint32_t localeListId = readLocaleListInternal(reader);
+        uint32_t fontsCount = reader->read<uint32_t>();
+        std::vector<std::shared_ptr<Font>> fonts;
+        fonts.reserve(fontsCount);
+        for (uint32_t i = 0; i < fontsCount; i++) {
+            fonts.emplace_back(Font::readFrom<typefaceReader>(reader, localeListId));
+        }
+        return readFromInternal(reader, std::move(fonts), localeListId);
+    }
+
+    template <Font::TypefaceWriter typefaceWriter>
+    void writeTo(BufferWriter* writer) const {
+        writeLocaleListInternal(writer);
+        writer->write<uint32_t>(mFonts.size());
+        for (const std::shared_ptr<Font>& font : mFonts) {
+            font->writeTo<typefaceWriter>(writer);
+        }
+        writeToInternal(writer);
+    }
 
     FakedFont getClosestMatch(FontStyle style) const;
 
@@ -45,8 +67,9 @@
 
     // API's for enumerating the fonts in a family. These don't guarantee any particular order
     size_t getNumFonts() const { return mFonts.size(); }
-    const Font* getFont(size_t index) const { return &mFonts[index]; }
-    FontStyle getStyle(size_t index) const { return mFonts[index].style(); }
+    const Font* getFont(size_t index) const { return mFonts[index].get(); }
+    const std::shared_ptr<Font>& getFontRef(size_t index) const { return mFonts[index]; }
+    FontStyle getStyle(size_t index) const { return mFonts[index]->style(); }
     bool isColorEmojiFamily() const { return mIsColorEmoji; }
     const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
     bool isCustomFallback() const { return mIsCustomFallback; }
@@ -67,11 +90,24 @@
             const std::vector<FontVariation>& variations) const;
 
 private:
+    FontFamily(uint32_t localeListId, FamilyVariant variant,
+               std::vector<std::shared_ptr<Font>>&& fonts,
+               std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+               bool isCustomFallback, SparseBitSet&& coverage,
+               std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage);
+
+    static uint32_t readLocaleListInternal(BufferReader* reader);
+    static std::shared_ptr<FontFamily> readFromInternal(BufferReader* reader,
+                                                        std::vector<std::shared_ptr<Font>>&& fonts,
+                                                        uint32_t localeListId);
+    void writeLocaleListInternal(BufferWriter* writer) const;
+    void writeToInternal(BufferWriter* writer) const;
+
     void computeCoverage();
 
     uint32_t mLocaleListId;
     FamilyVariant mVariant;
-    std::vector<Font> mFonts;
+    std::vector<std::shared_ptr<Font>> mFonts;
     std::unordered_set<AxisTag> mSupportedAxes;
     bool mIsColorEmoji;
     bool mIsCustomFallback;
diff --git a/include/minikin/FontFileParser.h b/include/minikin/FontFileParser.h
new file mode 100644
index 0000000..34880a3
--- /dev/null
+++ b/include/minikin/FontFileParser.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef MINIKIN_FONT_FILE_PARSER_H
+#define MINIKIN_FONT_FILE_PARSER_H
+
+#include "minikin/HbUtils.h"
+
+#include <optional>
+#include <string>
+
+namespace minikin {
+
+// FontFileParser provides various parser logic for OpenType font file.
+class FontFileParser {
+public:
+    // This class does not take an ownership of buffer. Caller must free it.
+    FontFileParser(const void* buffer, size_t size, uint32_t index);
+    explicit FontFileParser(const HbFaceUniquePtr& face);
+    explicit FontFileParser(const HbFontUniquePtr& font);
+
+    virtual ~FontFileParser();
+
+    std::optional<uint32_t> getFontRevision() const;
+    std::optional<std::string> getPostScriptName() const;
+    std::optional<bool> isPostScriptType1Font() const;
+
+protected:  // protected for testing purposes.
+    static bool analyzeFontRevision(const uint8_t* head_data, size_t head_size, uint32_t* out);
+    static bool checkPSName(const std::string& psName);
+
+private:
+    HbFaceUniquePtr mFace;
+
+    static HbFaceUniquePtr makeHbFace(const void* buffer, size_t size, uint32_t index);
+};
+
+}  // namespace minikin
+
+#endif  // MINIKIN_FONT_FILE_PARSER_H
diff --git a/include/minikin/FontStyle.h b/include/minikin/FontStyle.h
index 73cf427..51e4ad8 100644
--- a/include/minikin/FontStyle.h
+++ b/include/minikin/FontStyle.h
@@ -17,6 +17,8 @@
 #ifndef MINIKIN_FONT_STYLE_H
 #define MINIKIN_FONT_STYLE_H
 
+#include <minikin/Buffer.h>
+
 namespace minikin {
 
 // FontStyle represents style information.
@@ -46,6 +48,15 @@
     constexpr FontStyle(Weight weight, Slant slant)
             : FontStyle(static_cast<uint16_t>(weight), slant) {}
     constexpr FontStyle(uint16_t weight, Slant slant) : mWeight(weight), mSlant(slant) {}
+    explicit FontStyle(BufferReader* reader) {
+        mWeight = reader->read<uint16_t>();
+        mSlant = static_cast<Slant>(reader->read<uint8_t>());
+    }
+
+    void writeTo(BufferWriter* writer) const {
+        writer->write<uint16_t>(mWeight);
+        writer->write<uint8_t>(static_cast<uint8_t>(mSlant));
+    }
 
     constexpr uint16_t weight() const { return mWeight; }
     constexpr Slant slant() const { return mSlant; }
diff --git a/include/minikin/FontVariation.h b/include/minikin/FontVariation.h
index 0c38d6a..e0567c1 100644
--- a/include/minikin/FontVariation.h
+++ b/include/minikin/FontVariation.h
@@ -24,6 +24,7 @@
 typedef uint32_t AxisTag;
 
 struct FontVariation {
+    FontVariation() = default;
     FontVariation(AxisTag axisTag, float value) : axisTag(axisTag), value(value) {}
     AxisTag axisTag;
     float value;
diff --git a/include/minikin/Hasher.h b/include/minikin/Hasher.h
index 8a79b61..4a76b29 100644
--- a/include/minikin/Hasher.h
+++ b/include/minikin/Hasher.h
@@ -37,6 +37,26 @@
         return *this;
     }
 
+    inline Hasher& update(int32_t data) {
+        update(static_cast<uint32_t>(data));
+        return *this;
+    }
+
+    inline Hasher& update(uint64_t data) {
+        update(static_cast<uint32_t>(data));
+        update(static_cast<uint32_t>(data >> 32));
+        return *this;
+    }
+
+    inline Hasher& update(float data) {
+        union {
+            float f;
+            uint32_t i;
+        } bits;
+        bits.f = data;
+        return update(bits.i);
+    }
+
     inline Hasher& updateShorts(const uint16_t* data, uint32_t length) {
         update(length);
         uint32_t i;
diff --git a/include/minikin/Layout.h b/include/minikin/Layout.h
index 19f3109..388a7a7 100644
--- a/include/minikin/Layout.h
+++ b/include/minikin/Layout.h
@@ -84,7 +84,8 @@
 
     // public accessors
     size_t nGlyphs() const { return mGlyphs.size(); }
-    const MinikinFont* getFont(int i) const { return mGlyphs[i].font.font->typeface().get(); }
+    const Font* getFont(int i) const { return mGlyphs[i].font.font.get(); }
+    const std::shared_ptr<Font>& getFontRef(int i) const { return mGlyphs[i].font.font; }
     FontFakery getFakery(int i) const { return mGlyphs[i].font.fakery; }
     unsigned int getGlyphId(int i) const { return mGlyphs[i].glyph_id; }
     float getX(int i) const { return mGlyphs[i].x; }
@@ -92,8 +93,6 @@
     float getAdvance() const { return mAdvance; }
     float getCharAdvance(size_t i) const { return mAdvances[i]; }
     const std::vector<float>& getAdvances() const { return mAdvances; }
-    void getBounds(MinikinRect* rect) const { rect->set(mBounds); }
-    const MinikinRect& getBounds() const { return mBounds; }
 
     // Purge all caches, useful in low memory conditions
     static void purgeCaches();
@@ -135,7 +134,6 @@
     std::vector<float> mAdvances;
 
     float mAdvance;
-    MinikinRect mBounds;
 };
 
 }  // namespace minikin
diff --git a/include/minikin/LayoutCache.h b/include/minikin/LayoutCache.h
index ee51f11..a1cc34a 100644
--- a/include/minikin/LayoutCache.h
+++ b/include/minikin/LayoutCache.h
@@ -83,9 +83,9 @@
 
 private:
     const uint16_t* mChars;
-    size_t mNchars;
-    size_t mStart;
-    size_t mCount;
+    uint32_t mNchars;
+    uint32_t mStart;
+    uint32_t mCount;
     uint32_t mId;  // for the font collection
     FontStyle mStyle;
     float mSize;
diff --git a/include/minikin/LayoutCore.h b/include/minikin/LayoutCore.h
index 852b985..cade517 100644
--- a/include/minikin/LayoutCore.h
+++ b/include/minikin/LayoutCore.h
@@ -49,11 +49,10 @@
 
     // Low level accessors.
     const std::vector<uint8_t>& fontIndices() const { return mFontIndices; }
-    const std::vector<uint32_t> glyphIds() const { return mGlyphIds; }
-    const std::vector<Point> points() const { return mPoints; }
-    const std::vector<float> advances() const { return mAdvances; }
+    const std::vector<uint32_t>& glyphIds() const { return mGlyphIds; }
+    const std::vector<Point>& points() const { return mPoints; }
+    const std::vector<float>& advances() const { return mAdvances; }
     float advance() const { return mAdvance; }
-    const MinikinRect& bounds() const { return mBounds; }
     const MinikinExtent& extent() const { return mExtent; }
     const std::vector<FakedFont>& fonts() const { return mFonts; }
 
@@ -79,7 +78,6 @@
     std::vector<float> mAdvances;  // per code units
 
     float mAdvance;
-    MinikinRect mBounds;
     MinikinExtent mExtent;
 
     std::vector<FakedFont> mFonts;
diff --git a/include/minikin/LineBreaker.h b/include/minikin/LineBreaker.h
index 3410339..5d3e752 100644
--- a/include/minikin/LineBreaker.h
+++ b/include/minikin/LineBreaker.h
@@ -62,6 +62,9 @@
                 return mStops[i];
             }
         }
+        if (mTabWidth == 0) {
+            return 0;
+        }
         return floor(widthSoFar / mTabWidth + 1) * mTabWidth;
     }
 
diff --git a/include/minikin/LocaleList.h b/include/minikin/LocaleList.h
index bfc26c7..d173a46 100644
--- a/include/minikin/LocaleList.h
+++ b/include/minikin/LocaleList.h
@@ -21,11 +21,17 @@
 
 namespace minikin {
 
+// A special ID for the empty locale list.
+// This value must be 0 since the empty locale list is inserted into mLocaleLists by default.
+const static uint32_t kEmptyLocaleListId = 0;
+
 // Looks up a locale list from an internal cache and returns its ID.
 // If the passed locale list is not in the cache, registers it and returns newly assigned ID.
 // TODO: Introduce LocaleId type.
 uint32_t registerLocaleList(const std::string& locales);
 
+std::string getLocaleString(uint32_t id);
+
 }  // namespace minikin
 
 #endif  // MINIKIN_LOCALE_H
diff --git a/include/minikin/Measurement.h b/include/minikin/Measurement.h
index c8b97d1..d0aaa51 100644
--- a/include/minikin/Measurement.h
+++ b/include/minikin/Measurement.h
@@ -20,6 +20,8 @@
 #include <cstddef>
 #include <cstdint>
 
+#include <minikin/Layout.h>
+
 namespace minikin {
 
 float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
@@ -28,6 +30,10 @@
 size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
                            float advance);
 
+void getBounds(const U16StringPiece& str, const Range& range, Bidi bidiFlags,
+               const MinikinPaint& paint, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
+               MinikinRect* out);
+
 }  // namespace minikin
 
 #endif  // MINIKIN_MEASUREMENT_H
diff --git a/include/minikin/MinikinFont.h b/include/minikin/MinikinFont.h
index a5ba074..4d3ba9c 100644
--- a/include/minikin/MinikinFont.h
+++ b/include/minikin/MinikinFont.h
@@ -34,7 +34,7 @@
 // multiple actual implementations of fonts.
 class MinikinFont {
 public:
-    explicit MinikinFont(int32_t uniqueId) : mUniqueId(uniqueId) {}
+    MinikinFont() {}
 
     virtual ~MinikinFont() {}
 
@@ -54,6 +54,9 @@
     virtual void GetFontExtent(MinikinExtent* extent, const MinikinPaint& paint,
                                const FontFakery& fakery) const = 0;
 
+    // Returns the font path or an empty string.
+    virtual const std::string& GetFontPath() const = 0;
+
     // Override if font can provide access to raw data
     virtual const void* GetFontData() const { return nullptr; }
 
@@ -64,6 +67,8 @@
     // Returns index within OpenType collection
     virtual int GetFontIndex() const { return 0; }
 
+    virtual int GetSourceId() const { return 0; }
+
     virtual const std::vector<minikin::FontVariation>& GetAxes() const = 0;
 
     virtual std::shared_ptr<MinikinFont> createFontWithVariation(
@@ -74,11 +79,6 @@
     static uint32_t MakeTag(char c1, char c2, char c3, char c4) {
         return ((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) | ((uint32_t)c3 << 8) | (uint32_t)c4;
     }
-
-    int32_t GetUniqueId() const { return mUniqueId; }
-
-private:
-    const int32_t mUniqueId;
 };
 
 }  // namespace minikin
diff --git a/include/minikin/MinikinRect.h b/include/minikin/MinikinRect.h
index 6a3d88b..38c2180 100644
--- a/include/minikin/MinikinRect.h
+++ b/include/minikin/MinikinRect.h
@@ -28,6 +28,7 @@
     bool operator==(const MinikinRect& o) const {
         return mLeft == o.mLeft && mTop == o.mTop && mRight == o.mRight && mBottom == o.mBottom;
     }
+    bool operator!=(const MinikinRect& o) const { return !(*this == o); }
     float mLeft;
     float mTop;
     float mRight;
diff --git a/include/minikin/SparseBitSet.h b/include/minikin/SparseBitSet.h
index 9ccef12..3034243 100644
--- a/include/minikin/SparseBitSet.h
+++ b/include/minikin/SparseBitSet.h
@@ -17,6 +17,7 @@
 #ifndef MINIKIN_SPARSE_BIT_SET_H
 #define MINIKIN_SPARSE_BIT_SET_H
 
+#include <minikin/Buffer.h>
 #include <sys/types.h>
 #include <cstdint>
 #include <memory>
@@ -42,9 +43,13 @@
         initFromRanges(ranges, nRanges);
     }
 
+    explicit SparseBitSet(BufferReader* reader) : SparseBitSet() { initFromBuffer(reader); }
+
     SparseBitSet(SparseBitSet&&) = default;
     SparseBitSet& operator=(SparseBitSet&&) = default;
 
+    void writeTo(BufferWriter* writer) const;
+
     // Determine whether the value is included in the set
     bool get(uint32_t ch) const {
         if (ch >= mMaxVal) return false;
@@ -64,6 +69,7 @@
 
 private:
     void initFromRanges(const uint32_t* ranges, size_t nRanges);
+    void initFromBuffer(BufferReader* reader);
 
     static const uint32_t kMaximumCapacity = 0xFFFFFF;
     static const int kLogValuesPerPage = 8;
@@ -81,11 +87,16 @@
     static int CountLeadingZeros(element x);
 
     uint32_t mMaxVal;
-
-    std::unique_ptr<uint16_t[]> mIndices;
-    std::unique_ptr<element[]> mBitmaps;
+    uint32_t mIndicesCount;
+    const uint16_t* mIndices;
+    uint32_t mBitmapsCount;
+    const element* mBitmaps;
     uint16_t mZeroPageIndex;
 
+    // Owns allocated memory if this class is created from ranges, otherwise these are nullptr.
+    std::unique_ptr<uint16_t[]> mOwnedIndices;
+    std::unique_ptr<element[]> mOwnedBitmaps;
+
     // Forbid copy and assign.
     SparseBitSet(const SparseBitSet&) = delete;
     void operator=(const SparseBitSet&) = delete;
diff --git a/include/minikin/SystemFonts.h b/include/minikin/SystemFonts.h
index 4108215..cf4ab75 100644
--- a/include/minikin/SystemFonts.h
+++ b/include/minikin/SystemFonts.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <memory>
+#include <mutex>
 #include <string>
 
 #include "minikin/FontCollection.h"
@@ -33,39 +34,78 @@
         return getInstance().findFontCollectionInternal(familyName);
     }
 
-    // Do not call this function outside Zygote process.
     static void registerFallback(const std::string& familyName,
                                  const std::shared_ptr<FontCollection>& fc) {
         return getInstance().registerFallbackInternal(familyName, fc);
     }
 
-    // Do not call this function outside Zygote process.
     static void registerDefault(const std::shared_ptr<FontCollection>& fc) {
         return getInstance().registerDefaultInternal(fc);
     }
 
+    using FontMapDeleter = std::function<void()>;
+
+    static void addFontMap(std::shared_ptr<FontCollection>&& collections) {
+        return getInstance().addFontMapInternal(std::move(collections));
+    }
+
+    // This obtains a mutex inside, so do not call this method inside callback.
+    static void getFontMap(
+            std::function<void(const std::vector<std::shared_ptr<FontCollection>>&)> func) {
+        return getInstance().getFontMapInternal(func);
+    }
+
+    static void getFontSet(std::function<void(const std::vector<std::shared_ptr<Font>>&)> func) {
+        return getInstance().getFontSetInternal(func);
+    }
+
 protected:
     // Visible for testing purposes.
     SystemFonts() {}
     virtual ~SystemFonts() {}
 
-    std::shared_ptr<FontCollection> findFontCollectionInternal(const std::string& familyName) const;
+    std::shared_ptr<FontCollection> findFontCollectionInternal(const std::string& familyName);
     void registerFallbackInternal(const std::string& familyName,
                                   const std::shared_ptr<FontCollection>& fc) {
-        mSystemFallbacks.insert(std::make_pair(familyName, fc));
+        std::lock_guard<std::mutex> lock(mMutex);
+        mSystemFallbacks[familyName] = fc;
     }
 
     void registerDefaultInternal(const std::shared_ptr<FontCollection>& fc) {
+        std::lock_guard<std::mutex> lock(mMutex);
         mDefaultFallback = fc;
     }
 
+    void addFontMapInternal(std::shared_ptr<FontCollection>&& collections) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mCollections.emplace_back(std::move(collections));
+    }
+
+    void getFontMapInternal(
+            std::function<void(const std::vector<std::shared_ptr<FontCollection>>&)> func) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        func(mCollections);
+    }
+
+    void getFontSetInternal(std::function<void(const std::vector<std::shared_ptr<Font>>&)> func) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (!mFonts) {
+            buildFontSetLocked();
+        }
+        func(mFonts.value());
+    }
+
 private:
     static SystemFonts& getInstance();
 
-    // There is no mutex guard here since registerFallback is designed to be
-    // called only in Zygote.
-    std::map<std::string, std::shared_ptr<FontCollection>> mSystemFallbacks;
-    std::shared_ptr<FontCollection> mDefaultFallback;
+    void buildFontSetLocked() EXCLUSIVE_LOCKS_REQUIRED(mMutex);
+
+    std::map<std::string, std::shared_ptr<FontCollection>> mSystemFallbacks GUARDED_BY(mMutex);
+    std::shared_ptr<FontCollection> mDefaultFallback GUARDED_BY(mMutex);
+    std::vector<std::shared_ptr<FontCollection>> mCollections GUARDED_BY(mMutex);
+    std::optional<std::vector<std::shared_ptr<Font>>> mFonts GUARDED_BY(mMutex);
+
+    std::mutex mMutex;
 };
 
 }  // namespace minikin
diff --git a/libs/minikin/Android.bp b/libs/minikin/Android.bp
index 931c8d0..8356ea4 100644
--- a/libs/minikin/Android.bp
+++ b/libs/minikin/Android.bp
@@ -28,10 +28,13 @@
     host_supported: true,
     srcs: [
         "BidiUtils.cpp",
+        "BoundsCache.cpp",
         "CmapCoverage.cpp",
         "Emoji.cpp",
+        "Font.cpp",
         "FontCollection.cpp",
         "FontFamily.cpp",
+        "FontFileParser.cpp",
         "FontUtils.cpp",
         "GraphemeBreak.cpp",
         "GreedyLineBreaker.cpp",
diff --git a/libs/minikin/BoundsCache.cpp b/libs/minikin/BoundsCache.cpp
new file mode 100644
index 0000000..1edc853
--- /dev/null
+++ b/libs/minikin/BoundsCache.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "minikin/BoundsCache.h"
+
+namespace minikin {
+
+void ValueExtractor::operator()(const LayoutPiece& layoutPiece, const MinikinPaint& paint) {
+    value.reset(new BoundsValue);
+    value->rect = BoundsCache::getBounds(layoutPiece, paint);
+    value->advance = layoutPiece.advance();
+}
+
+// static
+MinikinRect BoundsCache::getBounds(const LayoutPiece& layoutPiece, const MinikinPaint& paint) {
+    MinikinRect pieceBounds;
+    MinikinRect tmpRect;
+    for (uint32_t i = 0; i < layoutPiece.glyphCount(); ++i) {
+        const FakedFont& font = layoutPiece.fontAt(i);
+        const Point& point = layoutPiece.pointAt(i);
+
+        MinikinFont* minikinFont = font.font->typeface().get();
+        minikinFont->GetBounds(&tmpRect, layoutPiece.glyphIdAt(i), paint, font.fakery);
+        tmpRect.offset(point.x, point.y);
+        pieceBounds.join(tmpRect);
+    }
+    return pieceBounds;
+}
+
+}  // namespace minikin
diff --git a/libs/minikin/Font.cpp b/libs/minikin/Font.cpp
new file mode 100644
index 0000000..c2e74b7
--- /dev/null
+++ b/libs/minikin/Font.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include "minikin/Font.h"
+
+#include <vector>
+
+#include <hb-ot.h>
+#include <hb.h>
+#include <log/log.h>
+
+#include "minikin/HbUtils.h"
+#include "minikin/MinikinFont.h"
+
+#include "FontUtils.h"
+#include "MinikinInternal.h"
+
+namespace minikin {
+
+std::shared_ptr<Font> Font::Builder::build() {
+    if (mIsWeightSet && mIsSlantSet) {
+        // No need to read OS/2 header of the font file.
+        return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
+                                              prepareFont(mTypeface), mLocaleListId));
+    }
+
+    HbFontUniquePtr font = prepareFont(mTypeface);
+    FontStyle styleFromFont = analyzeStyle(font);
+    if (!mIsWeightSet) {
+        mWeight = styleFromFont.weight();
+    }
+    if (!mIsSlantSet) {
+        mSlant = styleFromFont.slant();
+    }
+    return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
+                                          std::move(font), mLocaleListId));
+}
+
+const std::shared_ptr<MinikinFont>& Font::typeface() const {
+    std::lock_guard lock(mTypefaceMutex);
+    if (mTypeface) return mTypeface;
+    initTypefaceLocked();
+    return mTypeface;
+}
+
+const HbFontUniquePtr& Font::baseFont() const {
+    std::lock_guard lock(mTypefaceMutex);
+    if (mBaseFont) return mBaseFont;
+    initTypefaceLocked();
+    mBaseFont = prepareFont(mTypeface);
+    return mBaseFont;
+}
+
+void Font::initTypefaceLocked() const {
+    if (mTypeface) return;
+    MINIKIN_ASSERT(mTypefaceLoader, "mTypefaceLoader should not be empty when mTypeface is null");
+    mTypeface = mTypefaceLoader(mTypefaceMetadataReader);
+}
+
+// static
+HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
+    const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
+    size_t size = typeface->GetFontSize();
+    uint32_t ttcIndex = typeface->GetFontIndex();
+
+    HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
+    HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
+    HbFontUniquePtr parent(hb_font_create(face.get()));
+    hb_ot_font_set_funcs(parent.get());
+
+    uint32_t upem = hb_face_get_upem(face.get());
+    hb_font_set_scale(parent.get(), upem, upem);
+
+    HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
+    std::vector<hb_variation_t> variations;
+    variations.reserve(typeface->GetAxes().size());
+    for (const FontVariation& variation : typeface->GetAxes()) {
+        variations.push_back({variation.axisTag, variation.value});
+    }
+    hb_font_set_variations(font.get(), variations.data(), variations.size());
+    return font;
+}
+
+// static
+FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
+    HbBlob os2Table(font, MinikinFont::MakeTag('O', 'S', '/', '2'));
+    if (!os2Table) {
+        return FontStyle();
+    }
+
+    int weight;
+    bool italic;
+    if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
+        return FontStyle();
+    }
+    // TODO: Update weight/italic based on fvar value.
+    return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
+}
+
+std::unordered_set<AxisTag> Font::getSupportedAxes() const {
+    HbBlob fvarTable(baseFont(), MinikinFont::MakeTag('f', 'v', 'a', 'r'));
+    if (!fvarTable) {
+        return std::unordered_set<AxisTag>();
+    }
+    std::unordered_set<AxisTag> supportedAxes;
+    analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
+    return supportedAxes;
+}
+
+}  // namespace minikin
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 6cbabea..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);
@@ -83,24 +101,91 @@
     // A font can have a glyph for a base code point and variation selector pair but no glyph for
     // the base code point without variation selector. The family won't be listed in the range in
     // this case.
+    mOwnedRanges = std::make_unique<Range[]>(nPages);
+    mRanges = mOwnedRanges.get();
+    mRangesCount = nPages;
     for (size_t i = 0; i < nPages; i++) {
-        Range dummy;
-        mRanges.push_back(dummy);
-        Range* range = &mRanges.back();
-        range->start = mFamilyVec.size();
+        Range* range = &mOwnedRanges[i];
+        range->start = mOwnedFamilyVec.size();
         for (size_t j = 0; j < nTypefaces; j++) {
             if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
                 const std::shared_ptr<FontFamily>& family = mFamilies[j];
-                mFamilyVec.push_back(static_cast<uint8_t>(j));
+                mOwnedFamilyVec.push_back(static_cast<uint8_t>(j));
                 uint32_t nextChar = family->getCoverage().nextSetBit((i + 1) << kLogCharsPerPage);
                 lastChar[j] = nextChar;
             }
         }
-        range->end = mFamilyVec.size();
+        range->end = mOwnedFamilyVec.size();
     }
     // See the comment in Range for more details.
-    LOG_ALWAYS_FATAL_IF(mFamilyVec.size() >= 0xFFFF,
+    LOG_ALWAYS_FATAL_IF(mOwnedFamilyVec.size() >= 0xFFFF,
                         "Exceeded the maximum indexable cmap coverage.");
+    mFamilyVec = mOwnedFamilyVec.data();
+    mFamilyVecCount = mOwnedFamilyVec.size();
+}
+
+FontCollection::FontCollection(BufferReader* reader,
+                               const std::vector<std::shared_ptr<FontFamily>>& families) {
+    mId = gNextCollectionId++;
+    mMaxChar = reader->read<uint32_t>();
+    uint32_t familiesCount = reader->read<uint32_t>();
+    mFamilies.reserve(familiesCount);
+    for (uint32_t i = 0; i < familiesCount; i++) {
+        uint32_t index = reader->read<uint32_t>();
+        if (index >= families.size()) {
+            ALOGE("Invalid FontFamily index: %zu", (size_t)index);
+        } else {
+            mFamilies.push_back(families[index]);
+            if (families[index]->hasVSTable()) {
+                mVSFamilyVec.push_back(families[index]);
+            }
+        }
+    }
+    // Range is two packed uint16_t
+    static_assert(sizeof(Range) == 4);
+    std::tie(mRanges, mRangesCount) = reader->readArray<Range>();
+    std::tie(mFamilyVec, mFamilyVecCount) = reader->readArray<uint8_t>();
+    const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
+    mSupportedAxes.insert(axesPtr, axesPtr + axesCount);
+}
+
+void FontCollection::writeTo(BufferWriter* writer,
+                             const std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>&
+                                     fontFamilyToIndexMap) const {
+    writer->write<uint32_t>(mMaxChar);
+    writer->write<uint32_t>(mFamilies.size());
+    for (const std::shared_ptr<FontFamily>& fontFamily : mFamilies) {
+        auto it = fontFamilyToIndexMap.find(fontFamily);
+        if (it == fontFamilyToIndexMap.end()) {
+            ALOGE("fontFamily not found in fontFamilyToIndexMap");
+            writer->write<uint32_t>(-1);
+        } else {
+            writer->write<uint32_t>(it->second);
+        }
+    }
+    writer->writeArray<Range>(mRanges, mRangesCount);
+    writer->writeArray<uint8_t>(mFamilyVec, mFamilyVecCount);
+    // No need to serialize mVSFamilyVec as it can be reconstructed easily from mFamilies.
+    std::vector<AxisTag> axes(mSupportedAxes.begin(), mSupportedAxes.end());
+    // Sort axes to be deterministic.
+    std::sort(axes.begin(), axes.end());
+    writer->writeArray<AxisTag>(axes.data(), axes.size());
+}
+
+// static
+void FontCollection::collectAllFontFamilies(
+        const std::vector<std::shared_ptr<FontCollection>>& fontCollections,
+        std::vector<std::shared_ptr<FontFamily>>* outAllFontFamilies,
+        std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>* outFontFamilyToIndexMap) {
+    for (const auto& fontCollection : fontCollections) {
+        for (const std::shared_ptr<FontFamily>& fontFamily : fontCollection->mFamilies) {
+            bool inserted =
+                    outFontFamilyToIndexMap->emplace(fontFamily, outAllFontFamilies->size()).second;
+            if (inserted) {
+                outAllFontFamilies->push_back(fontFamily);
+            }
+        }
+    }
 }
 
 // Special scores for the font fallback.
@@ -249,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];
@@ -262,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)) {
@@ -290,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
@@ -312,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
@@ -323,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;
 }
@@ -368,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;
@@ -411,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
@@ -427,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;
@@ -465,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.
-        result.resize(runMax);
+        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/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index 85a5142..f1fd00a 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -18,11 +18,9 @@
 
 #include "minikin/FontFamily.h"
 
-#include <cstdint>
+#include <algorithm>
 #include <vector>
 
-#include <hb-ot.h>
-#include <hb.h>
 #include <log/log.h>
 
 #include "minikin/CmapCoverage.h"
@@ -37,82 +35,14 @@
 
 namespace minikin {
 
-Font Font::Builder::build() {
-    if (mIsWeightSet && mIsSlantSet) {
-        // No need to read OS/2 header of the font file.
-        return Font(std::move(mTypeface), FontStyle(mWeight, mSlant), prepareFont(mTypeface));
-    }
-
-    HbFontUniquePtr font = prepareFont(mTypeface);
-    FontStyle styleFromFont = analyzeStyle(font);
-    if (!mIsWeightSet) {
-        mWeight = styleFromFont.weight();
-    }
-    if (!mIsSlantSet) {
-        mSlant = styleFromFont.slant();
-    }
-    return Font(std::move(mTypeface), FontStyle(mWeight, mSlant), std::move(font));
-}
-
-// static
-HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
-    const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
-    size_t size = typeface->GetFontSize();
-    uint32_t ttcIndex = typeface->GetFontIndex();
-
-    HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
-    HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
-    HbFontUniquePtr parent(hb_font_create(face.get()));
-    hb_ot_font_set_funcs(parent.get());
-
-    uint32_t upem = hb_face_get_upem(face.get());
-    hb_font_set_scale(parent.get(), upem, upem);
-
-    HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
-    std::vector<hb_variation_t> variations;
-    variations.reserve(typeface->GetAxes().size());
-    for (const FontVariation& variation : typeface->GetAxes()) {
-        variations.push_back({variation.axisTag, variation.value});
-    }
-    hb_font_set_variations(font.get(), variations.data(), variations.size());
-    return font;
-}
-
-// static
-FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
-    HbBlob os2Table(font, MinikinFont::MakeTag('O', 'S', '/', '2'));
-    if (!os2Table) {
-        return FontStyle();
-    }
-
-    int weight;
-    bool italic;
-    if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
-        return FontStyle();
-    }
-    // TODO: Update weight/italic based on fvar value.
-    return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
-}
-
-std::unordered_set<AxisTag> Font::getSupportedAxes() const {
-    HbBlob fvarTable(mBaseFont, MinikinFont::MakeTag('f', 'v', 'a', 'r'));
-    if (!fvarTable) {
-        return std::unordered_set<AxisTag>();
-    }
-    std::unordered_set<AxisTag> supportedAxes;
-    analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
-    return supportedAxes;
-}
-
-FontFamily::FontFamily(std::vector<Font>&& fonts)
+FontFamily::FontFamily(std::vector<std::shared_ptr<Font>>&& fonts)
         : FontFamily(FamilyVariant::DEFAULT, std::move(fonts)) {}
 
-FontFamily::FontFamily(FamilyVariant variant, std::vector<Font>&& fonts)
-        : FontFamily(LocaleListCache::kEmptyListId, variant, std::move(fonts),
-                     false /* isCustomFallback */) {}
+FontFamily::FontFamily(FamilyVariant variant, std::vector<std::shared_ptr<Font>>&& fonts)
+        : FontFamily(kEmptyLocaleListId, variant, std::move(fonts), false /* isCustomFallback */) {}
 
-FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant, std::vector<Font>&& fonts,
-                       bool isCustomFallback)
+FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
+                       std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback)
         : mLocaleListId(localeListId),
           mVariant(variant),
           mFonts(std::move(fonts)),
@@ -123,6 +53,83 @@
     computeCoverage();
 }
 
+FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
+                       std::vector<std::shared_ptr<Font>>&& fonts,
+                       std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+                       bool isCustomFallback, SparseBitSet&& coverage,
+                       std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage)
+        : mLocaleListId(localeListId),
+          mVariant(variant),
+          mFonts(std::move(fonts)),
+          mSupportedAxes(std::move(supportedAxes)),
+          mIsColorEmoji(isColorEmoji),
+          mIsCustomFallback(isCustomFallback),
+          mCoverage(std::move(coverage)),
+          mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {}
+
+// Read fields other than mFonts, mLocaleList.
+// static
+std::shared_ptr<FontFamily> FontFamily::readFromInternal(BufferReader* reader,
+                                                         std::vector<std::shared_ptr<Font>>&& fonts,
+                                                         uint32_t localeListId) {
+    // FamilyVariant is uint8_t
+    static_assert(sizeof(FamilyVariant) == 1);
+    FamilyVariant variant = reader->read<FamilyVariant>();
+    // AxisTag is uint32_t
+    static_assert(sizeof(AxisTag) == 4);
+    const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
+    std::unordered_set<AxisTag> supportedAxes(axesPtr, axesPtr + axesCount);
+    bool isColorEmoji = static_cast<bool>(reader->read<uint8_t>());
+    bool isCustomFallback = static_cast<bool>(reader->read<uint8_t>());
+    SparseBitSet coverage(reader);
+    // Read mCmapFmt14Coverage. As it can have null entries, it is stored in the buffer as a sparse
+    // array (size, non-null entry count, array of (index, entry)).
+    uint32_t cmapFmt14CoverageSize = reader->read<uint32_t>();
+    std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage(cmapFmt14CoverageSize);
+    uint32_t cmapFmt14CoverageEntryCount = reader->read<uint32_t>();
+    for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) {
+        uint32_t index = reader->read<uint32_t>();
+        cmapFmt14Coverage[index] = std::make_unique<SparseBitSet>(reader);
+    }
+    return std::shared_ptr<FontFamily>(new FontFamily(
+            localeListId, variant, std::move(fonts), std::move(supportedAxes), isColorEmoji,
+            isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage)));
+}
+
+// static
+uint32_t FontFamily::readLocaleListInternal(BufferReader* reader) {
+    return LocaleListCache::readFrom(reader);
+}
+
+// Write fields other than mFonts.
+void FontFamily::writeToInternal(BufferWriter* writer) const {
+    writer->write<FamilyVariant>(mVariant);
+    std::vector<AxisTag> axes(mSupportedAxes.begin(), mSupportedAxes.end());
+    // Sort axes to be deterministic.
+    std::sort(axes.begin(), axes.end());
+    writer->writeArray<AxisTag>(axes.data(), axes.size());
+    writer->write<uint8_t>(mIsColorEmoji);
+    writer->write<uint8_t>(mIsCustomFallback);
+    mCoverage.writeTo(writer);
+    // Write mCmapFmt14Coverage as a sparse array (size, non-null entry count,
+    // array of (index, entry))
+    writer->write<uint32_t>(mCmapFmt14Coverage.size());
+    uint32_t cmapFmt14CoverageEntryCount = 0;
+    for (const std::unique_ptr<SparseBitSet>& coverage : mCmapFmt14Coverage) {
+        if (coverage != nullptr) cmapFmt14CoverageEntryCount++;
+    }
+    writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
+    for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) {
+        if (mCmapFmt14Coverage[i] != nullptr) {
+            writer->write<uint32_t>(i);
+            mCmapFmt14Coverage[i]->writeTo(writer);
+        }
+    }
+}
+
+void FontFamily::writeLocaleListInternal(BufferWriter* writer) const {
+    LocaleListCache::writeTo(writer, mLocaleListId);
+}
 // Compute a matching metric between two styles - 0 is an exact match
 static int computeMatch(FontStyle style1, FontStyle style2) {
     if (style1 == style2) return 0;
@@ -144,21 +151,23 @@
 }
 
 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
-    const Font* bestFont = &mFonts[0];
+    int bestIndex = 0;
+    Font* bestFont = mFonts[bestIndex].get();
     int bestMatch = computeMatch(bestFont->style(), style);
     for (size_t i = 1; i < mFonts.size(); i++) {
-        const Font& font = mFonts[i];
-        int match = computeMatch(font.style(), style);
+        Font* font = mFonts[i].get();
+        int match = computeMatch(font->style(), style);
         if (i == 0 || match < bestMatch) {
-            bestFont = &font;
+            bestFont = font;
+            bestIndex = i;
             bestMatch = match;
         }
     }
-    return FakedFont{bestFont, computeFakery(style, bestFont->style())};
+    return FakedFont{mFonts[bestIndex], computeFakery(style, bestFont->style())};
 }
 
 void FontFamily::computeCoverage() {
-    const Font* font = getClosestMatch(FontStyle()).font;
+    const std::shared_ptr<Font>& font = getClosestMatch(FontStyle()).font;
     HbBlob cmapTable(font->baseFont(), MinikinFont::MakeTag('c', 'm', 'a', 'p'));
     if (cmapTable.get() == nullptr) {
         ALOGE("Could not get cmap table size!\n");
@@ -168,7 +177,7 @@
     mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
 
     for (size_t i = 0; i < mFonts.size(); ++i) {
-        std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxes();
+        std::unordered_set<AxisTag> supportedAxes = mFonts[i]->getSupportedAxes();
         mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
     }
 }
@@ -216,10 +225,10 @@
         return nullptr;
     }
 
-    std::vector<Font> fonts;
-    for (const Font& font : mFonts) {
+    std::vector<std::shared_ptr<Font>> fonts;
+    for (const auto& font : mFonts) {
         bool supportedVariations = false;
-        std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxes();
+        std::unordered_set<AxisTag> supportedAxes = font->getSupportedAxes();
         if (!supportedAxes.empty()) {
             for (const FontVariation& variation : variations) {
                 if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
@@ -230,12 +239,13 @@
         }
         std::shared_ptr<MinikinFont> minikinFont;
         if (supportedVariations) {
-            minikinFont = font.typeface()->createFontWithVariation(variations);
+            minikinFont = font->typeface()->createFontWithVariation(variations);
         }
         if (minikinFont == nullptr) {
-            minikinFont = font.typeface();
+            fonts.push_back(font);
+        } else {
+            fonts.push_back(Font::Builder(minikinFont).setStyle(font->style()).build());
         }
-        fonts.push_back(Font::Builder(minikinFont).setStyle(font.style()).build());
     }
 
     return std::shared_ptr<FontFamily>(
diff --git a/libs/minikin/FontFileParser.cpp b/libs/minikin/FontFileParser.cpp
new file mode 100644
index 0000000..1bcb711
--- /dev/null
+++ b/libs/minikin/FontFileParser.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#include "minikin/FontFileParser.h"
+
+#define LOG_TAG "Minikin"
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <hb-ot.h>
+#include <hb.h>
+
+#include "MinikinInternal.h"
+#include "minikin/MinikinFont.h"
+
+namespace minikin {
+
+namespace {
+
+class SafeFontBufferReader {
+public:
+    SafeFontBufferReader(const void* buffer, size_t size)
+            : mBuffer(reinterpret_cast<const uint8_t*>(buffer)),
+              mSize(size),
+              mPos(0),
+              mError(false) {}
+
+    template <typename T>
+    T readBE() {
+        if (mError) return T();
+
+        if ((mSize - mPos) < sizeof(T)) {
+            mError = true;
+            return T();
+        }
+        const T* data = reinterpret_cast<const T*>(mBuffer + mPos);
+        mPos += sizeof(T);
+        return *data;
+    }
+
+    uint16_t readU16() {
+        if (mError) return 0;
+
+        if ((mSize - mPos) < 2) {
+            mError = true;
+            return 0;
+        }
+        uint16_t out = ((uint32_t)mBuffer[mPos]) << 8 | ((uint32_t)mBuffer[mPos + 1]);
+        mPos += 2;
+        return out;
+    };
+
+    uint32_t readU32() {
+        if (mError) return 0;
+
+        if ((mSize - mPos) < 4) {
+            mError = true;
+            return 0;
+        }
+
+        uint32_t out = ((uint32_t)mBuffer[mPos]) << 24 | ((uint32_t)mBuffer[mPos + 1]) << 16 |
+                       ((uint32_t)mBuffer[mPos + 2]) << 8 | ((uint32_t)mBuffer[mPos + 3]);
+        mPos += 4;
+        return out;
+    };
+
+    void seek(size_t pos) {
+        if (mError) return;
+
+        if (pos > mSize) {
+            mError = true;
+        } else {
+            mPos = pos;
+        }
+    }
+
+    size_t remaining() const {
+        if (mError) return 0;
+        return mSize - mPos;
+    }
+
+    bool error() const { return mError; }
+
+private:
+    const uint8_t* mBuffer;
+    size_t mSize;
+    size_t mPos;
+    bool mError;
+};
+
+bool isPostScriptNameAllowedChar(char c) {
+    // OpenType spec says only ASCII codes 33 to 126, ecept for the '[', ']', '(', ')', '{', '}',
+    // '<', '>', '/', '%'.
+    if (!(33 <= c && c <= 126)) {
+        return false;
+    }
+    if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || c == '<' ||
+        c == '>' || c == '/' || c == '%') {
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace
+
+// static
+bool FontFileParser::analyzeFontRevision(const uint8_t* head_data, size_t head_size,
+                                         uint32_t* out) {
+    SafeFontBufferReader reader(head_data, head_size);
+
+    if (reader.remaining() < 8) {
+        return false;  // At least head table has 8 bytes, for version and fontRevision
+    }
+
+    uint32_t majorVersion = reader.readU16();
+    if (reader.error()) return false;
+    uint32_t minorVersion = reader.readU16();
+    if (reader.error()) return false;
+
+    // Invalid head table header.
+    if (majorVersion != 1 && minorVersion != 0) return false;
+
+    *out = reader.readU32();
+    if (reader.error()) return false;
+    return true;
+}
+
+// static
+bool FontFileParser::checkPSName(const std::string& psName) {
+    if (psName.size() > 63) return false;
+
+    for (auto c : psName) {
+        if (!isPostScriptNameAllowedChar(c)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+FontFileParser::FontFileParser(const void* buffer, size_t size, uint32_t index)
+        : mFace(makeHbFace(buffer, size, index)) {}
+
+FontFileParser::FontFileParser(const HbFaceUniquePtr& face)
+        : mFace(hb_face_reference(face.get())) {}
+
+FontFileParser::FontFileParser(const HbFontUniquePtr& font)
+        : mFace(hb_face_reference(hb_font_get_face(font.get()))) {}
+
+FontFileParser::~FontFileParser() {}
+
+// static
+HbFaceUniquePtr FontFileParser::makeHbFace(const void* buffer, size_t size, uint32_t index) {
+    HbBlobUniquePtr blob(hb_blob_create(reinterpret_cast<const char*>(buffer), size,
+                                        HB_MEMORY_MODE_READONLY, nullptr, nullptr));
+    return HbFaceUniquePtr(hb_face_create(blob.get(), index));
+}
+
+std::optional<uint32_t> FontFileParser::getFontRevision() const {
+    if (!mFace) return std::optional<uint32_t>();
+
+    HbBlob headTable(mFace, MinikinFont::MakeTag('h', 'e', 'a', 'd'));
+    if (!headTable) return std::optional<uint32_t>();
+
+    uint32_t out = 0;
+    if (!analyzeFontRevision(headTable.get(), headTable.size(), &out)) {
+        return std::optional<uint32_t>();
+    }
+
+    return out;
+}
+
+std::optional<std::string> FontFileParser::getPostScriptName() const {
+    if (!mFace) return std::optional<std::string>();
+
+    unsigned int size = 64;  // PostScript name is up to 63 characters.
+    char buf[64] = {};
+
+    uint32_t result = hb_ot_name_get_utf8(mFace.get(), HB_OT_NAME_ID_POSTSCRIPT_NAME,
+                                          HB_LANGUAGE_INVALID, &size, buf);
+
+    if (result == 0) {  // not found.
+        return std::optional<std::string>();
+    }
+
+    std::string out(buf, size);
+
+    if (!checkPSName(out)) {  // Contains invalid characters.
+        return std::optional<std::string>();
+    }
+
+    return out;
+}
+
+std::optional<bool> FontFileParser::isPostScriptType1Font() const {
+    if (!mFace) return std::optional<bool>();
+
+    HbBlob cffTable(mFace, MinikinFont::MakeTag('C', 'F', 'F', ' '));
+    HbBlob cff2Table(mFace, MinikinFont::MakeTag('C', 'F', 'F', '2'));
+    return cffTable || cff2Table;
+}
+
+}  // namespace minikin
diff --git a/libs/minikin/GreedyLineBreaker.cpp b/libs/minikin/GreedyLineBreaker.cpp
index f6952a9..9bcb22d 100644
--- a/libs/minikin/GreedyLineBreaker.cpp
+++ b/libs/minikin/GreedyLineBreaker.cpp
@@ -255,7 +255,7 @@
 
 // TODO: Respect trailing line end spaces.
 bool GreedyLineBreaker::doLineBreakWithGraphemeBounds(const Range& range) {
-    double width = mMeasuredText.widths[range.getStart()];
+    float width = mMeasuredText.widths[range.getStart()];
 
     // Starting from + 1 since at least one character needs to be assigned to a line.
     for (uint32_t i = range.getStart() + 1; i < range.getEnd(); ++i) {
diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp
index b902648..e7d41c8 100644
--- a/libs/minikin/Layout.cpp
+++ b/libs/minikin/Layout.cpp
@@ -158,9 +158,6 @@
             mAdvances[start] += extraAdvance;
         }
     }
-    MinikinRect srcBounds(src.bounds());
-    srcBounds.offset(mAdvance, 0);
-    mBounds.join(srcBounds);
     mAdvance += src.advance() + extraAdvance;
 }
 
diff --git a/libs/minikin/LayoutCore.cpp b/libs/minikin/LayoutCore.cpp
index 6087646..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,14 +385,14 @@
          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;
-        auto it = fontMap.find(fakedFont.font);
+        FakedFont fakedFont = paint.font->getBestFont(substr, run, paint.fontStyle);
+        auto it = fontMap.find(fakedFont.font.get());
         uint8_t font_ix;
         if (it == fontMap.end()) {
             // First time to see this font.
             font_ix = mFonts.size();
             mFonts.push_back(fakedFont);
-            fontMap.insert(std::make_pair(fakedFont.font, font_ix));
+            fontMap.insert(std::make_pair(fakedFont.font.get(), font_ix));
 
             // We override some functions which are not thread safe.
             HbFontUniquePtr font(hb_font_create_sub_font(fakedFont.font->baseFont().get()));
@@ -421,8 +422,6 @@
         hb_font_set_ppem(hbFont.get(), size * scaleX, size);
         hb_font_set_scale(hbFont.get(), HBFloatToFixed(size * scaleX), HBFloatToFixed(size));
 
-        const bool is_color_bitmap_font = isColorBitmapFont(hbFont);
-
         // TODO: if there are multiple scripts within a font in an RTL run,
         // we need to reorder those runs. This is unlikely with our current
         // font stack, but should be done for correctness.
@@ -502,23 +501,6 @@
                 mGlyphIds.push_back(glyph_ix);
                 mPoints.emplace_back(x + xoff, y + yoff);
                 float xAdvance = HBFixedToFloat(positions[i].x_advance);
-                MinikinRect glyphBounds;
-                hb_glyph_extents_t extents = {};
-                if (is_color_bitmap_font &&
-                    hb_font_get_glyph_extents(hbFont.get(), glyph_ix, &extents)) {
-                    // Note that it is technically possible for a TrueType font to have outline and
-                    // embedded bitmap at the same time. We ignore modified bbox of hinted outline
-                    // glyphs in that case.
-                    glyphBounds.mLeft = roundf(HBFixedToFloat(extents.x_bearing));
-                    glyphBounds.mTop = roundf(HBFixedToFloat(-extents.y_bearing));
-                    glyphBounds.mRight = roundf(HBFixedToFloat(extents.x_bearing + extents.width));
-                    glyphBounds.mBottom =
-                            roundf(HBFixedToFloat(-extents.y_bearing - extents.height));
-                } else {
-                    fakedFont.font->typeface()->GetBounds(&glyphBounds, glyph_ix, paint,
-                                                          fakedFont.fakery);
-                }
-                glyphBounds.offset(xoff, yoff);
 
                 if (clusterBaseIndex < count) {
                     mAdvances[clusterBaseIndex] += xAdvance;
@@ -526,8 +508,6 @@
                     ALOGE("cluster %zu (start %zu) out of bounds of count %zu", clusterBaseIndex,
                           start, count);
                 }
-                glyphBounds.offset(x, y);
-                mBounds.join(glyphBounds);
                 x += xAdvance;
             }
             if (numGlyphs) {
diff --git a/libs/minikin/LineBreakerUtil.h b/libs/minikin/LineBreakerUtil.h
index eb058cc..8b383a4 100644
--- a/libs/minikin/LineBreakerUtil.h
+++ b/libs/minikin/LineBreakerUtil.h
@@ -36,7 +36,7 @@
 // paragraphs, accuracy could degrade using only 32-bit float. Note however that float is used
 // extensively on the Java side for this. This is a typedef so that we can easily change it based
 // on performance/accuracy tradeoff.
-typedef double ParaWidth;
+typedef float ParaWidth;
 
 // Hyphenates a string potentially containing non-breaking spaces.
 std::vector<HyphenationType> hyphenate(const U16StringPiece& string, const Hyphenator& hypenator);
diff --git a/libs/minikin/Locale.cpp b/libs/minikin/Locale.cpp
index c1ec389..553f61a 100644
--- a/libs/minikin/Locale.cpp
+++ b/libs/minikin/Locale.cpp
@@ -34,6 +34,18 @@
     return LocaleListCache::getId(locales);
 }
 
+std::string getLocaleString(uint32_t localeId) {
+    const LocaleList& localeList = LocaleListCache::getById(localeId);
+    std::string out;
+    for (size_t i = 0; i < localeList.size(); ++i) {
+        if (i != 0) {
+            out += ",";
+        }
+        out += localeList[i].getString();
+    }
+    return out;
+}
+
 // Check if a language code supports extension such as emoji and line break etc. according to its
 // subtag
 static bool isSubtag(const char* buf, size_t bufLen, const char* subtag, size_t subtagLen) {
diff --git a/libs/minikin/Locale.h b/libs/minikin/Locale.h
index 8052d6c..7557301 100644
--- a/libs/minikin/Locale.h
+++ b/libs/minikin/Locale.h
@@ -95,7 +95,17 @@
     // Parse from string
     Locale(const StringPiece& buf);
 
-    bool operator==(const Locale other) const {
+    // Parse from identifier. See getIdentifier() for the identifier format.
+    explicit Locale(uint64_t identifier)
+            : mScript(extractBits(identifier, 29, 20)),
+              mLanguage(extractBits(identifier, 49, 15)),
+              mRegion(extractBits(identifier, 14, 15)),
+              mSubScriptBits(scriptToSubScriptBits(mScript)),
+              mVariant(static_cast<Variant>(extractBits(identifier, 0, 2))),
+              mEmojiStyle(static_cast<EmojiStyle>(extractBits(identifier, 12, 2))),
+              mLBStyle(static_cast<LineBreakStyle>(extractBits(identifier, 10, 2))) {}
+
+    bool operator==(const Locale& other) const {
         return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage &&
                mRegion == other.mRegion && mVariant == other.mVariant &&
                mLBStyle == other.mLBStyle && mEmojiStyle == other.mEmojiStyle;
@@ -134,13 +144,13 @@
 
     // Identifier pattern:
     // |-------|-------|-------|-------|-------|-------|-------|-------|
-    // lllllllllllllll                                                   Language Code
-    //                ssssssssssssssssssss                               Script Code
-    //                                    rrrrrrrrrrrrrrr                Region Code
-    //                                                   ee              Emoji Style
-    //                                                     bb            Line Break Style
-    //                                                       XXXXXXXX    Free
-    //                                                               vv  German Variant
+    // lllllllllllllll                                                   Language Code (15 bits)
+    //                ssssssssssssssssssss                               Script Code (20 bits)
+    //                                    rrrrrrrrrrrrrrr                Region Code (15 bits)
+    //                                                   ee              Emoji Style (2 bits)
+    //                                                     bb            Line Break Style (2 bits)
+    //                                                       XXXXXXXX    Free (8 bits)
+    //                                                               vv  German Variant (2 bits)
     uint64_t getIdentifier() const {
         return ((uint64_t)mLanguage << 49) | ((uint64_t)mScript << 29) | ((uint64_t)mRegion << 14) |
                ((uint64_t)mEmojiStyle << 12) | ((uint64_t)mLBStyle << 10) | (uint64_t)mVariant;
@@ -181,6 +191,10 @@
 
     void resolveUnicodeExtension(const char* buf, size_t length);
 
+    inline static uint64_t extractBits(uint64_t value, uint8_t shift, uint8_t nBits) {
+        return (value >> shift) & ((1 << nBits) - 1);
+    }
+
     static uint8_t scriptToSubScriptBits(uint32_t rawScript);
 
     static LineBreakStyle resolveLineBreakStyle(const char* buf, size_t length);
diff --git a/libs/minikin/LocaleListCache.cpp b/libs/minikin/LocaleListCache.cpp
index eaf55ef..38800f7 100644
--- a/libs/minikin/LocaleListCache.cpp
+++ b/libs/minikin/LocaleListCache.cpp
@@ -21,6 +21,8 @@
 #include <unordered_set>
 
 #include <log/log.h>
+#include <minikin/Hasher.h>
+#include <minikin/LocaleList.h>
 #include <unicode/uloc.h>
 #include <unicode/umachine.h>
 
@@ -29,8 +31,6 @@
 
 namespace minikin {
 
-const uint32_t LocaleListCache::kEmptyListId;
-
 // Returns the text length of output.
 static size_t toLanguageTag(char* output, size_t outSize, const StringPiece& locale) {
     output[0] = '\0';
@@ -107,15 +107,39 @@
     return result;
 }
 
+size_t LocaleListCache::LocaleVectorHash::operator()(const std::vector<Locale>& locales) const {
+    Hasher hasher;
+    for (const auto& locale : locales) {
+        uint64_t id = locale.getIdentifier();
+        hasher.update(static_cast<uint32_t>((id >> 32) & 0xFFFFFFFF));
+        hasher.update(static_cast<uint32_t>(id & 0xFFFFFFFF));
+    }
+    return hasher.hash();
+}
+
 LocaleListCache::LocaleListCache() {
-    // Insert an empty locale list for mapping default locale list to kEmptyListId.
+    // Insert an empty locale list for mapping default locale list to kEmptyLocaleListId.
     // The default locale list has only one Locale and it is the unsupported locale.
     mLocaleLists.emplace_back();
-    mLocaleListLookupTable.insert(std::make_pair("", kEmptyListId));
+    mLocaleListLookupTable.emplace(std::vector<Locale>(), kEmptyLocaleListId);
+    mLocaleListStringCache.emplace("", kEmptyLocaleListId);
 }
 
 uint32_t LocaleListCache::getIdInternal(const std::string& locales) {
     std::lock_guard<std::mutex> lock(mMutex);
+    const auto& it = mLocaleListStringCache.find(locales);
+    if (it != mLocaleListStringCache.end()) {
+        return it->second;
+    }
+    uint32_t id = getIdInternal(parseLocaleList(locales));
+    mLocaleListStringCache.emplace(locales, id);
+    return id;
+}
+
+uint32_t LocaleListCache::getIdInternal(std::vector<Locale>&& locales) {
+    if (locales.empty()) {
+        return kEmptyLocaleListId;
+    }
     const auto& it = mLocaleListLookupTable.find(locales);
     if (it != mLocaleListLookupTable.end()) {
         return it->second;
@@ -123,15 +147,31 @@
 
     // Given locale list is not in cache. Insert it and return newly assigned ID.
     const uint32_t nextId = mLocaleLists.size();
-    LocaleList fontLocales(parseLocaleList(locales));
-    if (fontLocales.empty()) {
-        return kEmptyListId;
-    }
+    mLocaleListLookupTable.emplace(locales, nextId);
+    LocaleList fontLocales(std::move(locales));
     mLocaleLists.push_back(std::move(fontLocales));
-    mLocaleListLookupTable.insert(std::make_pair(locales, nextId));
     return nextId;
 }
 
+uint32_t LocaleListCache::readFromInternal(BufferReader* reader) {
+    uint32_t size = reader->read<uint32_t>();
+    std::vector<Locale> locales;
+    locales.reserve(size);
+    for (uint32_t i = 0; i < size; i++) {
+        locales.emplace_back(reader->read<uint64_t>());
+    }
+    std::lock_guard<std::mutex> lock(mMutex);
+    return getIdInternal(std::move(locales));
+}
+
+void LocaleListCache::writeToInternal(BufferWriter* writer, uint32_t id) {
+    const LocaleList& localeList = getByIdInternal(id);
+    writer->write<uint32_t>(localeList.size());
+    for (size_t i = 0; i < localeList.size(); i++) {
+        writer->write<uint64_t>(localeList[i].getIdentifier());
+    }
+}
+
 const LocaleList& LocaleListCache::getByIdInternal(uint32_t id) {
     std::lock_guard<std::mutex> lock(mMutex);
     MINIKIN_ASSERT(id < mLocaleLists.size(), "Lookup by unknown locale list ID.");
diff --git a/libs/minikin/LocaleListCache.h b/libs/minikin/LocaleListCache.h
index 61e3f81..a83b1c8 100644
--- a/libs/minikin/LocaleListCache.h
+++ b/libs/minikin/LocaleListCache.h
@@ -20,6 +20,7 @@
 #include <mutex>
 #include <unordered_map>
 
+#include "minikin/Buffer.h"
 #include "minikin/Macros.h"
 
 #include "Locale.h"
@@ -28,30 +29,39 @@
 
 class LocaleListCache {
 public:
-    // A special ID for the empty locale list.
-    // This value must be 0 since the empty locale list is inserted into mLocaleLists by
-    // default.
-    const static uint32_t kEmptyListId = 0;
-
     // A special ID for the invalid locale list.
     const static uint32_t kInvalidListId = (uint32_t)(-1);
 
     // Returns the locale list ID for the given string representation of LocaleList.
-    // Caller should acquire a lock before calling the method.
     static inline uint32_t getId(const std::string& locales) {
         return getInstance().getIdInternal(locales);
     }
 
-    // Caller should acquire a lock before calling the method.
+    // Returns the locale list ID for the LocaleList serialized in the buffer.
+    static inline uint32_t readFrom(BufferReader* reader) {
+        return getInstance().readFromInternal(reader);
+    }
+
+    static inline void writeTo(BufferWriter* writer, uint32_t id) {
+        return getInstance().writeToInternal(writer, id);
+    }
+
     static inline const LocaleList& getById(uint32_t id) {
         return getInstance().getByIdInternal(id);
     }
 
 private:
+    struct LocaleVectorHash {
+        size_t operator()(const std::vector<Locale>& locales) const;
+    };
+
     LocaleListCache();  // Singleton
     ~LocaleListCache() {}
 
     uint32_t getIdInternal(const std::string& locales);
+    uint32_t getIdInternal(std::vector<Locale>&& locales) EXCLUSIVE_LOCKS_REQUIRED(mMutex);
+    uint32_t readFromInternal(BufferReader* reader);
+    void writeToInternal(BufferWriter* writer, uint32_t id);
     const LocaleList& getByIdInternal(uint32_t id);
 
     // Caller should acquire a lock before calling the method.
@@ -62,8 +72,18 @@
 
     std::vector<LocaleList> mLocaleLists GUARDED_BY(mMutex);
 
-    // A map from the string representation of the font locale list to the ID.
-    std::unordered_map<std::string, uint32_t> mLocaleListLookupTable GUARDED_BY(mMutex);
+    // A map from the list of locale identifier to the ID.
+    //
+    // Locale's operator==() doesn't have reflexivity for unsupported locales,
+    // but it won't cause problems because we never store unsupported locales in
+    // LocaleListCache. See parseLocaleList() in LocaleListCache.cpp.
+    std::unordered_map<std::vector<Locale>, uint32_t, LocaleVectorHash> mLocaleListLookupTable
+            GUARDED_BY(mMutex);
+
+    // A cache map from the string representation of the font locale list to the ID.
+    // This is a mere cache over mLocaleListLookupTable. Some LocaleList objects may be in
+    // mLocaleListLookupTable even if they are not in mLocaleListStringCache.
+    std::unordered_map<std::string, uint32_t> mLocaleListStringCache GUARDED_BY(mMutex);
 
     std::mutex mMutex;
 };
diff --git a/libs/minikin/MeasuredText.cpp b/libs/minikin/MeasuredText.cpp
index af0d0ee..1bf3942 100644
--- a/libs/minikin/MeasuredText.cpp
+++ b/libs/minikin/MeasuredText.cpp
@@ -213,10 +213,20 @@
 public:
     BoundsCompositor() : mAdvance(0) {}
 
-    void operator()(const LayoutPiece& layoutPiece, const MinikinPaint& /* paint */) {
-        MinikinRect tmpBounds = layoutPiece.bounds();
-        tmpBounds.offset(mAdvance, 0);
-        mBounds.join(tmpBounds);
+    void operator()(const LayoutPiece& layoutPiece, const MinikinPaint& paint) {
+        MinikinRect pieceBounds;
+        MinikinRect tmpRect;
+        for (uint32_t i = 0; i < layoutPiece.glyphCount(); ++i) {
+            const FakedFont& font = layoutPiece.fontAt(i);
+            const Point& point = layoutPiece.pointAt(i);
+
+            MinikinFont* minikinFont = font.font->typeface().get();
+            minikinFont->GetBounds(&tmpRect, layoutPiece.glyphIdAt(i), paint, font.fakery);
+            tmpRect.offset(point.x, point.y);
+            pieceBounds.join(tmpRect);
+        }
+        pieceBounds.offset(mAdvance, 0);
+        mBounds.join(pieceBounds);
         mAdvance += layoutPiece.advance();
     }
 
diff --git a/libs/minikin/Measurement.cpp b/libs/minikin/Measurement.cpp
index 5d110c4..66886da 100644
--- a/libs/minikin/Measurement.cpp
+++ b/libs/minikin/Measurement.cpp
@@ -19,6 +19,9 @@
 #include <cfloat>
 #include <cmath>
 
+#include "BidiUtils.h"
+#include "LayoutSplitter.h"
+#include "minikin/BoundsCache.h"
 #include "minikin/GraphemeBreak.h"
 
 namespace minikin {
@@ -118,4 +121,40 @@
     return best;
 }
 
+struct BoundsComposer {
+    BoundsComposer() : mAdvance(0) {}
+
+    void operator()(const MinikinRect& rect, float advance) {
+        MinikinRect tmp = rect;
+        tmp.offset(mAdvance, 0);
+        mBounds.join(tmp);
+        mAdvance += advance;
+    }
+
+    float mAdvance;
+    MinikinRect mBounds;
+};
+
+void getBounds(const U16StringPiece& str, const Range& range, Bidi bidiFlag,
+               const MinikinPaint& paint, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
+               MinikinRect* out) {
+    BoundsComposer bc;
+    for (const BidiText::RunInfo info : BidiText(str, range, bidiFlag)) {
+        for (const auto [context, piece] : LayoutSplitter(str, info.range, info.isRtl)) {
+            const StartHyphenEdit pieceStartHyphen =
+                    (piece.getStart() == range.getStart()) ? startHyphen : StartHyphenEdit::NO_EDIT;
+            const EndHyphenEdit pieceEndHyphen =
+                    (piece.getEnd() == range.getEnd()) ? endHyphen : EndHyphenEdit::NO_EDIT;
+            BoundsCache::getInstance().getOrCreate(str.substr(context), piece - context.getStart(),
+                                                   paint, info.isRtl, pieceStartHyphen,
+                                                   pieceEndHyphen, bc);
+            // Increment word spacing for spacer
+            if (piece.getLength() == 1 && isWordSpace(str[piece.getStart()])) {
+                bc.mAdvance += paint.wordSpacing;
+            }
+        }
+    }
+    *out = bc.mBounds;
+}
+
 }  // namespace minikin
diff --git a/libs/minikin/SparseBitSet.cpp b/libs/minikin/SparseBitSet.cpp
index 66d6c02..41151f1 100644
--- a/libs/minikin/SparseBitSet.cpp
+++ b/libs/minikin/SparseBitSet.cpp
@@ -55,9 +55,14 @@
         return;
     }
     mMaxVal = maxVal;
-    mIndices.reset(new uint16_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
+    mIndicesCount = (mMaxVal + kPageMask) >> kLogValuesPerPage;
+    // Avoid zero-filling mOwnedIndices.
+    mOwnedIndices.reset(new uint16_t[mIndicesCount]);
+    mIndices = mOwnedIndices.get();
     uint32_t nPages = calcNumPages(ranges, nRanges);
-    mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
+    mBitmapsCount = nPages << (kLogValuesPerPage - kLogBitsPerEl);
+    mOwnedBitmaps = std::make_unique<element[]>(mBitmapsCount);
+    mBitmaps = mOwnedBitmaps.get();
     mZeroPageIndex = noZeroPage;
     uint32_t nonzeroPageEnd = 0;
     uint32_t currentPage = 0;
@@ -73,32 +78,52 @@
                     mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
                 }
                 for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {
-                    mIndices[j] = mZeroPageIndex;
+                    mOwnedIndices[j] = mZeroPageIndex;
                 }
             }
-            mIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
+            mOwnedIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
         }
 
         size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +
                        ((start & kPageMask) >> kLogBitsPerEl);
         size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;
         if (nElements == 1) {
-            mBitmaps[index] |=
+            mOwnedBitmaps[index] |=
                     (kElAllOnes >> (start & kElMask)) & (kElAllOnes << ((~end + 1) & kElMask));
         } else {
-            mBitmaps[index] |= kElAllOnes >> (start & kElMask);
+            mOwnedBitmaps[index] |= kElAllOnes >> (start & kElMask);
             for (size_t j = 1; j < nElements - 1; j++) {
-                mBitmaps[index + j] = kElAllOnes;
+                mOwnedBitmaps[index + j] = kElAllOnes;
             }
-            mBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
+            mOwnedBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
         }
         for (size_t j = startPage + 1; j < endPage + 1; j++) {
-            mIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
+            mOwnedIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
         }
         nonzeroPageEnd = endPage + 1;
     }
 }
 
+void SparseBitSet::initFromBuffer(BufferReader* reader) {
+    mMaxVal = reader->read<uint32_t>();
+    // mIndices and mBitmaps are not initialized when mMaxVal == 0
+    if (mMaxVal == 0) return;
+    std::tie(mIndices, mIndicesCount) = reader->readArray<uint16_t>();
+    // element is uint32_t
+    static_assert(sizeof(element) == 4);
+    std::tie(mBitmaps, mBitmapsCount) = reader->readArray<element>();
+    mZeroPageIndex = reader->read<uint16_t>();
+}
+
+void SparseBitSet::writeTo(BufferWriter* writer) const {
+    writer->write<uint32_t>(mMaxVal);
+    // mIndices and mBitmaps are not initialized when mMaxVal == 0
+    if (mMaxVal == 0) return;
+    writer->writeArray<uint16_t>(mIndices, mIndicesCount);
+    writer->writeArray<element>(mBitmaps, mBitmapsCount);
+    writer->write<uint16_t>(mZeroPageIndex);
+}
+
 int SparseBitSet::CountLeadingZeros(element x) {
     // Note: GCC / clang builtin
     return sizeof(element) <= sizeof(int) ? __builtin_clz(x) : __builtin_clzl(x);
diff --git a/libs/minikin/SystemFonts.cpp b/libs/minikin/SystemFonts.cpp
index 287fc61..9c8fa66 100644
--- a/libs/minikin/SystemFonts.cpp
+++ b/libs/minikin/SystemFonts.cpp
@@ -26,7 +26,8 @@
 }
 
 std::shared_ptr<FontCollection> SystemFonts::findFontCollectionInternal(
-        const std::string& familyName) const {
+        const std::string& familyName) {
+    std::lock_guard<std::mutex> lock(mMutex);
     auto it = mSystemFallbacks.find(familyName);
     if (it != mSystemFallbacks.end()) {
         return it->second;
@@ -35,4 +36,22 @@
     return mDefaultFallback;
 }
 
+void SystemFonts::buildFontSetLocked() {
+    std::unordered_set<FontFamily*> uniqueFamilies;
+
+    for (const auto& collection : mCollections) {
+        for (const auto& family : collection->getFamilies()) {
+            uniqueFamilies.insert(family.get());
+        }
+    }
+
+    std::vector<std::shared_ptr<Font>> result;
+    for (const auto family : uniqueFamilies) {
+        for (size_t i = 0; i < family->getNumFonts(); ++i) {
+            result.push_back(family->getFontRef(i));
+        }
+    }
+    mFonts = std::move(result);
+}
+
 }  // namespace minikin
diff --git a/tests/Android.bp b/tests/Android.bp
index 9acb028..3088ca4 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -7,6 +7,7 @@
     srcs: [
         "data/Arabic.ttf",
         "data/Ascii.ttf",
+        "data/Bbox.ttf",
         "data/Bold.ttf",
         "data/BoldItalic.ttf",
         "data/Cherokee.ttf",
@@ -14,6 +15,7 @@
         "data/ColorTextMixedEmojiFont.ttf",
         "data/CustomExtent.ttf",
         "data/Emoji.ttf",
+        "data/EmojiBase.ttf",
         "data/Hiragana.ttf",
         "data/Italic.ttf",
         "data/Ja.ttf",
@@ -23,6 +25,7 @@
         "data/MultiAxis.ttf",
         "data/NoCmapFormat14.ttf",
         "data/NoGlyphFont.ttf",
+        "data/OverrideEmoji.ttf",
         "data/Regular.ttf",
         "data/TextEmojiFont.ttf",
         "data/UnicodeBMPOnly.ttf",
@@ -32,6 +35,7 @@
         "data/ZhHans.ttf",
         "data/ZhHant.ttf",
         "data/emoji.xml",
+        "data/emoji_itemization.xml",
         "data/itemize.xml",
     ],
 }
diff --git a/tests/data/Ascii.ttf b/tests/data/Ascii.ttf
index be6d5fe..2e6835b 100644
--- a/tests/data/Ascii.ttf
+++ b/tests/data/Ascii.ttf
Binary files differ
diff --git a/tests/data/Ascii.ttx b/tests/data/Ascii.ttx
index 74eba96..c0a2ef6 100644
--- a/tests/data/Ascii.ttx
+++ b/tests/data/Ascii.ttx
@@ -18,6 +18,7 @@
   <GlyphOrder>
     <GlyphID id="0" name=".notdef"/>
     <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="2em"/>
   </GlyphOrder>
 
   <head>
@@ -117,6 +118,7 @@
   <hmtx>
     <mtx name=".notdef" width="50" lsb="0"/>
     <mtx name="1em" width="100" lsb="0"/>
+    <mtx name="2em" width="200" lsb="0"/>
   </hmtx>
 
   <cmap>
@@ -217,6 +219,12 @@
       <map code="0x007C" name="1em" /> <!-- '|' -->
       <map code="0x007D" name="1em" /> <!-- '}' -->
       <map code="0x007E" name="1em" /> <!-- '~' -->
+      <map code="0x02B1" name="2em" /> <!-- 'α' -->
+      <map code="0x02B2" name="2em" /> <!-- 'β' -->
+      <map code="0x02B3" name="2em" /> <!-- 'γ' -->
+      <map code="0x02B4" name="2em" /> <!-- 'δ' -->
+      <map code="0x02B5" name="2em" /> <!-- 'ε' -->
+      <map code="0x02B6" name="2em" /> <!-- 'ζ' -->
     </cmap_format_12>
   </cmap>
 
@@ -235,6 +243,15 @@
         </contour>
         <instructions><assembly></assembly></instructions>
     </TTGlyph>
+    <TTGlyph name="2em" xMin="0" yMin="0" xMax="200" yMax="200">
+        <contour>
+            <pt x="0" y="0" on="1" />
+            <pt x="200" y="0" on="1" />
+            <pt x="200" y="200" on="1" />
+            <pt x="0" y="200" on="1" />
+        </contour>
+        <instructions><assembly></assembly></instructions>
+    </TTGlyph>
   </glyf>
 
   <name>
diff --git a/tests/data/Bbox.ttf b/tests/data/Bbox.ttf
new file mode 100644
index 0000000..c89c59c
--- /dev/null
+++ b/tests/data/Bbox.ttf
Binary files differ
diff --git a/tests/data/Bbox.ttx b/tests/data/Bbox.ttx
new file mode 100644
index 0000000..e7d34bd
--- /dev/null
+++ b/tests/data/Bbox.ttx
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 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.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1emx1em"/>
+    <GlyphID id="2" name="2emx2em"/>
+    <GlyphID id="3" name="3emx3em"/>
+    <GlyphID id="4" name="2emx2em_lsb_1em"/>
+    <GlyphID id="5" name="1emx1em_y1em_origin"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap 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"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements 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="32"/>
+    <usLastCharIndex value="122"/>
+    <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="1emx1em" width="1000" lsb="0"/>
+    <mtx name="2emx2em" width="2000" lsb="0"/>
+    <mtx name="3emx3em" width="3000" lsb="0"/>
+    <mtx name="2emx2em_lsb_1em" width="2000" lsb="1000"/>
+    <mtx name="1emx1em_y1em_origin" width="1000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="3" nGroups="6" platformID="3" platEncID="1" language="0">
+      <map code="0x0028" name="1emx1em" />
+      <map code="0x0061" name="1emx1em" />
+      <map code="0x0062" name="2emx2em" />
+      <map code="0x0063" name="3emx3em" />
+      <map code="0x0064" name="2emx2em_lsb_1em" />
+      <map code="0x0065" name="1emx1em_y1em_origin" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1emx1em" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="500" y="1000" on="1" />
+        <pt x="1000" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="2emx2em" xMin="0" yMin="0" xMax="2000" yMax="2000">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="1000" y="2000" on="1" />
+        <pt x="2000" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="3emx3em" xMin="0" yMin="0" xMax="3000" yMax="3000">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="1500" y="3000" on="1" />
+        <pt x="3000" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="2emx2em_lsb_1em" xMin="0" yMin="0" xMax="2000" yMax="2000">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="1000" y="2000" on="1" />
+        <pt x="2000" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="1emx1em_y1em_origin" xMin="0" yMin="1000" xMax="1000" yMax="2000">
+      <contour>
+        <pt x="0" y="1000" on="1" />
+        <pt x="500" y="2000" on="1" />
+        <pt x="1000" y="1000" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <ScriptRecord index="0">
+      <ScriptTag value="latn"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <FeatureIndex index="0" value="0"/>
+        </DefaultLangSys>
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <FeatureRecord index="0">
+      <FeatureTag value="rtlm"/>
+      <Feature>
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <SingleSubst index="0" Format="2">
+        <Substitution in="1emx1em" out="3emx3em" />
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+
+  </GSUB>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </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>
+
+</ttFont>
diff --git a/tests/data/EmojiBase.ttf b/tests/data/EmojiBase.ttf
new file mode 100644
index 0000000..659226d
--- /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..e6e3da8
--- /dev/null
+++ b/tests/data/EmojiBase.ttx
@@ -0,0 +1,537 @@
+<?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"/>
+    <GlyphID id="21" name="LigaForm6"/>
+    <GlyphID id="22" name="LigaForm7"/>
+
+    <!-- Regional Indicators -->
+    <GlyphID id="23" name="U+1F1E6"/>
+    <GlyphID id="24" name="U+1F1E7"/>
+
+  </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+1F1E6" width="500" lsb="93"/>
+    <mtx name="U+1F1E7" 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"/>
+    <mtx name="LigaForm6" width="500" lsb="93"/>
+    <mtx name="LigaForm7" 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="0x1F1E6" name="U+1F1E6" />
+      <map code="0x1F1E7" name="U+1F1E7" />
+      <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+1F1E6" 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+1F1E7" 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>
+    <TTGlyph name="LigaForm6" 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="LigaForm7" 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>
+          <LigatureSet glyph="U+1F1E6">
+            <Ligature components="U+1F1E6" glyph="LigaForm6"/>
+            <Ligature components="U+1F1E7" glyph="LigaForm7"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+</ttFont>
diff --git a/tests/data/OverrideEmoji.ttf b/tests/data/OverrideEmoji.ttf
new file mode 100644
index 0000000..890796b
--- /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..d5d4091
--- /dev/null
+++ b/tests/data/OverrideEmoji.ttx
@@ -0,0 +1,521 @@
+<?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"/>
+    -->
+    <GlyphID id="21" name="LigaForm6"/>
+    <!--
+      Following glyph is removed from EmojiBase.ttf for verifying fallback.
+    <GlyphID id="22" name="LigaForm7"/>
+    -->
+
+    <GlyphID id="23" name="U+1F1E6"/>
+    <GlyphID id="24" name="U+1F1E7"/>
+  </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+1F1E6" width="500" lsb="93"/>
+    <mtx name="U+1F1E7" 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"/>
+    -->
+    <mtx name="LigaForm6" width="500" lsb="93"/>
+    <!--
+      Following glyph is removed from EmojiBase.ttf for verifying fallback.
+    <mtx name="LigaForm7" 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="0x1F1E6" name="U+1F1E6" />
+      <map code="0x1F1E7" name="U+1F1E7" />
+      <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+1F1E6" 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+1F1E7" 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">
+    -->
+    <TTGlyph name="LigaForm6" 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="LigaForm7">
+    -->
+  </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>
+          -->
+          <LigatureSet glyph="U+1F1E6">
+            <Ligature components="U+1F1E6" glyph="LigaForm6"/>
+            <Ligature components="U+1F1E7" glyph=".notdef"/>
+          </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/stresstest/FontFamilyTest.cpp b/tests/stresstest/FontFamilyTest.cpp
index 7554314..7a9813e 100644
--- a/tests/stresstest/FontFamilyTest.cpp
+++ b/tests/stresstest/FontFamilyTest.cpp
@@ -36,7 +36,7 @@
     int ttcIndex = GetParam().second;
 
     auto font = std::make_shared<FreeTypeMinikinFontForTest>(fontPath);
-    std::vector<Font> fonts;
+    std::vector<std::shared_ptr<Font>> fonts;
     fonts.push_back(Font::Builder(font).build());
     std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(std::move(fonts));
 
diff --git a/tests/unittest/Android.bp b/tests/unittest/Android.bp
index e47030a..cae9003 100644
--- a/tests/unittest/Android.bp
+++ b/tests/unittest/Android.bp
@@ -45,12 +45,15 @@
     srcs: [
         "AndroidLineBreakerHelperTest.cpp",
         "BidiUtilsTest.cpp",
+        "BufferTest.cpp",
+        "BoundsCacheTest.cpp",
         "CmapCoverageTest.cpp",
         "EmojiTest.cpp",
         "FontTest.cpp",
         "FontCollectionTest.cpp",
         "FontCollectionItemizeTest.cpp",
         "FontFamilyTest.cpp",
+        "FontFileParserTest.cpp",
         "FontLanguageListCacheTest.cpp",
         "FontUtilsTest.cpp",
         "HasherTest.cpp",
diff --git a/tests/unittest/BoundsCacheTest.cpp b/tests/unittest/BoundsCacheTest.cpp
new file mode 100644
index 0000000..8c727f9
--- /dev/null
+++ b/tests/unittest/BoundsCacheTest.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "minikin/BoundsCache.h"
+
+#include "FontTestUtils.h"
+#include "LocaleListCache.h"
+#include "UnicodeUtils.h"
+
+namespace minikin {
+
+class TestableBoundsCache : public BoundsCache {
+public:
+    TestableBoundsCache(uint32_t maxEntries) : BoundsCache(maxEntries) {}
+};
+
+class BoundsCapture {
+public:
+    BoundsCapture() {}
+
+    void operator()(const MinikinRect& rect, float advance) {
+        mRect = rect;
+        mAdvance = advance;
+    }
+
+    const MinikinRect& rect() const { return mRect; }
+    float advance() const { return mAdvance; }
+
+private:
+    MinikinRect mRect;
+    float mAdvance;
+};
+
+TEST(BoundsCacheTest, cacheHitTest) {
+    auto text = utf8ToUtf16("android");
+    Range range(0, text.size());
+    MinikinPaint paint(buildFontCollection("Ascii.ttf"));
+
+    TestableBoundsCache boundsCache(10);
+
+    BoundsCapture bounds1;
+    boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
+                            EndHyphenEdit::NO_EDIT, bounds1);
+
+    BoundsCapture bounds2;
+    boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
+                            EndHyphenEdit::NO_EDIT, bounds2);
+
+    EXPECT_EQ(bounds1.rect(), bounds2.rect());
+    EXPECT_EQ(bounds1.advance(), bounds2.advance());
+}
+
+TEST(BoundsCacheTest, cacheMissTest) {
+    auto text1 = utf8ToUtf16("android");
+    auto text2 = utf8ToUtf16("αβγδζ");
+    MinikinPaint paint(buildFontCollection("Ascii.ttf"));
+
+    TestableBoundsCache boundsCache(10);
+
+    BoundsCapture bounds1;
+    BoundsCapture bounds2;
+
+    {
+        SCOPED_TRACE("Different text");
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
+        boundsCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+        EXPECT_NE(bounds1.rect(), bounds2.rect());
+        EXPECT_NE(bounds1.advance(), bounds2.advance());
+    }
+    {
+        SCOPED_TRACE("Different range");
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
+        boundsCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+        EXPECT_NE(bounds1.rect(), bounds2.rect());
+        EXPECT_NE(bounds1.advance(), bounds2.advance());
+    }
+    {
+        SCOPED_TRACE("Different collection");
+        MinikinPaint paint1(buildFontCollection("Ascii.ttf"));
+        paint1.size = 10.0f;
+        paint1.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
+        MinikinPaint paint2(buildFontCollection("Emoji.ttf"));
+        paint2.size = 10.0f;
+        paint2.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+        EXPECT_NE(bounds1.rect(), bounds2.rect());
+        EXPECT_NE(bounds1.advance(), bounds2.advance());
+    }
+    {
+        SCOPED_TRACE("Different size");
+        auto collection = buildFontCollection("Ascii.ttf");
+        MinikinPaint paint1(collection);
+        paint1.size = 10.0f;
+        paint1.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
+        MinikinPaint paint2(collection);
+        paint2.size = 20.0f;
+        paint2.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+        EXPECT_NE(bounds1.rect(), bounds2.rect());
+        EXPECT_NE(bounds1.advance(), bounds2.advance());
+    }
+    {
+        SCOPED_TRACE("Different letter spacing");
+        auto collection = buildFontCollection("Ascii.ttf");
+        MinikinPaint paint1(collection);
+        paint1.letterSpacing = 0.0f;
+        paint1.size = 10.0f;
+        paint1.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
+        MinikinPaint paint2(collection);
+        paint2.letterSpacing = 1.0f;
+        paint2.size = 10.0f;
+        paint2.scaleX = 1.0f;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+        EXPECT_NE(bounds1.rect(), bounds2.rect());
+        EXPECT_NE(bounds1.advance(), bounds2.advance());
+    }
+}
+
+TEST(BoundsCacheTest, cacheOverflowTest) {
+    auto text = utf8ToUtf16("android");
+    Range range(0, text.size());
+    MinikinPaint paint(buildFontCollection("Ascii.ttf"));
+
+    TestableBoundsCache boundsCache(5);
+
+    BoundsCapture bounds1;
+    boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
+                            EndHyphenEdit::NO_EDIT, bounds1);
+
+    for (char c = 'a'; c <= 'z'; c++) {
+        auto text1 = utf8ToUtf16(std::string(10, c));
+        BoundsCapture bounds2;
+        boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
+                                StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
+    }
+
+    BoundsCapture bounds3;
+    boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
+                            EndHyphenEdit::NO_EDIT, bounds3);
+    EXPECT_EQ(bounds1.rect(), bounds3.rect());
+    EXPECT_EQ(bounds1.advance(), bounds3.advance());
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/BufferTest.cpp b/tests/unittest/BufferTest.cpp
new file mode 100644
index 0000000..8b1db33
--- /dev/null
+++ b/tests/unittest/BufferTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "minikin/Buffer.h"
+
+#include <gtest/gtest.h>
+
+namespace minikin {
+
+class TestObject {
+public:
+    void writeTo(BufferWriter* writer) const {
+        // Total size = 20
+        // uint8_t (1)
+        writer->write<uint8_t>(0xAB);
+        // padding (1), uint16_t (2)
+        writer->write<uint16_t>(0xCDEF);
+        // uint8_t(1)
+        writer->write<uint8_t>(0x01);
+        // padding (3), array size (4), uint32_t (4) * 2
+        uint32_t uint32Array[] = {0x98765432, 0x98765433};
+        writer->writeArray<uint32_t>(uint32Array, 2);
+    }
+};
+
+TEST(BufferTest, testMeasureWriteRead) {
+    TestObject testObject;
+    BufferWriter fakeWriter(nullptr);
+    testObject.writeTo(&fakeWriter);
+    ASSERT_EQ(fakeWriter.size(), 20u);
+    std::vector<uint8_t> buffer(fakeWriter.size());
+
+    BufferWriter writer(buffer.data());
+    testObject.writeTo(&writer);
+    ASSERT_EQ(writer.size(), buffer.size());
+
+    BufferReader reader(buffer.data());
+    ASSERT_EQ(reader.data(), buffer.data());
+    ASSERT_EQ(reader.pos(), 0u);
+    ASSERT_EQ(reader.read<uint8_t>(), 0xABu);
+    ASSERT_EQ(reader.pos(), 1u);
+    ASSERT_EQ(reader.read<uint16_t>(), 0xCDEFu);
+    ASSERT_EQ(reader.pos(), 4u);
+    ASSERT_EQ(reader.read<uint8_t>(), 0x01u);
+    ASSERT_EQ(reader.pos(), 5u);
+    auto [uint32Array, size] = reader.readArray<uint32_t>();
+    ASSERT_EQ(size, 2u);
+    ASSERT_EQ(uint32Array[0], 0x98765432u);
+    ASSERT_EQ(uint32Array[1], 0x98765433u);
+    ASSERT_EQ(reader.pos(), 20u);
+}
+
+TEST(BufferTest, testSkip) {
+    TestObject testObject;
+    BufferWriter fakeWriter(nullptr);
+    testObject.writeTo(&fakeWriter);
+    ASSERT_EQ(fakeWriter.size(), 20u);
+    std::vector<uint8_t> buffer(fakeWriter.size());
+
+    BufferWriter writer(buffer.data());
+    testObject.writeTo(&writer);
+    ASSERT_EQ(writer.size(), buffer.size());
+
+    BufferReader reader(buffer.data());
+    ASSERT_EQ(reader.data(), buffer.data());
+    ASSERT_EQ(reader.pos(), 0u);
+    reader.skip<uint8_t>();
+    ASSERT_EQ(reader.pos(), 1u);
+    reader.read<uint16_t>();
+    ASSERT_EQ(reader.pos(), 4u);
+    reader.skip<uint8_t>();
+    ASSERT_EQ(reader.pos(), 5u);
+    reader.skipArray<uint32_t>();
+    ASSERT_EQ(reader.pos(), 20u);
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index 8cd95aa..6f1e194 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,35 +82,38 @@
         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) {
-    EXPECT_NE(nullptr, run.fakedFont.font);
-    return getBasename(
-            ((FreeTypeMinikinFontForTest*)run.fakedFont.font->typeface().get())->fontPath());
+std::string getFontName(const Run& run) {
+    EXPECT_NE(nullptr, run.fakedFont.font.get());
+    return getBasename(run.fakedFont.font.get()->typeface()->GetFontPath());
 }
 
 // Utility function to obtain LocaleList from string.
@@ -515,13 +524,13 @@
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
-    EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontName(runs[0]));
+    EXPECT_TRUE(runs[0].fakedFont.font.get() == nullptr || kLatinFont == getFontName(runs[0]));
 
     runs = itemize(collection, "U+FE00", "zh-Hant");
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
-    EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontName(runs[0]));
+    EXPECT_TRUE(runs[0].fakedFont.font.get() == nullptr || kLatinFont == getFontName(runs[0]));
 
     // First font family (Regular.ttf) supports U+203C but doesn't support U+203C U+FE0F.
     // Emoji.ttf font supports U+203C U+FE0F.  Emoji.ttf should be selected.
@@ -652,13 +661,13 @@
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
-    EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontName(runs[0]));
+    EXPECT_TRUE(runs[0].fakedFont.font.get() == nullptr || kLatinFont == getFontName(runs[0]));
 
     runs = itemize(collection, "U+E0100", "zh-Hant");
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
-    EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontName(runs[0]));
+    EXPECT_TRUE(runs[0].fakedFont.font.get() == nullptr || kLatinFont == getFontName(runs[0]));
 }
 
 TEST(FontCollectionItemizeTest, itemize_no_crash) {
@@ -926,7 +935,7 @@
         // Prepare first font which doesn't supports U+9AA8
         auto firstFamilyMinikinFont =
                 std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(kNoGlyphFont));
-        std::vector<Font> fonts;
+        std::vector<std::shared_ptr<Font>> fonts;
         fonts.push_back(Font::Builder(firstFamilyMinikinFont).build());
         auto firstFamily =
                 std::make_shared<FontFamily>(registerLocaleList("und"), FamilyVariant::DEFAULT,
@@ -941,7 +950,7 @@
         for (size_t i = 0; i < testCase.fontLocales.size(); ++i) {
             auto minikinFont =
                     std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(kJAFont));
-            std::vector<Font> fonts;
+            std::vector<std::shared_ptr<Font>> fonts;
             fonts.push_back(Font::Builder(minikinFont).build());
             auto family = std::make_shared<FontFamily>(registerLocaleList(testCase.fontLocales[i]),
                                                        FamilyVariant::DEFAULT, std::move(fonts),
@@ -953,14 +962,15 @@
         // Do itemize
         auto runs = itemize(collection, "U+9AA8", testCase.userPreferredLocale);
         ASSERT_EQ(1U, runs.size());
-        ASSERT_NE(nullptr, runs[0].fakedFont.font);
+        ASSERT_NE(nullptr, runs[0].fakedFont.font.get());
 
         // First family doesn't support U+9AA8 and others support it, so the first font should not
         // be selected.
-        EXPECT_NE(firstFamilyMinikinFont.get(), runs[0].fakedFont.font->typeface().get());
+        EXPECT_NE(firstFamilyMinikinFont.get(), runs[0].fakedFont.font.get()->typeface().get());
 
         // Lookup used font family by MinikinFont*.
-        const int usedLocaleIndex = fontLocaleIdxMap[runs[0].fakedFont.font->typeface().get()];
+        const int usedLocaleIndex =
+                fontLocaleIdxMap[runs[0].fakedFont.font.get()->typeface().get()];
         EXPECT_EQ(testCase.selectedFontIndex, usedLocaleIndex);
     }
 }
@@ -1521,10 +1531,10 @@
     // Both fontA/fontB support U+35A8 but don't support U+35A8 U+E0100. The first font should be
     // selected.
     auto runs = itemize(collection, "U+35A8 U+E0100");
-    EXPECT_EQ(familyA->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(familyA->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(reversedCollection, "U+35A8 U+E0100");
-    EXPECT_EQ(familyB->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(familyB->getFont(0), runs[0].fakedFont.font.get());
 }
 
 // For b/29585939
@@ -1544,10 +1554,10 @@
     // Both hasCmapFormat14Font/noCmapFormat14Font support U+5380 but don't support U+5380 U+E0100.
     // The first font should be selected.
     auto runs = itemize(collection, "U+5380 U+E0100");
-    EXPECT_EQ(hasCmapFormat14Family->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(hasCmapFormat14Family->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(reversedCollection, "U+5380 U+E0100");
-    EXPECT_EQ(noCmapFormat14Family->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(noCmapFormat14Family->getFont(0), runs[0].fakedFont.font.get());
 }
 
 TEST(FontCollectionItemizeTest, colorEmojiSelectionTest) {
@@ -1561,44 +1571,44 @@
     // Both textEmojiFamily and colorEmojiFamily supports U+203C and U+23E9.
     // U+203C is text default emoji, and U+23E9 is color default emoji.
     auto runs = itemize(collection, "U+203C", "en-US,en-Zsym");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "en-US,en-Zsym");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "en-US,en-Zsye");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "en-US,en-Zsye");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-Zsym-JP");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-Zsym-JP");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-Zsye-JP");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-Zsye-JP");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-JP-u-em-text");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-JP-u-em-text");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-JP-u-em-emoji");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-JP-u-em-emoji");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-JP,und-Zsym");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-JP,und-Zsym");
-    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(textEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 
     runs = itemize(collection, "U+203C", "ja-JP,und-Zsye");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "U+23E9", "ja-JP,und-Zsye");
-    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(colorEmojiFamily->getFont(0), runs[0].fakedFont.font.get());
 }
 
 TEST(FontCollectionItemizeTest, customFallbackTest) {
@@ -1612,11 +1622,60 @@
     auto collection = std::make_shared<FontCollection>(families);
 
     auto runs = itemize(collection, "'a'", "");
-    EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "'a'", "en-US");
-    EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font);
+    EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font.get());
     runs = itemize(collection, "'a'", "ja-JP");
-    EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font);
+    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"));
+}
+
+TEST(FontCollectionItemizeTest, emojiFlagFallback) {
+    // If the OverrideEmojiFont supports U+1F1E6 U+1F1E6, use that font.
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F1E6 U+1F1E6"));
+
+    // Even if the OverrideEmojiFont directs to .notdef (i.e. Tofu glyph) for the sequence, use it.
+    EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F1E6 U+1F1E7"));
 }
 
 }  // namespace minikin
diff --git a/tests/unittest/FontCollectionTest.cpp b/tests/unittest/FontCollectionTest.cpp
index 6b39508..aa9d4a8 100644
--- a/tests/unittest/FontCollectionTest.cpp
+++ b/tests/unittest/FontCollectionTest.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "FontTestUtils.h"
+#include "FreeTypeMinikinFontForTest.h"
 #include "MinikinInternal.h"
 
 namespace minikin {
@@ -56,22 +57,23 @@
     }
 }
 
-TEST(FontCollectionTest, hasVariationSelectorTest) {
-    auto fc = buildFontCollection(kVsTestFont);
-
+void expectVSGlyphsForVsTestFont(const FontCollection* fc) {
     EXPECT_FALSE(fc->hasVariationSelector(0x82A6, 0));
-    expectVSGlyphs(fc.get(), 0x82A6,
-                   std::set<uint32_t>({0xFE00, 0xFE0E, 0xE0100, 0xE0101, 0xE0102}));
+    expectVSGlyphs(fc, 0x82A6, std::set<uint32_t>({0xFE00, 0xFE0E, 0xE0100, 0xE0101, 0xE0102}));
 
     EXPECT_FALSE(fc->hasVariationSelector(0x845B, 0));
-    expectVSGlyphs(fc.get(), 0x845B,
-                   std::set<uint32_t>({0xFE01, 0xFE0E, 0xE0101, 0xE0102, 0xE0103}));
+    expectVSGlyphs(fc, 0x845B, std::set<uint32_t>({0xFE01, 0xFE0E, 0xE0101, 0xE0102, 0xE0103}));
 
     EXPECT_FALSE(fc->hasVariationSelector(0x537F, 0));
-    expectVSGlyphs(fc.get(), 0x537F, std::set<uint32_t>({0xFE0E}));
+    expectVSGlyphs(fc, 0x537F, std::set<uint32_t>({0xFE0E}));
 
     EXPECT_FALSE(fc->hasVariationSelector(0x717D, 0));
-    expectVSGlyphs(fc.get(), 0x717D, std::set<uint32_t>({0xFE02, 0xE0102, 0xE0103}));
+    expectVSGlyphs(fc, 0x717D, std::set<uint32_t>({0xFE02, 0xE0102, 0xE0103}));
+}
+
+TEST(FontCollectionTest, hasVariationSelectorTest) {
+    auto fc = buildFontCollection(kVsTestFont);
+    expectVSGlyphsForVsTestFont(fc.get());
 }
 
 const char kEmojiXmlFile[] = "emoji.xml";
@@ -175,4 +177,144 @@
     }
 }
 
+std::vector<uint8_t> writeToBuffer(
+        const std::vector<std::shared_ptr<FontCollection>>& collections) {
+    BufferWriter fakeWriter(nullptr);
+    FontCollection::writeVector<writeFreeTypeMinikinFontForTest>(&fakeWriter, collections);
+    std::vector<uint8_t> buffer(fakeWriter.size());
+    BufferWriter writer(buffer.data());
+    FontCollection::writeVector<writeFreeTypeMinikinFontForTest>(&writer, collections);
+    return buffer;
+}
+
+TEST(FontCollectionTest, bufferTest) {
+    {
+        std::vector<std::shared_ptr<FontCollection>> original({buildFontCollection(kVsTestFont)});
+        std::vector<uint8_t> buffer = writeToBuffer(original);
+        BufferReader reader(buffer.data());
+        auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+        EXPECT_EQ(1u, copied.size());
+        expectVSGlyphsForVsTestFont(copied[0].get());
+        EXPECT_EQ(original[0]->getSupportedTags(), copied[0]->getSupportedTags());
+        // Id will be different.
+        EXPECT_NE(original[0]->getId(), copied[0]->getId());
+        std::vector<uint8_t> newBuffer = writeToBuffer(copied);
+        EXPECT_EQ(buffer, newBuffer);
+    }
+    {
+        // Test that FontFamily instances are shared.
+        std::vector<std::shared_ptr<FontFamily>> families = {buildFontFamily(kVsTestFont)};
+        auto fc1 = std::make_shared<FontCollection>(families);
+        auto fc2 = std::make_shared<FontCollection>(families);
+        std::vector<std::shared_ptr<FontCollection>> original({fc1, fc2});
+        std::vector<uint8_t> buffer = writeToBuffer(original);
+        BufferReader reader(buffer.data());
+        auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+        EXPECT_EQ(2u, copied.size());
+        EXPECT_EQ(copied[0]->mFamilies[0], copied[1]->mFamilies[0]);
+        std::vector<uint8_t> newBuffer = writeToBuffer(copied);
+        EXPECT_EQ(buffer, newBuffer);
+    }
+    {
+        // Test axes.
+        // This font has 'wdth' and 'wght' axes.
+        const char kMultiAxisFont[] = "MultiAxis.ttf";
+        std::vector<std::shared_ptr<FontCollection>> original(
+                {buildFontCollection(kMultiAxisFont)});
+        std::vector<uint8_t> buffer = writeToBuffer(original);
+        BufferReader reader(buffer.data());
+        auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+        EXPECT_EQ(1u, copied.size());
+        EXPECT_EQ(1u,
+                  copied[0]->getSupportedTags().count(MinikinFont::MakeTag('w', 'd', 't', 'h')));
+        EXPECT_EQ(1u,
+                  copied[0]->getSupportedTags().count(MinikinFont::MakeTag('w', 'g', 'h', 't')));
+        std::vector<uint8_t> newBuffer = writeToBuffer(copied);
+        EXPECT_EQ(buffer, newBuffer);
+    }
+}
+
+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
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
index 2b70faf..fd2fc9a 100644
--- a/tests/unittest/FontFamilyTest.cpp
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -20,6 +20,7 @@
 
 #include "minikin/LocaleList.h"
 
+#include "BufferUtils.h"
 #include "FontTestUtils.h"
 #include "FreeTypeMinikinFontForTest.h"
 #include "LocaleListCache.h"
@@ -156,6 +157,40 @@
     EXPECT_EQ("zzz-Zzzz-999", createLocaleWithoutICUSanitization("zzz-Zzzz-999").getString());
 }
 
+TEST(LocaleTest, ReconstructFromIdentifierTest) {
+    std::string locales[] = {
+            // Language
+            "en",
+            "fil",
+            "und",
+            // Script
+            "en-Latn",
+            "fil-Taga",
+            "und-Zsye",
+            // Region
+            "en-US",
+            "fil-PH",
+            "es-419",
+            // Variant
+            "de-Latn-DE",
+            "de-Latn-DE-1901",
+            "de-Latn-DE-1996",
+            // Line break style
+            "ja-JP-u-lb-loose",
+            "ja-JP-u-lb-normal",
+            "ja-JP-u-lb-strict",
+            // Emoji subtag
+            "es-Latn-419-u-em-emoji",
+            // Everything
+            "de-Latn-DE-1996-u-lb-loose-u-em-emoji",
+    };
+    for (const std::string& locale : locales) {
+        EXPECT_EQ(createLocaleWithoutICUSanitization(locale),
+                  Locale(createLocaleWithoutICUSanitization(locale).getIdentifier()))
+                << "locale = " << locale;
+    }
+}
+
 TEST(LocaleTest, ScriptEqualTest) {
     EXPECT_TRUE(createLocale("en").isEqualScript(createLocale("en")));
     EXPECT_TRUE(createLocale("en-Latn").isEqualScript(createLocale("en")));
@@ -515,9 +550,7 @@
     }
 }
 
-TEST_F(FontFamilyTest, hasVariationSelectorTest) {
-    std::shared_ptr<FontFamily> family = buildFontFamily(kVsTestFont);
-
+void expectVSGlyphsForVsTestFont(FontFamily* family) {
     const uint32_t kVS1 = 0xFE00;
     const uint32_t kVS2 = 0xFE01;
     const uint32_t kVS3 = 0xFE02;
@@ -528,23 +561,28 @@
 
     const uint32_t kSupportedChar1 = 0x82A6;
     EXPECT_TRUE(family->getCoverage().get(kSupportedChar1));
-    expectVSGlyphs(family.get(), kSupportedChar1, std::set<uint32_t>({kVS1, kVS17, kVS18, kVS19}));
+    expectVSGlyphs(family, kSupportedChar1, std::set<uint32_t>({kVS1, kVS17, kVS18, kVS19}));
 
     const uint32_t kSupportedChar2 = 0x845B;
     EXPECT_TRUE(family->getCoverage().get(kSupportedChar2));
-    expectVSGlyphs(family.get(), kSupportedChar2, std::set<uint32_t>({kVS2, kVS18, kVS19, kVS20}));
+    expectVSGlyphs(family, kSupportedChar2, std::set<uint32_t>({kVS2, kVS18, kVS19, kVS20}));
 
     const uint32_t kNoVsSupportedChar = 0x537F;
     EXPECT_TRUE(family->getCoverage().get(kNoVsSupportedChar));
-    expectVSGlyphs(family.get(), kNoVsSupportedChar, std::set<uint32_t>());
+    expectVSGlyphs(family, kNoVsSupportedChar, std::set<uint32_t>());
 
     const uint32_t kVsOnlySupportedChar = 0x717D;
     EXPECT_FALSE(family->getCoverage().get(kVsOnlySupportedChar));
-    expectVSGlyphs(family.get(), kVsOnlySupportedChar, std::set<uint32_t>({kVS3, kVS19, kVS20}));
+    expectVSGlyphs(family, kVsOnlySupportedChar, std::set<uint32_t>({kVS3, kVS19, kVS20}));
 
     const uint32_t kNotSupportedChar = 0x845C;
     EXPECT_FALSE(family->getCoverage().get(kNotSupportedChar));
-    expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
+    expectVSGlyphs(family, kNotSupportedChar, std::set<uint32_t>());
+}
+
+TEST_F(FontFamilyTest, hasVariationSelectorTest) {
+    std::shared_ptr<FontFamily> family = buildFontFamily(kVsTestFont);
+    expectVSGlyphsForVsTestFont(family.get());
 }
 
 TEST_F(FontFamilyTest, hasVSTableTest) {
@@ -728,7 +766,7 @@
 
     for (const TestCase& testCase : testCases) {
         std::vector<std::shared_ptr<MinikinFont>> dummyFonts;
-        std::vector<Font> fonts;
+        std::vector<std::shared_ptr<Font>> fonts;
         for (auto familyStyle : testCase.familyStyles) {
             std::shared_ptr<MinikinFont> dummyFont(
                     new FreeTypeMinikinFontForTest(getTestFontPath(kTestFont)));
@@ -756,4 +794,41 @@
     }
 }
 
+TEST_F(FontFamilyTest, bufferTest) {
+    {
+        // Font with variation selectors
+        std::shared_ptr<FontFamily> original = buildFontFamily(kVsTestFont);
+        std::vector<uint8_t> buffer =
+                writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*original);
+        BufferReader reader(buffer.data());
+        std::shared_ptr<FontFamily> copied =
+                FontFamily::readFrom<readFreeTypeMinikinFontForTest>(&reader);
+        ASSERT_EQ(original->localeListId(), copied->localeListId());
+        ASSERT_EQ(original->variant(), copied->variant());
+        ASSERT_EQ(original->getNumFonts(), copied->getNumFonts());
+        ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
+        ASSERT_EQ(original->isColorEmojiFamily(), copied->isColorEmojiFamily());
+        ASSERT_EQ(original->isCustomFallback(), copied->isCustomFallback());
+        ASSERT_EQ(original->hasVSTable(), copied->hasVSTable());
+        expectVSGlyphsForVsTestFont(copied.get());
+        std::vector<uint8_t> newBuffer =
+                writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*copied);
+        ASSERT_EQ(buffer, newBuffer);
+    }
+    {
+        // Font with axes
+        constexpr char kMultiAxisFont[] = "MultiAxis.ttf";
+        std::shared_ptr<FontFamily> original = buildFontFamily(kMultiAxisFont);
+        std::vector<uint8_t> buffer =
+                writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*original);
+        BufferReader reader(buffer.data());
+        std::shared_ptr<FontFamily> copied =
+                FontFamily::readFrom<readFreeTypeMinikinFontForTest>(&reader);
+        ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
+        std::vector<uint8_t> newBuffer =
+                writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*copied);
+        ASSERT_EQ(buffer, newBuffer);
+    }
+}
+
 }  // namespace minikin
diff --git a/tests/unittest/FontFileParserTest.cpp b/tests/unittest/FontFileParserTest.cpp
new file mode 100644
index 0000000..d960217
--- /dev/null
+++ b/tests/unittest/FontFileParserTest.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#include "minikin/FontFileParser.h"
+
+#include <gtest/gtest.h>
+
+#include "FontTestUtils.h"
+#include "FreeTypeMinikinFontForTest.h"
+#include "PathUtils.h"
+
+namespace minikin {
+namespace {
+
+static size_t writeU16(uint16_t x, uint8_t* out, size_t offset) {
+    out[offset] = x >> 8;
+    out[offset + 1] = x;
+    return offset + 2;
+}
+
+static size_t writeU32(uint32_t x, uint8_t* out, size_t offset) {
+    out[offset] = x >> 24;
+    out[offset + 1] = x >> 16;
+    out[offset + 2] = x >> 8;
+    out[offset + 3] = x;
+    return offset + 4;
+}
+
+class TestableFontFileParser : public FontFileParser {
+public:
+    using FontFileParser::analyzeFontRevision;
+    using FontFileParser::checkPSName;
+};
+
+// Returns valid head table contents.
+static std::vector<uint8_t> buildHeadTable(uint32_t fontRevision) {
+    std::vector<uint8_t> out(46);
+    size_t head = writeU16(1, out.data(), 0);         // major version
+    head = writeU16(0, out.data(), head);             // minor version
+    head = writeU32(fontRevision, out.data(), head);  // fontRevision
+    head = writeU32(0xB1B0AFBA, out.data(), head);    // checksum. (random value)
+    head = writeU32(0x5F0F3CF5, out.data(), head);    // magicNumber
+    head = writeU16(0, out.data(), head);             // flasgs
+    head = writeU16(1024, out.data(), head);          // unitsPerEm
+    head = writeU32(123457890, out.data(), head);     // created (random value)
+    head = writeU32(123457890, out.data(), head);     // modified (random value)
+    head = writeU16(0, out.data(), head);             // xMin
+    head = writeU16(100, out.data(), head);           // yMin
+    head = writeU16(1024, out.data(), head);          // xMax
+    head = writeU16(2048, out.data(), head);          // yMax
+    head = writeU16(0, out.data(), head);             // macStyle
+    head = writeU16(10, out.data(), head);            // lowestRecPPEM
+    head = writeU16(1, out.data(), head);             // fontDirectionHint
+    head = writeU16(1, out.data(), head);             // indexToLocFormat
+    head = writeU16(0, out.data(), head);             // glyphDataFormat;
+
+    return out;
+}
+
+TEST(FontFileParserTest, analyzeFontRevision) {
+    uint32_t rev = 0x12345678;
+    std::vector<uint8_t> head = buildHeadTable(rev);
+
+    uint32_t out = 0;
+    EXPECT_TRUE(TestableFontFileParser::analyzeFontRevision(head.data(), head.size(), &out));
+    EXPECT_EQ(rev, out);
+}
+
+TEST(FontFileParserTest, headInvalidLength) {
+    uint32_t rev = 0x12345678;
+    std::vector<uint8_t> head = buildHeadTable(rev);
+
+    uint32_t out = 0;
+    EXPECT_FALSE(TestableFontFileParser::analyzeFontRevision(head.data(), 6, &out));
+}
+
+TEST(FontFileParserTest, parseFontForRev) {
+    auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
+    auto parser = FontFileParser(minikinFont->GetFontData(), minikinFont->GetFontSize(), 0);
+
+    auto revision = parser.getFontRevision();
+    EXPECT_TRUE(revision.has_value());
+    EXPECT_EQ(0x00010000u, revision.value());
+}
+
+TEST(FontFileParser, checkPSName) {
+    EXPECT_TRUE(TestableFontFileParser::checkPSName("Roboto-Regular"));
+    EXPECT_TRUE(TestableFontFileParser::checkPSName("NotoColorEmoji"));
+
+    // Space character is not allowed.
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("Roboto Regular"));
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("Noto Color Emoji"));
+
+    // parens are not not allowed.
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("Roboto (Regular)"));
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("Noto <Color> {Emoji}"));
+
+    // control characters are not allowed
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("Roboto-Regular\b"));
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("NotoColorEmoji\t"));
+
+    // Up to 63 character is allowed.
+    EXPECT_FALSE(TestableFontFileParser::checkPSName(std::string(64, 'a')));
+
+    // Only printable ASCII is allowed.
+    EXPECT_FALSE(TestableFontFileParser::checkPSName("ろぼとふぉんと"));
+}
+
+TEST(FontFileParserTest, parseFontForPSName) {
+    auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
+    auto parser = FontFileParser(minikinFont->GetFontData(), minikinFont->GetFontSize(), 0);
+
+    auto psName = parser.getPostScriptName();
+    EXPECT_TRUE(psName.has_value());
+    EXPECT_EQ("SampleFont-Regular", psName.value());
+}
+
+}  // namespace
+}  // namespace minikin
diff --git a/tests/unittest/FontLanguageListCacheTest.cpp b/tests/unittest/FontLanguageListCacheTest.cpp
index e957cfc..e68922d 100644
--- a/tests/unittest/FontLanguageListCacheTest.cpp
+++ b/tests/unittest/FontLanguageListCacheTest.cpp
@@ -61,4 +61,25 @@
     EXPECT_EQ(japanese, locales2[1]);
 }
 
+TEST(LocaleListCacheTest, buffer) {
+    std::string locales[] = {"en", "jp", "en,zh-Hans"};
+    // Measure
+    BufferWriter fakeWriter(nullptr);
+    for (const std::string& locale : locales) {
+        LocaleListCache::writeTo(&fakeWriter, LocaleListCache::getId(locale));
+    }
+    // Write
+    std::vector<uint8_t> buffer(fakeWriter.size());
+    BufferWriter writer(buffer.data());
+    for (const std::string& locale : locales) {
+        LocaleListCache::writeTo(&writer, LocaleListCache::getId(locale));
+    }
+    // Read
+    BufferReader reader(buffer.data());
+    for (const std::string& locale : locales) {
+        EXPECT_EQ(LocaleListCache::getId(locale), LocaleListCache::readFrom(&reader))
+                << "locale = " << locale;
+    }
+}
+
 }  // namespace minikin
diff --git a/tests/unittest/FontTest.cpp b/tests/unittest/FontTest.cpp
index ff2f9bc..68f5b51 100644
--- a/tests/unittest/FontTest.cpp
+++ b/tests/unittest/FontTest.cpp
@@ -18,28 +18,25 @@
 
 #include <gtest/gtest.h>
 
+#include "BufferUtils.h"
 #include "FontTestUtils.h"
 #include "FreeTypeMinikinFontForTest.h"
 
 namespace minikin {
 
-TEST(FontTest, CopyTest) {
+TEST(FontTest, BufferTest) {
     auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
-    {
-        Font font = Font::Builder(minikinFont).build();
-        {
-            Font copied(font);
-            EXPECT_EQ(font.typeface(), copied.typeface());
-            EXPECT_EQ(font.style(), copied.style());
-            EXPECT_EQ(font.baseFont(), copied.baseFont());
-        }
-        {
-            Font copied = font;
-            EXPECT_EQ(font.typeface(), copied.typeface());
-            EXPECT_EQ(font.style(), copied.style());
-            EXPECT_EQ(font.baseFont(), copied.baseFont());
-        }
-    }
+    std::shared_ptr<Font> original = Font::Builder(minikinFont).build();
+    std::vector<uint8_t> buffer = writeToBuffer<Font, writeFreeTypeMinikinFontForTest>(*original);
+
+    BufferReader reader(buffer.data());
+    std::shared_ptr<Font> font =
+            Font::readFrom<readFreeTypeMinikinFontForTest>(&reader, kEmptyLocaleListId);
+    EXPECT_EQ(minikinFont->GetFontPath(), font->typeface()->GetFontPath());
+    EXPECT_EQ(original->style(), font->style());
+    EXPECT_NE(nullptr, font->baseFont());
+    std::vector<uint8_t> newBuffer = writeToBuffer<Font, writeFreeTypeMinikinFontForTest>(*font);
+    EXPECT_EQ(buffer, newBuffer);
 }
 
 }  // namespace minikin
diff --git a/tests/unittest/GreedyLineBreakerTest.cpp b/tests/unittest/GreedyLineBreakerTest.cpp
index 13cc03c..e9da1a1 100644
--- a/tests/unittest/GreedyLineBreakerTest.cpp
+++ b/tests/unittest/GreedyLineBreakerTest.cpp
@@ -93,6 +93,33 @@
     std::vector<uint8_t> mHyphenationPattern;
 };
 
+TEST_F(GreedyLineBreakerTest, roundingError) {
+    MeasuredTextBuilder builder;
+    auto family1 = buildFontFamily("Ascii.ttf");
+    std::vector<std::shared_ptr<FontFamily>> families = {family1};
+    auto fc = std::make_shared<FontCollection>(families);
+    MinikinPaint paint(fc);
+    paint.size = 56.0f;  // Make 1em=56px
+    paint.scaleX = 1;
+    paint.letterSpacing = -0.093f;
+    paint.localeListId = LocaleListCache::getId("en-US");
+    const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
+
+    float measured = Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
+                                         StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr);
+
+    builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
+    std::unique_ptr<MeasuredText> measuredText =
+            builder.build(textBuffer, false /* compute hyphenation */,
+                          false /* compute full layout */, nullptr /* no hint */);
+    RectangleLineWidth rectangleLineWidth(measured);
+    TabStops tabStops(nullptr, 0, 10);
+    LineBreakResult r = breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
+                                        false /* do hyphenation */);
+
+    EXPECT_EQ(1u, r.breakPoints.size());
+}
+
 TEST_F(GreedyLineBreakerTest, testBreakWithoutHyphenation) {
     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
diff --git a/tests/unittest/HasherTest.cpp b/tests/unittest/HasherTest.cpp
index 8e11cc6..02cbda8 100644
--- a/tests/unittest/HasherTest.cpp
+++ b/tests/unittest/HasherTest.cpp
@@ -35,4 +35,9 @@
     EXPECT_EQ(hasher.hash(), hasher.hash());
 }
 
+TEST(HasherTest, hasherTestFloat) {
+    float x = 1.1f;
+    EXPECT_NE(Hasher().update(x).hash(), Hasher().update(1).hash());
+}
+
 }  // namespace minikin
diff --git a/tests/unittest/LayoutCoreTest.cpp b/tests/unittest/LayoutCoreTest.cpp
index ef972a0..2ab7543 100644
--- a/tests/unittest/LayoutCoreTest.cpp
+++ b/tests/unittest/LayoutCoreTest.cpp
@@ -74,7 +74,6 @@
         auto layout = buildLayout("I", {"LayoutTestFont.ttf"});
         EXPECT_EQ(1u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -87,7 +86,6 @@
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -103,7 +101,6 @@
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 60.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -130,7 +127,6 @@
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
         EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 30.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
         EXPECT_EQ(2u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -146,7 +142,6 @@
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
         EXPECT_EQ(Point(20.0f, 0), layout.pointAt(1));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 30.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), layout.extent());
         EXPECT_EQ(2u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -168,7 +163,6 @@
         auto layout = buildLayout("fi", {"Ligature.ttf"});
         EXPECT_EQ(1u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -181,7 +175,6 @@
         auto layout = buildLayout("ff", {"Ligature.ttf"});
         EXPECT_EQ(1u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -194,7 +187,6 @@
         auto layout = buildLayout("fi", {"Ligature.ttf"}, "'liga' off");
         EXPECT_EQ(1u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -207,7 +199,6 @@
         auto layout = buildLayout("ff", {"Ligature.ttf"}, "'liga' off");
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -222,7 +213,6 @@
         auto layout = buildLayout("fii", {"Ligature.ttf"});
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
@@ -238,7 +228,6 @@
         auto layout = buildLayout("if", {"Ligature.ttf"});
         EXPECT_EQ(2u, layout.glyphCount());
         EXPECT_EQ(Point(0, 0), layout.pointAt(0));
-        EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.bounds());
         EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
         EXPECT_EQ(1u, layout.fonts().size());
         EXPECT_TRUE(layout.fontAt(0).font);
diff --git a/tests/unittest/LayoutTest.cpp b/tests/unittest/LayoutTest.cpp
index 4b97cee..7771051 100644
--- a/tests/unittest/LayoutTest.cpp
+++ b/tests/unittest/LayoutTest.cpp
@@ -20,6 +20,7 @@
 
 #include "minikin/FontCollection.h"
 #include "minikin/LayoutPieces.h"
+#include "minikin/Measurement.h"
 
 #include "FontTestUtils.h"
 #include "UnicodeUtils.h"
@@ -35,6 +36,12 @@
     }
 }
 
+static void getBounds(const U16StringPiece& text, Bidi bidiFlags, const MinikinPaint& paint,
+                      MinikinRect* out) {
+    getBounds(text, Range(0, text.size()), bidiFlags, paint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, out);
+}
+
 class LayoutTest : public testing::Test {
 protected:
     LayoutTest() : mCollection(nullptr) {}
@@ -64,7 +71,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(70.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(70.0f, rect.mRight);
@@ -82,7 +90,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(90.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(90.0f, rect.mRight);
@@ -100,7 +109,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(160.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(160.0f, rect.mRight);
@@ -118,7 +128,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(110.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(110.0f, rect.mRight);
@@ -148,7 +159,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(70.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(70.0f, rect.mRight);
@@ -166,7 +178,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(95.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(95.0f, rect.mRight);
@@ -185,7 +198,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(170.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(170.0f, rect.mRight);
@@ -205,7 +219,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(120.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(120.0f, rect.mRight);
@@ -238,7 +253,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(70.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(70.0f, rect.mRight);
@@ -256,7 +272,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(85.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(85.0f, rect.mRight);
@@ -275,7 +292,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(140.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(140.0f, rect.mRight);
@@ -295,7 +313,8 @@
         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
                       EndHyphenEdit::NO_EDIT);
         EXPECT_EQ(100.0f, layout.getAdvance());
-        layout.getBounds(&rect);
+
+        getBounds(text, Bidi::LTR, paint, &rect);
         EXPECT_EQ(0.0f, rect.mLeft);
         EXPECT_EQ(10.0f, rect.mTop);
         EXPECT_EQ(100.0f, rect.mRight);
diff --git a/tests/unittest/MeasuredTextTest.cpp b/tests/unittest/MeasuredTextTest.cpp
index 1934ed8..e5766a1 100644
--- a/tests/unittest/MeasuredTextTest.cpp
+++ b/tests/unittest/MeasuredTextTest.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "minikin/LineBreaker.h"
+#include "minikin/Measurement.h"
 
 #include "FontTestUtils.h"
 #include "UnicodeUtils.h"
@@ -75,6 +76,34 @@
     EXPECT_EQ(MinikinRect(0.0f, 10.0f, 130.0f, 0.0f), mt->getBounds(text, Range(0, text.size())));
 }
 
+TEST(MeasuredTextTest, getBoundsTest_LTR) {
+    auto text = utf8ToUtf16("\u0028");  // U+0028 has 1em in LTR, 3em in RTL.
+    auto font = buildFontCollection("Bbox.ttf");
+
+    MeasuredTextBuilder builder;
+    MinikinPaint paint(font);
+    paint.size = 10.0f;
+    builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+    auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
+                            nullptr /* no hint */);
+
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
+}
+
+TEST(MeasuredTextTest, getBoundsTest_RTL) {
+    auto text = utf8ToUtf16("\u0028");  // U+0028 has 1em in LTR, 3em in RTL.
+    auto font = buildFontCollection("Bbox.ttf");
+
+    MeasuredTextBuilder builder;
+    MinikinPaint paint(font);
+    paint.size = 10.0f;
+    builder.addStyleRun(0, text.size(), std::move(paint), true /* is RTL */);
+    auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
+                            nullptr /* no hint */);
+
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), mt->getBounds(text, Range(0, 2)));
+}
+
 TEST(MeasuredTextTest, getBoundsTest_multiStyle) {
     auto text = utf8ToUtf16("Hello, World!");
     auto font = buildFontCollection("Ascii.ttf");
@@ -155,6 +184,7 @@
     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
                             nullptr /* no hint */);
 
+    MinikinRect rect;
     MinikinPaint samePaint(font);
     samePaint.size = 10.0f;
 
@@ -171,7 +201,9 @@
     EXPECT_EQ(10.0f, layout.getAdvance());
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 1), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
                              EndHyphenEdit::NO_EDIT);
@@ -185,7 +217,9 @@
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(10.0f, layout.getCharAdvance(1));
     EXPECT_EQ(2u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(1, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
                              EndHyphenEdit::NO_EDIT);
@@ -196,7 +230,9 @@
     EXPECT_EQ(10.0f, layout.getAdvance());
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(1, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, samePaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -210,7 +246,9 @@
     }
     EXPECT_EQ(130.0f, layout.getAdvance());
     EXPECT_EQ(text.size(), layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 130.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, text.size()), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 130.0f, 0.0f), rect);
 }
 
 TEST(MeasuredTextTest, buildLayoutTest_multiStyle) {
@@ -229,6 +267,7 @@
     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
                             nullptr /* no hint */);
 
+    MinikinRect rect;
     MinikinPaint samePaint(font);
     samePaint.size = 10.0f;
 
@@ -245,7 +284,9 @@
     EXPECT_EQ(10.0f, layout.getAdvance());
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 1), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
                              EndHyphenEdit::NO_EDIT);
@@ -259,7 +300,9 @@
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(10.0f, layout.getCharAdvance(1));
     EXPECT_EQ(2u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 20.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(1, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
                              EndHyphenEdit::NO_EDIT);
@@ -270,7 +313,9 @@
     EXPECT_EQ(10.0f, layout.getAdvance());
     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(1, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(7, 7), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
                              EndHyphenEdit::NO_EDIT);
@@ -287,7 +332,9 @@
     EXPECT_EQ(20.0f, layout.getAdvance());
     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(7, 8), Bidi::LTR, samePaint2, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), rect);
 }
 
 TEST(MeasuredTextTest, buildLayoutTest_differentPaint) {
@@ -302,6 +349,7 @@
     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
                             nullptr /* no hint */);
 
+    MinikinRect rect;
     MinikinPaint differentPaint(font);
     differentPaint.size = 20.0f;
 
@@ -318,7 +366,9 @@
     EXPECT_EQ(20.0f, layout.getAdvance());
     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 1), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, 2), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -332,7 +382,9 @@
     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
     EXPECT_EQ(20.0f, layout.getCharAdvance(1));
     EXPECT_EQ(2u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 40.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 40.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(1, 2), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -343,7 +395,9 @@
     EXPECT_EQ(20.0f, layout.getAdvance());
     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(1, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 20.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -357,7 +411,9 @@
     }
     EXPECT_EQ(260.0f, layout.getAdvance());
     EXPECT_EQ(text.size(), layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 260.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, text.size()), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 20.0f, 260.0f, 0.0f), rect);
 }
 
 TEST(MeasuredTextTest, buildLayoutTest_multiStyle_differentPaint) {
@@ -376,6 +432,7 @@
     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
                             nullptr /* no hint */);
 
+    MinikinRect rect;
     MinikinPaint differentPaint(font);
     differentPaint.size = 30.0f;
 
@@ -392,7 +449,9 @@
     EXPECT_EQ(30.0f, layout.getAdvance());
     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 1), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, 2), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -406,7 +465,9 @@
     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
     EXPECT_EQ(30.0f, layout.getCharAdvance(1));
     EXPECT_EQ(2u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 60.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 60.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(1, 2), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -417,7 +478,9 @@
     EXPECT_EQ(30.0f, layout.getAdvance());
     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(1, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(7, 7), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -432,7 +495,9 @@
     EXPECT_EQ(30.0f, layout.getAdvance());
     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
     EXPECT_EQ(1u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(7, 8), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(6, 8), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -446,7 +511,9 @@
     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
     EXPECT_EQ(30.0f, layout.getCharAdvance(1));
     EXPECT_EQ(2u, layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 60.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(6, 8), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 60.0f, 0.0f), rect);
 
     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, differentPaint,
                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
@@ -460,7 +527,9 @@
     }
     EXPECT_EQ(390.0f, layout.getAdvance());
     EXPECT_EQ(text.size(), layout.getAdvances().size());
-    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 390.0f, 0.0f), layout.getBounds());
+    getBounds(text, Range(0, text.size()), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
+              EndHyphenEdit::NO_EDIT, &rect);
+    EXPECT_EQ(MinikinRect(0.0f, 30.0f, 390.0f, 0.0f), rect);
 }
 
 }  // namespace minikin
diff --git a/tests/unittest/OptimalLineBreakerTest.cpp b/tests/unittest/OptimalLineBreakerTest.cpp
index 51aab60..d6801cc 100644
--- a/tests/unittest/OptimalLineBreakerTest.cpp
+++ b/tests/unittest/OptimalLineBreakerTest.cpp
@@ -2080,5 +2080,33 @@
                                                    << toString(textBuf, actual);
     }
 }
+
+TEST_F(OptimalLineBreakerTest, roundingError) {
+    MeasuredTextBuilder builder;
+    auto family1 = buildFontFamily("Ascii.ttf");
+    std::vector<std::shared_ptr<FontFamily>> families = {family1};
+    auto fc = std::make_shared<FontCollection>(families);
+    MinikinPaint paint(fc);
+    paint.size = 56.0f;  // Make 1em=56px
+    paint.scaleX = 1;
+    paint.letterSpacing = -0.093f;
+    paint.localeListId = LocaleListCache::getId("en-US");
+    const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
+
+    float measured = Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
+                                         StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr);
+
+    builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
+    std::unique_ptr<MeasuredText> measuredText =
+            builder.build(textBuffer, false /* compute hyphenation */,
+                          false /* compute full layout */, nullptr /* no hint */);
+    RectangleLineWidth rectangleLineWidth(measured);
+    TabStops tabStops(nullptr, 0, 10);
+    LineBreakResult r = doLineBreak(textBuffer, *measuredText, BreakStrategy::Balanced,
+                                    HyphenationFrequency::None, measured);
+
+    EXPECT_EQ(1u, r.breakPoints.size());
+}
+
 }  // namespace
 }  // namespace minikin
diff --git a/tests/unittest/SparseBitSetTest.cpp b/tests/unittest/SparseBitSetTest.cpp
index 03a14d9..8c67964 100644
--- a/tests/unittest/SparseBitSetTest.cpp
+++ b/tests/unittest/SparseBitSetTest.cpp
@@ -20,6 +20,8 @@
 
 #include <gtest/gtest.h>
 
+#include "BufferUtils.h"
+
 namespace minikin {
 
 TEST(SparseBitSetTest, randomTest) {
@@ -52,4 +54,29 @@
     }
 }
 
+TEST(SparseBitSetTest, bufferTest) {
+    std::vector<uint32_t> range({10, 20});
+    SparseBitSet originalBitset(range.data(), range.size() / 2);
+    std::vector<uint8_t> buffer = writeToBuffer(originalBitset);
+    BufferReader reader(buffer.data());
+    SparseBitSet bitset(&reader);
+
+    for (size_t i = 0; i < 10; ++i) ASSERT_FALSE(bitset.get(i)) << i;
+    for (size_t i = 10; i < 20; ++i) ASSERT_TRUE(bitset.get(i)) << i;
+    for (size_t i = 20; i < 30; ++i) ASSERT_FALSE(bitset.get(i)) << i;
+    std::vector<uint8_t> newBuffer = writeToBuffer(bitset);
+    ASSERT_EQ(buffer, newBuffer);
+}
+
+TEST(SparseBitSetTest, emptyBitSetBufferTest) {
+    SparseBitSet empty;
+    std::vector<uint8_t> buffer = writeToBuffer(empty);
+    BufferReader reader(buffer.data());
+    SparseBitSet bitset(&reader);
+
+    ASSERT_FALSE(bitset.get(0));
+    std::vector<uint8_t> newBuffer = writeToBuffer(bitset);
+    ASSERT_EQ(buffer, newBuffer);
+}
+
 }  // namespace minikin
diff --git a/tests/unittest/SystemFontsTest.cpp b/tests/unittest/SystemFontsTest.cpp
index fe603a9..f1b0109 100644
--- a/tests/unittest/SystemFontsTest.cpp
+++ b/tests/unittest/SystemFontsTest.cpp
@@ -21,6 +21,7 @@
 #include "minikin/FontCollection.h"
 
 #include "FontTestUtils.h"
+#include "PathUtils.h"
 
 namespace minikin {
 namespace {
@@ -30,10 +31,18 @@
     TestableSystemFonts() : SystemFonts() {}
     virtual ~TestableSystemFonts() {}
 
-    std::shared_ptr<FontCollection> findFontCollection(const std::string& familyName) const {
+    std::shared_ptr<FontCollection> findFontCollection(const std::string& familyName) {
         return findFontCollectionInternal(familyName);
     }
 
+    void addFontMap(std::shared_ptr<FontCollection>&& collections) {
+        addFontMapInternal(std::move(collections));
+    }
+
+    void getFontSet(std::function<void(const std::vector<std::shared_ptr<Font>>&)> func) {
+        getFontSetInternal(func);
+    }
+
     void registerFallback(const std::string& familyName,
                           const std::shared_ptr<FontCollection>& fc) {
         registerFallbackInternal(familyName, fc);
@@ -66,5 +75,44 @@
     EXPECT_EQ(fc2, systemFonts.findFontCollection("sans"));
 }
 
+TEST(SystemFontsTest, updateDefaultAndFallback) {
+    TestableSystemFonts systemFonts;
+    auto fc1 = buildFontCollection("Ascii.ttf");
+    auto fc2 = buildFontCollection("Bold.ttf");
+    systemFonts.registerDefault(fc1);
+    systemFonts.registerFallback("sans", fc2);
+    systemFonts.registerDefault(fc2);
+    systemFonts.registerFallback("sans", fc1);
+    EXPECT_EQ(fc2, systemFonts.findFontCollection("unknown-name"));
+    EXPECT_EQ(fc1, systemFonts.findFontCollection("sans"));
+}
+
+TEST(SystemFontTest, getAvailableFont_dedupFonts) {
+    TestableSystemFonts systemFonts;
+    auto asciiFamily = buildFontFamily("Ascii.ttf");
+    auto boldFamily = buildFontFamily("Bold.ttf");
+    auto boldItalicFamily = buildFontFamily("BoldItalic.ttf");
+
+    auto fc1Families = std::vector<std::shared_ptr<FontFamily>>{asciiFamily, boldItalicFamily};
+    auto fc2Families = std::vector<std::shared_ptr<FontFamily>>{boldFamily, boldItalicFamily};
+    auto fc1 = std::make_shared<FontCollection>(std::move(fc1Families));
+    auto fc2 = std::make_shared<FontCollection>(std::move(fc2Families));
+
+    systemFonts.addFontMap(std::move(fc1));
+    systemFonts.addFontMap(std::move(fc2));
+
+    systemFonts.getFontSet([](const std::vector<std::shared_ptr<Font>>& fonts) {
+        EXPECT_EQ(3u, fonts.size());  // Ascii, Bold and BoldItalic
+        std::unordered_set<std::string> fontPaths;
+        for (const auto& font : fonts) {
+            fontPaths.insert(getBasename(font->typeface()->GetFontPath()));
+        }
+
+        EXPECT_TRUE(fontPaths.find("Ascii.ttf") != fontPaths.end());
+        EXPECT_TRUE(fontPaths.find("Bold.ttf") != fontPaths.end());
+        EXPECT_TRUE(fontPaths.find("BoldItalic.ttf") != fontPaths.end());
+    });
+}
+
 }  // namespace
 }  // namespace minikin
diff --git a/tests/util/BufferUtils.h b/tests/util/BufferUtils.h
new file mode 100644
index 0000000..355e74e
--- /dev/null
+++ b/tests/util/BufferUtils.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef MINIKIN_TEST_BUFFER_UTILS_H
+#define MINIKIN_TEST_BUFFER_UTILS_H
+
+#include <minikin/Buffer.h>
+#include <vector>
+
+namespace minikin {
+
+template <class T>
+std::vector<uint8_t> allocateBuffer(const T& t) {
+    BufferWriter writer(nullptr);
+    t.writeTo(&writer);
+    // Fill with 0xFF for debugging.
+    return std::vector<uint8_t>(writer.size(), 0xFFu);
+}
+
+template <class T, auto arg>
+std::vector<uint8_t> allocateBuffer(const T& t) {
+    BufferWriter writer(nullptr);
+    t.template writeTo<arg>(&writer);
+    // Fill with 0xFF for debugging.
+    return std::vector<uint8_t>(writer.size(), 0xFFu);
+}
+
+template <class T>
+std::vector<uint8_t> writeToBuffer(const T& t) {
+    std::vector<uint8_t> buffer = allocateBuffer(t);
+    BufferWriter writer(buffer.data());
+    t.writeTo(&writer);
+    return buffer;
+}
+
+template <class T, auto arg>
+std::vector<uint8_t> writeToBuffer(const T& t) {
+    std::vector<uint8_t> buffer = allocateBuffer<T, arg>(t);
+    BufferWriter writer(buffer.data());
+    t.template writeTo<arg>(&writer);
+    return buffer;
+}
+
+}  // namespace minikin
+
+#endif  // MINIKIN_TEST_BUFFER_UTILS_H
diff --git a/tests/util/FontTestUtils.cpp b/tests/util/FontTestUtils.cpp
index 4143c04..5370ab6 100644
--- a/tests/util/FontTestUtils.cpp
+++ b/tests/util/FontTestUtils.cpp
@@ -67,7 +67,7 @@
             }
         }
 
-        std::vector<Font> fonts;
+        std::vector<std::shared_ptr<Font>> fonts;
         for (xmlNode* fontNode = familyNode->children; fontNode; fontNode = fontNode->next) {
             if (xmlStrcmp(fontNode->name, (const xmlChar*)"font") != 0) {
                 continue;
@@ -124,7 +124,7 @@
 
 std::shared_ptr<FontFamily> buildFontFamily(const std::string& filePath) {
     auto font = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(filePath));
-    std::vector<Font> fonts;
+    std::vector<std::shared_ptr<Font>> fonts;
     fonts.push_back(Font::Builder(font).build());
     return std::make_shared<FontFamily>(std::move(fonts));
 }
@@ -132,7 +132,7 @@
 std::shared_ptr<FontFamily> buildFontFamily(const std::string& filePath, const std::string& lang,
                                             bool isCustomFallback) {
     auto font = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(filePath));
-    std::vector<Font> fonts;
+    std::vector<std::shared_ptr<Font>> fonts;
     fonts.push_back(Font::Builder(font).build());
     return std::make_shared<FontFamily>(LocaleListCache::getId(lang), FamilyVariant::DEFAULT,
                                         std::move(fonts), isCustomFallback);
diff --git a/tests/util/FreeTypeMinikinFontForTest.cpp b/tests/util/FreeTypeMinikinFontForTest.cpp
index 1ea0631..1be466a 100644
--- a/tests/util/FreeTypeMinikinFontForTest.cpp
+++ b/tests/util/FreeTypeMinikinFontForTest.cpp
@@ -37,8 +37,6 @@
 namespace minikin {
 namespace {
 
-static int uniqueId = 0;
-
 constexpr FT_Int32 LOAD_FLAG =
         FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
@@ -62,7 +60,7 @@
 }  // namespace
 
 FreeTypeMinikinFontForTest::FreeTypeMinikinFontForTest(const std::string& font_path, int index)
-        : MinikinFont(uniqueId++), mFontPath(font_path), mFontIndex(index) {
+        : mFontPath(font_path), mFontIndex(index) {
     int fd = open(font_path.c_str(), O_RDONLY);
     LOG_ALWAYS_FATAL_IF(fd == -1, "Open failed: %s", font_path.c_str());
     struct stat st = {};
@@ -114,4 +112,18 @@
     extent->descent = -static_cast<float>(mFtFace->descender) * paint.size / upem;
 }
 
+void writeFreeTypeMinikinFontForTest(BufferWriter* writer, const MinikinFont* typeface) {
+    writer->writeString(typeface->GetFontPath());
+}
+
+std::shared_ptr<MinikinFont> loadFreeTypeMinikinFontForTest(BufferReader reader) {
+    std::string fontPath(reader.readString());
+    return std::make_shared<FreeTypeMinikinFontForTest>(fontPath);
+}
+
+Font::TypefaceLoader* readFreeTypeMinikinFontForTest(BufferReader* reader) {
+    reader->skipString();  // fontPath
+    return &loadFreeTypeMinikinFontForTest;
+}
+
 }  // namespace minikin
diff --git a/tests/util/FreeTypeMinikinFontForTest.h b/tests/util/FreeTypeMinikinFontForTest.h
index 4b6ea05..4cdb6d8 100644
--- a/tests/util/FreeTypeMinikinFontForTest.h
+++ b/tests/util/FreeTypeMinikinFontForTest.h
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include "minikin/Buffer.h"
+#include "minikin/Font.h"
 #include "minikin/MinikinFont.h"
 
 #include <ft2build.h>
@@ -43,8 +45,7 @@
     void GetFontExtent(MinikinExtent* extent, const MinikinPaint& paint,
                        const FontFakery& fakery) const override;
 
-    const std::string& fontPath() const { return mFontPath; }
-
+    const std::string& GetFontPath() const override { return mFontPath; }
     const void* GetFontData() const { return mFontData; }
     size_t GetFontSize() const { return mFontSize; }
     int GetFontIndex() const { return mFontIndex; }
@@ -63,6 +64,10 @@
     MINIKIN_PREVENT_COPY_AND_ASSIGN(FreeTypeMinikinFontForTest);
 };
 
+void writeFreeTypeMinikinFontForTest(BufferWriter* writer, const MinikinFont* typeface);
+
+Font::TypefaceLoader* readFreeTypeMinikinFontForTest(BufferReader* reader);
+
 }  // namespace minikin
 
 #endif  // MINIKIN_TEST_FREE_TYPE_MINIKIN_FONT_FOR_TEST_H