Merge "Reject unsorted cmap entries. DO NOT MERGE am: 891e1569fa  -s ours" into lmp-mr1-dev am: abc4ac75da am: 33f42da9a9  -s ours am: a02dccd656 am: e4d2aeec0e am: 0fb4dfadd6 am: 5bc8e2aad7  -s ours am: 85f8b08e86 am: c741c242b6 am: 3c3c4a9c3c am: 5b013a6193
am: fa5c0e9773  -s ours

Change-Id: Ic452b5992272071c9a477fce616fd1194a171e7d
diff --git a/app/Android.mk b/app/Android.mk
index 2038683..23305b7 100644
--- a/app/Android.mk
+++ b/app/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     liblog \
-    libicuuc-host
+    libicuuc
 
 LOCAL_SRC_FILES += \
     HyphTool.cpp
diff --git a/app/HyphTool.cpp b/app/HyphTool.cpp
index 730abad..403d374 100644
--- a/app/HyphTool.cpp
+++ b/app/HyphTool.cpp
@@ -2,14 +2,16 @@
 #include <sys/stat.h>
 #include <string.h>
 
+#include "unicode/locid.h"
 #include "utils/Log.h"
 
 #include <vector>
 #include <minikin/Hyphenator.h>
 
-using android::Hyphenator;
+using minikin::HyphenationType;
+using minikin::Hyphenator;
 
-Hyphenator* loadHybFile(const char* fn) {
+Hyphenator* loadHybFile(const char* fn, int minPrefix, int minSuffix) {
     struct stat statbuf;
     int status = stat(fn, &statbuf);
     if (status < 0) {
@@ -24,17 +26,18 @@
     }
     uint8_t* buf = new uint8_t[size];
     size_t read_size = fread(buf, 1, size, f);
+    fclose(f);
     if (read_size < size) {
         fprintf(stderr, "error reading %s\n", fn);
         delete[] buf;
         return nullptr;
     }
-    return Hyphenator::loadBinary(buf);
+    return Hyphenator::loadBinary(buf, minPrefix, minSuffix);
 }
 
 int main(int argc, char** argv) {
-    Hyphenator* hyph = loadHybFile("/tmp/en.hyb");  // should also be configurable
-    std::vector<uint8_t> result;
+    Hyphenator* hyph = loadHybFile("/tmp/en.hyb", 2, 3);  // should also be configurable
+    std::vector<HyphenationType> result;
     std::vector<uint16_t> word;
     if (argc < 2) {
         fprintf(stderr, "usage: hyphtool word\n");
@@ -50,9 +53,9 @@
         // ASCII (or possibly ISO Latin 1), but kinda painful to do utf conversion :(
         word.push_back(c);
     }
-    hyph->hyphenate(&result, word.data(), word.size());
+    hyph->hyphenate(&result, word.data(), word.size(), icu::Locale::getUS());
     for (size_t i = 0; i < len; i++) {
-        if (result[i] != 0) {
+        if (result[i] != HyphenationType::DONT_BREAK) {
             printf("-");
         }
         printf("%c", word[i]);
diff --git a/include/minikin/CmapCoverage.h b/include/minikin/CmapCoverage.h
index 56abac7..af5960d 100644
--- a/include/minikin/CmapCoverage.h
+++ b/include/minikin/CmapCoverage.h
@@ -19,14 +19,17 @@
 
 #include <minikin/SparseBitSet.h>
 
-namespace android {
+#include <memory>
+#include <vector>
+
+namespace minikin {
 
 class CmapCoverage {
 public:
-    static bool getCoverage(SparseBitSet &coverage, const uint8_t* cmap_data, size_t cmap_size,
-            bool* has_cmap_format14_subtable);
+    static SparseBitSet getCoverage(const uint8_t* cmap_data, size_t cmap_size,
+            std::vector<std::unique_ptr<SparseBitSet>>* out);
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_CMAP_COVERAGE_H
diff --git a/libs/minikin/MinikinRefCounted.cpp b/include/minikin/Emoji.h
similarity index 60%
rename from libs/minikin/MinikinRefCounted.cpp
rename to include/minikin/Emoji.h
index 9fa3ae4..2826173 100644
--- a/libs/minikin/MinikinRefCounted.cpp
+++ b/include/minikin/Emoji.h
@@ -14,22 +14,21 @@
  * limitations under the License.
  */
 
-// Base class for reference counted objects in Minikin
+#include <unicode/uchar.h>
 
-#include "MinikinInternal.h"
+namespace minikin {
 
-#include <minikin/MinikinRefCounted.h>
+// Returns true if c is emoji.
+bool isEmoji(uint32_t c);
 
-namespace android {
+// Returns true if c is emoji modifier base.
+bool isEmojiBase(uint32_t c);
 
-void MinikinRefCounted::Ref() {
-    AutoMutex _l(gMinikinLock);
-    this->RefLocked();
-}
+// Returns true if c is emoji modifier.
+bool isEmojiModifier(uint32_t c);
 
-void MinikinRefCounted::Unref() {
-    AutoMutex _l(gMinikinLock);
-    this->UnrefLocked();
-}
+// Bidi override for ICU that knows about new emoji.
+UCharDirection emojiBidiOverride(const void* context, UChar32 c);
 
-}
+}  // namespace minikin
+
diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h
index c3c183d..138ba45 100644
--- a/include/minikin/FontCollection.h
+++ b/include/minikin/FontCollection.h
@@ -17,19 +17,19 @@
 #ifndef MINIKIN_FONT_COLLECTION_H
 #define MINIKIN_FONT_COLLECTION_H
 
+#include <memory>
+#include <unordered_set>
 #include <vector>
 
-#include <minikin/MinikinRefCounted.h>
 #include <minikin/MinikinFont.h>
 #include <minikin/FontFamily.h>
 
-namespace android {
+namespace minikin {
 
-class FontCollection : public MinikinRefCounted {
+class FontCollection {
 public:
-    explicit FontCollection(const std::vector<FontFamily*>& typefaces);
-
-    ~FontCollection();
+    explicit FontCollection(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
+    explicit FontCollection(std::shared_ptr<FontFamily>&& typeface);
 
     struct Run {
         FakedFont fakedFont;
@@ -45,29 +45,45 @@
     // selector pair, or invalid variation selector is passed.
     bool hasVariationSelector(uint32_t baseCodepoint, uint32_t variationSelector) const;
 
-    // Get the base font for the given style, useful for font-wide metrics.
-    MinikinFont* baseFont(FontStyle style);
-
     // Get base font with fakery information (fake bold could affect metrics)
     FakedFont baseFontFaked(FontStyle style);
 
+    // Creates new FontCollection based on this collection while applying font variations. Returns
+    // nullptr if none of variations apply to this collection.
+    std::shared_ptr<FontCollection>
+            createCollectionWithVariation(const std::vector<FontVariation>& variations);
+
+    const std::unordered_set<AxisTag>& getSupportedTags() const {
+        return mSupportedAxes;
+    }
+
     uint32_t getId() const;
 
 private:
     static const int kLogCharsPerPage = 8;
     static const int kPageMask = (1 << kLogCharsPerPage) - 1;
 
+    // mFamilyVec holds the indices of the mFamilies and mRanges holds the range of indices of
+    // mFamilyVec. The maximum number of pages is 0x10FF (U+10FFFF >> 8). The maximum number of
+    // the fonts is 0xFF. Thus, technically the maximum length of mFamilyVec is 0x10EE01
+    // (0x10FF * 0xFF). However, in practice, 16-bit integers are enough since most fonts supports
+    // only limited range of code points.
     struct Range {
-        size_t start;
-        size_t end;
+        uint16_t start;
+        uint16_t end;
     };
 
-    FontFamily* getFamilyForChar(uint32_t ch, uint32_t vs, uint32_t langListId, int variant) const;
+    // 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 langListId, int variant) const;
 
     uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
-                             FontFamily* fontFamily) const;
+            const std::shared_ptr<FontFamily>& fontFamily) const;
 
-    uint32_t calcCoverageScore(uint32_t ch, uint32_t vs, FontFamily* fontFamily) const;
+    uint32_t calcCoverageScore(uint32_t ch, uint32_t vs,
+            const std::shared_ptr<FontFamily>& fontFamily) const;
 
     static uint32_t calcLanguageMatchingScore(uint32_t userLangListId,
                                               const FontFamily& fontFamily);
@@ -83,21 +99,26 @@
     // Highest UTF-32 code point that can be mapped
     uint32_t mMaxChar;
 
-    // This vector has ownership of the bitsets and typeface objects.
+    // This vector has pointers to the all font family instances in this collection.
     // This vector can't be empty.
-    std::vector<FontFamily*> mFamilies;
+    std::vector<std::shared_ptr<FontFamily>> mFamilies;
 
-    // This vector contains pointers into mInstances
+    // Following two vectors are pre-calculated tables for resolving coverage faster.
+    // For example, to iterate over all fonts which support Unicode code point U+XXYYZZ,
+    // iterate font families index from mFamilyVec[mRanges[0xXXYY].start] to
+    // mFamilyVec[mRange[0xXXYY].end] instead of whole mFamilies.
+    // This vector contains indices into mFamilies.
     // This vector can't be empty.
-    std::vector<FontFamily*> mFamilyVec;
-
-    // This vector has pointers to the font family instance which has cmap 14 subtable.
-    std::vector<FontFamily*> mVSFamilyVec;
-
-    // These are offsets into mInstanceVec, one range per page
     std::vector<Range> mRanges;
+    std::vector<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;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_FONT_COLLECTION_H
diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h
index 81033d2..04c95bc 100644
--- a/include/minikin/FontFamily.h
+++ b/include/minikin/FontFamily.h
@@ -17,16 +17,18 @@
 #ifndef MINIKIN_FONT_FAMILY_H
 #define MINIKIN_FONT_FAMILY_H
 
-#include <vector>
+#include <memory>
 #include <string>
+#include <unordered_set>
+#include <vector>
+
 #include <hb.h>
 
 #include <utils/TypeHelpers.h>
 
-#include <minikin/MinikinRefCounted.h>
 #include <minikin/SparseBitSet.h>
 
-namespace android {
+namespace minikin {
 
 class MinikinFont;
 
@@ -37,7 +39,7 @@
 public:
     FontStyle() : FontStyle(0 /* variant */, 4 /* weight */, false /* italic */) {}
     FontStyle(int weight, bool italic) : FontStyle(0 /* variant */, weight, italic) {}
-    FontStyle(uint32_t langListId)
+    FontStyle(uint32_t langListId)  // NOLINT(implicit)
             : FontStyle(langListId, 0 /* variant */, 4 /* weight */, false /* italic */) {}
 
     FontStyle(int variant, int weight, bool italic);
@@ -52,7 +54,7 @@
           return bits == other.bits && mLanguageListId == other.mLanguageListId;
     }
 
-    hash_t hash() const;
+    android::hash_t hash() const;
 
     // Looks up a language list from an internal cache and returns its ID.
     // If the passed language list is not in the cache, registers it and returns newly assigned ID.
@@ -75,7 +77,7 @@
     VARIANT_ELEGANT = 2,
 };
 
-inline hash_t hash_type(const FontStyle &style) {
+inline android::hash_t hash_type(const FontStyle &style) {
     return style.hash();
 }
 
@@ -98,66 +100,80 @@
     FontFakery fakery;
 };
 
-class FontFamily : public MinikinRefCounted {
+typedef uint32_t AxisTag;
+
+struct Font {
+    Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style);
+    Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style);
+    Font(Font&& o);
+    Font(const Font& o);
+
+    std::shared_ptr<MinikinFont> typeface;
+    FontStyle style;
+
+    std::unordered_set<AxisTag> getSupportedAxesLocked() const;
+};
+
+struct FontVariation {
+    FontVariation(AxisTag axisTag, float value) : axisTag(axisTag), value(value) {}
+    AxisTag axisTag;
+    float value;
+};
+
+class FontFamily {
 public:
-    FontFamily();
+    explicit FontFamily(std::vector<Font>&& fonts);
+    FontFamily(int variant, std::vector<Font>&& fonts);
+    FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts);
 
-    FontFamily(int variant);
-
-    FontFamily(uint32_t langId, int variant)
-        : mLangId(langId),
-        mVariant(variant),
-        mHasVSTable(false),
-        mCoverageValid(false) {
-    }
-
-    ~FontFamily();
-
-    // Add font to family, extracting style information from the font
-    bool addFont(MinikinFont* typeface);
-
-    void addFont(MinikinFont* typeface, FontStyle style);
+    // TODO: Good to expose FontUtil.h.
+    static bool analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
+            bool* italic);
     FakedFont getClosestMatch(FontStyle style) const;
 
     uint32_t langId() const { return mLangId; }
     int variant() const { return mVariant; }
 
     // API's for enumerating the fonts in a family. These don't guarantee any particular order
-    size_t getNumFonts() const;
-    MinikinFont* getFont(size_t index) const;
-    FontStyle getStyle(size_t index) const;
+    size_t getNumFonts() const { return mFonts.size(); }
+    const std::shared_ptr<MinikinFont>& getFont(size_t index) const {
+        return mFonts[index].typeface;
+    }
+    FontStyle getStyle(size_t index) const { return mFonts[index].style; }
     bool isColorEmojiFamily() const;
+    const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
 
-    // Get Unicode coverage. Lifetime of returned bitset is same as receiver. May return nullptr on
-    // error.
-    const SparseBitSet* getCoverage();
+    // Get Unicode coverage.
+    const SparseBitSet& getCoverage() const { return mCoverage; }
 
     // Returns true if the font has a glyph for the code point and variation selector pair.
     // Caller should acquire a lock before calling the method.
-    bool hasGlyph(uint32_t codepoint, uint32_t variationSelector);
+    bool hasGlyph(uint32_t codepoint, uint32_t variationSelector) const;
 
     // Returns true if this font family has a variaion sequence table (cmap format 14 subtable).
-    bool hasVSTable() const;
+    bool hasVSTable() const { return !mCmapFmt14Coverage.empty(); }
+
+    // Creates new FontFamily based on this family while applying font variations. Returns nullptr
+    // if none of variations apply to this family.
+    std::shared_ptr<FontFamily> createFamilyWithVariation(
+            const std::vector<FontVariation>& variations) const;
 
 private:
-    void addFontLocked(MinikinFont* typeface, FontStyle style);
+    void computeCoverage();
 
-    class Font {
-    public:
-        Font(MinikinFont* typeface, FontStyle style) :
-            typeface(typeface), style(style) { }
-        MinikinFont* typeface;
-        FontStyle style;
-    };
     uint32_t mLangId;
     int mVariant;
     std::vector<Font> mFonts;
+    std::unordered_set<AxisTag> mSupportedAxes;
 
     SparseBitSet mCoverage;
-    bool mHasVSTable;
-    bool mCoverageValid;
+    std::vector<std::unique_ptr<SparseBitSet>> mCmapFmt14Coverage;
+
+    // Forbid copying and assignment.
+    FontFamily(const FontFamily&) = delete;
+    void operator=(const FontFamily&) = delete;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_FONT_FAMILY_H
diff --git a/include/minikin/GraphemeBreak.h b/include/minikin/GraphemeBreak.h
index 3120101..f1b5102 100644
--- a/include/minikin/GraphemeBreak.h
+++ b/include/minikin/GraphemeBreak.h
@@ -17,7 +17,7 @@
 #ifndef MINIKIN_GRAPHEME_BREAK_H
 #define MINIKIN_GRAPHEME_BREAK_H
 
-namespace android {
+namespace minikin {
 
 class GraphemeBreak {
 public:
@@ -31,17 +31,17 @@
     };
 
     // Determine whether the given offset is a grapheme break.
-    // This implementation generally follows Unicode TR29 extended
-    // grapheme break, but with some tweaks to more closely match
-    // existing implementations.
-    static bool isGraphemeBreak(const uint16_t* buf, size_t start, size_t count, size_t offset);
+    // This implementation generally follows Unicode's UTR #29 extended
+    // grapheme break, with various tweaks.
+    static bool isGraphemeBreak(const float* advances, const uint16_t* buf, size_t start,
+            size_t count, size_t offset);
 
     // Matches Android's Java API. Note, return (size_t)-1 for AT to
     // signal non-break because unsigned return type.
-    static size_t getTextRunCursor(const uint16_t* buf, size_t start, size_t count,
-            size_t offset, MoveOpt opt);
+    static size_t getTextRunCursor(const float* advances, const uint16_t* buf, size_t start,
+            size_t count, size_t offset, MoveOpt opt);
 };
 
-}  // namespace android
+}  // namespace minikin
 
-#endif  // MINIKIN_GRAPHEME_BREAK_H
\ No newline at end of file
+#endif  // MINIKIN_GRAPHEME_BREAK_H
diff --git a/include/minikin/Hyphenator.h b/include/minikin/Hyphenator.h
index 9605205..2b8ccb7 100644
--- a/include/minikin/Hyphenator.h
+++ b/include/minikin/Hyphenator.h
@@ -18,51 +18,140 @@
  * An implementation of Liang's hyphenation algorithm.
  */
 
+#include "unicode/locid.h"
 #include <memory>
 #include <unordered_map>
 
 #ifndef MINIKIN_HYPHENATOR_H
 #define MINIKIN_HYPHENATOR_H
 
-namespace android {
+namespace minikin {
+
+enum class HyphenationType : uint8_t {
+    // Note: There are implicit assumptions scattered in the code that DONT_BREAK is 0.
+
+    // Do not break.
+    DONT_BREAK = 0,
+    // Break the line and insert a normal hyphen.
+    BREAK_AND_INSERT_HYPHEN = 1,
+    // Break the line and insert an Armenian hyphen (U+058A).
+    BREAK_AND_INSERT_ARMENIAN_HYPHEN = 2,
+    // Break the line and insert a maqaf (Hebrew hyphen, U+05BE).
+    BREAK_AND_INSERT_MAQAF = 3,
+    // Break the line and insert a Canadian Syllabics hyphen (U+1400).
+    BREAK_AND_INSERT_UCAS_HYPHEN = 4,
+    // Break the line, but don't insert a hyphen. Used for cases when there is already a hyphen
+    // present or the script does not use a hyphen (e.g. in Malayalam).
+    BREAK_AND_DONT_INSERT_HYPHEN = 5,
+    // Break and replace the last code unit with hyphen. Used for Catalan "l·l" which hyphenates
+    // as "l-/l".
+    BREAK_AND_REPLACE_WITH_HYPHEN = 6,
+    // Break the line, and repeat the hyphen (which is the last character) at the beginning of the
+    // next line. Used in Polish, where "czerwono-niebieska" should hyphenate as
+    // "czerwono-/-niebieska".
+    BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE = 7,
+    // Break the line, insert a ZWJ and hyphen at the first line, and a ZWJ at the second line.
+    // This is used in Arabic script, mostly for writing systems of Central Asia. It's our default
+    // behavior when a soft hyphen is used in Arabic script.
+    BREAK_AND_INSERT_HYPHEN_AND_ZWJ = 8
+};
+
+// The hyphen edit represents an edit to the string when a word is
+// hyphenated. The most common hyphen edit is adding a "-" at the end
+// of a syllable, but nonstandard hyphenation allows for more choices.
+// Note that a HyphenEdit can hold two types of edits at the same time,
+// One at the beginning of the string/line and one at the end.
+class HyphenEdit {
+public:
+    static const uint32_t NO_EDIT = 0x00;
+
+    static const uint32_t INSERT_HYPHEN_AT_END = 0x01;
+    static const uint32_t INSERT_ARMENIAN_HYPHEN_AT_END = 0x02;
+    static const uint32_t INSERT_MAQAF_AT_END = 0x03;
+    static const uint32_t INSERT_UCAS_HYPHEN_AT_END = 0x04;
+    static const uint32_t INSERT_ZWJ_AND_HYPHEN_AT_END = 0x05;
+    static const uint32_t REPLACE_WITH_HYPHEN_AT_END = 0x06;
+    static const uint32_t BREAK_AT_END = 0x07;
+
+    static const uint32_t INSERT_HYPHEN_AT_START = 0x01 << 3;
+    static const uint32_t INSERT_ZWJ_AT_START = 0x02 << 3;
+    static const uint32_t BREAK_AT_START = 0x03 << 3;
+
+    // Keep in sync with the definitions in the Java code at:
+    // frameworks/base/graphics/java/android/graphics/Paint.java
+    static const uint32_t MASK_END_OF_LINE = 0x07;
+    static const uint32_t MASK_START_OF_LINE = 0x03 << 3;
+
+    inline static bool isReplacement(uint32_t hyph) {
+        return hyph == REPLACE_WITH_HYPHEN_AT_END;
+    }
+
+    inline static bool isInsertion(uint32_t hyph) {
+        return (hyph == INSERT_HYPHEN_AT_END
+                || hyph == INSERT_ARMENIAN_HYPHEN_AT_END
+                || hyph == INSERT_MAQAF_AT_END
+                || hyph == INSERT_UCAS_HYPHEN_AT_END
+                || hyph == INSERT_ZWJ_AND_HYPHEN_AT_END
+                || hyph == INSERT_HYPHEN_AT_START
+                || hyph == INSERT_ZWJ_AT_START);
+    }
+
+    const static uint32_t* getHyphenString(uint32_t hyph);
+    static uint32_t editForThisLine(HyphenationType type);
+    static uint32_t editForNextLine(HyphenationType type);
+
+    HyphenEdit() : hyphen(NO_EDIT) { }
+    HyphenEdit(uint32_t hyphenInt) : hyphen(hyphenInt) { }  // NOLINT(implicit)
+    uint32_t getHyphen() const { return hyphen; }
+    bool operator==(const HyphenEdit &other) const { return hyphen == other.hyphen; }
+
+    uint32_t getEnd() const { return hyphen & MASK_END_OF_LINE; }
+    uint32_t getStart() const { return hyphen & MASK_START_OF_LINE; }
+
+private:
+    uint32_t hyphen;
+};
 
 // hyb file header; implementation details are in the .cpp file
 struct Header;
 
 class Hyphenator {
 public:
-    // Note: this will also require a locale, for proper case folding behavior
-    static Hyphenator* load(const uint16_t* patternData, size_t size);
+    // Compute the hyphenation of a word, storing the hyphenation in result vector. Each entry in
+    // the vector is a "hyphenation type" for a potential hyphenation that can be applied at the
+    // corresponding code unit offset in the word.
+    //
+    // Example: word is "hyphen", result is the following, corresponding to "hy-phen":
+    // [DONT_BREAK, DONT_BREAK, BREAK_AND_INSERT_HYPHEN, DONT_BREAK, DONT_BREAK, DONT_BREAK]
+    void hyphenate(std::vector<HyphenationType>* result, const uint16_t* word, size_t len,
+            const icu::Locale& locale);
 
-    // Compute the hyphenation of a word, storing the hyphenation in result vector. Each
-    // entry in the vector is a "hyphen edit" to be applied at the corresponding code unit
-    // offset in the word. Currently 0 means no hyphen and 1 means insert hyphen and break,
-    // but this will be expanded to other edits for nonstandard hyphenation.
-    // Example: word is "hyphen", result is [0 0 1 0 0 0], corresponding to "hy-phen".
-    void hyphenate(std::vector<uint8_t>* result, const uint16_t* word, size_t len);
+    // Returns true if the codepoint is like U+2010 HYPHEN in line breaking and usage: a character
+    // immediately after which line breaks are allowed, but words containing it should not be
+    // automatically hyphenated.
+    static bool isLineBreakingHyphen(uint32_t cp);
 
     // pattern data is in binary format, as described in doc/hyb_file_format.md. Note:
     // the caller is responsible for ensuring that the lifetime of the pattern data is
     // at least as long as the Hyphenator object.
 
-    // Note: nullptr is valid input, in which case the hyphenator only processes soft hyphens
-    static Hyphenator* loadBinary(const uint8_t* patternData);
+    // Note: nullptr is valid input, in which case the hyphenator only processes soft hyphens.
+    static Hyphenator* loadBinary(const uint8_t* patternData, size_t minPrefix, size_t minSuffix);
 
 private:
-    // apply soft hyphens only, ignoring patterns
-    void hyphenateSoft(uint8_t* result, const uint16_t* word, size_t len);
+    // apply various hyphenation rules including hard and soft hyphens, ignoring patterns
+    void hyphenateWithNoPatterns(HyphenationType* result, const uint16_t* word, size_t len,
+            const icu::Locale& locale);
 
-    // try looking up word in alphabet table, return false if any code units fail to map
-    // Note that this methor writes len+2 entries into alpha_codes (including start and stop)
-    bool alphabetLookup(uint16_t* alpha_codes, const uint16_t* word, size_t len);
+    // Try looking up word in alphabet table, return DONT_BREAK if any code units fail to map.
+    // Otherwise, returns BREAK_AND_INSERT_HYPHEN, BREAK_AND_INSERT_ARMENIAN_HYPHEN, or
+    // BREAK_AND_DONT_INSERT_HYPHEN based on the the script of the characters seen.
+    // Note that this method writes len+2 entries into alpha_codes (including start and stop)
+    HyphenationType alphabetLookup(uint16_t* alpha_codes, const uint16_t* word, size_t len);
 
     // calculate hyphenation from patterns, assuming alphabet lookup has already been done
-    void hyphenateFromCodes(uint8_t* result, const uint16_t* codes, size_t len);
-
-    // TODO: these should become parameters, as they might vary by locale, screen size, and
-    // possibly explicit user control.
-    static const int MIN_PREFIX = 2;
-    static const int MIN_SUFFIX = 3;
+    void hyphenateFromCodes(HyphenationType* result, const uint16_t* codes, size_t len,
+            HyphenationType hyphenValue);
 
     // See also LONGEST_HYPHENATED_WORD in LineBreaker.cpp. Here the constant is used so
     // that temporary buffers can be stack-allocated without waste, which is a slightly
@@ -70,6 +159,7 @@
     static const size_t MAX_HYPHENATED_SIZE = 64;
 
     const uint8_t* patternData;
+    size_t minPrefix, minSuffix;
 
     // accessors for binary data
     const Header* getHeader() const {
@@ -78,6 +168,6 @@
 
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif   // MINIKIN_HYPHENATOR_H
diff --git a/include/minikin/Layout.h b/include/minikin/Layout.h
index d9bb01f..6d1de2f 100644
--- a/include/minikin/Layout.h
+++ b/include/minikin/Layout.h
@@ -19,32 +19,13 @@
 
 #include <hb.h>
 
+#include <memory>
 #include <vector>
 
 #include <minikin/FontCollection.h>
-#include <minikin/MinikinFontFreeType.h>
 
 namespace minikin {
 
-// The Bitmap class is for debugging. We'll probably move it out
-// of here into a separate lightweight software rendering module
-// (optional, as we'd hope most clients would do their own)
-class Bitmap {
-public:
-    Bitmap(int width, int height);
-    ~Bitmap();
-    void writePnm(std::ofstream& o) const;
-    void drawGlyph(const android::GlyphBitmap& bitmap, int x, int y);
-private:
-    int width;
-    int height;
-    uint8_t* buf;
-};
-
-} // namespace minikin
-
-namespace android {
-
 struct LayoutGlyph {
     // index into mFaces and mHbFonts vectors. We could imagine
     // moving this into a run length representation, because it's
@@ -75,37 +56,32 @@
 // Lifecycle and threading assumptions for Layout:
 // The object is assumed to be owned by a single thread; multiple threads
 // may not mutate it at the same time.
-// The lifetime of the FontCollection set through setFontCollection must
-// extend through the lifetime of the Layout object.
 class Layout {
 public:
 
-    Layout() : mGlyphs(), mAdvances(), mCollection(0), mFaces(), mAdvance(0), mBounds() {
+    Layout() : mGlyphs(), mAdvances(), mFaces(), mAdvance(0), mBounds() {
         mBounds.setEmpty();
     }
 
-    // Clears layout, ready to be used again
-    void reset();
+    Layout(Layout&& layout) = default;
+
+    // Forbid copying and assignment.
+    Layout(const Layout&) = delete;
+    void operator=(const Layout&) = delete;
 
     void dump() const;
-    void setFontCollection(const FontCollection* collection);
 
     void doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        int bidiFlags, const FontStyle &style, const MinikinPaint &paint);
+        int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
+        const std::shared_ptr<FontCollection>& collection);
 
     static float measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
         int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
-        const FontCollection* collection, float* advances);
-
-    void draw(minikin::Bitmap*, int x0, int y0, float size) const;
-
-    // Deprecated. Nont needed. Remove when callers are removed.
-    static void init();
+        const std::shared_ptr<FontCollection>& collection, float* advances);
 
     // public accessors
     size_t nGlyphs() const;
-    // Does not bump reference; ownership is still layout
-    MinikinFont *getFont(int i) const;
+    const MinikinFont* getFont(int i) const;
     FontFakery getFakery(int i) const;
     unsigned int getGlyphId(int i) const;
     float getX(int i) const;
@@ -121,7 +97,7 @@
     // start and count are the parameters to doLayout
     float getCharAdvance(size_t i) const { return mAdvances[i]; }
 
-    void getBounds(MinikinRect* rect);
+    void getBounds(MinikinRect* rect) const;
 
     // Purge all caches, useful in low memory conditions
     static void purgeCaches();
@@ -130,36 +106,38 @@
     friend class LayoutCacheKey;
 
     // Find a face in the mFaces vector, or create a new entry
-    int findFace(FakedFont face, LayoutContext* ctx);
+    int findFace(const FakedFont& face, LayoutContext* ctx);
+
+    // Clears layout, ready to be used again
+    void reset();
 
     // Lay out a single bidi run
     // When layout is not null, layout info will be stored in the object.
     // When advances is not null, measurement results will be stored in the array.
     static float doLayoutRunCached(const uint16_t* buf, size_t runStart, size_t runLength,
         size_t bufSize, bool isRtl, LayoutContext* ctx, size_t dstStart,
-        const FontCollection* collection, Layout* layout, float* advances);
+        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances);
 
     // Lay out a single word
     static float doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        bool isRtl, LayoutContext* ctx, size_t bufStart, const FontCollection* collection,
-        Layout* layout, float* advances);
+        bool isRtl, LayoutContext* ctx, size_t bufStart,
+        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances);
 
     // Lay out a single bidi run
     void doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        bool isRtl, LayoutContext* ctx);
+        bool isRtl, LayoutContext* ctx, const std::shared_ptr<FontCollection>& collection);
 
     // Append another layout (for example, cached value) into this one
-    void appendLayout(Layout* src, size_t start);
+    void appendLayout(Layout* src, size_t start, float extraAdvance);
 
     std::vector<LayoutGlyph> mGlyphs;
     std::vector<float> mAdvances;
 
-    const FontCollection* mCollection;
     std::vector<FakedFont> mFaces;
     float mAdvance;
     MinikinRect mBounds;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_LAYOUT_H
diff --git a/include/minikin/LineBreaker.h b/include/minikin/LineBreaker.h
index 1d81404..c91c0b3 100644
--- a/include/minikin/LineBreaker.h
+++ b/include/minikin/LineBreaker.h
@@ -29,7 +29,7 @@
 #include "minikin/Hyphenator.h"
 #include "minikin/WordBreaker.h"
 
-namespace android {
+namespace minikin {
 
 enum BreakStrategy {
     kBreakStrategy_Greedy = 0,
@@ -147,6 +147,8 @@
 
         void setStrategy(BreakStrategy strategy) { mStrategy = strategy; }
 
+        void setJustified(bool justified) { mJustified = justified; }
+
         HyphenationFrequency getHyphenationFrequency() const { return mHyphenationFrequency; }
 
         void setHyphenationFrequency(HyphenationFrequency frequency) {
@@ -157,8 +159,8 @@
         // Minikin to do the shaping of the strings. The main thing that would need to be changed
         // is having some kind of callback (or virtual class, or maybe even template), which could
         // easily be instantiated with Minikin's Layout. Future work for when needed.
-        float addStyleRun(MinikinPaint* paint, const FontCollection* typeface, FontStyle style,
-                size_t start, size_t end, bool isRtl);
+        float addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
+                FontStyle style, size_t start, size_t end, bool isRtl);
 
         void addReplacement(size_t start, size_t end, float width);
 
@@ -189,23 +191,28 @@
         struct Candidate {
             size_t offset;  // offset to text buffer, in code units
             size_t prev;  // index to previous break
-            ParaWidth preBreak;
-            ParaWidth postBreak;
+            ParaWidth preBreak;  // width of text until this point, if we decide to not break here
+            ParaWidth postBreak;  // width of text until this point, if we decide to break here
             float penalty;  // penalty of this break (for example, hyphen penalty)
             float score;  // best score found for this break
             size_t lineNumber;  // only updated for non-constant line widths
-            uint8_t hyphenEdit;
+            size_t preSpaceCount;  // preceding space count before breaking
+            size_t postSpaceCount;  // preceding space count after breaking
+            HyphenationType hyphenType;
         };
 
         float currentLineWidth() const;
 
-        void addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak, float penalty,
-                uint8_t hyph);
+        void addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak,
+                size_t preSpaceCount, size_t postSpaceCount, float penalty, HyphenationType hyph);
 
         void addCandidate(Candidate cand);
+        void pushGreedyBreak();
 
         // push an actual break to the output. Takes care of setting flags for tab
-        void pushBreak(int offset, float width, uint8_t hyph);
+        void pushBreak(int offset, float width, uint8_t hyphenEdit);
+
+        float getSpaceWidth() const;
 
         void computeBreaksGreedy();
 
@@ -214,15 +221,17 @@
         void finishBreaksOptimal();
 
         WordBreaker mWordBreaker;
+        icu::Locale mLocale;
         std::vector<uint16_t>mTextBuf;
         std::vector<float>mCharWidths;
 
         Hyphenator* mHyphenator;
-        std::vector<uint8_t> mHyphBuf;
+        std::vector<HyphenationType> mHyphBuf;
 
         // layout parameters
         BreakStrategy mStrategy = kBreakStrategy_Greedy;
         HyphenationFrequency mHyphenationFrequency = kHyphenationFrequency_Normal;
+        bool mJustified;
         LineWidths mLineWidths;
         TabStops mTabStops;
 
@@ -240,9 +249,11 @@
         size_t mBestBreak;
         float mBestScore;
         ParaWidth mPreBreak;  // prebreak of last break
+        uint32_t mLastHyphenation;  // hyphen edit of last break kept for next line
         int mFirstTabIndex;
+        size_t mSpaceCount;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_LINE_BREAKER_H
diff --git a/include/minikin/Measurement.h b/include/minikin/Measurement.h
index 7bcab66..b00c212 100644
--- a/include/minikin/Measurement.h
+++ b/include/minikin/Measurement.h
@@ -19,7 +19,7 @@
 
 #include <minikin/Layout.h>
 
-namespace android {
+namespace minikin {
 
 float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
         size_t offset);
@@ -27,6 +27,6 @@
 size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
         float advance);
 
-}
+}  // namespace minikin
 
 #endif  // MINIKIN_MEASUREMENT_H
diff --git a/include/minikin/MinikinFont.h b/include/minikin/MinikinFont.h
index 4951514..01af786 100644
--- a/include/minikin/MinikinFont.h
+++ b/include/minikin/MinikinFont.h
@@ -18,35 +18,23 @@
 #define MINIKIN_FONT_H
 
 #include <string>
+#include <memory>
 
-#include <minikin/MinikinRefCounted.h>
 #include <minikin/FontFamily.h>
+#include <minikin/Hyphenator.h>
 
 // An abstraction for platform fonts, allowing Minikin to be used with
 // multiple actual implementations of fonts.
 
-namespace android {
-
-// The hyphen edit represents an edit to the string when a word is
-// hyphenated. The most common hyphen edit is adding a "-" at the end
-// of a syllable, but nonstandard hyphenation allows for more choices.
-class HyphenEdit {
-public:
-    HyphenEdit() : hyphen(0) { }
-    HyphenEdit(uint32_t hyphenInt) : hyphen(hyphenInt) { }
-    bool hasHyphen() const { return hyphen != 0; }
-    bool operator==(const HyphenEdit &other) const { return hyphen == other.hyphen; }
-private:
-    uint32_t hyphen;
-};
+namespace minikin {
 
 class MinikinFont;
 
 // Possibly move into own .h file?
 // Note: if you add a field here, either add it to LayoutCacheKey or to skipCache()
 struct MinikinPaint {
-    MinikinPaint() : font(0), size(0), scaleX(0), skewX(0), letterSpacing(0), paintFlags(0),
-            fakery(), fontFeatureSettings() { }
+    MinikinPaint() : font(nullptr), size(0), scaleX(0), skewX(0), letterSpacing(0), wordSpacing(0),
+            paintFlags(0), fakery(), hyphenEdit(), fontFeatureSettings() { }
 
     bool skipCache() const {
         return !fontFeatureSettings.empty();
@@ -57,6 +45,7 @@
     float scaleX;
     float skewX;
     float letterSpacing;
+    float wordSpacing;
     uint32_t paintFlags;
     FontFakery fakery;
     HyphenEdit hyphenEdit;
@@ -92,14 +81,12 @@
     void join(const MinikinRect& r);
 };
 
-class MinikinFontFreeType;
-
 // Callback for freeing data
 typedef void (*MinikinDestroyFunc) (void* data);
 
-class MinikinFont : public MinikinRefCounted {
+class MinikinFont {
 public:
-    MinikinFont(int32_t uniqueId) : mUniqueId(uniqueId) {}
+    explicit MinikinFont(int32_t uniqueId) : mUniqueId(uniqueId) {}
 
     virtual ~MinikinFont();
 
@@ -109,8 +96,6 @@
     virtual void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
         const MinikinPaint &paint) const = 0;
 
-    virtual const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) = 0;
-
     // Override if font can provide access to raw data
     virtual const void* GetFontData() const {
         return nullptr;
@@ -127,6 +112,13 @@
         return 0;
     }
 
+    virtual const std::vector<minikin::FontVariation>& GetAxes() const = 0;
+
+    virtual std::shared_ptr<MinikinFont> createFontWithVariation(
+            const std::vector<FontVariation>&) const {
+        return nullptr;
+    }
+
     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;
@@ -137,6 +129,6 @@
     const int32_t mUniqueId;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_FONT_H
diff --git a/include/minikin/MinikinFontFreeType.h b/include/minikin/MinikinFontFreeType.h
deleted file mode 100644
index baa08df..0000000
--- a/include/minikin/MinikinFontFreeType.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 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_FREETYPE_H
-#define MINIKIN_FONT_FREETYPE_H
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_TRUETYPE_TABLES_H
-
-#include <minikin/MinikinFont.h>
-
-// An abstraction for platform fonts, allowing Minikin to be used with
-// multiple actual implementations of fonts.
-
-namespace android {
-
-struct GlyphBitmap {
-    uint8_t *buffer;
-    int width;
-    int height;
-    int left;
-    int top;
-};
-
-class MinikinFontFreeType : public MinikinFont {
-public:
-    explicit MinikinFontFreeType(FT_Face typeface);
-
-    ~MinikinFontFreeType();
-
-    float GetHorizontalAdvance(uint32_t glyph_id,
-        const MinikinPaint &paint) const;
-
-    void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
-        const MinikinPaint& paint) const;
-
-    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
-
-    // TODO: provide access to raw data, as an optimization.
-
-    // Not a virtual method, as the protocol to access rendered
-    // glyph bitmaps is probably different depending on the
-    // backend.
-    bool Render(uint32_t glyph_id,
-        const MinikinPaint &paint, GlyphBitmap *result);
-
-    MinikinFontFreeType* GetFreeType();
-
-private:
-    FT_Face mTypeface;
-    static int32_t sIdCounter;
-};
-
-}  // namespace android
-
-#endif  // MINIKIN_FONT_FREETYPE_H
diff --git a/include/minikin/MinikinRefCounted.h b/include/minikin/MinikinRefCounted.h
deleted file mode 100644
index 603aff0..0000000
--- a/include/minikin/MinikinRefCounted.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-// Base class for reference counted objects in Minikin
-
-#ifndef MINIKIN_REF_COUNTED_H
-#define MINIKIN_REF_COUNTED_H
-
-namespace android {
-
-class MinikinRefCounted {
-public:
-    void RefLocked() { mRefcount_++; }
-    void UnrefLocked() { if (--mRefcount_ == 0) { delete this; } }
-
-    // These refcount operations take the global lock.
-    void Ref();
-    void Unref();
-
-    MinikinRefCounted() : mRefcount_(1) { }
-
-    virtual ~MinikinRefCounted() { };
-private:
-    int mRefcount_;
-};
-
-// An RAII container for reference counted objects.
-// Note: this is only suitable for clients which are _not_ holding the global lock.
-template <typename T>
-class MinikinAutoUnref {
-public:
-    MinikinAutoUnref(T* obj) : mObj(obj) {
-    }
-    ~MinikinAutoUnref() {
-        mObj->Unref();
-    }
-    T& operator*() const { return *mObj; }
-    T* operator->() const { return mObj; }
-    T* get() const { return mObj; }
-private:
-    T* mObj;
-};
-
-}
-
-#endif   // MINIKIN_REF_COUNTED_H
\ No newline at end of file
diff --git a/include/minikin/SparseBitSet.h b/include/minikin/SparseBitSet.h
index 72b8305..62aece2 100644
--- a/include/minikin/SparseBitSet.h
+++ b/include/minikin/SparseBitSet.h
@@ -19,35 +19,37 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <UniquePtr.h>
+
+#include <memory>
 
 // ---------------------------------------------------------------------------
 
-namespace android {
+namespace minikin {
 
 // This is an implementation of a set of integers. It is optimized for
 // values that are somewhat sparse, in the ballpark of a maximum value
 // of thousands to millions. It is particularly efficient when there are
 // large gaps. The motivating example is Unicode coverage of a font, but
 // the abstraction itself is fully general.
-
 class SparseBitSet {
 public:
-    SparseBitSet(): mMaxVal(0) {
-    }
-
-    // Clear the set
-    void clear();
+    // Create an empty bit set.
+    SparseBitSet() : mMaxVal(0) {}
 
     // Initialize the set to a new value, represented by ranges. For
     // simplicity, these ranges are arranged as pairs of values,
     // inclusive of start, exclusive of end, laid out in a uint32 array.
-    void initFromRanges(const uint32_t* ranges, size_t nRanges);
+    SparseBitSet(const uint32_t* ranges, size_t nRanges) : SparseBitSet() {
+        initFromRanges(ranges, nRanges);
+    }
+
+    SparseBitSet(SparseBitSet&&) = default;
+    SparseBitSet& operator=(SparseBitSet&&) = default;
 
     // Determine whether the value is included in the set
     bool get(uint32_t ch) const {
         if (ch >= mMaxVal) return false;
-        uint32_t *bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
+        const uint32_t *bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
         uint32_t index = ch & kPageMask;
         return (bitmap[index >> kLogBitsPerEl] & (kElFirst >> (index & kElMask))) != 0;
     }
@@ -64,6 +66,9 @@
     static const uint32_t kNotFound = ~0u;
 
 private:
+    void initFromRanges(const uint32_t* ranges, size_t nRanges);
+
+    static const uint32_t kMaximumCapacity = 0xFFFFFF;
     static const int kLogValuesPerPage = 8;
     static const int kPageMask = (1 << kLogValuesPerPage) - 1;
     static const int kLogBytesPerEl = 2;
@@ -73,20 +78,22 @@
     typedef uint32_t element;
     static const element kElAllOnes = ~((element)0);
     static const element kElFirst = ((element)1) << kElMask;
-    static const uint32_t noZeroPage = ~0u;
+    static const uint16_t noZeroPage = 0xFFFF;
 
     static uint32_t calcNumPages(const uint32_t* ranges, size_t nRanges);
     static int CountLeadingZeros(element x);
 
     uint32_t mMaxVal;
-    UniquePtr<uint32_t[]> mIndices;
-    UniquePtr<element[]> mBitmaps;
-    uint32_t mZeroPageIndex;
+
+    std::unique_ptr<uint16_t[]> mIndices;
+    std::unique_ptr<element[]> mBitmaps;
+    uint16_t mZeroPageIndex;
+
+    // Forbid copy and assign.
+    SparseBitSet(const SparseBitSet&) = delete;
+    void operator=(const SparseBitSet&) = delete;
 };
 
-// Note: this thing cannot be used in vectors yet. If that were important, we'd need to
-// make the copy constructor work, and probably set up move traits as well.
-
-}; // namespace android
+}  // namespace minikin
 
 #endif // MINIKIN_SPARSE_BIT_SET_H
diff --git a/include/minikin/WordBreaker.h b/include/minikin/WordBreaker.h
index 4eff9d1..6971ce2 100644
--- a/include/minikin/WordBreaker.h
+++ b/include/minikin/WordBreaker.h
@@ -26,7 +26,7 @@
 #include "unicode/brkiter.h"
 #include <memory>
 
-namespace android {
+namespace minikin {
 
 class WordBreaker {
 public:
@@ -55,6 +55,10 @@
     void finish();
 
 private:
+    int32_t iteratorNext();
+    void detectEmailOrUrl();
+    ssize_t findNextBreakInEmailOrUrl();
+
     std::unique_ptr<icu::BreakIterator> mBreakIterator;
     UText mUText = UTEXT_INITIALIZER;
     const uint16_t* mText = nullptr;
@@ -68,6 +72,6 @@
     bool mInEmailOrUrl;
 };
 
-}  // namespace
+}  // namespace minikin
 
 #endif  // MINIKIN_WORD_BREAKER_H
diff --git a/libs/minikin/AnalyzeStyle.cpp b/libs/minikin/AnalyzeStyle.cpp
deleted file mode 100644
index 0961645..0000000
--- a/libs/minikin/AnalyzeStyle.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdlib.h>
-#include <stdint.h>
-
-#include <minikin/AnalyzeStyle.h>
-
-namespace android {
-
-// should  we have a single FontAnalyzer class this stuff lives in, to avoid dup?
-static int32_t readU16(const uint8_t* data, size_t offset) {
-    return data[offset] << 8 | data[offset + 1];
-}
-
-bool analyzeStyle(const uint8_t* os2_data, size_t os2_size, int* weight, bool* italic) {
-    const size_t kUsWeightClassOffset = 4;
-    const size_t kFsSelectionOffset = 62;
-    const uint16_t kItalicFlag = (1 << 0);
-    if (os2_size < kFsSelectionOffset + 2) {
-        return false;
-    }
-    uint16_t weightClass = readU16(os2_data, kUsWeightClassOffset);
-    *weight = weightClass / 100;
-    uint16_t fsSelection = readU16(os2_data, kFsSelectionOffset);
-    *italic = (fsSelection & kItalicFlag) != 0;
-    return true;
-}
-
-}  // namespace android
diff --git a/libs/minikin/Android.mk b/libs/minikin/Android.mk
index 9d82579..bb6234a 100644
--- a/libs/minikin/Android.mk
+++ b/libs/minikin/Android.mk
@@ -15,27 +15,16 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-# Generate unicode emoji data from UCD.
-UNICODE_EMOJI_H_GEN_PY := $(LOCAL_PATH)/unicode_emoji_h_gen.py
-UNICODE_EMOJI_DATA := $(TOP)/external/unicode/emoji-data.txt
-
-UNICODE_EMOJI_H := $(intermediates)/generated/UnicodeData.h
-$(UNICODE_EMOJI_H): $(UNICODE_EMOJI_H_GEN_PY) $(UNICODE_EMOJI_DATA)
-$(LOCAL_PATH)/MinikinInternal.cpp: $(UNICODE_EMOJI_H)
-$(UNICODE_EMOJI_H): PRIVATE_CUSTOM_TOOL := python $(UNICODE_EMOJI_H_GEN_PY) \
-    -i $(UNICODE_EMOJI_DATA) \
-    -o $(UNICODE_EMOJI_H)
-$(UNICODE_EMOJI_H):
-		$(transform-generated-source)
 
 include $(CLEAR_VARS)
 minikin_src_files := \
-    AnalyzeStyle.cpp \
     CmapCoverage.cpp \
+    Emoji.cpp \
     FontCollection.cpp \
     FontFamily.cpp \
     FontLanguage.cpp \
     FontLanguageListCache.cpp \
+    FontUtils.cpp \
     GraphemeBreak.cpp \
     HbFontCache.cpp \
     Hyphenator.cpp \
@@ -44,15 +33,12 @@
     LineBreaker.cpp \
     Measurement.cpp \
     MinikinInternal.cpp \
-    MinikinRefCounted.cpp \
     MinikinFont.cpp \
-    MinikinFontFreeType.cpp \
     SparseBitSet.cpp \
     WordBreaker.cpp
 
 minikin_c_includes := \
     external/harfbuzz_ng/src \
-    external/freetype/include \
     frameworks/minikin/include \
     $(intermediates)
 
@@ -109,7 +95,7 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := frameworks/minikin/include
 LOCAL_C_INCLUDES := $(minikin_c_includes)
 LOCAL_CPPFLAGS += -Werror -Wall -Wextra $(enable_race_detection)
-LOCAL_SHARED_LIBRARIES := liblog libicuuc-host
+LOCAL_SHARED_LIBRARIES := liblog libicuuc
 
 LOCAL_SRC_FILES := Hyphenator.cpp
 
diff --git a/libs/minikin/CmapCoverage.cpp b/libs/minikin/CmapCoverage.cpp
index c02526c..c56d07c 100644
--- a/libs/minikin/CmapCoverage.cpp
+++ b/libs/minikin/CmapCoverage.cpp
@@ -17,21 +17,33 @@
 // Determine coverage of font given its raw "cmap" OpenType table
 
 #define LOG_TAG "Minikin"
-#include <cutils/log.h>
 
+#include <algorithm>
 #include <vector>
 using std::vector;
 
+#include <log/log.h>
+
 #include <minikin/SparseBitSet.h>
 #include <minikin/CmapCoverage.h>
+#include "MinikinInternal.h"
 
-namespace android {
+#include <MinikinInternal.h>
+
+namespace minikin {
+
+constexpr uint32_t U32MAX = std::numeric_limits<uint32_t>::max();
 
 // These could perhaps be optimized to use __builtin_bswap16 and friends.
 static uint32_t readU16(const uint8_t* data, size_t offset) {
     return ((uint32_t)data[offset]) << 8 | ((uint32_t)data[offset + 1]);
 }
 
+static uint32_t readU24(const uint8_t* data, size_t offset) {
+    return ((uint32_t)data[offset]) << 16 | ((uint32_t)data[offset + 1]) << 8 |
+        ((uint32_t)data[offset + 2]);
+}
+
 static uint32_t readU32(const uint8_t* data, size_t offset) {
     return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
         ((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
@@ -59,6 +71,99 @@
     }
 }
 
+struct Range {
+    uint32_t start;  // inclusive
+    uint32_t end;  // exclusive
+
+    static Range InvalidRange() {
+        return Range({ U32MAX, U32MAX });
+    }
+
+    inline bool isValid() const {
+        return start != U32MAX && end != U32MAX;
+    }
+
+    // Returns true if left and right intersect.
+    inline static bool intersects(const Range& left, const Range& right) {
+        return left.isValid() && right.isValid() &&
+                left.start < right.end && right.start < left.end;
+    }
+
+    // Returns merged range. This method assumes left and right are not invalid ranges and they have
+    // an intersection.
+    static Range merge(const Range& left, const Range& right) {
+        return Range({ std::min(left.start, right.start), std::max(left.end, right.end) });
+    }
+};
+
+// Returns Range from given ranges vector. Returns InvalidRange if i is out of range.
+static inline Range getRange(const std::vector<uint32_t>& r, size_t i) {
+    return i + 1 < r.size() ? Range({ r[i], r[i + 1] }) : Range::InvalidRange();
+}
+
+// Merge two sorted lists of ranges into one sorted list.
+static std::vector<uint32_t> mergeRanges(
+        const std::vector<uint32_t>& lRanges, const std::vector<uint32_t>& rRanges) {
+    std::vector<uint32_t> out;
+
+    const size_t lsize = lRanges.size();
+    const size_t rsize = rRanges.size();
+    out.reserve(lsize + rsize);
+    size_t ri = 0;
+    size_t li = 0;
+    while (li < lsize || ri < rsize) {
+        Range left = getRange(lRanges, li);
+        Range right = getRange(rRanges, ri);
+
+        if (!right.isValid()) {
+            // No ranges left in rRanges. Just put all remaining ranges in lRanges.
+            do {
+                Range r = getRange(lRanges, li);
+                addRange(out, r.start, r.end);  // Input is sorted. Never returns false.
+                li += 2;
+            } while (li < lsize);
+            break;
+        } else if (!left.isValid()) {
+            // No ranges left in lRanges. Just put all remaining ranges in rRanges.
+            do {
+                Range r = getRange(rRanges, ri);
+                addRange(out, r.start, r.end);  // Input is sorted. Never returns false.
+                ri += 2;
+            } while (ri < rsize);
+            break;
+        } else if (!Range::intersects(left, right)) {
+            // No intersection. Add smaller range.
+            if (left.start < right.start) {
+                addRange(out, left.start, left.end);  // Input is sorted. Never returns false.
+                li += 2;
+            } else {
+                addRange(out, right.start, right.end);  // Input is sorted. Never returns false.
+                ri += 2;
+            }
+        } else {
+            Range merged = Range::merge(left, right);
+            li += 2;
+            ri += 2;
+            left = getRange(lRanges, li);
+            right = getRange(rRanges, ri);
+            while (Range::intersects(merged, left) || Range::intersects(merged, right)) {
+                if (Range::intersects(merged, left)) {
+                    merged = Range::merge(merged, left);
+                    li += 2;
+                    left = getRange(lRanges, li);
+                } else {
+                    merged = Range::merge(merged, right);
+                    ri += 2;
+                    right = getRange(rRanges, ri);
+                }
+            }
+            addRange(out, merged.start, merged.end);  // Input is sorted. Never returns false.
+        }
+    }
+
+    return out;
+}
+
 // Get the coverage information out of a Format 4 subtable, storing it in the coverage vector
 static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
     const size_t kSegCountOffset = 6;
@@ -142,6 +247,15 @@
             android_errorWriteLog(0x534e4554, "26413177");
             return false;
         }
+
+        // No need to read outside of Unicode code point range.
+        if (start > MAX_UNICODE_CODE_POINT) {
+            return true;
+        }
+        if (end > MAX_UNICODE_CODE_POINT) {
+            // file is inclusive, vector is exclusive
+            return addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1);
+        }
         if (!addRange(coverage, start, end + 1)) {  // file is inclusive, vector is exclusive
             return false;
         }
@@ -149,76 +263,279 @@
     return true;
 }
 
-bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, size_t cmap_size,
-        bool* has_cmap_format14_subtable) {
-    vector<uint32_t> coverageVec;
-    const size_t kHeaderSize = 4;
-    const size_t kNumTablesOffset = 2;
-    const size_t kTableSize = 8;
-    const size_t kPlatformIdOffset = 0;
-    const size_t kEncodingIdOffset = 2;
-    const size_t kOffsetOffset = 4;
-    const uint16_t kUnicodePlatformId = 0;
-    const uint16_t kMicrosoftPlatformId = 3;
-    const uint16_t kUnicodeBmpEncodingId = 1;
-    const uint16_t kVariationSequencesEncodingId = 5;
-    const uint16_t kUnicodeUcs4EncodingId = 10;
-    const uint32_t kNoTable = UINT32_MAX;
-    if (kHeaderSize > cmap_size) {
-        return false;
+// Lower value has higher priority. 0 for the highest priority table.
+// kLowestPriority for unsupported tables.
+// This order comes from HarfBuzz's hb-ot-font.cc and needs to be kept in sync with it.
+constexpr uint8_t kLowestPriority = 255;
+uint8_t getTablePriority(uint16_t platformId, uint16_t encodingId) {
+    if (platformId == 3 && encodingId == 10) {
+        return 0;
     }
-    uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
-    if (kHeaderSize + numTables * kTableSize > cmap_size) {
-        return false;
+    if (platformId == 0 && encodingId == 6) {
+        return 1;
     }
-    uint32_t bestTable = kNoTable;
-    bool hasCmapFormat14Subtable = false;
-    for (uint32_t i = 0; i < numTables; i++) {
-        uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset);
-        uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset);
-        if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeUcs4EncodingId) {
-            bestTable = i;
-            break;
-        } else if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeBmpEncodingId) {
-            bestTable = i;
-        } else if (platformId == kUnicodePlatformId &&
-                encodingId == kVariationSequencesEncodingId) {
-            uint32_t offset = readU32(cmap_data, kHeaderSize + i * kTableSize + kOffsetOffset);
-            if (offset <= cmap_size - 2 && readU16(cmap_data, offset) == 14) {
-                hasCmapFormat14Subtable = true;
+    if (platformId == 0 && encodingId == 4) {
+        return 2;
+    }
+    if (platformId == 3 && encodingId == 1) {
+        return 3;
+    }
+    if (platformId == 0 && encodingId == 3) {
+        return 4;
+    }
+    if (platformId == 0 && encodingId == 2) {
+        return 5;
+    }
+    if (platformId == 0 && encodingId == 1) {
+        return 6;
+    }
+    if (platformId == 0 && encodingId == 0) {
+        return 7;
+    }
+    // Tables other than above are not supported.
+    return kLowestPriority;
+}
+
+// Get merged coverage information from default UVS Table and non-default UVS Table. Note that this
+// function assumes code points in both default UVS Table and non-default UVS table are stored in
+// ascending order. This is required by the standard.
+static bool getVSCoverage(std::vector<uint32_t>* out_ranges, const uint8_t* data, size_t size,
+        uint32_t defaultUVSTableOffset, uint32_t nonDefaultUVSTableOffset,
+        const SparseBitSet& baseCoverage) {
+    // Need to merge supported ranges from default UVS Table and non-default UVS Table.
+    // First, collect all supported code points from non default UVS table.
+    std::vector<uint32_t> rangesFromNonDefaultUVSTable;
+    if (nonDefaultUVSTableOffset != 0) {
+        constexpr size_t kHeaderSize = 4;
+        constexpr size_t kUVSMappingRecordSize = 5;
+
+        const uint8_t* nonDefaultUVSTable = data + nonDefaultUVSTableOffset;
+        // This subtraction doesn't underflow since the caller already checked
+        // size > nonDefaultUVSTableOffset.
+        const size_t nonDefaultUVSTableRemaining = size - nonDefaultUVSTableOffset;
+        if (nonDefaultUVSTableRemaining < kHeaderSize) {
+            return false;
+        }
+        const uint32_t numRecords = readU32(nonDefaultUVSTable, 0);
+        if (numRecords * kUVSMappingRecordSize + kHeaderSize > nonDefaultUVSTableRemaining) {
+            return false;
+        }
+        for (uint32_t i = 0; i < numRecords; ++i) {
+            const size_t recordOffset = kHeaderSize + kUVSMappingRecordSize * i;
+            const uint32_t codePoint = readU24(nonDefaultUVSTable, recordOffset);
+            if (!addRange(rangesFromNonDefaultUVSTable, codePoint, codePoint + 1)) {
+                return false;
             }
         }
     }
-    *has_cmap_format14_subtable = hasCmapFormat14Subtable;
-#ifdef VERBOSE_DEBUG
-    ALOGD("best table = %d\n", bestTable);
-#endif
-    if (bestTable == kNoTable) {
-        return false;
+
+    // Then, construct range from default UVS Table with merging code points from non default UVS
+    // table.
+    std::vector<uint32_t> rangesFromDefaultUVSTable;
+    if (defaultUVSTableOffset != 0) {
+        constexpr size_t kHeaderSize = 4;
+        constexpr size_t kUnicodeRangeRecordSize = 4;
+
+        const uint8_t* defaultUVSTable = data + defaultUVSTableOffset;
+        // This subtraction doesn't underflow since the caller already checked
+        // size > defaultUVSTableOffset.
+        const size_t defaultUVSTableRemaining = size - defaultUVSTableOffset;
+
+        if (defaultUVSTableRemaining < kHeaderSize) {
+            return false;
+        }
+        const uint32_t numRecords = readU32(defaultUVSTable, 0);
+        if (numRecords * kUnicodeRangeRecordSize + kHeaderSize > defaultUVSTableRemaining) {
+            return false;
+        }
+
+        for (uint32_t i = 0; i < numRecords; ++i) {
+            const size_t recordOffset = kHeaderSize + kUnicodeRangeRecordSize * i;
+            const uint32_t startCp = readU24(defaultUVSTable, recordOffset);
+            const uint8_t rangeLength = defaultUVSTable[recordOffset + 3];
+
+            // Then insert range from default UVS Table, but exclude if the base codepoint is not
+            // supported.
+            for (uint32_t cp = startCp; cp <= startCp + rangeLength; ++cp) {
+                // All codepoints in default UVS table should go to the glyphs of the codepoints
+                // without variation selectors. We need to check the default glyph availability and
+                // exclude the codepoint if it is not supported by defualt cmap table.
+                if (baseCoverage.get(cp)) {
+                    if (!addRange(rangesFromDefaultUVSTable, cp, cp + 1 /* exclusive */)) {
+                        return false;
+                    }
+                }
+            }
+        }
     }
-    uint32_t offset = readU32(cmap_data, kHeaderSize + bestTable * kTableSize + kOffsetOffset);
-    if (offset > cmap_size - 2) {
-        return false;
-    }
-    uint16_t format = readU16(cmap_data, offset);
-    bool success = false;
-    const uint8_t* tableData = cmap_data + offset;
-    const size_t tableSize = cmap_size - offset;
-    if (format == 4) {
-        success = getCoverageFormat4(coverageVec, tableData, tableSize);
-    } else if (format == 12) {
-        success = getCoverageFormat12(coverageVec, tableData, tableSize);
-    }
-    if (success) {
-        coverage.initFromRanges(&coverageVec.front(), coverageVec.size() >> 1);
-    }
-#ifdef VERBOSE_DEBUG
-    for (size_t i = 0; i < coverageVec.size(); i += 2) {
-        ALOGD("%x:%x\n", coverageVec[i], coverageVec[i + 1]);
-    }
-    ALOGD("success = %d", success);
-#endif
-    return success;
+    *out_ranges = mergeRanges(rangesFromDefaultUVSTable, rangesFromNonDefaultUVSTable);
+    return true;
 }
 
-}  // namespace android
+static void getCoverageFormat14(std::vector<std::unique_ptr<SparseBitSet>>* out,
+        const uint8_t* data, size_t size, const SparseBitSet& baseCoverage) {
+    constexpr size_t kHeaderSize = 10;
+    constexpr size_t kRecordSize = 11;
+    constexpr size_t kLengthOffset = 2;
+    constexpr size_t kNumRecordOffset = 6;
+
+    out->clear();
+    if (size < kHeaderSize) {
+        return;
+    }
+
+    const uint32_t length = readU32(data, kLengthOffset);
+    if (size < length) {
+        return;
+    }
+
+    uint32_t numRecords = readU32(data, kNumRecordOffset);
+    if (numRecords == 0 || kHeaderSize + kRecordSize * numRecords > length) {
+        return;
+    }
+
+    for (uint32_t i = 0; i < numRecords; ++i) {
+        // Insert from the largest code points since it determines the size of the output vector.
+        const uint32_t recordHeadOffset = kHeaderSize + kRecordSize * (numRecords - i - 1);
+        const uint32_t vsCodePoint = readU24(data, recordHeadOffset);
+        const uint32_t defaultUVSOffset = readU32(data, recordHeadOffset + 3);
+        const uint32_t nonDefaultUVSOffset = readU32(data, recordHeadOffset + 7);
+        if (defaultUVSOffset > length || nonDefaultUVSOffset > length) {
+            continue;
+        }
+
+        const uint16_t vsIndex = getVsIndex(vsCodePoint);
+        if (vsIndex == INVALID_VS_INDEX) {
+            continue;
+        }
+        std::vector<uint32_t> ranges;
+        if (!getVSCoverage(&ranges, data, length, defaultUVSOffset, nonDefaultUVSOffset,
+                baseCoverage)) {
+            continue;
+        }
+        if (out->size() < vsIndex + 1) {
+            out->resize(vsIndex + 1);
+        }
+        (*out)[vsIndex].reset(new SparseBitSet(ranges.data(), ranges.size() >> 1));
+    }
+
+    out->shrink_to_fit();
+}
+
+SparseBitSet CmapCoverage::getCoverage(const uint8_t* cmap_data, size_t cmap_size,
+        std::vector<std::unique_ptr<SparseBitSet>>* out) {
+    constexpr size_t kHeaderSize = 4;
+    constexpr size_t kNumTablesOffset = 2;
+    constexpr size_t kTableSize = 8;
+    constexpr size_t kPlatformIdOffset = 0;
+    constexpr size_t kEncodingIdOffset = 2;
+    constexpr size_t kOffsetOffset = 4;
+    constexpr size_t kFormatOffset = 0;
+    constexpr uint32_t kNoTable = UINT32_MAX;
+
+    if (kHeaderSize > cmap_size) {
+        return SparseBitSet();
+    }
+    uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
+    if (kHeaderSize + numTables * kTableSize > cmap_size) {
+        return SparseBitSet();
+    }
+
+    uint32_t bestTableOffset = kNoTable;
+    uint16_t bestTableFormat = 0;
+    uint8_t bestTablePriority = kLowestPriority;
+    uint32_t vsTableOffset = kNoTable;
+    for (uint32_t i = 0; i < numTables; ++i) {
+        const uint32_t tableHeadOffset = kHeaderSize + i * kTableSize;
+        const uint16_t platformId = readU16(cmap_data, tableHeadOffset + kPlatformIdOffset);
+        const uint16_t encodingId = readU16(cmap_data, tableHeadOffset + kEncodingIdOffset);
+        const uint32_t offset = readU32(cmap_data, tableHeadOffset + kOffsetOffset);
+
+        if (offset > cmap_size - 2) {
+            continue;  // Invalid table: not enough space to read.
+        }
+        const uint16_t format = readU16(cmap_data, offset + kFormatOffset);
+
+        if (platformId == 0 /* Unicode */ && encodingId == 5 /* Variation Sequences */) {
+            if (vsTableOffset == kNoTable && format == 14) {
+                vsTableOffset = offset;
+            } else {
+                // Ignore the (0, 5) table if we have already seen another valid one or it's in a
+                // format we don't understand.
+            }
+        } else {
+            uint32_t length;
+            uint32_t language;
+
+            if (format == 4) {
+                constexpr size_t lengthOffset = 2;
+                constexpr size_t languageOffset = 4;
+                constexpr size_t minTableSize = languageOffset + 2;
+                if (offset > cmap_size - minTableSize) {
+                    continue;  // Invalid table: not enough space to read.
+                }
+                length = readU16(cmap_data, offset + lengthOffset);
+                language = readU16(cmap_data, offset + languageOffset);
+            } else if (format == 12) {
+                constexpr size_t lengthOffset = 4;
+                constexpr size_t languageOffset = 8;
+                constexpr size_t minTableSize = languageOffset + 4;
+                if (offset > cmap_size - minTableSize) {
+                    continue;  // Invalid table: not enough space to read.
+                }
+                length = readU32(cmap_data, offset + lengthOffset);
+                language = readU32(cmap_data, offset + languageOffset);
+            } else {
+                continue;
+            }
+
+            if (length > cmap_size - offset) {
+                continue;  // Invalid table: table length is larger than whole cmap data size.
+            }
+            if (language != 0) {
+                // Unsupported or invalid table: this is either a subtable for the Macintosh
+                // platform (which we don't support), or an invalid subtable since language field
+                // should be zero for non-Macintosh subtables.
+                continue;
+            }
+            const uint8_t priority = getTablePriority(platformId, encodingId);
+            if (priority < bestTablePriority) {
+                bestTableOffset = offset;
+                bestTablePriority = priority;
+                bestTableFormat = format;
+            }
+        }
+        if (vsTableOffset != kNoTable && bestTablePriority == 0 /* highest priority */) {
+            // Already found the highest priority table and variation sequences table. No need to
+            // look at remaining tables.
+            break;
+        }
+    }
+
+    SparseBitSet coverage;
+
+    if (bestTableOffset != kNoTable) {
+        const uint8_t* tableData = cmap_data + bestTableOffset;
+        const size_t tableSize = cmap_size - bestTableOffset;
+        bool success;
+        vector<uint32_t> coverageVec;
+        if (bestTableFormat == 4) {
+            success = getCoverageFormat4(coverageVec, tableData, tableSize);
+        } else {
+            success = getCoverageFormat12(coverageVec, tableData, tableSize);
+        }
+
+        if (success) {
+            coverage = SparseBitSet(&coverageVec.front(), coverageVec.size() >> 1);
+        }
+    }
+
+    if (vsTableOffset != kNoTable) {
+        const uint8_t* tableData = cmap_data + vsTableOffset;
+        const size_t tableSize = cmap_size - vsTableOffset;
+        getCoverageFormat14(out, tableData, tableSize, coverage);
+    }
+    return coverage;
+}
+
+}  // namespace minikin
diff --git a/libs/minikin/Emoji.cpp b/libs/minikin/Emoji.cpp
new file mode 100644
index 0000000..fbe68ca
--- /dev/null
+++ b/libs/minikin/Emoji.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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/Emoji.h>
+
+namespace minikin {
+
+bool isNewEmoji(uint32_t c) {
+    // Emoji characters new in Unicode emoji 5.0.
+    // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
+    // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
+    if (c < 0x1F6F7 || c > 0x1F9E6) {
+        // Optimization for characters outside the new emoji range.
+        return false;
+    }
+    return (0x1F6F7 <= c && c <= 0x1F6F8)
+            || c == 0x1F91F
+            || (0x1F928 <= c && c <= 0x1F92F)
+            || (0x1F931 <= c && c <= 0x1F932)
+            || c == 0x1F94C
+            || (0x1F95F <= c && c <= 0x1F96B)
+            || (0x1F992 <= c && c <= 0x1F997)
+            || (0x1F9D0 <= c && c <= 0x1F9E6);
+}
+
+bool isEmoji(uint32_t c) {
+    return isNewEmoji(c) || u_hasBinaryProperty(c, UCHAR_EMOJI);
+}
+
+bool isEmojiModifier(uint32_t c) {
+    // Emoji modifier are not expected to change, so there's a small change we need to customize
+    // this.
+    return u_hasBinaryProperty(c, UCHAR_EMOJI_MODIFIER);
+}
+
+bool isEmojiBase(uint32_t c) {
+    // These two characters were removed from Emoji_Modifier_Base in Emoji 4.0, but we need to keep
+    // them as emoji modifier bases since there are fonts and user-generated text out there that
+    // treats these as potential emoji bases.
+    if (c == 0x1F91D || c == 0x1F93C) {
+        return true;
+    }
+    // Emoji Modifier Base characters new in Unicode emoji 5.0.
+    // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
+    // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
+    if (c == 0x1F91F
+            || (0x1F931 <= c && c <= 0x1F932)
+            || (0x1F9D1 <= c && c <= 0x1F9DD)) {
+        return true;
+    }
+    return u_hasBinaryProperty(c, UCHAR_EMOJI_MODIFIER_BASE);
+}
+
+UCharDirection emojiBidiOverride(const void* /* context */, UChar32 c) {
+    if (isNewEmoji(c)) {
+        // All new emoji characters in Unicode 10.0 are of the bidi class ON.
+        return U_OTHER_NEUTRAL;
+    } else {
+        return u_charDirection(c);
+    }
+}
+
+}  // namespace minikin
+
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 33418ab..871d974 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -17,20 +17,22 @@
 // #define VERBOSE_DEBUG
 
 #define LOG_TAG "Minikin"
-#include <cutils/log.h>
+
 #include <algorithm>
 
+#include <log/log.h>
 #include "unicode/unistr.h"
 #include "unicode/unorm2.h"
 
 #include "FontLanguage.h"
 #include "FontLanguageListCache.h"
 #include "MinikinInternal.h"
+#include <minikin/Emoji.h>
 #include <minikin/FontCollection.h>
 
 using std::vector;
 
-namespace android {
+namespace minikin {
 
 template <typename T>
 static inline T max(T a, T b) {
@@ -40,47 +42,21 @@
 const uint32_t EMOJI_STYLE_VS = 0xFE0F;
 const uint32_t TEXT_STYLE_VS = 0xFE0E;
 
-// See http://www.unicode.org/Public/9.0.0/ucd/StandardizedVariants.txt
-// U+2640, U+2642, U+2695 are now in emoji category but not listed in above file, so added them by
-// manual.
-// Must be sorted.
-const uint32_t EMOJI_STYLE_VS_BASES[] = {
-    0x0023, 0x002A, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
-    0x00A9, 0x00AE, 0x203C, 0x2049, 0x2122, 0x2139, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199,
-    0x21A9, 0x21AA, 0x231A, 0x231B, 0x2328, 0x23CF, 0x23ED, 0x23EE, 0x23EF, 0x23F1, 0x23F2, 0x23F8,
-    0x23F9, 0x23FA, 0x24C2, 0x25AA, 0x25AB, 0x25B6, 0x25C0, 0x25FB, 0x25FC, 0x25FD, 0x25FE, 0x2600,
-    0x2601, 0x2602, 0x2603, 0x2604, 0x260E, 0x2611, 0x2614, 0x2615, 0x2618, 0x261D, 0x2620, 0x2622,
-    0x2623, 0x2626, 0x262A, 0x262E, 0x262F, 0x2638, 0x2639, 0x263A, 0x2640, 0x2642, 0x2648, 0x2649,
-    0x264A, 0x264B, 0x264C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653, 0x2660, 0x2663,
-    0x2665, 0x2666, 0x2668, 0x267B, 0x267F, 0x2692, 0x2693, 0x2694, 0x2695, 0x2696, 0x2697, 0x2699,
-    0x269B, 0x269C, 0x26A0, 0x26A1, 0x26AA, 0x26AB, 0x26B0, 0x26B1, 0x26BD, 0x26BE, 0x26C4, 0x26C5,
-    0x26C8, 0x26CF, 0x26D1, 0x26D3, 0x26D4, 0x26E9, 0x26EA, 0x26F0, 0x26F1, 0x26F2, 0x26F3, 0x26F4,
-    0x26F5, 0x26F7, 0x26F8, 0x26F9, 0x26FA, 0x26FD, 0x2702, 0x2708, 0x2709, 0x270C, 0x270D, 0x270F,
-    0x2712, 0x2714, 0x2716, 0x271D, 0x2721, 0x2733, 0x2734, 0x2744, 0x2747, 0x2757, 0x2763, 0x2764,
-    0x27A1, 0x2934, 0x2935, 0x2B05, 0x2B06, 0x2B07, 0x2B1B, 0x2B1C, 0x2B50, 0x2B55, 0x3030, 0x303D,
-    0x3297, 0x3299, 0x1F004, 0x1F170, 0x1F171, 0x1F17E, 0x1F17F, 0x1F202, 0x1F21A, 0x1F22F, 0x1F237,
-    0x1F321, 0x1F324, 0x1F325, 0x1F326, 0x1F327, 0x1F328, 0x1F329, 0x1F32A, 0x1F32B, 0x1F32C,
-    0x1F336, 0x1F37D, 0x1F396, 0x1F397, 0x1F399, 0x1F39A, 0x1F39B, 0x1F39E, 0x1F39F, 0x1F3CB,
-    0x1F3CC, 0x1F3CD, 0x1F3CE, 0x1F3D4, 0x1F3D5, 0x1F3D6, 0x1F3D7, 0x1F3D8, 0x1F3D9, 0x1F3DA,
-    0x1F3DB, 0x1F3DC, 0x1F3DD, 0x1F3DE, 0x1F3DF, 0x1F3F3, 0x1F3F5, 0x1F3F7, 0x1F43F, 0x1F441,
-    0x1F4FD, 0x1F549, 0x1F54A, 0x1F56F, 0x1F570, 0x1F573, 0x1F574, 0x1F575, 0x1F576, 0x1F577,
-    0x1F578, 0x1F579, 0x1F587, 0x1F58A, 0x1F58B, 0x1F58C, 0x1F58D, 0x1F590, 0x1F5A5, 0x1F5A8,
-    0x1F5B1, 0x1F5B2, 0x1F5BC, 0x1F5C2, 0x1F5C3, 0x1F5C4, 0x1F5D1, 0x1F5D2, 0x1F5D3, 0x1F5DC,
-    0x1F5DD, 0x1F5DE, 0x1F5E1, 0x1F5E3, 0x1F5E8, 0x1F5EF, 0x1F5F3, 0x1F5FA, 0x1F6CB, 0x1F6CD,
-    0x1F6CE, 0x1F6CF, 0x1F6E0, 0x1F6E1, 0x1F6E2, 0x1F6E3, 0x1F6E4, 0x1F6E5, 0x1F6E9, 0x1F6F0,
-    0x1F6F3,
-};
-
-static bool isEmojiStyleVSBase(uint32_t cp) {
-    const size_t length = sizeof(EMOJI_STYLE_VS_BASES) / sizeof(EMOJI_STYLE_VS_BASES[0]);
-    return std::binary_search(EMOJI_STYLE_VS_BASES, EMOJI_STYLE_VS_BASES + length, cp);
-}
-
 uint32_t FontCollection::sNextId = 0;
 
-FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
+FontCollection::FontCollection(std::shared_ptr<FontFamily>&& typeface) : mMaxChar(0) {
+    std::vector<std::shared_ptr<FontFamily>> typefaces;
+    typefaces.push_back(typeface);
+    init(typefaces);
+}
+
+FontCollection::FontCollection(const vector<std::shared_ptr<FontFamily>>& typefaces) :
     mMaxChar(0) {
-    AutoMutex _l(gMinikinLock);
+    init(typefaces);
+}
+
+void FontCollection::init(const vector<std::shared_ptr<FontFamily>>& typefaces) {
+    android::AutoMutex _l(gMinikinLock);
     mId = sNextId++;
     vector<uint32_t> lastChar;
     size_t nTypefaces = typefaces.size();
@@ -89,29 +65,27 @@
 #endif
     const FontStyle defaultStyle;
     for (size_t i = 0; i < nTypefaces; i++) {
-        FontFamily* family = typefaces[i];
-        MinikinFont* typeface = family->getClosestMatch(defaultStyle).font;
-        if (typeface == NULL) {
+        const std::shared_ptr<FontFamily>& family = typefaces[i];
+        if (family->getClosestMatch(defaultStyle).font == nullptr) {
             continue;
         }
-        family->RefLocked();
-        const SparseBitSet* coverage = family->getCoverage();
-        if (coverage == nullptr) {
-            family->UnrefLocked();
-            continue;
-        }
+        const SparseBitSet& coverage = family->getCoverage();
         mFamilies.push_back(family);  // emplace_back would be better
         if (family->hasVSTable()) {
             mVSFamilyVec.push_back(family);
         }
-        mMaxChar = max(mMaxChar, coverage->length());
-        lastChar.push_back(coverage->nextSetBit(0));
+        mMaxChar = max(mMaxChar, coverage.length());
+        lastChar.push_back(coverage.nextSetBit(0));
+
+        const std::unordered_set<AxisTag>& supportedAxes = family->supportedAxes();
+        mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
     }
     nTypefaces = mFamilies.size();
     LOG_ALWAYS_FATAL_IF(nTypefaces == 0,
         "Font collection must have at least one valid typeface");
+    LOG_ALWAYS_FATAL_IF(nTypefaces > 254,
+        "Font collection may only have up to 254 font families.");
     size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage;
-    size_t offset = 0;
     // TODO: Use variation selector map for mRanges construction.
     // 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
@@ -123,27 +97,23 @@
 #ifdef VERBOSE_DEBUG
         ALOGD("i=%zd: range start = %zd\n", i, offset);
 #endif
-        range->start = offset;
+        range->start = mFamilyVec.size();
         for (size_t j = 0; j < nTypefaces; j++) {
             if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
-                FontFamily* family = mFamilies[j];
-                mFamilyVec.push_back(family);
-                offset++;
-                uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage);
+                const std::shared_ptr<FontFamily>& family = mFamilies[j];
+                mFamilyVec.push_back(static_cast<uint8_t>(j));
+                uint32_t nextChar = family->getCoverage().nextSetBit((i + 1) << kLogCharsPerPage);
 #ifdef VERBOSE_DEBUG
                 ALOGD("nextChar = %d (j = %zd)\n", nextChar, j);
 #endif
                 lastChar[j] = nextChar;
             }
         }
-        range->end = offset;
+        range->end = mFamilyVec.size();
     }
-}
-
-FontCollection::~FontCollection() {
-    for (size_t i = 0; i < mFamilies.size(); i++) {
-        mFamilies[i]->UnrefLocked();
-    }
+    // See the comment in Range for more details.
+    LOG_ALWAYS_FATAL_IF(mFamilyVec.size() >= 0xFFFF,
+        "Exceeded the maximum indexable cmap coverage.");
 }
 
 // Special scores for the font fallback.
@@ -167,7 +137,7 @@
 //  - kFirstFontScore: When the font is the first font family in the collection and it supports the
 //    given character or variation sequence.
 uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
-                                        FontFamily* fontFamily) const {
+        const std::shared_ptr<FontFamily>& fontFamily) const {
 
     const uint32_t coverageScore = calcCoverageScore(ch, vs, fontFamily);
     if (coverageScore == kFirstFontScore || coverageScore == kUnsupportedFontScore) {
@@ -194,9 +164,10 @@
 // - Returns 2 if the vs is a text variation selector (U+FE0E) and if the font is not an emoji font.
 // - Returns 1 if the variation selector is not specified or if the font family only supports the
 //   variation sequence's base character.
-uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, FontFamily* fontFamily) const {
+uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs,
+        const std::shared_ptr<FontFamily>& fontFamily) const {
     const bool hasVSGlyph = (vs != 0) && fontFamily->hasGlyph(ch, vs);
-    if (!hasVSGlyph && !fontFamily->getCoverage()->get(ch)) {
+    if (!hasVSGlyph && !fontFamily->getCoverage().get(ch)) {
         // The font doesn't support either variation sequence or even the base character.
         return kUnsupportedFontScore;
     }
@@ -219,7 +190,7 @@
         const FontLanguages& langs = FontLanguageListCache::getById(fontFamily->langId());
         bool hasEmojiFlag = false;
         for (size_t i = 0; i < langs.size(); ++i) {
-            if (langs[i].hasEmojiFlag()) {
+            if (langs[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
                 hasEmojiFlag = true;
                 break;
             }
@@ -234,22 +205,24 @@
     return 1;
 }
 
-// Calculates font scores based on the script matching and primary langauge matching.
+// Calculate font scores based on the script matching, subtag matching and primary langauge matching.
 //
-// If the font's script doesn't support the requested script, the font gets a score of 0. If the
-// font's script supports the requested script and the font has the same primary language as the
-// requested one, the font gets a score of 2. If the font's script supports the requested script
-// but the primary language is different from the requested one, the font gets a score of 1.
+// 1. If only the font's language matches or there is no matches between requested font and
+//    supported font, then the font obtains a score of 0.
+// 2. Without a match in language, considering subtag may change font's EmojiStyle over script,
+//    a match in subtag gets a score of 2 and a match in scripts gains a score of 1.
+// 3. Regarding to two elements matchings, language-and-subtag matching has a score of 4, while
+//    language-and-script obtains a socre of 3 with the same reason above.
 //
 // If two languages in the requested list have the same language score, the font matching with
 // higher priority language gets a higher score. For example, in the case the user requested
 // language list is "ja-Jpan,en-Latn". The score of for the font of "ja-Jpan" gets a higher score
 // than the font of "en-Latn".
 //
-// To achieve the above two conditions, the language score is determined as follows:
-//   LanguageScore = s(0) * 3^(m - 1) + s(1) * 3^(m - 2) + ... + s(m - 2) * 3 + s(m - 1)
+// To achieve score calculation with priorities, the language score is determined as follows:
+//   LanguageScore = s(0) * 5^(m - 1) + s(1) * 5^(m - 2) + ... + s(m - 2) * 5 + s(m - 1)
 // Here, m is the maximum number of languages to be compared, and s(i) is the i-th language's
-// matching score. The possible values of s(i) are 0, 1 and 2.
+// matching score. The possible values of s(i) are 0, 1, 2, 3 and 4.
 uint32_t FontCollection::calcLanguageMatchingScore(
         uint32_t userLangListId, const FontFamily& fontFamily) {
     const FontLanguages& langList = FontLanguageListCache::getById(userLangListId);
@@ -258,7 +231,7 @@
     const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT);
     uint32_t score = 0;
     for (size_t i = 0; i < maxCompareNum; ++i) {
-        score = score * 3u + langList[i].calcScoreFor(fontLanguages);
+        score = score * 5u + langList[i].calcScoreFor(fontLanguages);
     }
     return score;
 }
@@ -275,37 +248,26 @@
 // 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.
-FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
+const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
             uint32_t langListId, int variant) const {
     if (ch >= mMaxChar) {
         return mFamilies[0];
     }
 
-    const std::vector<FontFamily*>* familyVec = &mFamilyVec;
     Range range = mRanges[ch >> kLogCharsPerPage];
 
-    std::vector<FontFamily*> familyVecForVS;
     if (vs != 0) {
-        // If variation selector is specified, need to search for both the variation sequence and
-        // its base codepoint. Compute the union vector of them.
-        familyVecForVS = mVSFamilyVec;
-        familyVecForVS.insert(familyVecForVS.end(),
-                mFamilyVec.begin() + range.start, mFamilyVec.begin() + range.end);
-        std::sort(familyVecForVS.begin(), familyVecForVS.end());
-        auto last = std::unique(familyVecForVS.begin(), familyVecForVS.end());
-        familyVecForVS.erase(last, familyVecForVS.end());
-
-        familyVec = &familyVecForVS;
-        range = { 0, familyVecForVS.size() };
+        range = { 0, static_cast<uint16_t>(mFamilies.size()) };
     }
 
 #ifdef VERBOSE_DEBUG
     ALOGD("querying range %zd:%zd\n", range.start, range.end);
 #endif
-    FontFamily* bestFamily = nullptr;
+    int bestFamilyIndex = -1;
     uint32_t bestScore = kUnsupportedFontScore;
     for (size_t i = range.start; i < range.end; i++) {
-        FontFamily* family = (*familyVec)[i];
+        const std::shared_ptr<FontFamily>& family =
+                vs == 0 ? mFamilies[mFamilyVec[i]] : mFamilies[i];
         const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family);
         if (score == kFirstFontScore) {
             // If the first font family supports the given character or variation sequence, always
@@ -314,10 +276,10 @@
         }
         if (score > bestScore) {
             bestScore = score;
-            bestFamily = family;
+            bestFamilyIndex = i;
         }
     }
-    if (bestFamily == nullptr) {
+    if (bestFamilyIndex == -1) {
         UErrorCode errorCode = U_ZERO_ERROR;
         const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
         if (U_SUCCESS(errorCode)) {
@@ -329,24 +291,27 @@
                 return getFamilyForChar(ch, vs, langListId, variant);
             }
         }
-        bestFamily = mFamilies[0];
+        return mFamilies[0];
     }
-    return bestFamily;
+    return vs == 0 ? mFamilies[mFamilyVec[bestFamilyIndex]] : mFamilies[bestFamilyIndex];
 }
 
-const uint32_t NBSP = 0xa0;
-const uint32_t ZWJ = 0x200c;
-const uint32_t ZWNJ = 0x200d;
+const uint32_t NBSP = 0x00A0;
+const uint32_t SOFT_HYPHEN = 0x00AD;
+const uint32_t ZWJ = 0x200C;
+const uint32_t ZWNJ = 0x200D;
 const uint32_t HYPHEN = 0x2010;
 const uint32_t NB_HYPHEN = 0x2011;
+const uint32_t NNBSP = 0x202F;
 const uint32_t FEMALE_SIGN = 0x2640;
 const uint32_t MALE_SIGN = 0x2642;
 const uint32_t STAFF_OF_AESCULAPIUS = 0x2695;
 
 // 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[] = { '!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ,
-        HYPHEN, NB_HYPHEN, FEMALE_SIGN, MALE_SIGN, STAFF_OF_AESCULAPIUS };
+static const uint32_t stickyWhitelist[] = {
+        '!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ,
+        HYPHEN, NB_HYPHEN, NNBSP, FEMALE_SIGN, MALE_SIGN, STAFF_OF_AESCULAPIUS };
 
 static bool isStickyWhitelisted(uint32_t c) {
     for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) {
@@ -355,10 +320,6 @@
     return false;
 }
 
-static bool isVariationSelector(uint32_t c) {
-    return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF);
-}
-
 bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
         uint32_t variationSelector) const {
     if (!isVariationSelector(variationSelector)) {
@@ -368,8 +329,6 @@
         return false;
     }
 
-    AutoMutex _l(gMinikinLock);
-
     // Currently mRanges can not be used here since it isn't aware of the variation sequence.
     for (size_t i = 0; i < mVSFamilyVec.size(); i++) {
         if (mVSFamilyVec[i]->hasGlyph(baseCodepoint, variationSelector)) {
@@ -377,12 +336,17 @@
         }
     }
 
+    // TODO: We can remove this lock by precomputing color emoji information.
+    android::AutoMutex _l(gMinikinLock);
+
     // Even if there is no cmap format 14 subtable entry for the given sequence, should return true
-    // for emoji + U+FE0E case since we have special fallback rule for the sequence.
-    if (isEmojiStyleVSBase(baseCodepoint) && variationSelector == TEXT_STYLE_VS) {
+    // for <char, text presentation selector> case since we have special fallback rule for the
+    // sequence. Note that we don't need to restrict this to already standardized variation
+    // sequences, since Unicode is adding variation sequences more frequently now and may even move
+    // towards allowing text and emoji variation selectors on any character.
+    if (variationSelector == TEXT_STYLE_VS) {
         for (size_t i = 0; i < mFamilies.size(); ++i) {
-            if (!mFamilies[i]->isColorEmojiFamily() && variationSelector == TEXT_STYLE_VS &&
-                mFamilies[i]->hasGlyph(baseCodepoint, 0)) {
+            if (!mFamilies[i]->isColorEmojiFamily() && mFamilies[i]->hasGlyph(baseCodepoint, 0)) {
                 return true;
             }
         }
@@ -395,7 +359,7 @@
         vector<Run>* result) const {
     const uint32_t langListId = style.getLanguageListId();
     int variant = style.getVariant();
-    FontFamily* lastFamily = NULL;
+    const FontFamily* lastFamily = nullptr;
     Run* run = NULL;
 
     if (string_size == 0) {
@@ -424,17 +388,17 @@
         if (lastFamily != nullptr) {
             if (isStickyWhitelisted(ch)) {
                 // Continue using existing font as long as it has coverage and is whitelisted
-                shouldContinueRun = lastFamily->getCoverage()->get(ch);
-            } else if (isVariationSelector(ch)) {
-                // Always continue if the character is a variation selector.
+                shouldContinueRun = lastFamily->getCoverage().get(ch);
+            } else if (ch == SOFT_HYPHEN || isVariationSelector(ch)) {
+                // Always continue if the character is the soft hyphen or a variation selector.
                 shouldContinueRun = true;
             }
         }
 
         if (!shouldContinueRun) {
-            FontFamily* family = getFamilyForChar(ch, isVariationSelector(nextCh) ? nextCh : 0,
-                    langListId, variant);
-            if (utf16Pos == 0 || family != lastFamily) {
+            const std::shared_ptr<FontFamily>& family = getFamilyForChar(
+                    ch, isVariationSelector(nextCh) ? nextCh : 0, langListId, variant);
+            if (utf16Pos == 0 || family.get() != lastFamily) {
                 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
@@ -444,7 +408,7 @@
                 if (utf16Pos != 0 &&
                         ((U_GET_GC_MASK(ch) & U_GC_M_MASK) != 0 ||
                          (isEmojiModifier(ch) && isEmojiBase(prevCh))) &&
-                        family && family->getCoverage()->get(prevCh)) {
+                        family != nullptr && family->getCoverage().get(prevCh)) {
                     const size_t prevChLength = U16_LENGTH(prevCh);
                     run->end -= prevChLength;
                     if (run->start == run->end) {
@@ -452,12 +416,9 @@
                     }
                     start -= prevChLength;
                 }
-                Run dummy;
-                result->push_back(dummy);
+                result->push_back({family->getClosestMatch(style), static_cast<int>(start), 0});
                 run = &result->back();
-                run->fakedFont = family->getClosestMatch(style);
-                lastFamily = family;
-                run->start = start;
+                lastFamily = family.get();
             }
         }
         prevCh = ch;
@@ -465,16 +426,43 @@
     } while (nextCh != kEndOfString);
 }
 
-MinikinFont* FontCollection::baseFont(FontStyle style) {
-    return baseFontFaked(style).font;
-}
-
 FakedFont FontCollection::baseFontFaked(FontStyle style) {
     return mFamilies[0]->getClosestMatch(style);
 }
 
+std::shared_ptr<FontCollection> FontCollection::createCollectionWithVariation(
+        const std::vector<FontVariation>& variations) {
+    if (variations.empty() || mSupportedAxes.empty()) {
+        return nullptr;
+    }
+
+    bool hasSupportedAxis = false;
+    for (const FontVariation& variation : variations) {
+        if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
+            hasSupportedAxis = true;
+            break;
+        }
+    }
+    if (!hasSupportedAxis) {
+        // None of variation axes are supported by this font collection.
+        return nullptr;
+    }
+
+    std::vector<std::shared_ptr<FontFamily> > families;
+    for (const std::shared_ptr<FontFamily>& family : mFamilies) {
+        std::shared_ptr<FontFamily> newFamily = family->createFamilyWithVariation(variations);
+        if (newFamily) {
+            families.push_back(newFamily);
+        } else {
+            families.push_back(family);
+        }
+    }
+
+    return std::shared_ptr<FontCollection>(new FontCollection(families));
+}
+
 uint32_t FontCollection::getId() const {
     return mId;
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index e2d86f0..a93cb4f 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -16,29 +16,29 @@
 
 #define LOG_TAG "Minikin"
 
-#include <cutils/log.h>
-#include <stdlib.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 
+#include <log/log.h>
+#include <utils/JenkinsHash.h>
+
 #include <hb.h>
 #include <hb-ot.h>
 
-#include <utils/JenkinsHash.h>
-
 #include "FontLanguage.h"
 #include "FontLanguageListCache.h"
+#include "FontUtils.h"
 #include "HbFontCache.h"
 #include "MinikinInternal.h"
-#include <minikin/MinikinFont.h>
-#include <minikin/AnalyzeStyle.h>
 #include <minikin/CmapCoverage.h>
+#include <minikin/MinikinFont.h>
 #include <minikin/FontFamily.h>
-#include <UniquePtr.h>
+#include <minikin/MinikinFont.h>
 
 using std::vector;
 
-namespace android {
+namespace minikin {
 
 FontStyle::FontStyle(int variant, int weight, bool italic)
         : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
@@ -48,15 +48,15 @@
         : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
 }
 
-hash_t FontStyle::hash() const {
-    uint32_t hash = JenkinsHashMix(0, bits);
-    hash = JenkinsHashMix(hash, mLanguageListId);
-    return JenkinsHashWhiten(hash);
+android::hash_t FontStyle::hash() const {
+    uint32_t hash = android::JenkinsHashMix(0, bits);
+    hash = android::JenkinsHashMix(hash, mLanguageListId);
+    return android::JenkinsHashWhiten(hash);
 }
 
 // static
 uint32_t FontStyle::registerLanguageList(const std::string& languages) {
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     return FontLanguageListCache::getId(languages);
 }
 
@@ -65,45 +65,57 @@
     return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
 }
 
-FontFamily::FontFamily() : FontFamily(0 /* variant */) {
+Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
+    : typeface(typeface), style(style) {
 }
 
-FontFamily::FontFamily(int variant) : FontFamily(FontLanguageListCache::kEmptyListId, variant) {
+Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
+    : typeface(typeface), style(style) {
 }
 
-FontFamily::~FontFamily() {
-    for (size_t i = 0; i < mFonts.size(); i++) {
-        mFonts[i].typeface->UnrefLocked();
+std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
+    const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
+    HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
+    if (fvarTable.size() == 0) {
+        return std::unordered_set<AxisTag>();
     }
+
+    std::unordered_set<AxisTag> supportedAxes;
+    analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
+    return supportedAxes;
 }
 
-bool FontFamily::addFont(MinikinFont* typeface) {
-    AutoMutex _l(gMinikinLock);
+Font::Font(Font&& o) {
+    typeface = std::move(o.typeface);
+    style = o.style;
+    o.typeface = nullptr;
+}
+
+Font::Font(const Font& o) {
+    typeface = o.typeface;
+    style = o.style;
+}
+
+// static
+FontFamily::FontFamily(std::vector<Font>&& fonts) : FontFamily(0 /* variant */, std::move(fonts)) {
+}
+
+FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
+    : FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) {
+}
+
+FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
+    : mLangId(langId), mVariant(variant), mFonts(std::move(fonts)) {
+    computeCoverage();
+}
+
+bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
+        bool* italic) {
+    android::AutoMutex _l(gMinikinLock);
     const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
-    HbBlob os2Table(getFontTable(typeface, os2Tag));
+    HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
     if (os2Table.get() == nullptr) return false;
-    int weight;
-    bool italic;
-    if (analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
-        //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
-        FontStyle style(weight, italic);
-        addFontLocked(typeface, style);
-        return true;
-    } else {
-        ALOGD("failed to analyze style");
-    }
-    return false;
-}
-
-void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
-    AutoMutex _l(gMinikinLock);
-    addFontLocked(typeface, style);
-}
-
-void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) {
-    typeface->RefLocked();
-    mFonts.push_back(Font(typeface, style));
-    mCoverageValid = false;
+    return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight, italic);
 }
 
 // Compute a matching metric between two styles - 0 is an exact match
@@ -127,7 +139,7 @@
 }
 
 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
-    const Font* bestFont = NULL;
+    const Font* bestFont = nullptr;
     int bestMatch = 0;
     for (size_t i = 0; i < mFonts.size(); i++) {
         const Font& font = mFonts[i];
@@ -137,81 +149,107 @@
             bestMatch = match;
         }
     }
-    FakedFont result;
-    if (bestFont == NULL) {
-        result.font = NULL;
-    } else {
-        result.font = bestFont->typeface;
-        result.fakery = computeFakery(style, bestFont->style);
+    if (bestFont != nullptr) {
+        return FakedFont{ bestFont->typeface.get(), computeFakery(style, bestFont->style) };
     }
-    return result;
-}
-
-size_t FontFamily::getNumFonts() const {
-    return mFonts.size();
-}
-
-MinikinFont* FontFamily::getFont(size_t index) const {
-    return mFonts[index].typeface;
-}
-
-FontStyle FontFamily::getStyle(size_t index) const {
-    return mFonts[index].style;
+    return FakedFont{ nullptr, FontFakery() };
 }
 
 bool FontFamily::isColorEmojiFamily() const {
     const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
     for (size_t i = 0; i < languageList.size(); ++i) {
-        if (languageList[i].hasEmojiFlag()) {
+        if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
             return true;
         }
     }
     return false;
 }
 
-const SparseBitSet* FontFamily::getCoverage() {
-    if (!mCoverageValid) {
-        const FontStyle defaultStyle;
-        MinikinFont* typeface = getClosestMatch(defaultStyle).font;
-        const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
-        HbBlob cmapTable(getFontTable(typeface, cmapTag));
-        if (cmapTable.get() == nullptr) {
-            ALOGE("Could not get cmap table size!\n");
-            // Note: This means we will retry on the next call to getCoverage, as we can't store
-            //       the failure. This is fine, as we assume this doesn't really happen in practice.
-            return nullptr;
-        }
-        // TODO: Error check?
-        CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable);
-#ifdef VERBOSE_DEBUG
-        ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(),
-                mCoverage.nextSetBit(0));
-#endif
-        mCoverageValid = true;
+void FontFamily::computeCoverage() {
+    android::AutoMutex _l(gMinikinLock);
+    const FontStyle defaultStyle;
+    const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
+    const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
+    HbBlob cmapTable(getFontTable(typeface, cmapTag));
+    if (cmapTable.get() == nullptr) {
+        ALOGE("Could not get cmap table size!\n");
+        return;
     }
-    return &mCoverage;
+    mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
+
+    for (size_t i = 0; i < mFonts.size(); ++i) {
+        std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxesLocked();
+        mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
+    }
 }
 
-bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) {
-    assertMinikinLocked();
-    if (variationSelector != 0 && !mHasVSTable) {
-        // Early exit if the variation selector is specified but the font doesn't have a cmap format
-        // 14 subtable.
+bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
+    if (variationSelector == 0) {
+        return mCoverage.get(codepoint);
+    }
+
+    if (mCmapFmt14Coverage.empty()) {
         return false;
     }
 
-    const FontStyle defaultStyle;
-    MinikinFont* minikinFont = getClosestMatch(defaultStyle).font;
-    hb_font_t* font = getHbFontLocked(minikinFont);
-    uint32_t unusedGlyph;
-    bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
-    hb_font_destroy(font);
-    return result;
+    const uint16_t vsIndex = getVsIndex(variationSelector);
+
+    if (vsIndex >= mCmapFmt14Coverage.size()) {
+        // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
+        // be at the maximum end of the range.
+        return false;
+    }
+
+    const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
+    if (bitset.get() == nullptr) {
+        return false;
+    }
+
+    return bitset->get(codepoint);
 }
 
-bool FontFamily::hasVSTable() const {
-    LOG_ALWAYS_FATAL_IF(!mCoverageValid, "Do not call this method before getCoverage() call");
-    return mHasVSTable;
+std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
+        const std::vector<FontVariation>& variations) const {
+    if (variations.empty() || mSupportedAxes.empty()) {
+        return nullptr;
+    }
+
+    bool hasSupportedAxis = false;
+    for (const FontVariation& variation : variations) {
+        if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
+            hasSupportedAxis = true;
+            break;
+        }
+    }
+    if (!hasSupportedAxis) {
+        // None of variation axes are suppored by this family.
+        return nullptr;
+    }
+
+    std::vector<Font> fonts;
+    for (const Font& font : mFonts) {
+        bool supportedVariations = false;
+        android::AutoMutex _l(gMinikinLock);
+        std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
+        if (!supportedAxes.empty()) {
+            for (const FontVariation& variation : variations) {
+                if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
+                    supportedVariations = true;
+                    break;
+                }
+            }
+        }
+        std::shared_ptr<MinikinFont> minikinFont;
+        if (supportedVariations) {
+            minikinFont = font.typeface->createFontWithVariation(variations);
+        }
+        if (minikinFont == nullptr) {
+            minikinFont = font.typeface;
+        }
+        fonts.push_back(Font(std::move(minikinFont), font.style));
+    }
+
+    return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/FontLanguage.cpp b/libs/minikin/FontLanguage.cpp
index bccb4bf..0897c06 100644
--- a/libs/minikin/FontLanguage.cpp
+++ b/libs/minikin/FontLanguage.cpp
@@ -18,43 +18,192 @@
 
 #include "FontLanguage.h"
 
+#include <algorithm>
 #include <hb.h>
+#include <string.h>
 #include <unicode/uloc.h>
 
-namespace android {
+namespace minikin {
 
 #define SCRIPT_TAG(c1, c2, c3, c4) \
         (((uint32_t)(c1)) << 24 | ((uint32_t)(c2)) << 16 | ((uint32_t)(c3)) <<  8 | \
          ((uint32_t)(c4)))
 
+// Check if a language code supports emoji according to its subtag
+static bool isEmojiSubtag(const char* buf, size_t bufLen, const char* subtag, size_t subtagLen) {
+    if (bufLen < subtagLen) {
+        return false;
+    }
+    if (strncmp(buf, subtag, subtagLen) != 0) {
+        return false;  // no match between two strings
+    }
+    return (bufLen == subtagLen || buf[subtagLen] == '\0' ||
+            buf[subtagLen] == '-' || buf[subtagLen] == '_');
+}
+
+// Pack the three letter code into 15 bits and stored to 16 bit integer. The highest bit is 0.
+// For the region code, the letters must be all digits in three letter case, so the number of
+// possible values are 10. For the language code, the letters must be all small alphabets, so the
+// number of possible values are 26. Thus, 5 bits are sufficient for each case and we can pack the
+// three letter language code or region code to 15 bits.
+//
+// In case of two letter code, use fullbit(0x1f) for the first letter instead.
+static uint16_t packLanguageOrRegion(const char* c, size_t length, uint8_t twoLetterBase,
+        uint8_t threeLetterBase) {
+    if (length == 2) {
+        return 0x7c00u |  // 0x1fu << 10
+                (uint16_t)(c[0] - twoLetterBase) << 5 |
+                (uint16_t)(c[1] - twoLetterBase);
+    } else {
+        return ((uint16_t)(c[0] - threeLetterBase) << 10) |
+                (uint16_t)(c[1] - threeLetterBase) << 5 |
+                (uint16_t)(c[2] - threeLetterBase);
+    }
+}
+
+static size_t unpackLanguageOrRegion(uint16_t in, char* out, uint8_t twoLetterBase,
+        uint8_t threeLetterBase) {
+    uint8_t first = (in >> 10) & 0x1f;
+    uint8_t second = (in >> 5) & 0x1f;
+    uint8_t third = in & 0x1f;
+
+    if (first == 0x1f) {
+        out[0] = second + twoLetterBase;
+        out[1] = third + twoLetterBase;
+        return 2;
+    } else {
+        out[0] = first + threeLetterBase;
+        out[1] = second + threeLetterBase;
+        out[2] = third + threeLetterBase;
+        return 3;
+    }
+}
+
+// Find the next '-' or '_' index from startOffset position. If not found, returns bufferLength.
+static size_t nextDelimiterIndex(const char* buffer, size_t bufferLength, size_t startOffset) {
+    for (size_t i = startOffset; i < bufferLength; ++i) {
+        if (buffer[i] == '-' || buffer[i] == '_') {
+            return i;
+        }
+    }
+    return bufferLength;
+}
+
+static inline bool isLowercase(char c) {
+    return 'a' <= c && c <= 'z';
+}
+
+static inline bool isUppercase(char c) {
+    return 'A' <= c && c <= 'Z';
+}
+
+static inline bool isDigit(char c) {
+    return '0' <= c && c <= '9';
+}
+
+// Returns true if the buffer is valid for language code.
+static inline bool isValidLanguageCode(const char* buffer, size_t length) {
+    if (length != 2 && length != 3) return false;
+    if (!isLowercase(buffer[0])) return false;
+    if (!isLowercase(buffer[1])) return false;
+    if (length == 3 && !isLowercase(buffer[2])) return false;
+    return true;
+}
+
+// Returns true if buffer is valid for script code. The length of buffer must be 4.
+static inline bool isValidScriptCode(const char* buffer) {
+    return isUppercase(buffer[0]) && isLowercase(buffer[1]) && isLowercase(buffer[2]) &&
+        isLowercase(buffer[3]);
+}
+
+// Returns true if the buffer is valid for region code.
+static inline bool isValidRegionCode(const char* buffer, size_t length) {
+    return (length == 2 && isUppercase(buffer[0]) && isUppercase(buffer[1])) ||
+            (length == 3 && isDigit(buffer[0]) && isDigit(buffer[1]) && isDigit(buffer[2]));
+}
+
 // Parse BCP 47 language identifier into internal structure
 FontLanguage::FontLanguage(const char* buf, size_t length) : FontLanguage() {
-    size_t i;
-    for (i = 0; i < length; i++) {
-        char c = buf[i];
-        if (c == '-' || c == '_') break;
-    }
-    if (i == 2 || i == 3) {  // only accept two or three letter language code.
-        mLanguage = buf[0] | (buf[1] << 8) | ((i == 3) ? (buf[2] << 16) : 0);
+    size_t firstDelimiterPos = nextDelimiterIndex(buf, length, 0);
+    if (isValidLanguageCode(buf, firstDelimiterPos)) {
+        mLanguage = packLanguageOrRegion(buf, firstDelimiterPos, 'a', 'a');
     } else {
         // We don't understand anything other than two-letter or three-letter
         // language codes, so we skip parsing the rest of the string.
-        mLanguage = 0ul;
         return;
     }
 
-    size_t next;
-    for (i++; i < length; i = next + 1) {
-        for (next = i; next < length; next++) {
-            char c = buf[next];
-            if (c == '-' || c == '_') break;
+    if (firstDelimiterPos == length) {
+        mHbLanguage = hb_language_from_string(getString().c_str(), -1);
+        return;  // Language code only.
+    }
+
+    size_t nextComponentStartPos = firstDelimiterPos + 1;
+    size_t nextDelimiterPos = nextDelimiterIndex(buf, length, nextComponentStartPos);
+    size_t componentLength = nextDelimiterPos - nextComponentStartPos;
+
+    if (componentLength == 4) {
+        // Possibly script code.
+        const char* p = buf + nextComponentStartPos;
+        if (isValidScriptCode(p)) {
+            mScript = SCRIPT_TAG(p[0], p[1], p[2], p[3]);
+            mSubScriptBits = scriptToSubScriptBits(mScript);
         }
-        if (next - i == 4 && 'A' <= buf[i] && buf[i] <= 'Z') {
-            mScript = SCRIPT_TAG(buf[i], buf[i + 1], buf[i + 2], buf[i + 3]);
+
+        if (nextDelimiterPos == length) {
+            mHbLanguage = hb_language_from_string(getString().c_str(), -1);
+            mEmojiStyle = resolveEmojiStyle(buf, length, mScript);
+            return;  // No region code.
+        }
+
+        nextComponentStartPos = nextDelimiterPos + 1;
+        nextDelimiterPos = nextDelimiterIndex(buf, length, nextComponentStartPos);
+        componentLength = nextDelimiterPos - nextComponentStartPos;
+    }
+
+    if (componentLength == 2 || componentLength == 3) {
+        // Possibly region code.
+        const char* p = buf + nextComponentStartPos;
+        if (isValidRegionCode(p, componentLength)) {
+            mRegion = packLanguageOrRegion(p, componentLength, 'A', '0');
         }
     }
 
-    mSubScriptBits = scriptToSubScriptBits(mScript);
+    mHbLanguage = hb_language_from_string(getString().c_str(), -1);
+    mEmojiStyle = resolveEmojiStyle(buf, length, mScript);
+}
+
+// static
+FontLanguage::EmojiStyle FontLanguage::resolveEmojiStyle(const char* buf, size_t length,
+        uint32_t script) {
+    // First, lookup emoji subtag.
+    // 10 is the length of "-u-em-text", which is the shortest emoji subtag,
+    // unnecessary comparison can be avoided if total length is smaller than 10.
+    const size_t kMinSubtagLength = 10;
+    if (length >= kMinSubtagLength) {
+        static const char kPrefix[] = "-u-em-";
+        const char *pos = std::search(buf, buf + length, kPrefix, kPrefix + strlen(kPrefix));
+        if (pos != buf + length) {  // found
+            pos += strlen(kPrefix);
+            const size_t remainingLength = length - (pos - buf);
+            if (isEmojiSubtag(pos, remainingLength, "emoji", 5)){
+                return EMSTYLE_EMOJI;
+            } else if (isEmojiSubtag(pos, remainingLength, "text", 4)){
+                return EMSTYLE_TEXT;
+            } else if (isEmojiSubtag(pos, remainingLength, "default", 7)){
+                return EMSTYLE_DEFAULT;
+            }
+        }
+    }
+
+    // If no emoji subtag was provided, resolve the emoji style from script code.
+    if (script == SCRIPT_TAG('Z', 's', 'y', 'e')) {
+        return EMSTYLE_EMOJI;
+    } else if (script == SCRIPT_TAG('Z', 's', 'y', 'm')) {
+        return EMSTYLE_TEXT;
+    }
+
+    return EMSTYLE_EMPTY;
 }
 
 //static
@@ -95,29 +244,26 @@
         case SCRIPT_TAG('K', 'o', 'r', 'e'):
             subScriptBits = kHanFlag | kHangulFlag;
             break;
-        case SCRIPT_TAG('Z', 's', 'y', 'e'):
-            subScriptBits = kEmojiFlag;
-            break;
     }
     return subScriptBits;
 }
 
 std::string FontLanguage::getString() const {
-    if (mLanguage == 0ul) {
+    if (isUnsupported()) {
         return "und";
     }
     char buf[16];
-    size_t i = 0;
-    buf[i++] = mLanguage & 0xFF ;
-    buf[i++] = (mLanguage >> 8) & 0xFF;
-    char third_letter = (mLanguage >> 16) & 0xFF;
-    if (third_letter != 0) buf[i++] = third_letter;
+    size_t i = unpackLanguageOrRegion(mLanguage, buf, 'a', 'a');
     if (mScript != 0) {
-      buf[i++] = '-';
-      buf[i++] = (mScript >> 24) & 0xFFu;
-      buf[i++] = (mScript >> 16) & 0xFFu;
-      buf[i++] = (mScript >> 8) & 0xFFu;
-      buf[i++] = mScript & 0xFFu;
+        buf[i++] = '-';
+        buf[i++] = (mScript >> 24) & 0xFFu;
+        buf[i++] = (mScript >> 16) & 0xFFu;
+        buf[i++] = (mScript >> 8) & 0xFFu;
+        buf[i++] = mScript & 0xFFu;
+    }
+    if (mRegion != INVALID_CODE) {
+        buf[i++] = '-';
+        i += unpackLanguageOrRegion(mRegion, buf + i, 'A', '0');
     }
     return std::string(buf, i);
 }
@@ -139,28 +285,41 @@
 }
 
 int FontLanguage::calcScoreFor(const FontLanguages& supported) const {
-    int score = 0;
+    bool languageScriptMatch = false;
+    bool subtagMatch = false;
+    bool scriptMatch = false;
+
     for (size_t i = 0; i < supported.size(); ++i) {
+        if (mEmojiStyle != EMSTYLE_EMPTY &&
+               mEmojiStyle == supported[i].mEmojiStyle) {
+            subtagMatch = true;
+            if (mLanguage == supported[i].mLanguage) {
+                return 4;
+            }
+        }
         if (isEqualScript(supported[i]) ||
                 supportsScript(supported[i].mSubScriptBits, mSubScriptBits)) {
+            scriptMatch = true;
             if (mLanguage == supported[i].mLanguage) {
-                return 2;
-            } else {
-                score = 1;
+                languageScriptMatch = true;
             }
         }
     }
 
-    if (score == 1) {
-        return score;
-    }
-
     if (supportsScript(supported.getUnionOfSubScriptBits(), mSubScriptBits)) {
-        // Gives score of 2 only if the language matches all of the font languages except for the
-        // exact match case handled above.
-        return (mLanguage == supported[0].mLanguage && supported.isAllTheSameLanguage()) ? 2 : 1;
+        scriptMatch = true;
+        if (mLanguage == supported[0].mLanguage && supported.isAllTheSameLanguage()) {
+            return 3;
+        }
     }
 
+    if (languageScriptMatch) {
+        return 3;
+    } else if (subtagMatch) {
+        return 2;
+    } else if (scriptMatch) {
+        return 1;
+    }
     return 0;
 }
 
@@ -183,4 +342,4 @@
 }
 
 #undef SCRIPT_TAG
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/FontLanguage.h b/libs/minikin/FontLanguage.h
index f944174..6a50b1d 100644
--- a/libs/minikin/FontLanguage.h
+++ b/libs/minikin/FontLanguage.h
@@ -22,11 +22,15 @@
 
 #include <hb.h>
 
-namespace android {
+namespace minikin {
 
-// Due to the limits in font fallback score calculation, we can't use anything more than 17
+// Due to the limits in font fallback score calculation, we can't use anything more than 12
 // languages.
-const size_t FONT_LANGUAGES_LIMIT = 17;
+const size_t FONT_LANGUAGES_LIMIT = 12;
+
+// The language or region code is encoded to 15 bits.
+const uint16_t INVALID_CODE = 0x7fff;
+
 class FontLanguages;
 
 // FontLanguage is a compact representation of a BCP 47 language tag. It
@@ -34,22 +38,37 @@
 // font rendering.
 struct FontLanguage {
 public:
+    enum EmojiStyle : uint8_t {
+        EMSTYLE_EMPTY = 0,
+        EMSTYLE_DEFAULT = 1,
+        EMSTYLE_EMOJI = 2,
+        EMSTYLE_TEXT = 3,
+    };
     // Default constructor creates the unsupported language.
-    FontLanguage() : mScript(0ul), mLanguage(0ul), mSubScriptBits(0ul) {}
+    FontLanguage()
+            : mScript(0ul),
+            mLanguage(INVALID_CODE),
+            mRegion(INVALID_CODE),
+            mHbLanguage(HB_LANGUAGE_INVALID),
+            mSubScriptBits(0ul),
+            mEmojiStyle(EMSTYLE_EMPTY) {}
 
     // Parse from string
     FontLanguage(const char* buf, size_t length);
 
     bool operator==(const FontLanguage other) const {
-        return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage;
+        return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage &&
+                mRegion == other.mRegion && mEmojiStyle == other.mEmojiStyle;
     }
 
     bool operator!=(const FontLanguage other) const {
         return !(*this == other);
     }
 
-    bool isUnsupported() const { return mLanguage == 0ul; }
-    bool hasEmojiFlag() const { return mSubScriptBits & kEmojiFlag; }
+    bool isUnsupported() const { return mLanguage == INVALID_CODE; }
+    EmojiStyle getEmojiStyle() const { return mEmojiStyle; }
+    hb_language_t getHbLanguage() const { return mHbLanguage; }
+
 
     bool isEqualScript(const FontLanguage& other) const;
 
@@ -64,7 +83,10 @@
     // 0 = no match, 1 = script match, 2 = script and primary language match.
     int calcScoreFor(const FontLanguages& supported) const;
 
-    uint64_t getIdentifier() const { return (uint64_t)mScript << 32 | (uint64_t)mLanguage; }
+    uint64_t getIdentifier() const {
+        return ((uint64_t)mLanguage << 49) | ((uint64_t)mScript << 17) | ((uint64_t)mRegion << 2) |
+                mEmojiStyle;
+    }
 
 private:
     friend class FontLanguages;  // for FontLanguages constructor
@@ -73,23 +95,33 @@
     uint32_t mScript;
 
     // ISO 639-1 or ISO 639-2 compliant language code.
-    // The two or three letter language code is packed into 32 bit integer.
+    // The two- or three-letter language code is packed into a 15 bit integer.
     // mLanguage = 0 means the FontLanguage is unsupported.
-    uint32_t mLanguage;
+    uint16_t mLanguage;
 
-    // For faster comparing, use 8 bits for specific scripts.
+    // ISO 3166-1 or UN M.49 compliant region code. The two-letter or three-digit region code is
+    // packed into a 15 bit integer.
+    uint16_t mRegion;
+
+    // The language to be passed HarfBuzz shaper.
+    hb_language_t mHbLanguage;
+
+    // For faster comparing, use 7 bits for specific scripts.
     static const uint8_t kBopomofoFlag = 1u;
-    static const uint8_t kEmojiFlag = 1u << 1;
-    static const uint8_t kHanFlag = 1u << 2;
-    static const uint8_t kHangulFlag = 1u << 3;
-    static const uint8_t kHiraganaFlag = 1u << 4;
-    static const uint8_t kKatakanaFlag = 1u << 5;
-    static const uint8_t kSimplifiedChineseFlag = 1u << 6;
-    static const uint8_t kTraditionalChineseFlag = 1u << 7;
+    static const uint8_t kHanFlag = 1u << 1;
+    static const uint8_t kHangulFlag = 1u << 2;
+    static const uint8_t kHiraganaFlag = 1u << 3;
+    static const uint8_t kKatakanaFlag = 1u << 4;
+    static const uint8_t kSimplifiedChineseFlag = 1u << 5;
+    static const uint8_t kTraditionalChineseFlag = 1u << 6;
     uint8_t mSubScriptBits;
 
+    EmojiStyle mEmojiStyle;
+
     static uint8_t scriptToSubScriptBits(uint32_t script);
 
+    static EmojiStyle resolveEmojiStyle(const char* buf, size_t length, uint32_t script);
+
     // Returns true if the provide subscript bits has the requested subscript bits.
     // Note that this function returns false if the requested subscript bits are empty.
     static bool supportsScript(uint8_t providedBits, uint8_t requestedBits);
@@ -98,7 +130,7 @@
 // An immutable list of languages.
 class FontLanguages {
 public:
-    FontLanguages(std::vector<FontLanguage>&& languages);
+    explicit FontLanguages(std::vector<FontLanguage>&& languages);
     FontLanguages() : mUnionOfSubScriptBits(0), mIsAllTheSameLanguage(false) {}
     FontLanguages(FontLanguages&&) = default;
 
@@ -121,6 +153,6 @@
     void operator=(const FontLanguages&) = delete;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_FONT_LANGUAGE_H
diff --git a/libs/minikin/FontLanguageListCache.cpp b/libs/minikin/FontLanguageListCache.cpp
index 6b661f0..f1e14f0 100644
--- a/libs/minikin/FontLanguageListCache.cpp
+++ b/libs/minikin/FontLanguageListCache.cpp
@@ -18,14 +18,15 @@
 
 #include "FontLanguageListCache.h"
 
-#include <cutils/log.h>
 #include <unicode/uloc.h>
 #include <unordered_set>
 
-#include "MinikinInternal.h"
-#include "FontLanguage.h"
+#include <log/log.h>
 
-namespace android {
+#include "FontLanguage.h"
+#include "MinikinInternal.h"
+
+namespace minikin {
 
 const uint32_t FontLanguageListCache::kEmptyListId;
 
@@ -152,4 +153,4 @@
     return instance;
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/FontLanguageListCache.h b/libs/minikin/FontLanguageListCache.h
index c961882..9bf156f 100644
--- a/libs/minikin/FontLanguageListCache.h
+++ b/libs/minikin/FontLanguageListCache.h
@@ -22,7 +22,7 @@
 #include <minikin/FontFamily.h>
 #include "FontLanguage.h"
 
-namespace android {
+namespace minikin {
 
 class FontLanguageListCache {
 public:
@@ -51,6 +51,6 @@
     std::unordered_map<std::string, uint32_t> mLanguageListLookupTable;
 };
 
-}  // namespace android
+}  // namespace minikin
 
 #endif  // MINIKIN_FONT_LANGUAGE_LIST_CACHE_H
diff --git a/libs/minikin/FontUtils.cpp b/libs/minikin/FontUtils.cpp
new file mode 100644
index 0000000..c5a32f8
--- /dev/null
+++ b/libs/minikin/FontUtils.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <stdint.h>
+
+#include "FontUtils.h"
+
+namespace minikin {
+
+static uint16_t readU16(const uint8_t* data, size_t offset) {
+    return data[offset] << 8 | data[offset + 1];
+}
+
+static uint32_t readU32(const uint8_t* data, size_t offset) {
+    return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
+            ((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
+}
+
+bool analyzeStyle(const uint8_t* os2_data, size_t os2_size, int* weight, bool* italic) {
+    const size_t kUsWeightClassOffset = 4;
+    const size_t kFsSelectionOffset = 62;
+    const uint16_t kItalicFlag = (1 << 0);
+    if (os2_size < kFsSelectionOffset + 2) {
+        return false;
+    }
+    uint16_t weightClass = readU16(os2_data, kUsWeightClassOffset);
+    *weight = weightClass / 100;
+    uint16_t fsSelection = readU16(os2_data, kFsSelectionOffset);
+    *italic = (fsSelection & kItalicFlag) != 0;
+    return true;
+}
+
+void analyzeAxes(const uint8_t* fvar_data, size_t fvar_size, std::unordered_set<uint32_t>* axes) {
+    const size_t kMajorVersionOffset = 0;
+    const size_t kMinorVersionOffset = 2;
+    const size_t kOffsetToAxesArrayOffset = 4;
+    const size_t kAxisCountOffset = 8;
+    const size_t kAxisSizeOffset = 10;
+
+    axes->clear();
+
+    if (fvar_size < kAxisSizeOffset + 2) {
+        return;
+    }
+    const uint16_t majorVersion = readU16(fvar_data, kMajorVersionOffset);
+    const uint16_t minorVersion = readU16(fvar_data, kMinorVersionOffset);
+    const uint32_t axisOffset = readU16(fvar_data, kOffsetToAxesArrayOffset);
+    const uint32_t axisCount = readU16(fvar_data, kAxisCountOffset);
+    const uint32_t axisSize = readU16(fvar_data, kAxisSizeOffset);
+
+    if (majorVersion != 1 || minorVersion != 0 || axisOffset != 0x10 || axisSize != 0x14) {
+        return;  // Unsupported version.
+    }
+    if (fvar_size < axisOffset + axisOffset * axisCount) {
+        return;  // Invalid table size.
+    }
+    for (uint32_t i = 0; i < axisCount; ++i) {
+        size_t axisRecordOffset = axisOffset + i * axisSize;
+        uint32_t tag = readU32(fvar_data, axisRecordOffset);
+        axes->insert(tag);
+    }
+}
+}  // namespace minikin
diff --git a/include/minikin/AnalyzeStyle.h b/libs/minikin/FontUtils.h
similarity index 70%
rename from include/minikin/AnalyzeStyle.h
rename to libs/minikin/FontUtils.h
index 2989477..d26d5e4 100644
--- a/include/minikin/AnalyzeStyle.h
+++ b/libs/minikin/FontUtils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef MINIKIN_ANALYZE_STYLE_H
-#define MINIKIN_ANALYZE_STYLE_H
+#ifndef MINIKIN_FONT_UTILS_H
+#define MINIKIN_FONT_UTILS_H
 
-namespace android {
+#include <unordered_set>
+
+namespace minikin {
 
 bool analyzeStyle(const uint8_t* os2_data, size_t os2_size, int* weight, bool* italic);
+void analyzeAxes(const uint8_t* fvar_data, size_t fvar_size, std::unordered_set<uint32_t>* axes);
 
-}  // namespace android
+}  // namespace minikin
 
-#endif  // MINIKIN_ANALYZE_STYLE_H
\ No newline at end of file
+#endif  // MINIKIN_ANALYZE_STYLE_H
diff --git a/libs/minikin/GraphemeBreak.cpp b/libs/minikin/GraphemeBreak.cpp
index 45dd0ff..87de421 100644
--- a/libs/minikin/GraphemeBreak.cpp
+++ b/libs/minikin/GraphemeBreak.cpp
@@ -20,9 +20,10 @@
 #include <unicode/utf16.h>
 
 #include <minikin/GraphemeBreak.h>
+#include <minikin/Emoji.h>
 #include "MinikinInternal.h"
 
-namespace android {
+namespace minikin {
 
 int32_t tailoredGraphemeClusterBreak(uint32_t c) {
     // Characters defined as Control that we want to treat them as Extend.
@@ -39,17 +40,6 @@
             || c == 0xFEFF                  // BOM
             || ((c | 0x7F) == 0xE007F))     // recently undeprecated tag characters in Plane 14
         return U_GCB_EXTEND;
-    // UTC-approved characters for the Prepend class, per
-    // http://www.unicode.org/L2/L2015/15183r-graph-cluster-brk.txt
-    // These should be removed when our copy of ICU gets updated to Unicode 9.0 (~2016 or 2017).
-    else if ((0x0600 <= c && c <= 0x0605) // Arabic subtending marks
-            || c == 0x06DD                // ARABIC SUBTENDING MARK
-            || c == 0x070F                // SYRIAC ABBREVIATION MARK
-            || c == 0x0D4E                // MALAYALAM LETTER DOT REPH
-            || c == 0x110BD               // KAITHI NUMBER SIGN
-            || c == 0x111C2               // SHARADA SIGN JIHVAMULIYA
-            || c == 0x111C3)              // SHARADA SIGN UPADHMANIYA
-        return U_GCB_PREPEND;
     // THAI CHARACTER SARA AM is treated as a normal letter by most other implementations: they
     // allow a grapheme break before it.
     else if (c == 0x0E33)
@@ -59,15 +49,15 @@
 }
 
 // Returns true for all characters whose IndicSyllabicCategory is Pure_Killer.
-// From http://www.unicode.org/Public/8.0.0/ucd/IndicSyllabicCategory.txt
+// From http://www.unicode.org/Public/9.0.0/ucd/IndicSyllabicCategory.txt
 bool isPureKiller(uint32_t c) {
     return (c == 0x0E3A || c == 0x0E4E || c == 0x0F84 || c == 0x103A || c == 0x1714 || c == 0x1734
             || c == 0x17D1 || c == 0x1BAA || c == 0x1BF2 || c == 0x1BF3 || c == 0xA806
             || c == 0xA953 || c == 0xABED || c == 0x11134 || c == 0x112EA || c == 0x1172B);
 }
 
-bool GraphemeBreak::isGraphemeBreak(const uint16_t* buf, size_t start, size_t count,
-        size_t offset) {
+bool GraphemeBreak::isGraphemeBreak(const float* advances, const uint16_t* buf, size_t start,
+        size_t count, const size_t offset) {
     // This implementation closely follows Unicode Standard Annex #29 on
     // Unicode Text Segmentation (http://www.unicode.org/reports/tr29/),
     // implementing a tailored version of extended grapheme clusters.
@@ -84,8 +74,9 @@
     uint32_t c1 = 0;
     uint32_t c2 = 0;
     size_t offset_back = offset;
+    size_t offset_forward = offset;
     U16_PREV(buf, start, offset_back, c1);
-    U16_NEXT(buf, offset, start + count, c2);
+    U16_NEXT(buf, offset_forward, start + count, c2);
     int32_t p1 = tailoredGraphemeClusterBreak(c1);
     int32_t p2 = tailoredGraphemeClusterBreak(c2);
     // Rule GB3, CR x LF
@@ -112,73 +103,107 @@
     if ((p1 == U_GCB_LVT || p1 == U_GCB_T) && p2 == U_GCB_T) {
         return false;
     }
-    // Rule GB8a that looks at even-off cases.
-    //
-    // sot   (RI RI)*  RI x RI
-    // [^RI] (RI RI)*  RI x RI
-    //                 RI ÷ RI
-    if (p1 == U_GCB_REGIONAL_INDICATOR && p2 == U_GCB_REGIONAL_INDICATOR) {
-        // Look at up to 1000 code units.
-        start = std::max((ssize_t)start, (ssize_t)offset_back - 1000);
-        while (offset_back > start) {
-            U16_PREV(buf, start, offset_back, c1);
-            if (tailoredGraphemeClusterBreak(c1) != U_GCB_REGIONAL_INDICATOR) {
-                offset_back += U16_LENGTH(c1);
-                break;
-            }
-        }
+    // Rule GB9, x (Extend | ZWJ); Rule GB9a, x SpacingMark; Rule GB9b, Prepend x
+    if (p2 == U_GCB_EXTEND || p2 == U_GCB_ZWJ || p2 == U_GCB_SPACING_MARK || p1 == U_GCB_PREPEND) {
+        return false;
+    }
 
-        // Note that the offset has moved forwared 2 code units by U16_NEXT.
-        // The number 4 comes from the number of code units in a whole flag.
-        return (offset - 2 - offset_back) % 4 == 0;
+    // This is used to decide font-dependent grapheme clusters. If we don't have the advance
+    // information, we become conservative in grapheme breaking and assume that it has no advance.
+    const bool c2_has_advance = (advances != nullptr && advances[offset - start] != 0.0);
+
+    // All the following rules are font-dependent, in the way that if we know c2 has an advance,
+    // we definitely know that it cannot form a grapheme with the character(s) before it. So we
+    // make the decision in favor a grapheme break early.
+    if (c2_has_advance) {
+        return true;
     }
-    // Rule GB9, x Extend; Rule GB9a, x SpacingMark; Rule GB9b, Prepend x
-    if (p2 == U_GCB_EXTEND || p2 == U_GCB_SPACING_MARK || p1 == U_GCB_PREPEND) {
-        return false;
-    }
-    // Cluster indic syllables together (tailoring of UAX #29)
-    // Known limitation: this is overly conservative, and assumes that the virama may form a
-    // conjunct with the following letter, which doesn't always happen.
+
+    // Note: For Rule GB10 and GB11 below, we do not use the Unicode line breaking properties for
+    // determining emoji-ness and carry our own data, because our data could be more fresh than what
+    // ICU provides.
     //
-    // There is no easy solution to do this correctly. Even querying the font does not help (with
-    // the current font technoloies), since the font may be creating the conjunct using multiple
-    // glyphs, while the user may be perceiving that sequence of glyphs as one conjunct or one
-    // letter.
-    if (u_getIntPropertyValue(c1, UCHAR_CANONICAL_COMBINING_CLASS) == 9  // virama
-            && !isPureKiller(c1)
-            && u_getIntPropertyValue(c2, UCHAR_GENERAL_CATEGORY) == U_OTHER_LETTER) {
-        return false;
+    // Tailored version of Rule GB10, (E_Base | EBG) Extend* × E_Modifier.
+    // The rule itself says do not break between emoji base and emoji modifiers, skipping all Extend
+    // characters. Variation selectors are considered Extend, so they are handled fine.
+    //
+    // We tailor this by requiring that an actual ligature is formed. If the font doesn't form a
+    // ligature, we allow a break before the modifier.
+    if (isEmojiModifier(c2)) {
+        uint32_t c0 = c1;
+        size_t offset_backback = offset_back;
+        int32_t p0 = p1;
+        if (p0 == U_GCB_EXTEND && offset_backback > start) {
+            // skip over emoji variation selector
+            U16_PREV(buf, start, offset_backback, c0);
+            p0 = tailoredGraphemeClusterBreak(c0);
+        }
+        if (isEmojiBase(c0)) {
+            return false;
+        }
     }
-    // Tailoring: make emoji sequences with ZWJ a single grapheme cluster
-    if (c1 == 0x200D && isEmoji(c2) && offset_back > start) {
+    // Tailored version of Rule GB11, ZWJ × (Glue_After_Zwj | EBG)
+    // We try to make emoji sequences with ZWJ a single grapheme cluster, but only if they actually
+    // merge to one cluster. So we are more relaxed than the UAX #29 rules in accepting any emoji
+    // character after the ZWJ, but are tighter in that we only treat it as one cluster if a
+    // ligature is actually formed and we also require the character before the ZWJ to also be an
+    // emoji.
+    if (p1 == U_GCB_ZWJ && isEmoji(c2) && offset_back > start) {
         // look at character before ZWJ to see that both can participate in an emoji zwj sequence
         uint32_t c0 = 0;
-        U16_PREV(buf, start, offset_back, c0);
-        if (c0 == 0xFE0F && offset_back > start) {
+        size_t offset_backback = offset_back;
+        U16_PREV(buf, start, offset_backback, c0);
+        if (c0 == 0xFE0F && offset_backback > start) {
             // skip over emoji variation selector
-            U16_PREV(buf, start, offset_back, c0);
+            U16_PREV(buf, start, offset_backback, c0);
         }
         if (isEmoji(c0)) {
             return false;
         }
     }
-    // Proposed Rule GB9c from http://www.unicode.org/L2/L2016/16011r3-break-prop-emoji.pdf
-    // E_Base x E_Modifier
-    if (isEmojiModifier(c2)) {
-        if (c1 == 0xFE0F && offset_back > start) {
-            // skip over emoji variation selector
-            U16_PREV(buf, start, offset_back, c1);
-        }
-        if (isEmojiBase(c1)) {
+    // Tailored version of Rule GB12 and Rule GB13 that look at even-odd cases.
+    // sot   (RI RI)*  RI x RI
+    // [^RI] (RI RI)*  RI x RI
+    //
+    // If we have font information, we have already broken the cluster if and only if the second
+    // character had no advance, which means a ligature was formed. If we don't, we look back like
+    // UAX #29 recommends, but only up to 1000 code units.
+    if (p1 == U_GCB_REGIONAL_INDICATOR && p2 == U_GCB_REGIONAL_INDICATOR) {
+        if (advances != nullptr) {
+            // We have advances information. But if we are here, we already know c2 has no advance.
+            // So we should definitely disallow a break.
             return false;
+        } else {
+            // Look at up to 1000 code units.
+            const size_t lookback_barrier = std::max((ssize_t)start, (ssize_t)offset_back - 1000);
+            size_t offset_backback = offset_back;
+            while (offset_backback > lookback_barrier) {
+                uint32_t c0 = 0;
+                U16_PREV(buf, lookback_barrier, offset_backback, c0);
+                if (tailoredGraphemeClusterBreak(c0) != U_GCB_REGIONAL_INDICATOR) {
+                    offset_backback += U16_LENGTH(c0);
+                    break;
+                }
+            }
+            // The number 4 comes from the number of code units in a whole flag.
+            return (offset - offset_backback) % 4 == 0;
         }
     }
-    // Rule GB10, Any ÷ Any
+    // Cluster Indic syllables together (tailoring of UAX #29).
+    // Immediately after each virama (that is not just a pure killer) followed by a letter, we
+    // disallow grapheme breaks (if we are here, we don't know about advances, or we already know
+    // that c2 has no advance).
+    if (u_getIntPropertyValue(c1, UCHAR_CANONICAL_COMBINING_CLASS) == 9  // virama
+            && !isPureKiller(c1)
+            && u_getIntPropertyValue(c2, UCHAR_GENERAL_CATEGORY) == U_OTHER_LETTER) {
+        return false;
+    }
+    // Rule GB999, Any ÷ Any
     return true;
 }
 
-size_t GraphemeBreak::getTextRunCursor(const uint16_t* buf, size_t start, size_t count,
-        size_t offset, MoveOpt opt) {
+size_t GraphemeBreak::getTextRunCursor(const float* advances, const uint16_t* buf, size_t start,
+        size_t count, size_t offset, MoveOpt opt) {
     switch (opt) {
     case AFTER:
         if (offset < start + count) {
@@ -186,7 +211,7 @@
         }
         // fall through
     case AT_OR_AFTER:
-        while (!isGraphemeBreak(buf, start, count, offset)) {
+        while (!isGraphemeBreak(advances, buf, start, count, offset)) {
             offset++;
         }
         break;
@@ -196,12 +221,12 @@
         }
         // fall through
     case AT_OR_BEFORE:
-        while (!isGraphemeBreak(buf, start, count, offset)) {
+        while (!isGraphemeBreak(advances, buf, start, count, offset)) {
             offset--;
         }
         break;
     case AT:
-        if (!isGraphemeBreak(buf, start, count, offset)) {
+        if (!isGraphemeBreak(advances, buf, start, count, offset)) {
             offset = (size_t)-1;
         }
         break;
@@ -209,4 +234,4 @@
     return offset;
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/HbFontCache.cpp b/libs/minikin/HbFontCache.cpp
index 3be942d..af3d783 100644
--- a/libs/minikin/HbFontCache.cpp
+++ b/libs/minikin/HbFontCache.cpp
@@ -18,33 +18,18 @@
 
 #include "HbFontCache.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
+#include <utils/LruCache.h>
+
 #include <hb.h>
 #include <hb-ot.h>
-#include <utils/LruCache.h>
 
 #include <minikin/MinikinFont.h>
 #include "MinikinInternal.h"
 
-namespace android {
+namespace minikin {
 
-static hb_blob_t* referenceTable(hb_face_t* /* face */, hb_tag_t tag, void* userData) {
-    MinikinFont* font = reinterpret_cast<MinikinFont*>(userData);
-    MinikinDestroyFunc destroy = 0;
-    size_t size = 0;
-    const void* buffer = font->GetTable(tag, &size, &destroy);
-    if (buffer == nullptr) {
-        return nullptr;
-    }
-#ifdef VERBOSE_DEBUG
-    ALOGD("referenceTable %c%c%c%c length=%zd",
-        (tag >>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, size);
-#endif
-    return hb_blob_create(reinterpret_cast<const char*>(buffer), size,
-            HB_MEMORY_MODE_READONLY, const_cast<void*>(buffer), destroy);
-}
-
-class HbFontCache : private OnEntryRemoved<int32_t, hb_font_t*> {
+class HbFontCache : private android::OnEntryRemoved<int32_t, hb_font_t*> {
 public:
     HbFontCache() : mCache(kMaxEntries) {
         mCache.setOnEntryRemovedListener(this);
@@ -74,7 +59,7 @@
 private:
     static const size_t kMaxEntries = 100;
 
-    LruCache<int32_t, hb_font_t*> mCache;
+    android::LruCache<int32_t, hb_font_t*> mCache;
 };
 
 HbFontCache* getFontCacheLocked() {
@@ -99,7 +84,7 @@
 
 // Returns a new reference to a hb_font_t object, caller is
 // responsible for calling hb_font_destroy() on it.
-hb_font_t* getHbFontLocked(MinikinFont* minikinFont) {
+hb_font_t* getHbFontLocked(const MinikinFont* minikinFont) {
     assertMinikinLocked();
     // TODO: get rid of nullFaceFont
     static hb_font_t* nullFaceFont = nullptr;
@@ -119,15 +104,12 @@
 
     hb_face_t* face;
     const void* buf = minikinFont->GetFontData();
-    if (buf == nullptr) {
-        face = hb_face_create_for_tables(referenceTable, minikinFont, nullptr);
-    } else {
-        size_t size = minikinFont->GetFontSize();
-        hb_blob_t* blob = hb_blob_create(reinterpret_cast<const char*>(buf), size,
-            HB_MEMORY_MODE_READONLY, nullptr, nullptr);
-        face = hb_face_create(blob, minikinFont->GetFontIndex());
-        hb_blob_destroy(blob);
-    }
+    size_t size = minikinFont->GetFontSize();
+    hb_blob_t* blob = hb_blob_create(reinterpret_cast<const char*>(buf), size,
+        HB_MEMORY_MODE_READONLY, nullptr, nullptr);
+    face = hb_face_create(blob, minikinFont->GetFontIndex());
+    hb_blob_destroy(blob);
+
     hb_font_t* parent_font = hb_font_create(face);
     hb_ot_font_set_funcs(parent_font);
 
@@ -135,10 +117,15 @@
     hb_font_set_scale(parent_font, upem, upem);
 
     font = hb_font_create_sub_font(parent_font);
+    std::vector<hb_variation_t> variations;
+    for (const FontVariation& variation : minikinFont->GetAxes()) {
+        variations.push_back({variation.axisTag, variation.value});
+    }
+    hb_font_set_variations(font, variations.data(), variations.size());
     hb_font_destroy(parent_font);
     hb_face_destroy(face);
     fontCache->put(fontId, font);
     return hb_font_reference(font);
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/HbFontCache.h b/libs/minikin/HbFontCache.h
index 449b354..59969e2 100644
--- a/libs/minikin/HbFontCache.h
+++ b/libs/minikin/HbFontCache.h
@@ -19,12 +19,12 @@
 
 struct hb_font_t;
 
-namespace android {
+namespace minikin {
 class MinikinFont;
 
 void purgeHbFontCacheLocked();
 void purgeHbFontLocked(const MinikinFont* minikinFont);
-hb_font_t* getHbFontLocked(MinikinFont* minikinFont);
+hb_font_t* getHbFontLocked(const MinikinFont* minikinFont);
 
-}  // namespace android
+}  // namespace minikin
 #endif  // MINIKIN_HBFONT_CACHE_H
diff --git a/libs/minikin/Hyphenator.cpp b/libs/minikin/Hyphenator.cpp
index c5eb60b..0605b27 100644
--- a/libs/minikin/Hyphenator.cpp
+++ b/libs/minikin/Hyphenator.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <string>
 #include <unicode/uchar.h>
+#include <unicode/uscript.h>
 
 // HACK: for reading pattern file
 #include <fcntl.h>
@@ -30,9 +31,12 @@
 
 using std::vector;
 
-namespace android {
+namespace minikin {
 
+static const uint16_t CHAR_HYPHEN_MINUS = 0x002D;
 static const uint16_t CHAR_SOFT_HYPHEN = 0x00AD;
+static const uint16_t CHAR_MIDDLE_DOT = 0x00B7;
+static const uint16_t CHAR_HYPHEN = 0x2010;
 
 // The following are structs that correspond to tables inside the hyb file format
 
@@ -104,40 +108,224 @@
     }
 };
 
-Hyphenator* Hyphenator::loadBinary(const uint8_t* patternData) {
+Hyphenator* Hyphenator::loadBinary(const uint8_t* patternData, size_t minPrefix, size_t minSuffix) {
     Hyphenator* result = new Hyphenator;
     result->patternData = patternData;
+    result->minPrefix = minPrefix;
+    result->minSuffix = minSuffix;
     return result;
 }
 
-void Hyphenator::hyphenate(vector<uint8_t>* result, const uint16_t* word, size_t len) {
+void Hyphenator::hyphenate(vector<HyphenationType>* result, const uint16_t* word, size_t len,
+        const icu::Locale& locale) {
     result->clear();
     result->resize(len);
     const size_t paddedLen = len + 2;  // start and stop code each count for 1
     if (patternData != nullptr &&
-            (int)len >= MIN_PREFIX + MIN_SUFFIX && paddedLen <= MAX_HYPHENATED_SIZE) {
+            len >= minPrefix + minSuffix && paddedLen <= MAX_HYPHENATED_SIZE) {
         uint16_t alpha_codes[MAX_HYPHENATED_SIZE];
-        if (alphabetLookup(alpha_codes, word, len)) {
-            hyphenateFromCodes(result->data(), alpha_codes, paddedLen);
+        const HyphenationType hyphenValue = alphabetLookup(alpha_codes, word, len);
+        if (hyphenValue != HyphenationType::DONT_BREAK) {
+            hyphenateFromCodes(result->data(), alpha_codes, paddedLen, hyphenValue);
             return;
         }
         // TODO: try NFC normalization
         // TODO: handle non-BMP Unicode (requires remapping of offsets)
     }
-    hyphenateSoft(result->data(), word, len);
+    // Note that we will always get here if the word contains a hyphen or a soft hyphen, because the
+    // alphabet is not expected to contain a hyphen or a soft hyphen character, so alphabetLookup
+    // would return DONT_BREAK.
+    hyphenateWithNoPatterns(result->data(), word, len, locale);
 }
 
-// If any soft hyphen is present in the word, use soft hyphens to decide hyphenation,
-// as recommended in UAX #14 (Use of Soft Hyphen)
-void Hyphenator::hyphenateSoft(uint8_t* result, const uint16_t* word, size_t len) {
-    result[0] = 0;
+// This function determines whether a character is like U+2010 HYPHEN in
+// line breaking and usage: a character immediately after which line breaks
+// are allowed, but words containing it should not be automatically
+// hyphenated using patterns. This is a curated set, created by manually
+// inspecting all the characters that have the Unicode line breaking
+// property of BA or HY and seeing which ones are hyphens.
+bool Hyphenator::isLineBreakingHyphen(uint32_t c) {
+    return (c == 0x002D || // HYPHEN-MINUS
+            c == 0x058A || // ARMENIAN HYPHEN
+            c == 0x05BE || // HEBREW PUNCTUATION MAQAF
+            c == 0x1400 || // CANADIAN SYLLABICS HYPHEN
+            c == 0x2010 || // HYPHEN
+            c == 0x2013 || // EN DASH
+            c == 0x2027 || // HYPHENATION POINT
+            c == 0x2E17 || // DOUBLE OBLIQUE HYPHEN
+            c == 0x2E40);  // DOUBLE HYPHEN
+}
+
+const static uint32_t HYPHEN_STR[] = {0x2010, 0};
+const static uint32_t ARMENIAN_HYPHEN_STR[] = {0x058A, 0};
+const static uint32_t MAQAF_STR[] = {0x05BE, 0};
+const static uint32_t UCAS_HYPHEN_STR[] = {0x1400, 0};
+const static uint32_t ZWJ_STR[] = {0x200D, 0};
+const static uint32_t ZWJ_AND_HYPHEN_STR[] = {0x200D, 0x2010, 0};
+
+const uint32_t* HyphenEdit::getHyphenString(uint32_t hyph) {
+    switch (hyph) {
+        case INSERT_HYPHEN_AT_END:
+        case REPLACE_WITH_HYPHEN_AT_END:
+        case INSERT_HYPHEN_AT_START:
+            return HYPHEN_STR;
+        case INSERT_ARMENIAN_HYPHEN_AT_END:
+            return ARMENIAN_HYPHEN_STR;
+        case INSERT_MAQAF_AT_END:
+            return MAQAF_STR;
+        case INSERT_UCAS_HYPHEN_AT_END:
+            return UCAS_HYPHEN_STR;
+        case INSERT_ZWJ_AND_HYPHEN_AT_END:
+            return ZWJ_AND_HYPHEN_STR;
+        case INSERT_ZWJ_AT_START:
+            return ZWJ_STR;
+        default:
+            return nullptr;
+    }
+}
+
+uint32_t HyphenEdit::editForThisLine(HyphenationType type) {
+    switch (type) {
+        case HyphenationType::DONT_BREAK:
+            return NO_EDIT;
+        case HyphenationType::BREAK_AND_INSERT_HYPHEN:
+            return INSERT_HYPHEN_AT_END;
+        case HyphenationType::BREAK_AND_INSERT_ARMENIAN_HYPHEN:
+            return INSERT_ARMENIAN_HYPHEN_AT_END;
+        case HyphenationType::BREAK_AND_INSERT_MAQAF:
+            return INSERT_MAQAF_AT_END;
+        case HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN:
+            return INSERT_UCAS_HYPHEN_AT_END;
+        case HyphenationType::BREAK_AND_REPLACE_WITH_HYPHEN:
+            return REPLACE_WITH_HYPHEN_AT_END;
+        case HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ:
+            return INSERT_ZWJ_AND_HYPHEN_AT_END;
+        default:
+            return BREAK_AT_END;
+    }
+}
+
+uint32_t HyphenEdit::editForNextLine(HyphenationType type) {
+    switch (type) {
+        case HyphenationType::DONT_BREAK:
+            return NO_EDIT;
+        case HyphenationType::BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE:
+            return INSERT_HYPHEN_AT_START;
+        case HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ:
+            return INSERT_ZWJ_AT_START;
+        default:
+            return BREAK_AT_START;
+    }
+}
+
+static UScriptCode getScript(uint32_t codePoint) {
+    UErrorCode errorCode = U_ZERO_ERROR;
+    const UScriptCode script = uscript_getScript(static_cast<UChar32>(codePoint), &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        return script;
+    } else {
+        return USCRIPT_INVALID_CODE;
+    }
+}
+
+static HyphenationType hyphenationTypeBasedOnScript(uint32_t codePoint) {
+    // Note: It's not clear what the best hyphen for Hebrew is. While maqaf is the "correct" hyphen
+    // for Hebrew, modern practice may have shifted towards Western hyphens. We use normal hyphens
+    // for now to be safe.  BREAK_AND_INSERT_MAQAF is already implemented, so if we want to switch
+    // to maqaf for Hebrew, we can simply add a condition here.
+    const UScriptCode script = getScript(codePoint);
+    if (script == USCRIPT_KANNADA
+            || script == USCRIPT_MALAYALAM
+            || script == USCRIPT_TAMIL
+            || script == USCRIPT_TELUGU) {
+        // Grantha is not included, since we don't support non-BMP hyphenation yet.
+        return HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
+    } else if (script == USCRIPT_ARMENIAN) {
+        return HyphenationType::BREAK_AND_INSERT_ARMENIAN_HYPHEN;
+    } else if (script == USCRIPT_CANADIAN_ABORIGINAL) {
+        return HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN;
+    } else {
+        return HyphenationType::BREAK_AND_INSERT_HYPHEN;
+    }
+}
+
+static inline int32_t getJoiningType(UChar32 codepoint) {
+    return u_getIntPropertyValue(codepoint, UCHAR_JOINING_TYPE);
+}
+
+// Assumption for caller: location must be >= 2 and word[location] == CHAR_SOFT_HYPHEN.
+// This function decides if the letters before and after the hyphen should appear as joining.
+static inline HyphenationType getHyphTypeForArabic(const uint16_t* word, size_t len,
+        size_t location) {
+    ssize_t i = location;
+    int32_t type = U_JT_NON_JOINING;
+    while (static_cast<size_t>(i) < len && (type = getJoiningType(word[i])) == U_JT_TRANSPARENT) {
+        i++;
+    }
+    if (type == U_JT_DUAL_JOINING || type == U_JT_RIGHT_JOINING || type == U_JT_JOIN_CAUSING) {
+        // The next character is of the type that may join the last character. See if the last
+        // character is also of the right type.
+        i = location - 2; // Skip the soft hyphen
+        type = U_JT_NON_JOINING;
+        while (i >= 0 && (type = getJoiningType(word[i])) == U_JT_TRANSPARENT) {
+            i--;
+        }
+        if (type == U_JT_DUAL_JOINING || type == U_JT_LEFT_JOINING || type == U_JT_JOIN_CAUSING) {
+            return HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ;
+        }
+    }
+    return HyphenationType::BREAK_AND_INSERT_HYPHEN;
+}
+
+// Use various recommendations of UAX #14 Unicode Line Breaking Algorithm for hyphenating words
+// that didn't match patterns, especially words that contain hyphens or soft hyphens (See sections
+// 5.3, Use of Hyphen, and 5.4, Use of Soft Hyphen).
+void Hyphenator::hyphenateWithNoPatterns(HyphenationType* result, const uint16_t* word, size_t len,
+        const icu::Locale& locale) {
+    result[0] = HyphenationType::DONT_BREAK;
     for (size_t i = 1; i < len; i++) {
-        result[i] = word[i - 1] == CHAR_SOFT_HYPHEN;
+        const uint16_t prevChar = word[i - 1];
+        if (i > 1 && isLineBreakingHyphen(prevChar)) {
+            // Break after hyphens, but only if they don't start the word.
+
+            if ((prevChar == CHAR_HYPHEN_MINUS || prevChar == CHAR_HYPHEN)
+                    && strcmp(locale.getLanguage(), "pl") == 0
+                    && getScript(word[i]) == USCRIPT_LATIN ) {
+                // In Polish, hyphens get repeated at the next line. To be safe,
+                // we will do this only if the next character is Latin.
+                result[i] = HyphenationType::BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE;
+            } else {
+                result[i] = HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
+            }
+        } else if (i > 1 && prevChar == CHAR_SOFT_HYPHEN) {
+            // Break after soft hyphens, but only if they don't start the word (a soft hyphen
+            // starting the word doesn't give any useful break opportunities). The type of the break
+            // is based on the script of the character we break on.
+            if (getScript(word[i]) == USCRIPT_ARABIC) {
+                // For Arabic, we need to look and see if the characters around the soft hyphen
+                // actually join. If they don't, we'll just insert a normal hyphen.
+                result[i] = getHyphTypeForArabic(word, len, i);
+            } else {
+                result[i] = hyphenationTypeBasedOnScript(word[i]);
+            }
+        } else if (prevChar == CHAR_MIDDLE_DOT
+                && minPrefix < i && i <= len - minSuffix
+                && ((word[i - 2] == 'l' && word[i] == 'l')
+                        || (word[i - 2] == 'L' && word[i] == 'L'))
+                && strcmp(locale.getLanguage(), "ca") == 0) {
+            // In Catalan, "l·l" should break as "l-" on the first line
+            // and "l" on the next line.
+            result[i] = HyphenationType::BREAK_AND_REPLACE_WITH_HYPHEN;
+        } else {
+            result[i] = HyphenationType::DONT_BREAK;
+        }
      }
 }
 
-bool Hyphenator::alphabetLookup(uint16_t* alpha_codes, const uint16_t* word, size_t len) {
+HyphenationType Hyphenator::alphabetLookup(uint16_t* alpha_codes, const uint16_t* word,
+        size_t len) {
     const Header* header = getHeader();
+    HyphenationType result = HyphenationType::BREAK_AND_INSERT_HYPHEN;
     // TODO: check header magic
     uint32_t alphabetVersion = header->alphabetVersion();
     if (alphabetVersion == 0) {
@@ -148,16 +336,19 @@
         for (size_t i = 0; i < len; i++) {
             uint16_t c = word[i];
             if (c < min_codepoint || c >= max_codepoint) {
-                return false;
+                return HyphenationType::DONT_BREAK;
             }
             uint8_t code = alphabet->data[c - min_codepoint];
             if (code == 0) {
-                return false;
+                return HyphenationType::DONT_BREAK;
+            }
+            if (result == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
+                result = hyphenationTypeBasedOnScript(c);
             }
             alpha_codes[i + 1] = code;
         }
         alpha_codes[len + 1] = 0;  // word termination
-        return true;
+        return result;
     } else if (alphabetVersion == 1) {
         const AlphabetTable1* alphabet = header->alphabetTable1();
         size_t n_entries = alphabet->n_entries;
@@ -168,18 +359,21 @@
             uint16_t c = word[i];
             auto p = std::lower_bound(begin, end, c << 11);
             if (p == end) {
-                return false;
+                return HyphenationType::DONT_BREAK;
             }
             uint32_t entry = *p;
             if (AlphabetTable1::codepoint(entry) != c) {
-                return false;
+                return HyphenationType::DONT_BREAK;
+            }
+            if (result == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
+                result = hyphenationTypeBasedOnScript(c);
             }
             alpha_codes[i + 1] = AlphabetTable1::value(entry);
         }
         alpha_codes[len + 1] = 0;
-        return true;
+        return result;
     }
-    return false;
+    return HyphenationType::DONT_BREAK;
 }
 
 /**
@@ -187,7 +381,12 @@
  * has been done by now, and all characters have been found in the alphabet.
  * Note: len here is the padded length including 0 codes at start and end.
  **/
-void Hyphenator::hyphenateFromCodes(uint8_t* result, const uint16_t* codes, size_t len) {
+void Hyphenator::hyphenateFromCodes(HyphenationType* result, const uint16_t* codes, size_t len,
+        HyphenationType hyphenValue) {
+    static_assert(sizeof(HyphenationType) == sizeof(uint8_t), "HyphnationType must be uint8_t.");
+    // Reuse the result array as a buffer for calculating intermediate hyphenation numbers.
+    uint8_t* buffer = reinterpret_cast<uint8_t*>(result);
+
     const Header* header = getHeader();
     const Trie* trie = header->trieTable();
     const Pattern* pattern = header->patternTable();
@@ -195,7 +394,7 @@
     uint32_t link_shift = trie->link_shift;
     uint32_t link_mask = trie->link_mask;
     uint32_t pattern_shift = trie->pattern_shift;
-    size_t maxOffset = len - MIN_SUFFIX - 1;
+    size_t maxOffset = len - minSuffix - 1;
     for (size_t i = 0; i < len - 1; i++) {
         uint32_t node = 0;  // index into Trie table
         for (size_t j = i; j < len; j++) {
@@ -209,27 +408,28 @@
             uint32_t pat_ix = trie->data[node] >> pattern_shift;
             // pat_ix contains a 3-tuple of length, shift (number of trailing zeros), and an offset
             // into the buf pool. This is the pattern for the substring (i..j) we just matched,
-            // which we combine (via point-wise max) into the result vector.
+            // which we combine (via point-wise max) into the buffer vector.
             if (pat_ix != 0) {
                 uint32_t pat_entry = pattern->data[pat_ix];
                 int pat_len = Pattern::len(pat_entry);
                 int pat_shift = Pattern::shift(pat_entry);
                 const uint8_t* pat_buf = pattern->buf(pat_entry);
                 int offset = j + 1 - (pat_len + pat_shift);
-                // offset is the index within result that lines up with the start of pat_buf
-                int start = std::max(MIN_PREFIX - offset, 0);
+                // offset is the index within buffer that lines up with the start of pat_buf
+                int start = std::max((int)minPrefix - offset, 0);
                 int end = std::min(pat_len, (int)maxOffset - offset);
                 for (int k = start; k < end; k++) {
-                    result[offset + k] = std::max(result[offset + k], pat_buf[k]);
+                    buffer[offset + k] = std::max(buffer[offset + k], pat_buf[k]);
                 }
             }
         }
     }
     // Since the above calculation does not modify values outside
-    // [MIN_PREFIX, len - MIN_SUFFIX], they are left as 0.
-    for (size_t i = MIN_PREFIX; i < maxOffset; i++) {
-        result[i] &= 1;
+    // [minPrefix, len - minSuffix], they are left as 0 = DONT_BREAK.
+    for (size_t i = minPrefix; i < maxOffset; i++) {
+        // Hyphenation opportunities happen when the hyphenation numbers are odd.
+        result[i] = (buffer[i] & 1u) ? hyphenValue : HyphenationType::DONT_BREAK;
     }
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp
index 9c1d6a8..568e038 100644
--- a/libs/minikin/Layout.cpp
+++ b/libs/minikin/Layout.cpp
@@ -15,31 +15,31 @@
  */
 
 #define LOG_TAG "Minikin"
-#include <cutils/log.h>
-
-#include <math.h>
 
 #include <algorithm>
 #include <fstream>
 #include <iostream>  // for debugging
+#include <math.h>
 #include <string>
+#include <unicode/ubidi.h>
+#include <unicode/utf16.h>
 #include <vector>
 
+#include <log/log.h>
 #include <utils/JenkinsHash.h>
 #include <utils/LruCache.h>
 #include <utils/Singleton.h>
 #include <utils/String16.h>
 
-#include <unicode/ubidi.h>
 #include <hb-icu.h>
 #include <hb-ot.h>
 
 #include "FontLanguage.h"
 #include "FontLanguageListCache.h"
-#include "LayoutUtils.h"
 #include "HbFontCache.h"
+#include "LayoutUtils.h"
 #include "MinikinInternal.h"
-#include <minikin/MinikinFontFreeType.h>
+#include <minikin/Emoji.h>
 #include <minikin/Layout.h>
 
 using std::string;
@@ -47,48 +47,6 @@
 
 namespace minikin {
 
-Bitmap::Bitmap(int width, int height) : width(width), height(height) {
-    buf = new uint8_t[width * height]();
-}
-
-Bitmap::~Bitmap() {
-    delete[] buf;
-}
-
-void Bitmap::writePnm(std::ofstream &o) const {
-    o << "P5" << std::endl;
-    o << width << " " << height << std::endl;
-    o << "255" << std::endl;
-    o.write((const char *)buf, width * height);
-    o.close();
-}
-
-void Bitmap::drawGlyph(const android::GlyphBitmap& bitmap, int x, int y) {
-    int bmw = bitmap.width;
-    int bmh = bitmap.height;
-    x += bitmap.left;
-    y -= bitmap.top;
-    int x0 = std::max(0, x);
-    int x1 = std::min(width, x + bmw);
-    int y0 = std::max(0, y);
-    int y1 = std::min(height, y + bmh);
-    const unsigned char* src = bitmap.buffer + (y0 - y) * bmw + (x0 - x);
-    uint8_t* dst = buf + y0 * width;
-    for (int yy = y0; yy < y1; yy++) {
-        for (int xx = x0; xx < x1; xx++) {
-            int pixel = (int)dst[xx] + (int)src[xx - x];
-            pixel = pixel > 0xff ? 0xff : pixel;
-            dst[xx] = pixel;
-        }
-        src += bmw;
-        dst += width;
-    }
-}
-
-} // namespace minikin
-
-namespace android {
-
 const int kDirection_Mask = 0x1;
 
 struct LayoutContext {
@@ -109,8 +67,9 @@
 
 class LayoutCacheKey {
 public:
-    LayoutCacheKey(const FontCollection* collection, const MinikinPaint& paint, FontStyle style,
-            const uint16_t* chars, size_t start, size_t count, size_t nchars, bool dir)
+    LayoutCacheKey(const std::shared_ptr<FontCollection>& collection, const MinikinPaint& paint,
+            FontStyle style, const uint16_t* chars, size_t start, size_t count, size_t nchars,
+            bool dir)
             : mChars(chars), mNchars(nchars),
             mStart(start), mCount(count), mId(collection->getId()), mStyle(style),
             mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX),
@@ -120,7 +79,7 @@
     }
     bool operator==(const LayoutCacheKey &other) const;
 
-    hash_t hash() const {
+    android::hash_t hash() const {
         return mHash;
     }
 
@@ -134,11 +93,11 @@
         mChars = NULL;
     }
 
-    void doLayout(Layout* layout, LayoutContext* ctx, const FontCollection* collection) const {
-        layout->setFontCollection(collection);
+    void doLayout(Layout* layout, LayoutContext* ctx,
+            const std::shared_ptr<FontCollection>& collection) const {
         layout->mAdvances.resize(mCount, 0);
         ctx->clearHbFonts();
-        layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx);
+        layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx, collection);
     }
 
 private:
@@ -157,12 +116,12 @@
     bool mIsRtl;
     // Note: any fields added to MinikinPaint must also be reflected here.
     // TODO: language matching (possibly integrate into style)
-    hash_t mHash;
+    android::hash_t mHash;
 
-    hash_t computeHash() const;
+    android::hash_t computeHash() const;
 };
 
-class LayoutCache : private OnEntryRemoved<LayoutCacheKey, Layout*> {
+class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, Layout*> {
 public:
     LayoutCache() : mCache(kMaxEntries) {
         mCache.setOnEntryRemovedListener(this);
@@ -172,7 +131,8 @@
         mCache.clear();
     }
 
-    Layout* get(LayoutCacheKey& key, LayoutContext* ctx, const FontCollection* collection) {
+    Layout* get(LayoutCacheKey& key, LayoutContext* ctx,
+            const std::shared_ptr<FontCollection>& collection) {
         Layout* layout = mCache.get(key);
         if (layout == NULL) {
             key.copyText();
@@ -190,7 +150,7 @@
         delete value;
     }
 
-    LruCache<LayoutCacheKey, Layout*> mCache;
+    android::LruCache<LayoutCacheKey, Layout*> mCache;
 
     //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
 
@@ -204,7 +164,7 @@
     return 0;
 }
 
-class LayoutEngine : public Singleton<LayoutEngine> {
+class LayoutEngine : public ::android::Singleton<LayoutEngine> {
 public:
     LayoutEngine() {
         unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
@@ -220,8 +180,6 @@
     LayoutCache layoutCache;
 };
 
-ANDROID_SINGLETON_STATIC_INSTANCE(LayoutEngine);
-
 bool LayoutCacheKey::operator==(const LayoutCacheKey& other) const {
     return mId == other.mId
             && mStart == other.mStart
@@ -238,23 +196,23 @@
             && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t));
 }
 
-hash_t LayoutCacheKey::computeHash() const {
-    uint32_t hash = JenkinsHashMix(0, mId);
-    hash = JenkinsHashMix(hash, mStart);
-    hash = JenkinsHashMix(hash, mCount);
-    hash = JenkinsHashMix(hash, hash_type(mStyle));
-    hash = JenkinsHashMix(hash, hash_type(mSize));
-    hash = JenkinsHashMix(hash, hash_type(mScaleX));
-    hash = JenkinsHashMix(hash, hash_type(mSkewX));
-    hash = JenkinsHashMix(hash, hash_type(mLetterSpacing));
-    hash = JenkinsHashMix(hash, hash_type(mPaintFlags));
-    hash = JenkinsHashMix(hash, hash_type(mHyphenEdit.hasHyphen()));
-    hash = JenkinsHashMix(hash, hash_type(mIsRtl));
-    hash = JenkinsHashMixShorts(hash, mChars, mNchars);
-    return JenkinsHashWhiten(hash);
+android::hash_t LayoutCacheKey::computeHash() const {
+    uint32_t hash = android::JenkinsHashMix(0, mId);
+    hash = android::JenkinsHashMix(hash, mStart);
+    hash = android::JenkinsHashMix(hash, mCount);
+    hash = android::JenkinsHashMix(hash, hash_type(mStyle));
+    hash = android::JenkinsHashMix(hash, hash_type(mSize));
+    hash = android::JenkinsHashMix(hash, hash_type(mScaleX));
+    hash = android::JenkinsHashMix(hash, hash_type(mSkewX));
+    hash = android::JenkinsHashMix(hash, hash_type(mLetterSpacing));
+    hash = android::JenkinsHashMix(hash, hash_type(mPaintFlags));
+    hash = android::JenkinsHashMix(hash, hash_type(mHyphenEdit.getHyphen()));
+    hash = android::JenkinsHashMix(hash, hash_type(mIsRtl));
+    hash = android::JenkinsHashMixShorts(hash, mChars, mNchars);
+    return android::JenkinsHashWhiten(hash);
 }
 
-hash_t hash_type(const LayoutCacheKey& key) {
+android::hash_t hash_type(const LayoutCacheKey& key) {
     return key.hash();
 }
 
@@ -269,10 +227,6 @@
     }
 }
 
-// Deprecated. Remove when callers are removed.
-void Layout::init() {
-}
-
 void Layout::reset() {
     mGlyphs.clear();
     mFaces.clear();
@@ -281,15 +235,10 @@
     mAdvance = 0;
 }
 
-void Layout::setFontCollection(const FontCollection* collection) {
-    mCollection = collection;
-}
-
 static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* /* hbFont */, void* fontData,
         hb_codepoint_t glyph, void* /* userData */) {
     MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
-    MinikinFont* font = paint->font;
-    float advance = font->GetHorizontalAdvance(glyph, *paint);
+    float advance = paint->font->GetHorizontalAdvance(glyph, *paint);
     return 256 * advance + 0.5;
 }
 
@@ -301,16 +250,36 @@
     return true;
 }
 
-hb_font_funcs_t* getHbFontFuncs() {
-    static hb_font_funcs_t* hbFontFuncs = 0;
+hb_font_funcs_t* getHbFontFuncs(bool forColorBitmapFont) {
+    assertMinikinLocked();
 
-    if (hbFontFuncs == 0) {
-        hbFontFuncs = hb_font_funcs_create();
-        hb_font_funcs_set_glyph_h_advance_func(hbFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
-        hb_font_funcs_set_glyph_h_origin_func(hbFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
-        hb_font_funcs_make_immutable(hbFontFuncs);
+    static hb_font_funcs_t* hbFuncs = nullptr;
+    static hb_font_funcs_t* hbFuncsForColorBitmap = nullptr;
+
+    hb_font_funcs_t** funcs = forColorBitmapFont ? &hbFuncs : &hbFuncsForColorBitmap;
+    if (*funcs == nullptr) {
+        *funcs = hb_font_funcs_create();
+        if (forColorBitmapFont) {
+            // Don't override the h_advance function since we use HarfBuzz's implementation for
+            // emoji for performance reasons.
+            // Note that it is technically possible for a TrueType font to have outline and embedded
+            // bitmap at the same time. We ignore modified advances of hinted outline glyphs in that
+            // case.
+        } else {
+            // Override the h_advance function since we can't use HarfBuzz's implemenation. It may
+            // return the wrong value if the font uses hinting aggressively.
+            hb_font_funcs_set_glyph_h_advance_func(*funcs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
+        }
+        hb_font_funcs_set_glyph_h_origin_func(*funcs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
+        hb_font_funcs_make_immutable(*funcs);
     }
-    return hbFontFuncs;
+    return *funcs;
+}
+
+static bool isColorBitmapFont(hb_font_t* font) {
+    hb_face_t* face = hb_font_get_face(font);
+    HbBlob cbdt(hb_face_reference_table(face, HB_TAG('C', 'B', 'D', 'T')));
+    return cbdt.size() > 0;
 }
 
 static float HBFixedToFloat(hb_position_t v)
@@ -330,7 +299,7 @@
     }
 }
 
-int Layout::findFace(FakedFont face, LayoutContext* ctx) {
+int Layout::findFace(const FakedFont& face, LayoutContext* ctx) {
     unsigned int ix;
     for (ix = 0; ix < mFaces.size(); ix++) {
         if (mFaces[ix].font == face.font) {
@@ -342,7 +311,7 @@
     // corresponding hb_font object.
     if (ctx != NULL) {
         hb_font_t* font = getHbFontLocked(face.font);
-        hb_font_set_funcs(font, getHbFontFuncs(), &ctx->paint, 0);
+        hb_font_set_funcs(font, getHbFontFuncs(isColorBitmapFont(font)), &ctx->paint, 0);
         ctx->hbFonts.push_back(font);
     }
     return ix;
@@ -496,7 +465,8 @@
     size_t mRunCount;
     bool mIsRtl;
 
-    DISALLOW_COPY_AND_ASSIGN(BidiText);
+    BidiText(const BidiText&) = delete;
+    void operator=(const BidiText&) = delete;
 };
 
 BidiText::Iter::Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount,
@@ -553,6 +523,13 @@
         return;
     }
     UErrorCode status = U_ZERO_ERROR;
+    // Set callbacks to override bidi classes of new emoji
+    ubidi_setClassCallback(mBidi, emojiBidiOverride, nullptr, nullptr, nullptr, &status);
+    if (!U_SUCCESS(status)) {
+        ALOGE("error setting bidi callback function, status = %d", status);
+        return;
+    }
+
     UBiDiLevel bidiReq = bidiFlags;
     if (bidiFlags == kBidi_Default_LTR) {
         bidiReq = UBIDI_DEFAULT_LTR;
@@ -577,8 +554,9 @@
 }
 
 void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        int bidiFlags, const FontStyle &style, const MinikinPaint &paint) {
-    AutoMutex _l(gMinikinLock);
+        int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
+        const std::shared_ptr<FontCollection>& collection) {
+    android::AutoMutex _l(gMinikinLock);
 
     LayoutContext ctx;
     ctx.style = style;
@@ -589,15 +567,15 @@
 
     for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
         doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, runInfo.mIsRtl, &ctx,
-                start, mCollection, this, NULL);
+                start, collection, this, NULL);
     }
     ctx.clearHbFonts();
 }
 
 float Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
         int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
-        const FontCollection* collection, float* advances) {
-    AutoMutex _l(gMinikinLock);
+        const std::shared_ptr<FontCollection>& collection, float* advances) {
+    android::AutoMutex _l(gMinikinLock);
 
     LayoutContext ctx;
     ctx.style = style;
@@ -615,9 +593,9 @@
 }
 
 float Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        bool isRtl, LayoutContext* ctx, size_t dstStart, const FontCollection* collection,
-        Layout* layout, float* advances) {
-    HyphenEdit hyphen = ctx->paint.hyphenEdit;
+        bool isRtl, LayoutContext* ctx, size_t dstStart,
+        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
+    const uint32_t originalHyphen = ctx->paint.hyphenEdit.getHyphen();
     float advance = 0;
     if (!isRtl) {
         // left to right
@@ -626,8 +604,15 @@
         size_t wordend;
         for (size_t iter = start; iter < start + count; iter = wordend) {
             wordend = getNextWordBreakForCache(buf, iter, bufSize);
-            // Only apply hyphen to the last word in the string.
-            ctx->paint.hyphenEdit = wordend >= start + count ? hyphen : HyphenEdit();
+            // Only apply hyphen to the first or last word in the string.
+            uint32_t hyphen = originalHyphen;
+            if (iter != start) { // Not the first word
+                hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
+            }
+            if (wordend < start + count) { // Not the last word
+                hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
+            }
+            ctx->paint.hyphenEdit = hyphen;
             size_t wordcount = std::min(start + count, wordend) - iter;
             advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount,
                     wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout,
@@ -641,8 +626,15 @@
         size_t wordend = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize);
         for (size_t iter = end; iter > start; iter = wordstart) {
             wordstart = getPrevWordBreakForCache(buf, iter, bufSize);
-            // Only apply hyphen to the last (leftmost) word in the string.
-            ctx->paint.hyphenEdit = iter == end ? hyphen : HyphenEdit();
+            // Only apply hyphen to the first (rightmost) or last (leftmost) word in the string.
+            uint32_t hyphen = originalHyphen;
+            if (wordstart > start) { // Not the first word
+                hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
+            }
+            if (iter != end) { // Not the last word
+                hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
+            }
+            ctx->paint.hyphenEdit = hyphen;
             size_t bufStart = std::max(start, wordstart);
             advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart,
                     wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout,
@@ -654,31 +646,42 @@
 }
 
 float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        bool isRtl, LayoutContext* ctx, size_t bufStart, const FontCollection* collection,
-        Layout* layout, float* advances) {
+        bool isRtl, LayoutContext* ctx, size_t bufStart,
+        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
     LayoutCache& cache = LayoutEngine::getInstance().layoutCache;
     LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl);
-    bool skipCache = ctx->paint.skipCache();
-    if (skipCache) {
+
+    float wordSpacing = count == 1 && isWordSpace(buf[start]) ? ctx->paint.wordSpacing : 0;
+
+    float advance;
+    if (ctx->paint.skipCache()) {
         Layout layoutForWord;
         key.doLayout(&layoutForWord, ctx, collection);
         if (layout) {
-            layout->appendLayout(&layoutForWord, bufStart);
+            layout->appendLayout(&layoutForWord, bufStart, wordSpacing);
         }
         if (advances) {
             layoutForWord.getAdvances(advances);
         }
-        return layoutForWord.getAdvance();
+        advance = layoutForWord.getAdvance();
     } else {
         Layout* layoutForWord = cache.get(key, ctx, collection);
         if (layout) {
-            layout->appendLayout(layoutForWord, bufStart);
+            layout->appendLayout(layoutForWord, bufStart, wordSpacing);
         }
         if (advances) {
             layoutForWord->getAdvances(advances);
         }
-        return layoutForWord->getAdvance();
+        advance = layoutForWord->getAdvance();
     }
+
+    if (wordSpacing != 0) {
+        advance += wordSpacing;
+        if (advances) {
+            advances[0] += wordSpacing;
+        }
+    }
+    return advance;
 }
 
 static void addFeatures(const string &str, vector<hb_feature_t>* features) {
@@ -702,14 +705,149 @@
     }
 }
 
+static const hb_codepoint_t CHAR_HYPHEN = 0x2010; /* HYPHEN */
+
+static inline hb_codepoint_t determineHyphenChar(hb_codepoint_t preferredHyphen, hb_font_t* font) {
+    hb_codepoint_t glyph;
+    if (preferredHyphen == 0x058A /* ARMENIAN_HYPHEN */
+                || preferredHyphen == 0x05BE /* HEBREW PUNCTUATION MAQAF */
+                || preferredHyphen == 0x1400 /* CANADIAN SYLLABIC HYPHEN */) {
+        if (hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
+            return preferredHyphen;
+        } else {
+            // The original hyphen requested was not supported. Let's try and see if the
+            // Unicode hyphen is supported.
+            preferredHyphen = CHAR_HYPHEN;
+        }
+    }
+    if (preferredHyphen == CHAR_HYPHEN) { /* HYPHEN */
+        // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for the preferred hyphen.
+        // Note that we intentionally don't do anything special if the font doesn't have a
+        // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something missing.
+        if (!hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
+            return 0x002D; // HYPHEN-MINUS
+        }
+    }
+    return preferredHyphen;
+}
+
+static inline void addHyphenToHbBuffer(hb_buffer_t* buffer, hb_font_t* font, uint32_t hyphen,
+        uint32_t cluster) {
+    const uint32_t* hyphenStr = HyphenEdit::getHyphenString(hyphen);
+    while (*hyphenStr != 0) {
+        hb_codepoint_t hyphenChar = determineHyphenChar(*hyphenStr, font);
+        hb_buffer_add(buffer, hyphenChar, cluster);
+        hyphenStr++;
+    }
+}
+
+// Returns the cluster value assigned to the first codepoint added to the buffer, which can be used
+// to translate cluster values returned by HarfBuzz to input indices.
+static inline uint32_t addToHbBuffer(hb_buffer_t* buffer,
+        const uint16_t* buf, size_t start, size_t count, size_t bufSize,
+        ssize_t scriptRunStart, ssize_t scriptRunEnd,
+        HyphenEdit hyphenEdit, hb_font_t* hbFont) {
+
+    // Only hyphenate the very first script run for starting hyphens.
+    const uint32_t startHyphen = (scriptRunStart == 0)
+            ? hyphenEdit.getStart()
+            : HyphenEdit::NO_EDIT;
+    // Only hyphenate the very last script run for ending hyphens.
+    const uint32_t endHyphen = (static_cast<size_t>(scriptRunEnd) == count)
+            ? hyphenEdit.getEnd()
+            : HyphenEdit::NO_EDIT;
+
+    // In the following code, we drop the pre-context and/or post-context if there is a
+    // hyphen edit at that end. This is not absolutely necessary, since HarfBuzz uses
+    // contexts only for joining scripts at the moment, e.g. to determine if the first or
+    // last letter of a text range to shape should take a joining form based on an
+    // adjacent letter or joiner (that comes from the context).
+    //
+    // TODO: Revisit this for:
+    // 1. Desperate breaks for joining scripts like Arabic (where it may be better to keep
+    //    the context);
+    // 2. Special features like start-of-word font features (not implemented in HarfBuzz
+    //    yet).
+
+    // We don't have any start-of-line replacement edit yet, so we don't need to check for
+    // those.
+    if (HyphenEdit::isInsertion(startHyphen)) {
+        // A cluster value of zero guarantees that the inserted hyphen will be in the same
+        // cluster with the next codepoint, since there is no pre-context.
+        addHyphenToHbBuffer(buffer, hbFont, startHyphen, 0 /* cluster value */);
+    }
+
+    const uint16_t* hbText;
+    int hbTextLength;
+    unsigned int hbItemOffset;
+    unsigned int hbItemLength = scriptRunEnd - scriptRunStart; // This is >= 1.
+
+    const bool hasEndInsertion = HyphenEdit::isInsertion(endHyphen);
+    const bool hasEndReplacement = HyphenEdit::isReplacement(endHyphen);
+    if (hasEndReplacement) {
+        // Skip the last code unit while copying the buffer for HarfBuzz if it's a replacement. We
+        // don't need to worry about non-BMP characters yet since replacements are only done for
+        // code units at the moment.
+        hbItemLength -= 1;
+    }
+
+    if (startHyphen == HyphenEdit::NO_EDIT) {
+        // No edit at the beginning. Use the whole pre-context.
+        hbText = buf;
+        hbItemOffset = start + scriptRunStart;
+    } else {
+        // There's an edit at the beginning. Drop the pre-context and start the buffer at where we
+        // want to start shaping.
+        hbText = buf + start + scriptRunStart;
+        hbItemOffset = 0;
+    }
+
+    if (endHyphen == HyphenEdit::NO_EDIT) {
+        // No edit at the end, use the whole post-context.
+        hbTextLength = (buf + bufSize) - hbText;
+    } else {
+        // There is an edit at the end. Drop the post-context.
+        hbTextLength = hbItemOffset + hbItemLength;
+    }
+
+    hb_buffer_add_utf16(buffer, hbText, hbTextLength, hbItemOffset, hbItemLength);
+
+    unsigned int numCodepoints;
+    hb_glyph_info_t* cpInfo = hb_buffer_get_glyph_infos(buffer, &numCodepoints);
+
+    // Add the hyphen at the end, if there's any.
+    if (hasEndInsertion || hasEndReplacement) {
+        // When a hyphen is inserted, by assigning the added hyphen and the last
+        // codepoint added to the HarfBuzz buffer to the same cluster, we can make sure
+        // that they always remain in the same cluster, even if the last codepoint gets
+        // merged into another cluster (for example when it's a combining mark).
+        //
+        // When a replacement happens instead, we want it to get the cluster value of
+        // the character it's replacing, which is one "codepoint length" larger than
+        // the last cluster. But since the character replaced is always just one
+        // code unit, we can just add 1.
+        uint32_t hyphenCluster;
+        if (numCodepoints == 0) {
+            // Nothing was added to the HarfBuzz buffer. This can only happen if
+            // we have a replacement that is replacing a one-code unit script run.
+            hyphenCluster = 0;
+        } else {
+            hyphenCluster = cpInfo[numCodepoints - 1].cluster + (uint32_t) hasEndReplacement;
+        }
+        addHyphenToHbBuffer(buffer, hbFont, endHyphen, hyphenCluster);
+        // Since we have just added to the buffer, cpInfo no longer necessarily points to
+        // the right place. Refresh it.
+        cpInfo = hb_buffer_get_glyph_infos(buffer, nullptr /* we don't need the size */);
+    }
+    return cpInfo[0].cluster;
+}
+
+
 void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-        bool isRtl, LayoutContext* ctx) {
+        bool isRtl, LayoutContext* ctx, const std::shared_ptr<FontCollection>& collection) {
     hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
     vector<FontCollection::Run> items;
-    mCollection->itemize(buf + start, count, ctx->style, &items);
-    if (isRtl) {
-        std::reverse(items.begin(), items.end());
-    }
+    collection->itemize(buf + start, count, ctx->style, &items);
 
     vector<hb_feature_t> features;
     // Disable default-on non-required ligature features if letter-spacing
@@ -731,7 +869,9 @@
 
     float x = mAdvance;
     float y = 0;
-    for (size_t run_ix = 0; run_ix < items.size(); run_ix++) {
+    for (int run_ix = isRtl ? items.size() - 1 : 0;
+            isRtl ? run_ix >= 0 : run_ix < static_cast<int>(items.size());
+            isRtl ? --run_ix : ++run_ix) {
         FontCollection::Run &run = items[run_ix];
         if (run.fakedFont.font == NULL) {
             ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
@@ -748,13 +888,26 @@
         hb_font_set_ppem(hbFont, size * scaleX, size);
         hb_font_set_scale(hbFont, 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.
-        ssize_t srunend;
-        for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) {
-            srunend = srunstart;
-            hb_script_t script = getScriptRun(buf + start, run.end, &srunend);
+
+        // Note: scriptRunStart and scriptRunEnd, as well as run.start and run.end, run between 0
+        // and count.
+        ssize_t scriptRunEnd;
+        for (ssize_t scriptRunStart = run.start;
+                scriptRunStart < run.end;
+                scriptRunStart = scriptRunEnd) {
+            scriptRunEnd = scriptRunStart;
+            hb_script_t script = getScriptRun(buf + start, run.end, &scriptRunEnd /* iterator */);
+            // After the last line, scriptRunEnd is guaranteed to have increased, since the only
+            // time getScriptRun does not increase its iterator is when it has already reached the
+            // end of the buffer. But that can't happen, since if we have already reached the end
+            // of the buffer, we should have had (scriptRunEnd == run.end), which means
+            // (scriptRunStart == run.end) which is impossible due to the exit condition of the for
+            // loop. So we can be sure that scriptRunEnd > scriptRunStart.
 
             double letterSpace = 0.0;
             double letterSpaceHalfLeft = 0.0;
@@ -784,33 +937,31 @@
                         break;
                     }
                 }
-                hb_buffer_set_language(buffer,
-                        hb_language_from_string(hbLanguage->getString().c_str(), -1));
+                hb_buffer_set_language(buffer, hbLanguage->getHbLanguage());
             }
-            hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart);
-            if (ctx->paint.hyphenEdit.hasHyphen() && srunend > srunstart) {
-                // TODO: check whether this is really the desired semantics. It could have the
-                // effect of assigning the hyphen width to a nonspacing mark
-                unsigned int lastCluster = start + srunend - 1;
 
-                hb_codepoint_t hyphenChar = 0x2010; // HYPHEN
-                hb_codepoint_t glyph;
-                // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for HYPHEN. Note
-                // that we intentionally don't do anything special if the font doesn't have a
-                // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something
-                // missing.
-                if (!hb_font_get_glyph(hbFont, hyphenChar, 0, &glyph)) {
-                    hyphenChar = 0x002D; // HYPHEN-MINUS
-                }
-                hb_buffer_add(buffer, hyphenChar, lastCluster);
-            }
+            const uint32_t clusterStart = addToHbBuffer(
+                buffer,
+                buf, start, count, bufSize,
+                scriptRunStart, scriptRunEnd,
+                ctx->paint.hyphenEdit, hbFont);
+
             hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
             unsigned int numGlyphs;
             hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
             hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
+
+            // At this point in the code, the cluster values in the info buffer correspond to the
+            // input characters with some shift. The cluster value clusterStart corresponds to the
+            // first character passed to HarfBuzz, which is at buf[start + scriptRunStart] whose
+            // advance needs to be saved into mAdvances[scriptRunStart]. So cluster values need to
+            // be reduced by (clusterStart - scriptRunStart) to get converted to indices of
+            // mAdvances.
+            const ssize_t clusterOffset = clusterStart - scriptRunStart;
+
             if (numGlyphs)
             {
-                mAdvances[info[0].cluster - start] += letterSpaceHalfLeft;
+                mAdvances[info[0].cluster - clusterOffset] += letterSpaceHalfLeft;
                 x += letterSpaceHalfLeft;
             }
             for (unsigned int i = 0; i < numGlyphs; i++) {
@@ -823,8 +974,8 @@
                         positions[i].x_offset, positions[i].y_offset);
 #endif
                 if (i > 0 && info[i - 1].cluster != info[i].cluster) {
-                    mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight;
-                    mAdvances[info[i].cluster - start] += letterSpaceHalfLeft;
+                    mAdvances[info[i - 1].cluster - clusterOffset] += letterSpaceHalfRight;
+                    mAdvances[info[i].cluster - clusterOffset] += letterSpaceHalfLeft;
                     x += letterSpace;
                 }
 
@@ -839,20 +990,32 @@
                     xAdvance = roundf(xAdvance);
                 }
                 MinikinRect glyphBounds;
-                ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
+                hb_glyph_extents_t extents = {};
+                if (is_color_bitmap_font && hb_font_get_glyph_extents(hbFont, 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 {
+                    ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
+                }
                 glyphBounds.offset(x + xoff, y + yoff);
                 mBounds.join(glyphBounds);
-                if (info[i].cluster - start < count) {
-                    mAdvances[info[i].cluster - start] += xAdvance;
+                if (static_cast<size_t>(info[i].cluster - clusterOffset) < count) {
+                    mAdvances[info[i].cluster - clusterOffset] += xAdvance;
                 } else {
                     ALOGE("cluster %zu (start %zu) out of bounds of count %zu",
-                        info[i].cluster - start, start, count);
+                        info[i].cluster - clusterOffset, start, count);
                 }
                 x += xAdvance;
             }
             if (numGlyphs)
             {
-                mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight;
+                mAdvances[info[numGlyphs - 1].cluster - clusterOffset] += letterSpaceHalfRight;
                 x += letterSpaceHalfRight;
             }
         }
@@ -860,7 +1023,7 @@
     mAdvance = x;
 }
 
-void Layout::appendLayout(Layout* src, size_t start) {
+void Layout::appendLayout(Layout* src, size_t start, float extraAdvance) {
     int fontMapStack[16];
     int* fontMap;
     if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) {
@@ -884,50 +1047,24 @@
     }
     for (size_t i = 0; i < src->mAdvances.size(); i++) {
         mAdvances[i + start] = src->mAdvances[i];
+        if (i == 0)
+          mAdvances[i + start] += extraAdvance;
     }
     MinikinRect srcBounds(src->mBounds);
     srcBounds.offset(x0, 0);
     mBounds.join(srcBounds);
-    mAdvance += src->mAdvance;
+    mAdvance += src->mAdvance + extraAdvance;
 
     if (fontMap != fontMapStack) {
         delete[] fontMap;
     }
 }
 
-void Layout::draw(minikin::Bitmap* surface, int x0, int y0, float size) const {
-    /*
-    TODO: redo as MinikinPaint settings
-    if (mProps.hasTag(minikinHinting)) {
-        int hintflags = mProps.value(minikinHinting).getIntValue();
-        if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING;
-        if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT;
-    }
-    */
-    for (size_t i = 0; i < mGlyphs.size(); i++) {
-        const LayoutGlyph& glyph = mGlyphs[i];
-        MinikinFont* mf = mFaces[glyph.font_ix].font;
-        MinikinFontFreeType* face = static_cast<MinikinFontFreeType*>(mf);
-        GlyphBitmap glyphBitmap;
-        MinikinPaint paint;
-        paint.size = size;
-        bool ok = face->Render(glyph.glyph_id, paint, &glyphBitmap);
-#ifdef VERBOSE_DEBUG
-        ALOGD("glyphBitmap.width=%d, glyphBitmap.height=%d (%d, %d) x=%f, y=%f, ok=%d",
-            glyphBitmap.width, glyphBitmap.height, glyphBitmap.left, glyphBitmap.top, glyph.x, glyph.y, ok);
-#endif
-        if (ok) {
-            surface->drawGlyph(glyphBitmap,
-                x0 + int(floor(glyph.x + 0.5)), y0 + int(floor(glyph.y + 0.5)));
-        }
-    }
-}
-
 size_t Layout::nGlyphs() const {
     return mGlyphs.size();
 }
 
-MinikinFont* Layout::getFont(int i) const {
+const MinikinFont* Layout::getFont(int i) const {
     const LayoutGlyph& glyph = mGlyphs[i];
     return mFaces[glyph.font_ix].font;
 }
@@ -960,15 +1097,22 @@
     memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
 }
 
-void Layout::getBounds(MinikinRect* bounds) {
+void Layout::getBounds(MinikinRect* bounds) const {
     bounds->set(mBounds);
 }
 
 void Layout::purgeCaches() {
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
     layoutCache.clear();
     purgeHbFontCacheLocked();
 }
 
+}  // namespace minikin
+
+// Unable to define the static data member outside of android.
+// TODO: introduce our own Singleton to drop android namespace.
+namespace android {
+ANDROID_SINGLETON_STATIC_INSTANCE(minikin::LayoutEngine);
 }  // namespace android
+
diff --git a/libs/minikin/LayoutUtils.cpp b/libs/minikin/LayoutUtils.cpp
index 4182682..a3238d4 100644
--- a/libs/minikin/LayoutUtils.cpp
+++ b/libs/minikin/LayoutUtils.cpp
@@ -18,13 +18,24 @@
 
 #include "LayoutUtils.h"
 
+namespace minikin {
+
+const uint16_t CHAR_NBSP = 0x00A0;
+
+/*
+ * Determine whether the code unit is a word space for the purposes of justification.
+ */
+bool isWordSpace(uint16_t code_unit) {
+    return code_unit == ' ' || code_unit == CHAR_NBSP;
+}
+
 /**
  * For the purpose of layout, a word break is a boundary with no
  * kerning or complex script processing. This is necessarily a
  * heuristic, but should be accurate most of the time.
  */
-static bool isWordBreakAfter(int c) {
-    if (c == ' ' || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
+static bool isWordBreakAfter(uint16_t c) {
+    if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
         // spaces
         return true;
     }
@@ -32,7 +43,7 @@
     return false;
 }
 
-static bool isWordBreakBefore(int c) {
+static bool isWordBreakBefore(uint16_t c) {
     // CJK ideographs (and yijing hexagram symbols)
     return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
 }
@@ -74,3 +85,5 @@
     }
     return len;
 }
+
+}  // namespace minikin
diff --git a/libs/minikin/LayoutUtils.h b/libs/minikin/LayoutUtils.h
index 83ddd0a..b89004c 100644
--- a/libs/minikin/LayoutUtils.h
+++ b/libs/minikin/LayoutUtils.h
@@ -19,6 +19,13 @@
 
 #include <stdint.h>
 
+namespace minikin {
+
+/*
+ * Determine whether the code unit is a word space for the purposes of justification.
+ */
+bool isWordSpace(uint16_t code_unit);
+
 /**
  * Return offset of previous word break. It is either < offset or == 0.
  *
@@ -39,4 +46,5 @@
 size_t getNextWordBreakForCache(
         const uint16_t* chars, size_t offset, size_t len);
 
+}  // namespace minikin
 #endif  // MINIKIN_LAYOUT_UTILS_H
diff --git a/libs/minikin/LineBreaker.cpp b/libs/minikin/LineBreaker.cpp
index 2a71f04..e75c7bf 100644
--- a/libs/minikin/LineBreaker.cpp
+++ b/libs/minikin/LineBreaker.cpp
@@ -16,17 +16,19 @@
 
 #define VERBOSE_DEBUG 0
 
+#define LOG_TAG "Minikin"
+
 #include <limits>
 
-#define LOG_TAG "Minikin"
-#include <cutils/log.h>
+#include <log/log.h>
 
+#include "LayoutUtils.h"
 #include <minikin/Layout.h>
 #include <minikin/LineBreaker.h>
 
 using std::vector;
 
-namespace android {
+namespace minikin {
 
 const int CHAR_TAB = 0x0009;
 
@@ -43,6 +45,9 @@
 // probably not the most appropriate method.
 const float LINE_PENALTY_MULTIPLIER = 2.0f;
 
+// Penalty assigned to shrinking the whitepsace.
+const float SHRINK_PENALTY_MULTIPLIER = 4.0f;
+
 // Very long words trigger O(n^2) behavior in hyphenation, so we disable hyphenation for
 // unreasonably long words. This is somewhat of a heuristic because extremely long words
 // are possible in some languages. This does mean that very long real words can get
@@ -53,9 +58,12 @@
 // to avoid allocation.
 const size_t MAX_TEXT_BUF_RETAIN = 32678;
 
+// Maximum amount that spaces can shrink, in justified text.
+const float SHRINKABILITY = 1.0 / 3.0;
+
 void LineBreaker::setLocale(const icu::Locale& locale, Hyphenator* hyphenator) {
     mWordBreaker.setLocale(locale);
-
+    mLocale = locale;
     mHyphenator = hyphenator;
 }
 
@@ -65,7 +73,7 @@
     // handle initial break here because addStyleRun may never be called
     mWordBreaker.next();
     mCandidates.clear();
-    Candidate cand = {0, 0, 0.0, 0.0, 0.0, 0.0, 0, 0};
+    Candidate cand = {0, 0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, HyphenationType::DONT_BREAK};
     mCandidates.push_back(cand);
 
     // reset greedy breaker state
@@ -76,7 +84,9 @@
     mBestBreak = 0;
     mBestScore = SCORE_INFTY;
     mPreBreak = 0;
+    mLastHyphenation = HyphenEdit::NO_EDIT;
     mFirstTabIndex = INT_MAX;
+    mSpaceCount = 0;
 }
 
 void LineBreaker::setLineWidths(float firstWidth, int firstWidthLineCount, float restWidth) {
@@ -97,30 +107,12 @@
             c == 0x205F || c == 0x3000;
 }
 
-// This function determines whether a character is like U+2010 HYPHEN in
-// line breaking and usage: a character immediately after which line breaks
-// are allowed, but words containing it should not be automatically
-// hyphenated. This is a curated set, created by manually inspecting all
-// the characters that have the Unicode line breaking property of BA or HY
-// and seeing which ones are hyphens.
-static bool isLineBreakingHyphen(uint16_t c) {
-    return (c == 0x002D || // HYPHEN-MINUS
-            c == 0x058A || // ARMENIAN HYPHEN
-            c == 0x05BE || // HEBREW PUNCTUATION MAQAF
-            c == 0x1400 || // CANADIAN SYLLABICS HYPHEN
-            c == 0x2010 || // HYPHEN
-            c == 0x2013 || // EN DASH
-            c == 0x2027 || // HYPHENATION POINT
-            c == 0x2E17 || // DOUBLE OBLIQUE HYPHEN
-            c == 0x2E40);  // DOUBLE HYPHEN
-}
-
 // Ordinarily, this method measures the text in the range given. However, when paint
 // is nullptr, it assumes the widths have already been calculated and stored in the
 // width buffer.
 // This method finds the candidate word breaks (using the ICU break iterator) and sends them
 // to addCandidate.
-float LineBreaker::addStyleRun(MinikinPaint* paint, const FontCollection* typeface,
+float LineBreaker::addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
         FontStyle style, size_t start, size_t end, bool isRtl) {
     float width = 0.0f;
     int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
@@ -136,7 +128,14 @@
             hyphenPenalty *= 4.0; // TODO: Replace with a better value after some testing
         }
 
-        mLinePenalty = std::max(mLinePenalty, hyphenPenalty * LINE_PENALTY_MULTIPLIER);
+        if (mJustified) {
+            // Make hyphenation more aggressive for fully justified text (so that "normal" in
+            // justified mode is the same as "full" in ragged-right).
+            hyphenPenalty *= 0.25;
+        } else {
+            // Line penalty is zero for justified text.
+            mLinePenalty = std::max(mLinePenalty, hyphenPenalty * LINE_PENALTY_MULTIPLIER);
+        }
     }
 
     size_t current = (size_t)mWordBreaker.current();
@@ -144,7 +143,7 @@
     size_t lastBreak = start;
     ParaWidth lastBreakWidth = mWidth;
     ParaWidth postBreak = mWidth;
-    bool temporarilySkipHyphenation = false;
+    size_t postSpaceCount = mSpaceCount;
     for (size_t i = start; i < end; i++) {
         uint16_t c = mTextBuf[i];
         if (c == CHAR_TAB) {
@@ -155,28 +154,31 @@
             // fall back to greedy; other modes don't know how to deal with tabs
             mStrategy = kBreakStrategy_Greedy;
         } else {
+            if (isWordSpace(c)) mSpaceCount += 1;
             mWidth += mCharWidths[i];
             if (!isLineEndSpace(c)) {
                 postBreak = mWidth;
+                postSpaceCount = mSpaceCount;
                 afterWord = i + 1;
             }
         }
         if (i + 1 == current) {
-            // TODO: Add a new type of HyphenEdit for breaks whose hyphen already exists, so
-            // we can pass the whole word down to Hyphenator like the soft hyphen case.
-            bool wordEndsInHyphen = isLineBreakingHyphen(c);
             size_t wordStart = mWordBreaker.wordStart();
             size_t wordEnd = mWordBreaker.wordEnd();
             if (paint != nullptr && mHyphenator != nullptr &&
                     mHyphenationFrequency != kHyphenationFrequency_None &&
-                    !wordEndsInHyphen && !temporarilySkipHyphenation &&
                     wordStart >= start && wordEnd > wordStart &&
                     wordEnd - wordStart <= LONGEST_HYPHENATED_WORD) {
-                mHyphenator->hyphenate(&mHyphBuf, &mTextBuf[wordStart], wordEnd - wordStart);
+                mHyphenator->hyphenate(&mHyphBuf,
+                        &mTextBuf[wordStart],
+                        wordEnd - wordStart,
+                        mLocale);
 #if VERBOSE_DEBUG
                 std::string hyphenatedString;
                 for (size_t j = wordStart; j < wordEnd; j++) {
-                    if (mHyphBuf[j - wordStart]) hyphenatedString.push_back('-');
+                    if (mHyphBuf[j - wordStart] == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
+                        hyphenatedString.push_back('-');
+                    }
                     // Note: only works with ASCII, should do UTF-8 conversion here
                     hyphenatedString.push_back(buffer()[j]);
                 }
@@ -185,31 +187,33 @@
 
                 // measure hyphenated substrings
                 for (size_t j = wordStart; j < wordEnd; j++) {
-                    uint8_t hyph = mHyphBuf[j - wordStart];
-                    if (hyph) {
-                        paint->hyphenEdit = hyph;
-
+                    HyphenationType hyph = mHyphBuf[j - wordStart];
+                    if (hyph != HyphenationType::DONT_BREAK) {
+                        paint->hyphenEdit = HyphenEdit::editForThisLine(hyph);
                         const float firstPartWidth = Layout::measureText(mTextBuf.data(),
                                 lastBreak, j - lastBreak, mTextBuf.size(), bidiFlags, style,
                                 *paint, typeface, nullptr);
                         ParaWidth hyphPostBreak = lastBreakWidth + firstPartWidth;
-                        paint->hyphenEdit = 0;
 
-                        const float secondPartWith = Layout::measureText(mTextBuf.data(), j,
+                        paint->hyphenEdit = HyphenEdit::editForNextLine(hyph);
+                        const float secondPartWidth = Layout::measureText(mTextBuf.data(), j,
                                 afterWord - j, mTextBuf.size(), bidiFlags, style, *paint,
                                 typeface, nullptr);
-                        ParaWidth hyphPreBreak = postBreak - secondPartWith;
-                        addWordBreak(j, hyphPreBreak, hyphPostBreak, hyphenPenalty, hyph);
+                        ParaWidth hyphPreBreak = postBreak - secondPartWidth;
+
+                        addWordBreak(j, hyphPreBreak, hyphPostBreak, postSpaceCount, postSpaceCount,
+                                hyphenPenalty, hyph);
+
+                        paint->hyphenEdit = HyphenEdit::NO_EDIT;
                     }
                 }
             }
-            // Skip hyphenating the next word if and only if the present word ends in a hyphen
-            temporarilySkipHyphenation = wordEndsInHyphen;
 
             // Skip break for zero-width characters inside replacement span
             if (paint != nullptr || current == end || mCharWidths[current] > 0) {
                 float penalty = hyphenPenalty * mWordBreaker.breakBadness();
-                addWordBreak(current, mWidth, postBreak, penalty, 0);
+                addWordBreak(current, mWidth, postBreak, mSpaceCount, postSpaceCount, penalty,
+                        HyphenationType::DONT_BREAK);
             }
             lastBreak = current;
             lastBreakWidth = mWidth;
@@ -223,7 +227,7 @@
 // add a word break (possibly for a hyphenated fragment), and add desperate breaks if
 // needed (ie when word exceeds current line width)
 void LineBreaker::addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak,
-        float penalty, uint8_t hyph) {
+        size_t preSpaceCount, size_t postSpaceCount, float penalty, HyphenationType hyph) {
     Candidate cand;
     ParaWidth width = mCandidates.back().preBreak;
     if (postBreak - width > currentLineWidth()) {
@@ -238,8 +242,11 @@
                 cand.offset = i;
                 cand.preBreak = width;
                 cand.postBreak = width;
+                // postSpaceCount doesn't include trailing spaces
+                cand.preSpaceCount = postSpaceCount;
+                cand.postSpaceCount = postSpaceCount;
                 cand.penalty = SCORE_DESPERATE;
-                cand.hyphenEdit = 0;
+                cand.hyphenType = HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
 #if VERBOSE_DEBUG
                 ALOGD("desperate cand: %zd %g:%g",
                         mCandidates.size(), cand.postBreak, cand.preBreak);
@@ -254,42 +261,79 @@
     cand.preBreak = preBreak;
     cand.postBreak = postBreak;
     cand.penalty = penalty;
-    cand.hyphenEdit = hyph;
+    cand.preSpaceCount = preSpaceCount;
+    cand.postSpaceCount = postSpaceCount;
+    cand.hyphenType = hyph;
 #if VERBOSE_DEBUG
     ALOGD("cand: %zd %g:%g", mCandidates.size(), cand.postBreak, cand.preBreak);
 #endif
     addCandidate(cand);
 }
 
+// Helper method for addCandidate()
+void LineBreaker::pushGreedyBreak() {
+    const Candidate& bestCandidate = mCandidates[mBestBreak];
+    pushBreak(bestCandidate.offset, bestCandidate.postBreak - mPreBreak,
+            mLastHyphenation | HyphenEdit::editForThisLine(bestCandidate.hyphenType));
+    mBestScore = SCORE_INFTY;
+#if VERBOSE_DEBUG
+    ALOGD("break: %d %g", mBreaks.back(), mWidths.back());
+#endif
+    mLastBreak = mBestBreak;
+    mPreBreak = bestCandidate.preBreak;
+    mLastHyphenation = HyphenEdit::editForNextLine(bestCandidate.hyphenType);
+}
+
 // TODO performance: could avoid populating mCandidates if greedy only
 void LineBreaker::addCandidate(Candidate cand) {
-    size_t candIndex = mCandidates.size();
+    const size_t candIndex = mCandidates.size();
     mCandidates.push_back(cand);
+
+    // mLastBreak is the index of the last line break we decided to do in mCandidates,
+    // and mPreBreak is its preBreak value. mBestBreak is the index of the best line breaking candidate
+    // we have found since then, and mBestScore is its penalty.
     if (cand.postBreak - mPreBreak > currentLineWidth()) {
         // This break would create an overfull line, pick the best break and break there (greedy)
         if (mBestBreak == mLastBreak) {
+            // No good break has been found since last break. Break here.
             mBestBreak = candIndex;
         }
-        pushBreak(mCandidates[mBestBreak].offset, mCandidates[mBestBreak].postBreak - mPreBreak,
-                mCandidates[mBestBreak].hyphenEdit);
-        mBestScore = SCORE_INFTY;
-#if VERBOSE_DEBUG
-        ALOGD("break: %d %g", mBreaks.back(), mWidths.back());
-#endif
-        mLastBreak = mBestBreak;
-        mPreBreak = mCandidates[mBestBreak].preBreak;
+        pushGreedyBreak();
     }
+
+    while (mLastBreak != candIndex && cand.postBreak - mPreBreak > currentLineWidth()) {
+        // We should rarely come here. But if we are here, we have broken the line, but the
+        // remaining part still doesn't fit. We now need to break at the second best place after the
+        // last break, but we have not kept that information, so we need to go back and find it.
+        //
+        // In some really rare cases, postBreak - preBreak of a candidate itself may be over the
+        // current line width. We protect ourselves against an infinite loop in that case by
+        // checking that we have not broken the line at this candidate already.
+        for (size_t i = mLastBreak + 1; i < candIndex; i++) {
+            const float penalty = mCandidates[i].penalty;
+            if (penalty <= mBestScore) {
+                mBestBreak = i;
+                mBestScore = penalty;
+            }
+        }
+        if (mBestBreak == mLastBreak) {
+            // We didn't find anything good. Break here.
+            mBestBreak = candIndex;
+        }
+        pushGreedyBreak();
+    }
+
     if (cand.penalty <= mBestScore) {
         mBestBreak = candIndex;
         mBestScore = cand.penalty;
     }
 }
 
-void LineBreaker::pushBreak(int offset, float width, uint8_t hyph) {
+void LineBreaker::pushBreak(int offset, float width, uint8_t hyphenEdit) {
     mBreaks.push_back(offset);
     mWidths.push_back(width);
     int flags = (mFirstTabIndex < mBreaks.back()) << kTab_Shift;
-    flags |= hyph;
+    flags |= hyphenEdit;
     mFlags.push_back(flags);
     mFirstTabIndex = INT_MAX;
 }
@@ -300,6 +344,18 @@
     addStyleRun(nullptr, nullptr, FontStyle(), start, end, false);
 }
 
+// Get the width of a space. May return 0 if there are no spaces.
+// Note: if there are multiple different widths for spaces (for example, because of mixing of
+// fonts), it's only guaranteed to pick one.
+float LineBreaker::getSpaceWidth() const {
+    for (size_t i = 0; i < mTextBuf.size(); i++) {
+        if (isWordSpace(mTextBuf[i])) {
+            return mCharWidths[i];
+        }
+    }
+    return 0.0f;
+}
+
 float LineBreaker::currentLineWidth() const {
     return mLineWidths.getLineWidth(mBreaks.size());
 }
@@ -308,7 +364,8 @@
     // All breaks but the last have been added in addCandidate already.
     size_t nCand = mCandidates.size();
     if (nCand == 1 || mLastBreak != nCand - 1) {
-        pushBreak(mCandidates[nCand - 1].offset, mCandidates[nCand - 1].postBreak - mPreBreak, 0);
+        pushBreak(mCandidates[nCand - 1].offset, mCandidates[nCand - 1].postBreak - mPreBreak,
+                mLastHyphenation);
         // don't need to update mBestScore, because we're done
 #if VERBOSE_DEBUG
         ALOGD("final break: %d %g", mBreaks.back(), mWidths.back());
@@ -328,7 +385,11 @@
         prev = mCandidates[i].prev;
         mBreaks.push_back(mCandidates[i].offset);
         mWidths.push_back(mCandidates[i].postBreak - mCandidates[prev].preBreak);
-        mFlags.push_back(mCandidates[i].hyphenEdit);
+        int flags = HyphenEdit::editForThisLine(mCandidates[i].hyphenType);
+        if (prev > 0) {
+            flags |= HyphenEdit::editForNextLine(mCandidates[prev].hyphenType);
+        }
+        mFlags.push_back(flags);
     }
     std::reverse(mBreaks.begin(), mBreaks.end());
     std::reverse(mWidths.begin(), mWidths.end());
@@ -339,6 +400,10 @@
     size_t active = 0;
     size_t nCand = mCandidates.size();
     float width = mLineWidths.getLineWidth(0);
+    float shortLineFactor = mJustified ? 0.75f : 0.5f;
+    float maxShrink = mJustified ? SHRINKABILITY * getSpaceWidth() : 0.0f;
+
+    // "i" iterates through candidates for the end of the line.
     for (size_t i = 1; i < nCand; i++) {
         bool atEnd = i == nCand - 1;
         float best = SCORE_INFTY;
@@ -352,6 +417,7 @@
         ParaWidth leftEdge = mCandidates[i].postBreak - width;
         float bestHope = 0;
 
+        // "j" iterates through candidates for the beginning of the line.
         for (size_t j = active; j < i; j++) {
             if (!isRectangle) {
                 size_t lineNumber = mCandidates[j].lineNumber;
@@ -376,13 +442,24 @@
             // breaks are considered.
             float widthScore = 0.0f;
             float additionalPenalty = 0.0f;
-            if (delta < 0) {
+            if ((atEnd || !mJustified) && delta < 0) {
                 widthScore = SCORE_OVERFULL;
             } else if (atEnd && mStrategy != kBreakStrategy_Balanced) {
                 // increase penalty for hyphen on last line
                 additionalPenalty = LAST_LINE_PENALTY_MULTIPLIER * mCandidates[j].penalty;
+                // Penalize very short (< 1 - shortLineFactor of total width) lines.
+                float underfill = delta - shortLineFactor * width;
+                widthScore = underfill > 0 ? underfill * underfill : 0;
             } else {
                 widthScore = delta * delta;
+                if (delta < 0) {
+                    if (-delta < maxShrink *
+                            (mCandidates[i].postSpaceCount - mCandidates[j].preSpaceCount)) {
+                        widthScore *= SHRINK_PENALTY_MULTIPLIER;
+                    } else {
+                        widthScore = SCORE_OVERFULL;
+                    }
+                }
             }
 
             if (delta < 0) {
@@ -439,6 +516,7 @@
     mStrategy = kBreakStrategy_Greedy;
     mHyphenationFrequency = kHyphenationFrequency_Normal;
     mLinePenalty = 0.0f;
+    mJustified = false;
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/Measurement.cpp b/libs/minikin/Measurement.cpp
index 1ba6678..f0d15f2 100644
--- a/libs/minikin/Measurement.cpp
+++ b/libs/minikin/Measurement.cpp
@@ -15,15 +15,16 @@
  */
 
 #define LOG_TAG "Minikin"
-#include <cutils/log.h>
 
 #include <cmath>
 #include <unicode/uchar.h>
 
+#include <android/log.h>
+
 #include <minikin/GraphemeBreak.h>
 #include <minikin/Measurement.h>
 
-namespace android {
+namespace minikin {
 
 // These could be considered helper methods of layout, but need only be loosely coupled, so
 // are separate.
@@ -53,7 +54,8 @@
         int numGraphemeClustersAfter = 0;
         for (size_t i = lastCluster; i < nextCluster; i++) {
             bool isAfter = i >= offset;
-            if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) {
+            if (GraphemeBreak::isGraphemeBreak(
+                    advances + (start - layoutStart), buf, start, count, i)) {
                 numGraphemeClusters++;
                 if (isAfter) {
                     numGraphemeClustersAfter++;
@@ -85,7 +87,7 @@
     float x = 0.0f, xLastClusterStart = 0.0f, xSearchStart = 0.0f;
     size_t lastClusterStart = start, searchStart = start;
     for (size_t i = start; i < start + count; i++) {
-        if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) {
+        if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
             searchStart = lastClusterStart;
             xSearchStart = xLastClusterStart;
         }
@@ -102,7 +104,7 @@
     size_t best = searchStart;
     float bestDist = FLT_MAX;
     for (size_t i = searchStart; i <= start + count; i++) {
-        if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) {
+        if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
             // "getRunAdvance(layout, buf, start, count, i) - advance" but more efficient
             float delta = getRunAdvance(advances, buf, start, searchStart, count - searchStart, i)
 
@@ -119,4 +121,4 @@
     return best;
 }
 
-}
+}  // namespace minikin
diff --git a/libs/minikin/MinikinFont.cpp b/libs/minikin/MinikinFont.cpp
index ef42e9b..6bf6a4a 100644
--- a/libs/minikin/MinikinFont.cpp
+++ b/libs/minikin/MinikinFont.cpp
@@ -16,11 +16,13 @@
 
 #include <minikin/MinikinFont.h>
 #include "HbFontCache.h"
+#include "MinikinInternal.h"
 
-namespace android {
+namespace minikin {
 
 MinikinFont::~MinikinFont() {
+    android::AutoMutex _l(gMinikinLock);
     purgeHbFontLocked(this);
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/MinikinFontFreeType.cpp b/libs/minikin/MinikinFontFreeType.cpp
deleted file mode 100644
index 4a1b115..0000000
--- a/libs/minikin/MinikinFontFreeType.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-// Implementation of MinikinFont abstraction specialized for FreeType
-
-#include <stdint.h>
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_TRUETYPE_TABLES_H
-#include FT_ADVANCES_H
-
-#include <minikin/MinikinFontFreeType.h>
-
-namespace android {
-
-int32_t MinikinFontFreeType::sIdCounter = 0;
-
-MinikinFontFreeType::MinikinFontFreeType(FT_Face typeface) : 
-    MinikinFont(sIdCounter++),
-    mTypeface(typeface) {
-}
-
-MinikinFontFreeType::~MinikinFontFreeType() {
-    FT_Done_Face(mTypeface);
-}
-
-float MinikinFontFreeType::GetHorizontalAdvance(uint32_t glyph_id,
-    const MinikinPaint &paint) const {
-    FT_Set_Pixel_Sizes(mTypeface, 0, paint.size);
-    FT_UInt32 flags = FT_LOAD_DEFAULT;  // TODO: respect hinting settings
-    FT_Fixed advance;
-    FT_Get_Advance(mTypeface, glyph_id, flags, &advance);
-    return advance * (1.0 / 65536);
-}
-
-void MinikinFontFreeType::GetBounds(MinikinRect* /* bounds */, uint32_t /* glyph_id*/,
-        const MinikinPaint& /* paint */) const {
-    // TODO: NYI
-}
-
-const void* MinikinFontFreeType::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
-    FT_ULong ftsize = 0;
-    FT_Error error = FT_Load_Sfnt_Table(mTypeface, tag, 0, nullptr, &ftsize);
-    if (error != 0) {
-        return nullptr;
-    }
-    FT_Byte* buf = reinterpret_cast<FT_Byte*>(malloc(ftsize));
-    if (buf == nullptr) {
-        return nullptr;
-    }
-    error = FT_Load_Sfnt_Table(mTypeface, tag, 0, buf, &ftsize);
-    if (error != 0) {
-        free(buf);
-        return nullptr;
-    }
-    *destroy = free;
-    *size = ftsize;
-    return buf;
-}
-
-bool MinikinFontFreeType::Render(uint32_t glyph_id, const MinikinPaint& /* paint */,
-        GlyphBitmap *result) {
-    FT_Error error;
-    FT_Int32 load_flags = FT_LOAD_DEFAULT;  // TODO: respect hinting settings
-    error = FT_Load_Glyph(mTypeface, glyph_id, load_flags);
-    if (error != 0) {
-        return false;
-    }
-    error = FT_Render_Glyph(mTypeface->glyph, FT_RENDER_MODE_NORMAL);
-    if (error != 0) {
-        return false;
-    }
-    FT_Bitmap &bitmap = mTypeface->glyph->bitmap;
-    result->buffer = bitmap.buffer;
-    result->width = bitmap.width;
-    result->height = bitmap.rows;
-    result->left = mTypeface->glyph->bitmap_left;
-    result->top = mTypeface->glyph->bitmap_top;
-    return true;
-}
-
-MinikinFontFreeType* MinikinFontFreeType::GetFreeType() {
-    return this;
-}
-
-}  // namespace android
diff --git a/libs/minikin/MinikinInternal.cpp b/libs/minikin/MinikinInternal.cpp
index 5cb9491..cfa43bc 100644
--- a/libs/minikin/MinikinInternal.cpp
+++ b/libs/minikin/MinikinInternal.cpp
@@ -15,16 +15,16 @@
  */
 
 // Definitions internal to Minikin
+#define LOG_TAG "Minikin"
 
 #include "MinikinInternal.h"
 #include "HbFontCache.h"
-#include "generated/UnicodeData.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
-namespace android {
+namespace minikin {
 
-Mutex gMinikinLock;
+android::Mutex gMinikinLock;
 
 void assertMinikinLocked() {
 #ifdef ENABLE_RACE_DETECTION
@@ -32,59 +32,7 @@
 #endif
 }
 
-bool isEmoji(uint32_t c) {
-    // U+2695 U+2640 U+2642 are not in emoji category in Unicode 9 but they are now emoji category.
-    // TODO: remove once emoji database is updated.
-    if (c == 0x2695 || c == 0x2640 || c == 0x2642) {
-        return true;
-    }
-    const size_t length = sizeof(generated::EMOJI_LIST) / sizeof(generated::EMOJI_LIST[0]);
-    return std::binary_search(generated::EMOJI_LIST, generated::EMOJI_LIST + length, c);
-}
-
-// Based on Modifiers from http://www.unicode.org/L2/L2016/16011-data-file.txt
-bool isEmojiModifier(uint32_t c) {
-    return (0x1F3FB <= c && c <= 0x1F3FF);
-}
-
-// Based on Emoji_Modifier_Base from
-// http://www.unicode.org/Public/emoji/3.0/emoji-data.txt
-bool isEmojiBase(uint32_t c) {
-    if (0x261D <= c && c <= 0x270D) {
-        return (c == 0x261D || c == 0x26F9 || (0x270A <= c && c <= 0x270D));
-    } else if (0x1F385 <= c && c <= 0x1F93E) {
-        return (c == 0x1F385
-                || (0x1F3C3 <= c && c <= 0x1F3C4)
-                || (0x1F3CA <= c && c <= 0x1F3CB)
-                || (0x1F442 <= c && c <= 0x1F443)
-                || (0x1F446 <= c && c <= 0x1F450)
-                || (0x1F466 <= c && c <= 0x1F469)
-                || c == 0x1F46E
-                || (0x1F470 <= c && c <= 0x1F478)
-                || c == 0x1F47C
-                || (0x1F481 <= c && c <= 0x1F483)
-                || (0x1F485 <= c && c <= 0x1F487)
-                || c == 0x1F4AA
-                || c == 0x1F575
-                || c == 0x1F57A
-                || c == 0x1F590
-                || (0x1F595 <= c && c <= 0x1F596)
-                || (0x1F645 <= c && c <= 0x1F647)
-                || (0x1F64B <= c && c <= 0x1F64F)
-                || c == 0x1F6A3
-                || (0x1F6B4 <= c && c <= 0x1F6B6)
-                || c == 0x1F6C0
-                || (0x1F918 <= c && c <= 0x1F91E)
-                || c == 0x1F926
-                || c == 0x1F930
-                || (0x1F933 <= c && c <= 0x1F939)
-                || (0x1F93B <= c && c <= 0x1F93E));
-    } else {
-        return false;
-    }
-}
-
-hb_blob_t* getFontTable(MinikinFont* minikinFont, uint32_t tag) {
+hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag) {
     assertMinikinLocked();
     hb_font_t* font = getHbFontLocked(minikinFont);
     hb_face_t* face = hb_font_get_face(font);
@@ -93,4 +41,26 @@
     return blob;
 }
 
+inline static bool isBMPVariationSelector(uint32_t codePoint) {
+    return VS1 <= codePoint && codePoint <= VS16;
 }
+
+inline static bool isVariationSelectorSupplement(uint32_t codePoint) {
+    return VS17 <= codePoint && codePoint <= VS256;
+}
+
+uint16_t getVsIndex(uint32_t codePoint) {
+    if (isBMPVariationSelector(codePoint)) {
+        return codePoint - VS1;
+    } else if (isVariationSelectorSupplement(codePoint)) {
+        return codePoint - VS17 + 16;
+    } else {
+        return INVALID_VS_INDEX;
+    }
+}
+
+bool isVariationSelector(uint32_t codePoint) {
+    return isBMPVariationSelector(codePoint) || isVariationSelectorSupplement(codePoint);
+}
+
+}  // namespace minikin
diff --git a/libs/minikin/MinikinInternal.h b/libs/minikin/MinikinInternal.h
index 88cc947..a59e55d 100644
--- a/libs/minikin/MinikinInternal.h
+++ b/libs/minikin/MinikinInternal.h
@@ -25,34 +25,44 @@
 
 #include <minikin/MinikinFont.h>
 
-namespace android {
+namespace minikin {
 
 // All external Minikin interfaces are designed to be thread-safe.
 // Presently, that's implemented by through a global lock, and having
 // all external interfaces take that lock.
 
-extern Mutex gMinikinLock;
+extern android::Mutex gMinikinLock;
 
 // Aborts if gMinikinLock is not acquired. Do nothing on the release build.
 void assertMinikinLocked();
 
-// Returns true if c is emoji.
-bool isEmoji(uint32_t c);
+hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag);
 
-// Returns true if c is emoji modifier base.
-bool isEmojiBase(uint32_t c);
+constexpr uint32_t MAX_UNICODE_CODE_POINT = 0x10FFFF;
 
-// Returns true if c is emoji modifier.
-bool isEmojiModifier(uint32_t c);
+constexpr uint32_t VS1 = 0xFE00;
+constexpr uint32_t VS16 = 0xFE0F;
+constexpr uint32_t VS17 = 0xE0100;
+constexpr uint32_t VS256 = 0xE01EF;
 
-hb_blob_t* getFontTable(MinikinFont* minikinFont, uint32_t tag);
+// Returns variation selector index. This is one unit less than the variation selector number. For
+// example, VARIATION SELECTOR-25 maps to 24.
+// [0x00-0x0F] for U+FE00..U+FE0F
+// [0x10-0xFF] for U+E0100..U+E01EF
+// INVALID_VS_INDEX for other input.
+constexpr uint16_t INVALID_VS_INDEX = 0xFFFF;
+uint16_t getVsIndex(uint32_t codePoint);
+
+// Returns true if the code point is a variation selector.
+// Note that this function returns false for Mongolian free variation selectors.
+bool isVariationSelector(uint32_t codePoint);
 
 // An RAII wrapper for hb_blob_t
 class HbBlob {
 public:
     // Takes ownership of hb_blob_t object, caller is no longer
     // responsible for calling hb_blob_destroy().
-    HbBlob(hb_blob_t* blob) : mBlob(blob) {
+    explicit HbBlob(hb_blob_t* blob) : mBlob(blob) {
     }
 
     ~HbBlob() {
@@ -65,15 +75,13 @@
     }
 
     size_t size() const {
-        unsigned int length = 0;
-        hb_blob_get_data(mBlob, &length);
-        return (size_t)length;
+        return (size_t)hb_blob_get_length(mBlob);
     }
 
 private:
     hb_blob_t* mBlob;
 };
 
-}
+}  // namespace minikin
 
 #endif  // MINIKIN_INTERNAL_H
diff --git a/libs/minikin/SparseBitSet.cpp b/libs/minikin/SparseBitSet.cpp
index de07914..9fad6a0 100644
--- a/libs/minikin/SparseBitSet.cpp
+++ b/libs/minikin/SparseBitSet.cpp
@@ -14,21 +14,19 @@
  * limitations under the License.
  */
 
-#include <cutils/log.h>
+#define LOG_TAG "SparseBitSet"
+
 #include <stddef.h>
 #include <string.h>
+
+#include <log/log.h>
+
 #include <minikin/SparseBitSet.h>
 
-namespace android {
+namespace minikin {
 
 const uint32_t SparseBitSet::kNotFound;
 
-void SparseBitSet::clear() {
-    mMaxVal = 0;
-    mIndices.reset();
-    mBitmaps.reset();
-}
-
 uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) {
     bool haveZeroPage = false;
     uint32_t nonzeroPageEnd = 0;
@@ -55,17 +53,16 @@
 
 void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
     if (nRanges == 0) {
-        mMaxVal = 0;
-        mIndices.reset();
-        mBitmaps.reset();
         return;
     }
-    mMaxVal = ranges[nRanges * 2 - 1];
-    size_t indexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage;
-    mIndices.reset(new uint32_t[indexSize]);
+    const uint32_t maxVal = ranges[nRanges * 2 - 1];
+    if (maxVal >= kMaximumCapacity) {
+        return;
+    }
+    mMaxVal = maxVal;
+    mIndices.reset(new uint16_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
     uint32_t nPages = calcNumPages(ranges, nRanges);
-    mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]);
-    memset(mBitmaps.get(), 0, nPages << (kLogValuesPerPage - 3));
+    mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
     mZeroPageIndex = noZeroPage;
     uint32_t nonzeroPageEnd = 0;
     uint32_t currentPage = 0;
@@ -131,7 +128,7 @@
     }
     uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
     for (uint32_t page = fromPage + 1; page < maxPage; page++) {
-        uint32_t index = mIndices[page];
+        uint16_t index = mIndices[page];
         if (index == mZeroPageIndex) {
             continue;
         }
@@ -146,4 +143,4 @@
     return kNotFound;
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/WordBreaker.cpp b/libs/minikin/WordBreaker.cpp
index 38f03ca..16edca7 100644
--- a/libs/minikin/WordBreaker.cpp
+++ b/libs/minikin/WordBreaker.cpp
@@ -15,15 +15,18 @@
  */
 
 #define LOG_TAG "Minikin"
-#include <cutils/log.h>
 
+#include <android/log.h>
+
+#include <minikin/Emoji.h>
+#include <minikin/Hyphenator.h>
 #include <minikin/WordBreaker.h>
 #include "MinikinInternal.h"
 
 #include <unicode/uchar.h>
 #include <unicode/utf16.h>
 
-namespace android {
+namespace minikin {
 
 const uint32_t CHAR_SOFT_HYPHEN = 0x00AD;
 const uint32_t CHAR_ZWJ = 0x200D;
@@ -56,14 +59,6 @@
     return mCurrent;
 }
 
-enum ScanState {
-    START,
-    SAW_AT,
-    SAW_COLON,
-    SAW_COLON_SLASH,
-    SAW_COLON_SLASH_SLASH,
-};
-
 /**
  * Determine whether a line break at position i within the buffer buf is valid. This
  * represents customization beyond the ICU behavior, because plain ICU provides some
@@ -73,7 +68,8 @@
     uint32_t codePoint;
     size_t prev_offset = i;
     U16_PREV(buf, 0, prev_offset, codePoint);
-    if (codePoint == CHAR_SOFT_HYPHEN) {
+    // Do not break on hard or soft hyphens. These are handled by automatic hyphenation.
+    if (Hyphenator::isLineBreakingHyphen(codePoint) || codePoint == CHAR_SOFT_HYPHEN) {
         return false;
     }
     // For Myanmar kinzi sequences, created by <consonant, ASAT, VIRAMA, consonant>. This is to go
@@ -88,23 +84,13 @@
     size_t next_offset = i;
     U16_NEXT(buf, next_offset, bufEnd, next_codepoint);
 
-    // Proposed change to LB24 from http://www.unicode.org/L2/L2016/16043r-line-break-pr-po.txt
-    // (AL | HL) × (PR | PO)
-    int32_t lineBreak = u_getIntPropertyValue(codePoint, UCHAR_LINE_BREAK);
-    if (lineBreak == U_LB_ALPHABETIC || lineBreak == U_LB_HEBREW_LETTER) {
-        lineBreak = u_getIntPropertyValue(next_codepoint, UCHAR_LINE_BREAK);
-        if (lineBreak == U_LB_PREFIX_NUMERIC || lineBreak == U_LB_POSTFIX_NUMERIC) {
-            return false;
-        }
-    }
-
-    // Emoji ZWJ sequences.
+    // Rule LB8 for Emoji ZWJ sequences. We need to do this ourselves since we may have fresher
+    // emoji data than ICU does.
     if (codePoint == CHAR_ZWJ && isEmoji(next_codepoint)) {
         return false;
     }
 
-    // Proposed Rule LB30b from http://www.unicode.org/L2/L2016/16011r3-break-prop-emoji.pdf
-    // EB x EM
+    // Rule LB30b. We need to this ourselves since we may have fresher emoji data than ICU does.
     if (isEmojiModifier(next_codepoint)) {
         if (codePoint == 0xFE0F && prev_offset > 0) {
             // skip over emoji variation selector
@@ -117,6 +103,22 @@
     return true;
 }
 
+// Customized iteratorNext that takes care of both resets and our modifications
+// to ICU's behavior.
+int32_t WordBreaker::iteratorNext() {
+    int32_t result;
+    do {
+        if (mIteratorWasReset) {
+            result = mBreakIterator->following(mCurrent);
+            mIteratorWasReset = false;
+        } else {
+            result = mBreakIterator->next();
+        }
+    } while (!(result == icu::BreakIterator::DONE || (size_t)result == mTextSize
+            || isBreakValid(mText, mTextSize, result)));
+    return result;
+}
+
 // Chicago Manual of Style recommends breaking after these characters in URLs and email addresses
 static bool breakAfter(uint16_t c) {
     return c == ':' || c == '=' || c == '&';
@@ -128,9 +130,15 @@
             || c == '%' || c == '=' || c == '&';
 }
 
-ssize_t WordBreaker::next() {
-    mLast = mCurrent;
+enum ScanState {
+    START,
+    SAW_AT,
+    SAW_COLON,
+    SAW_COLON_SLASH,
+    SAW_COLON_SLASH_SLASH,
+};
 
+void WordBreaker::detectEmailOrUrl() {
     // scan forward from current ICU position for email address or URL
     if (mLast >= mScanOffset) {
         ScanState state = START;
@@ -155,6 +163,9 @@
         }
         if (state == SAW_AT || state == SAW_COLON_SLASH_SLASH) {
             if (!mBreakIterator->isBoundary(i)) {
+                // If there are combining marks or such at the end of the URL or the email address,
+                // consider them a part of the URL or the email, and skip to the next actual
+                // boundary.
                 i = mBreakIterator->following(i);
             }
             mInEmailOrUrl = true;
@@ -164,48 +175,46 @@
         }
         mScanOffset = i;
     }
+}
 
-    if (mInEmailOrUrl) {
-        // special rules for email addresses and URL's as per Chicago Manual of Style (16th ed.)
-        uint16_t lastChar = mText[mLast];
-        ssize_t i;
-        for (i = mLast + 1; i < mScanOffset; i++) {
-            if (breakAfter(lastChar)) {
-                break;
-            }
-            // break after double slash
-            if (lastChar == '/' && i >= mLast + 2 && mText[i - 2] == '/') {
-                break;
-            }
-            uint16_t thisChar = mText[i];
-            // never break after hyphen
-            if (lastChar != '-') {
-                if (breakBefore(thisChar)) {
-                    break;
-                }
-                // break before single slash
-                if (thisChar == '/' && lastChar != '/' &&
-                            !(i + 1 < mScanOffset && mText[i + 1] == '/')) {
-                    break;
-                }
-            }
-            lastChar = thisChar;
+ssize_t WordBreaker::findNextBreakInEmailOrUrl() {
+    // special rules for email addresses and URL's as per Chicago Manual of Style (16th ed.)
+    uint16_t lastChar = mText[mLast];
+    ssize_t i;
+    for (i = mLast + 1; i < mScanOffset; i++) {
+        if (breakAfter(lastChar)) {
+            break;
         }
-        mCurrent = i;
-        return mCurrent;
+        // break after double slash
+        if (lastChar == '/' && i >= mLast + 2 && mText[i - 2] == '/') {
+            break;
+        }
+        const uint16_t thisChar = mText[i];
+        // never break after hyphen
+        if (lastChar != '-') {
+            if (breakBefore(thisChar)) {
+                break;
+            }
+            // break before single slash
+            if (thisChar == '/' && lastChar != '/' &&
+                        !(i + 1 < mScanOffset && mText[i + 1] == '/')) {
+                break;
+            }
+        }
+        lastChar = thisChar;
     }
+    return i;
+}
 
-    int32_t result;
-    do {
-        if (mIteratorWasReset) {
-            result = mBreakIterator->following(mCurrent);
-            mIteratorWasReset = false;
-        } else {
-            result = mBreakIterator->next();
-        }
-    } while (result != icu::BreakIterator::DONE && (size_t)result != mTextSize
-            && !isBreakValid(mText, mTextSize, result));
-    mCurrent = (ssize_t)result;
+ssize_t WordBreaker::next() {
+    mLast = mCurrent;
+
+    detectEmailOrUrl();
+    if (mInEmailOrUrl) {
+        mCurrent = findNextBreakInEmailOrUrl();
+    } else {  // Business as usual
+        mCurrent = (ssize_t) iteratorNext();
+    }
     return mCurrent;
 }
 
@@ -218,7 +227,7 @@
         UChar32 c;
         ssize_t ix = result;
         U16_NEXT(mText, ix, mCurrent, c);
-        int32_t lb = u_getIntPropertyValue(c, UCHAR_LINE_BREAK);
+        const int32_t lb = u_getIntPropertyValue(c, UCHAR_LINE_BREAK);
         // strip leading punctuation, defined as OP and QU line breaking classes,
         // see UAX #14
         if (!(lb == U_LB_OPEN_PUNCTUATION || lb == U_LB_QUOTATION)) {
@@ -238,7 +247,7 @@
         UChar32 c;
         ssize_t ix = result;
         U16_PREV(mText, mLast, ix, c);
-        int32_t gc_mask = U_GET_GC_MASK(c);
+        const int32_t gc_mask = U_GET_GC_MASK(c);
         // strip trailing space and punctuation
         if ((gc_mask & (U_GC_ZS_MASK | U_GC_P_MASK)) == 0) {
             break;
@@ -258,4 +267,4 @@
     utext_close(&mUText);
 }
 
-}  // namespace android
+}  // namespace minikin
diff --git a/libs/minikin/unicode_emoji_h_gen.py b/libs/minikin/unicode_emoji_h_gen.py
deleted file mode 100644
index 7233ef6..0000000
--- a/libs/minikin/unicode_emoji_h_gen.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-"""Generate header file for unicode data."""
-
-import optparse
-import sys
-
-
-UNICODE_EMOJI_TEMPLATE="""
-/* file generated by frameworks/minikin/lib/minikin/Android.mk */
-#ifndef MINIKIN_UNICODE_EMOJI_H
-#define MINIKIN_UNICODE_EMOJI_H
-
-#include <stdint.h>
-
-namespace android {
-namespace generated {
-
-int32_t EMOJI_LIST[] = {
-@@@EMOJI_DATA@@@
-};
-
-}  // namespace generated
-}  // namespace android
-
-#endif  // MINIKIN_UNICODE_EMOJI_H
-"""
-
-
-def _create_opt_parser():
-  parser = optparse.OptionParser()
-  parser.add_option('-i', '--input', type='str', action='store',
-                    help='path to input emoji-data.txt')
-  parser.add_option('-o', '--output', type='str', action='store',
-                    help='path to output UnicodeEmoji.h')
-  return parser
-
-
-def _read_emoji_data(emoji_data_file_path):
-  result = []
-  with open(emoji_data_file_path) as emoji_data_file:
-    for line in emoji_data_file:
-      if '#' in line:
-        line = line[:line.index('#')]  # Drop comments.
-      if not line.strip():
-        continue  # Skip empty line.
-
-      code_points, prop = line.split(';')
-      code_points = code_points.strip()
-      prop = prop.strip()
-      if prop != 'Emoji':
-        break  # Only collect Emoji property code points
-
-      if '..' in code_points:  # code point range
-        cp_start, cp_end = code_points.split('..')
-        result.extend(xrange(int(cp_start, 16), int(cp_end, 16) + 1))
-      else:
-        code_point = int(code_points, 16)
-        result.append(code_point)
-  return result
-
-
-def _generate_header_contents(emoji_list):
-  INDENT = ' ' * 4
-  JOINER = ', '
-
-  hex_list = ['0x%04X' % x for x in emoji_list]
-  lines = []
-  tmp_line = '%s%s' % (INDENT, hex_list[0])
-  for hex_str in hex_list[1:]:
-    if len(tmp_line) + len(JOINER) + len(hex_str) >= 100:
-      lines.append(tmp_line + ',')
-      tmp_line = '%s%s' % (INDENT, hex_str)
-    else:
-      tmp_line = '%s%s%s' % (tmp_line, JOINER, hex_str)
-  lines.append(tmp_line)
-
-  template = UNICODE_EMOJI_TEMPLATE
-  template = template.replace('@@@EMOJI_DATA@@@', '\n'.join(lines))
-  return template
-
-
-if __name__ == '__main__':
-  opt_parser = _create_opt_parser()
-  opts, _ = opt_parser.parse_args()
-
-  emoji_list = _read_emoji_data(opts.input)
-  header = _generate_header_contents(emoji_list)
-  with open(opts.output, 'w') as header_file:
-    header_file.write(header)
-
diff --git a/sample/Android.mk b/sample/Android.mk
deleted file mode 100644
index c4a644d..0000000
--- a/sample/Android.mk
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_C_INCLUDES += \
-	external/harfbuzz_ng/src \
-	external/freetype/include \
-	frameworks/minikin/include
-
-LOCAL_SRC_FILES:= example.cpp
-
-LOCAL_SHARED_LIBRARIES += \
-	libutils \
-	liblog \
-	libcutils \
-	libharfbuzz_ng \
-	libicuuc \
-	libft2 \
-	libpng \
-	libz \
-	libminikin
-
-LOCAL_MODULE:= minikin_example
-
-include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAG := tests
-
-LOCAL_C_INCLUDES += \
-	external/harfbuzz_ng/src \
-	external/freetype/include \
-	frameworks/minikin/include \
-	external/skia/src/core
-
-LOCAL_SRC_FILES:= example_skia.cpp \
-	MinikinSkia.cpp
-
-LOCAL_SHARED_LIBRARIES += \
-	libutils \
-	liblog \
-	libcutils \
-	libharfbuzz_ng \
-	libicuuc \
-	libskia \
-	libminikin \
-	libft2
-
-LOCAL_MODULE:= minikin_skia_example
-
-include $(BUILD_EXECUTABLE)
diff --git a/sample/MinikinSkia.cpp b/sample/MinikinSkia.cpp
deleted file mode 100644
index e2ecde0..0000000
--- a/sample/MinikinSkia.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <SkTypeface.h>
-#include <SkPaint.h>
-
-#include <minikin/MinikinFont.h>
-#include "MinikinSkia.h"
-
-namespace android {
-
-MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
-    MinikinFont(typeface->uniqueID()),
-    mTypeface(typeface) {
-}
-
-MinikinFontSkia::~MinikinFontSkia() {
-    SkSafeUnref(mTypeface);
-}
-
-static void MinikinFontSkia_SetSkiaPaint(SkTypeface* typeface, SkPaint* skPaint, const MinikinPaint& paint) {
-    skPaint->setTypeface(typeface);
-    skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    // TODO: set more paint parameters from Minikin
-    skPaint->setTextSize(paint.size);
-}
-
-float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id,
-    const MinikinPaint &paint) const {
-    SkPaint skPaint;
-    uint16_t glyph16 = glyph_id;
-    SkScalar skWidth;
-    MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint);
-    skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL);
-#ifdef VERBOSE
-    ALOGD("width for typeface %d glyph %d = %f", mTypeface->uniqueID(), glyph_id
-#endif
-    return skWidth;
-}
-
-void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id,
-    const MinikinPaint& paint) const {
-    SkPaint skPaint;
-    uint16_t glyph16 = glyph_id;
-    SkRect skBounds;
-    MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint);
-    skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds);
-    bounds->mLeft = skBounds.fLeft;
-    bounds->mTop = skBounds.fTop;
-    bounds->mRight = skBounds.fRight;
-    bounds->mBottom = skBounds.fBottom;
-}
-
-const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
-    // we don't have a buffer to the font data, copy to own buffer
-    const size_t tableSize = mTypeface->getTableSize(tag);
-    *size = tableSize;
-    if (tableSize == 0) {
-        return nullptr;
-    }
-    void* buf = malloc(tableSize);
-    if (buf == nullptr) {
-        return nullptr;
-    }
-    mTypeface->getTableData(tag, 0, tableSize, buf);
-    *destroy = free;
-    return buf;
-}
-
-SkTypeface *MinikinFontSkia::GetSkTypeface() {
-    return mTypeface;
-}
-
-}
diff --git a/sample/MinikinSkia.h b/sample/MinikinSkia.h
deleted file mode 100644
index 6eb9065..0000000
--- a/sample/MinikinSkia.h
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace android {
-
-class MinikinFontSkia : public MinikinFont {
-public:
-    explicit MinikinFontSkia(SkTypeface *typeface);
-
-    ~MinikinFontSkia();
-
-    float GetHorizontalAdvance(uint32_t glyph_id,
-        const MinikinPaint &paint) const;
-
-    void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
-        const MinikinPaint& paint) const;
-
-    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
-
-    SkTypeface *GetSkTypeface();
-
-private:
-    SkTypeface *mTypeface;
-
-};
-
-}  // namespace android
diff --git a/sample/example.cpp b/sample/example.cpp
deleted file mode 100644
index f4c6a07..0000000
--- a/sample/example.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-// This is a test program that uses Minikin to layout and draw some text.
-// At the moment, it just draws a string into /data/local/tmp/foo.pgm.
-
-#include <stdio.h>
-#include <vector>
-#include <fstream>
-
-#include <unicode/unistr.h>
-#include <unicode/utf16.h>
-
-#include <minikin/MinikinFontFreeType.h>
-#include <minikin/Layout.h>
-
-using std::vector;
-using namespace android;
-using namespace minikin;
-
-FT_Library library;  // TODO: this should not be a global
-
-FontCollection *makeFontCollection() {
-    vector<FontFamily *>typefaces;
-    const char *fns[] = {
-        "/system/fonts/Roboto-Regular.ttf",
-        "/system/fonts/Roboto-Italic.ttf",
-        "/system/fonts/Roboto-BoldItalic.ttf",
-        "/system/fonts/Roboto-Light.ttf",
-        "/system/fonts/Roboto-Thin.ttf",
-        "/system/fonts/Roboto-Bold.ttf",
-        "/system/fonts/Roboto-ThinItalic.ttf",
-        "/system/fonts/Roboto-LightItalic.ttf"
-    };
-
-    FontFamily *family = new FontFamily();
-    FT_Face face;
-    FT_Error error;
-    for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) {
-        const char *fn = fns[i];
-        printf("adding %s\n", fn);
-        error = FT_New_Face(library, fn, 0, &face);
-        if (error != 0) {
-            printf("error loading %s, %d\n", fn, error);
-        }
-        MinikinFont *font = new MinikinFontFreeType(face);
-        family->addFont(font);
-    }
-    typefaces.push_back(family);
-
-#if 1
-    family = new FontFamily();
-    const char *fn = "/system/fonts/DroidSansDevanagari-Regular.ttf";
-    error = FT_New_Face(library, fn, 0, &face);
-    MinikinFont *font = new MinikinFontFreeType(face);
-    family->addFont(font);
-    typefaces.push_back(family);
-#endif
-
-    return new FontCollection(typefaces);
-}
-
-int runMinikinTest() {
-    FT_Error error = FT_Init_FreeType(&library);
-    if (error) {
-        return -1;
-    }
-    Layout::init();
-
-    FontCollection *collection = makeFontCollection();
-    Layout layout;
-    layout.setFontCollection(collection);
-    const char *text = "fine world \xe0\xa4\xa8\xe0\xa4\xae\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x87";
-    int bidiFlags = 0;
-    FontStyle fontStyle;
-    MinikinPaint paint;
-    paint.size = 32;
-    icu::UnicodeString icuText = icu::UnicodeString::fromUTF8(text);
-    layout.doLayout(icuText.getBuffer(), 0, icuText.length(), icuText.length(), bidiFlags, fontStyle, paint);
-    layout.dump();
-    Bitmap bitmap(250, 50);
-    layout.draw(&bitmap, 10, 40, 32);
-    std::ofstream o;
-    o.open("/data/local/tmp/foo.pgm", std::ios::out | std::ios::binary);
-    bitmap.writePnm(o);
-    return 0;
-}
-
-int main() {
-    return runMinikinTest();
-}
diff --git a/sample/example_skia.cpp b/sample/example_skia.cpp
deleted file mode 100644
index f892b8c..0000000
--- a/sample/example_skia.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-// This is a test program that uses Minikin to layout and draw some text.
-// At the moment, it just draws a string into /data/local/tmp/foo.pgm.
-
-#include <stdio.h>
-#include <vector>
-#include <fstream>
-
-#include <unicode/unistr.h>
-#include <unicode/utf16.h>
-
-#include <minikin/MinikinFontFreeType.h>
-#include <minikin/Layout.h>
-
-#include <SkCanvas.h>
-#include <SkGraphics.h>
-#include <SkImageEncoder.h>
-#include <SkTypeface.h>
-#include <SkPaint.h>
-
-#include "MinikinSkia.h"
-
-using std::vector;
-
-namespace android {
-
-FT_Library library;  // TODO: this should not be a global
-
-FontCollection *makeFontCollection() {
-    vector<FontFamily *>typefaces;
-    const char *fns[] = {
-        "/system/fonts/Roboto-Regular.ttf",
-        "/system/fonts/Roboto-Italic.ttf",
-        "/system/fonts/Roboto-BoldItalic.ttf",
-        "/system/fonts/Roboto-Light.ttf",
-        "/system/fonts/Roboto-Thin.ttf",
-        "/system/fonts/Roboto-Bold.ttf",
-        "/system/fonts/Roboto-ThinItalic.ttf",
-        "/system/fonts/Roboto-LightItalic.ttf"
-    };
-
-    FontFamily *family = new FontFamily();
-    for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) {
-        const char *fn = fns[i];
-        SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
-        MinikinFont *font = new MinikinFontSkia(skFace);
-        family->addFont(font);
-    }
-    typefaces.push_back(family);
-
-#if 1
-    family = new FontFamily();
-    const char *fn = "/system/fonts/DroidSansDevanagari-Regular.ttf";
-    SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
-    MinikinFont *font = new MinikinFontSkia(skFace);
-    family->addFont(font);
-    typefaces.push_back(family);
-#endif
-
-    return new FontCollection(typefaces);
-}
-
-// Maybe move to MinikinSkia (esp. instead of opening GetSkTypeface publicly)?
-
-void drawToSkia(SkCanvas *canvas, SkPaint *paint, Layout *layout, float x, float y) {
-    size_t nGlyphs = layout->nGlyphs();
-    uint16_t *glyphs = new uint16_t[nGlyphs];
-    SkPoint *pos = new SkPoint[nGlyphs];
-    SkTypeface *lastFace = NULL;
-    SkTypeface *skFace = NULL;
-    size_t start = 0;
-
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    for (size_t i = 0; i < nGlyphs; i++) {
-        MinikinFontSkia *mfs = static_cast<MinikinFontSkia *>(layout->getFont(i));
-        skFace = mfs->GetSkTypeface();
-        glyphs[i] = layout->getGlyphId(i);
-        pos[i].fX = x + layout->getX(i);
-        pos[i].fY = y + layout->getY(i);
-        if (i > 0 && skFace != lastFace) {
-            paint->setTypeface(lastFace);
-            canvas->drawPosText(glyphs + start, (i - start) << 1, pos + start, *paint);
-            start = i;
-        }
-        lastFace = skFace;
-    }
-    paint->setTypeface(skFace);
-    canvas->drawPosText(glyphs + start, (nGlyphs - start) << 1, pos + start, *paint);
-    delete[] glyphs;
-    delete[] pos;
-}
-
-int runMinikinTest() {
-    FT_Error error = FT_Init_FreeType(&library);
-    if (error) {
-        return -1;
-    }
-    Layout::init();
-
-    FontCollection *collection = makeFontCollection();
-    Layout layout;
-    layout.setFontCollection(collection);
-    const char *text = "fine world \xe0\xa4\xa8\xe0\xa4\xae\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x87";
-    int bidiFlags = 0;
-    FontStyle fontStyle(7);
-    MinikinPaint minikinPaint;
-    minikinPaint.size = 32;
-    icu::UnicodeString icuText = icu::UnicodeString::fromUTF8(text);
-    layout.doLayout(icuText.getBuffer(), 0, icuText.length(), icuText.length(), bidiFlags, fontStyle, minikinPaint);
-    layout.dump();
-
-    SkAutoGraphics ag;
-
-    int width = 800;
-    int height = 600;
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(width, height);
-    SkCanvas canvas(bitmap);
-    SkPaint paint;
-    paint.setARGB(255, 0, 0, 128);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(2);
-    paint.setTextSize(100);
-    paint.setAntiAlias(true);
-    canvas.drawLine(10, 300, 10 + layout.getAdvance(), 300, paint);
-    paint.setStyle(SkPaint::kFill_Style);
-    drawToSkia(&canvas, &paint, &layout, 10, 300);
-
-    SkImageEncoder::EncodeFile("/data/local/tmp/foo.png", bitmap, SkImageEncoder::kPNG_Type, 100);
-    return 0;
-}
-
-}
-
-int main(int argc, const char** argv) {
-    return android::runMinikinTest();
-}
diff --git a/tests/FontCollectionTest.cpp b/tests/FontCollectionTest.cpp
deleted file mode 100644
index fa95242..0000000
--- a/tests/FontCollectionTest.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-
-#include <minikin/FontCollection.h>
-#include "FontTestUtils.h"
-#include "MinikinFontForTest.h"
-#include "MinikinInternal.h"
-
-namespace android {
-
-// The test font has following glyphs.
-// U+82A6
-// U+82A6 U+FE00 (VS1)
-// U+82A6 U+E0100 (VS17)
-// U+82A6 U+E0101 (VS18)
-// U+82A6 U+E0102 (VS19)
-// U+845B
-// U+845B U+FE01 (VS2)
-// U+845B U+E0101 (VS18)
-// U+845B U+E0102 (VS19)
-// U+845B U+E0103 (VS20)
-// U+537F
-// U+717D U+FE02 (VS3)
-// U+717D U+E0102 (VS19)
-// U+717D U+E0103 (VS20)
-const char kVsTestFont[] = kTestFontDir "/VarioationSelectorTest-Regular.ttf";
-
-void expectVSGlyphs(const FontCollection& fc, uint32_t codepoint, const std::set<uint32_t>& vsSet) {
-    for (uint32_t vs = 0xFE00; vs <= 0xE01EF; ++vs) {
-        // Move to variation selectors supplements after variation selectors.
-        if (vs == 0xFF00) {
-            vs = 0xE0100;
-        }
-        if (vsSet.find(vs) == vsSet.end()) {
-            EXPECT_FALSE(fc.hasVariationSelector(codepoint, vs))
-                << "Glyph for U+" << std::hex << codepoint << " U+" << vs;
-        } else {
-            EXPECT_TRUE(fc.hasVariationSelector(codepoint, vs))
-                << "Glyph for U+" << std::hex << codepoint << " U+" << vs;
-        }
-    }
-}
-
-TEST(FontCollectionTest, hasVariationSelectorTest) {
-  FontFamily* family = new FontFamily();
-  family->addFont(new MinikinFontForTest(kVsTestFont));
-  std::vector<FontFamily*> families({family});
-  FontCollection fc(families);
-  family->Unref();
-
-  EXPECT_FALSE(fc.hasVariationSelector(0x82A6, 0));
-  expectVSGlyphs(fc, 0x82A6, std::set<uint32_t>({0xFE00, 0xE0100, 0xE0101, 0xE0102}));
-
-  EXPECT_FALSE(fc.hasVariationSelector(0x845B, 0));
-  expectVSGlyphs(fc, 0x845B, std::set<uint32_t>({0xFE01, 0xE0101, 0xE0102, 0xE0103}));
-
-  EXPECT_FALSE(fc.hasVariationSelector(0x537F, 0));
-  expectVSGlyphs(fc, 0x537F, std::set<uint32_t>({}));
-
-  EXPECT_FALSE(fc.hasVariationSelector(0x717D, 0));
-  expectVSGlyphs(fc, 0x717D, std::set<uint32_t>({0xFE02, 0xE0102, 0xE0103}));
-}
-
-const char kEmojiXmlFile[] = kTestFontDir "emoji.xml";
-
-TEST(FontCollectionTest, hasVariationSelectorTest_emoji) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
-
-    // Both text/color font have cmap format 14 subtable entry for VS15/VS16 respectively.
-    EXPECT_TRUE(collection->hasVariationSelector(0x2623, 0xFE0E));
-    EXPECT_TRUE(collection->hasVariationSelector(0x2623, 0xFE0F));
-
-    // The text font has cmap format 14 subtable entry for VS15 but the color font doesn't have for
-    // VS16
-    EXPECT_TRUE(collection->hasVariationSelector(0x2626, 0xFE0E));
-    EXPECT_FALSE(collection->hasVariationSelector(0x2626, 0xFE0F));
-
-    // The color font has cmap format 14 subtable entry for VS16 but the text font doesn't have for
-    // VS15.
-    EXPECT_TRUE(collection->hasVariationSelector(0x262A, 0xFE0E));
-    EXPECT_TRUE(collection->hasVariationSelector(0x262A, 0xFE0F));
-
-    // Neither text/color font have cmap format 14 subtable entry for VS15/VS16.
-    EXPECT_TRUE(collection->hasVariationSelector(0x262E, 0xFE0E));
-    EXPECT_FALSE(collection->hasVariationSelector(0x262E, 0xFE0F));
-
-    // Text font doesn't have U+262F U+FE0E or even its base code point U+262F.
-    EXPECT_FALSE(collection->hasVariationSelector(0x262F, 0xFE0E));
-
-    // VS15/VS16 is only for emoji, should return false for not an emoji code point.
-    EXPECT_FALSE(collection->hasVariationSelector(0x2229, 0xFE0E));
-    EXPECT_FALSE(collection->hasVariationSelector(0x2229, 0xFE0F));
-
-}
-
-TEST(FontCollectionTest, newEmojiTest) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
-
-    // U+2695, U+2640, U+2642 are not in emoji catrgory in Unicode 9 but they are now in emoji
-    // category. Should return true even if U+FE0E was appended.
-    // These three emojis are only avalilable in TextEmoji.ttf but U+2695 is excluded here since it
-    // is used in other tests.
-    EXPECT_TRUE(collection->hasVariationSelector(0x2640, 0xFE0E));
-    EXPECT_FALSE(collection->hasVariationSelector(0x2640, 0xFE0F));
-    EXPECT_TRUE(collection->hasVariationSelector(0x2642, 0xFE0E));
-    EXPECT_FALSE(collection->hasVariationSelector(0x2642, 0xFE0F));
-}
-
-}  // namespace android
diff --git a/tests/FontFamilyTest.cpp b/tests/FontFamilyTest.cpp
deleted file mode 100644
index 1b24576..0000000
--- a/tests/FontFamilyTest.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-
-#include <minikin/FontFamily.h>
-
-#include <cutils/log.h>
-
-#include "FontLanguageListCache.h"
-#include "ICUTestBase.h"
-#include "MinikinFontForTest.h"
-#include "MinikinInternal.h"
-
-namespace android {
-
-typedef ICUTestBase FontLanguagesTest;
-typedef ICUTestBase FontLanguageTest;
-
-static const FontLanguages& createFontLanguages(const std::string& input) {
-    AutoMutex _l(gMinikinLock);
-    uint32_t langId = FontLanguageListCache::getId(input);
-    return FontLanguageListCache::getById(langId);
-}
-
-static FontLanguage createFontLanguage(const std::string& input) {
-    AutoMutex _l(gMinikinLock);
-    uint32_t langId = FontLanguageListCache::getId(input);
-    return FontLanguageListCache::getById(langId)[0];
-}
-
-TEST_F(FontLanguageTest, basicTests) {
-    FontLanguage defaultLang;
-    FontLanguage emptyLang("", 0);
-    FontLanguage english = createFontLanguage("en");
-    FontLanguage french = createFontLanguage("fr");
-    FontLanguage und = createFontLanguage("und");
-    FontLanguage undZsye = createFontLanguage("und-Zsye");
-
-    EXPECT_EQ(english, english);
-    EXPECT_EQ(french, french);
-
-    EXPECT_TRUE(defaultLang != defaultLang);
-    EXPECT_TRUE(emptyLang != emptyLang);
-    EXPECT_TRUE(defaultLang != emptyLang);
-    EXPECT_TRUE(defaultLang != und);
-    EXPECT_TRUE(emptyLang != und);
-    EXPECT_TRUE(english != defaultLang);
-    EXPECT_TRUE(english != emptyLang);
-    EXPECT_TRUE(english != french);
-    EXPECT_TRUE(english != undZsye);
-    EXPECT_TRUE(und != undZsye);
-    EXPECT_TRUE(english != und);
-
-    EXPECT_TRUE(defaultLang.isUnsupported());
-    EXPECT_TRUE(emptyLang.isUnsupported());
-
-    EXPECT_FALSE(english.isUnsupported());
-    EXPECT_FALSE(french.isUnsupported());
-    EXPECT_FALSE(und.isUnsupported());
-    EXPECT_FALSE(undZsye.isUnsupported());
-}
-
-TEST_F(FontLanguageTest, getStringTest) {
-    EXPECT_EQ("en-Latn", createFontLanguage("en").getString());
-    EXPECT_EQ("en-Latn", createFontLanguage("en-Latn").getString());
-
-    // Capitalized language code or lowercased script should be normalized.
-    EXPECT_EQ("en-Latn", createFontLanguage("EN-LATN").getString());
-    EXPECT_EQ("en-Latn", createFontLanguage("EN-latn").getString());
-    EXPECT_EQ("en-Latn", createFontLanguage("en-latn").getString());
-
-    // Invalid script should be kept.
-    EXPECT_EQ("en-Xyzt", createFontLanguage("en-xyzt").getString());
-
-    EXPECT_EQ("en-Latn", createFontLanguage("en-Latn-US").getString());
-    EXPECT_EQ("ja-Jpan", createFontLanguage("ja").getString());
-    EXPECT_EQ("und", createFontLanguage("und").getString());
-    EXPECT_EQ("und", createFontLanguage("UND").getString());
-    EXPECT_EQ("und", createFontLanguage("Und").getString());
-    EXPECT_EQ("und-Zsye", createFontLanguage("und-Zsye").getString());
-    EXPECT_EQ("und-Zsye", createFontLanguage("Und-ZSYE").getString());
-    EXPECT_EQ("und-Zsye", createFontLanguage("Und-zsye").getString());
-
-    EXPECT_EQ("de-Latn", createFontLanguage("de-1901").getString());
-
-    // This is not a necessary desired behavior, just known behavior.
-    EXPECT_EQ("en-Latn", createFontLanguage("und-Abcdefgh").getString());
-}
-
-TEST_F(FontLanguageTest, ScriptEqualTest) {
-    EXPECT_TRUE(createFontLanguage("en").isEqualScript(createFontLanguage("en")));
-    EXPECT_TRUE(createFontLanguage("en-Latn").isEqualScript(createFontLanguage("en")));
-    EXPECT_TRUE(createFontLanguage("jp-Latn").isEqualScript(createFontLanguage("en-Latn")));
-    EXPECT_TRUE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Jpan")));
-
-    EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hira")));
-    EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hani")));
-}
-
-TEST_F(FontLanguageTest, ScriptMatchTest) {
-    const bool SUPPORTED = true;
-    const bool NOT_SUPPORTED = false;
-
-    struct TestCase {
-        const std::string baseScript;
-        const std::string requestedScript;
-        bool isSupported;
-    } testCases[] = {
-        // Same scripts
-        { "en-Latn", "Latn", SUPPORTED },
-        { "ja-Jpan", "Jpan", SUPPORTED },
-        { "ja-Hira", "Hira", SUPPORTED },
-        { "ja-Kana", "Kana", SUPPORTED },
-        { "ja-Hrkt", "Hrkt", SUPPORTED },
-        { "zh-Hans", "Hans", SUPPORTED },
-        { "zh-Hant", "Hant", SUPPORTED },
-        { "zh-Hani", "Hani", SUPPORTED },
-        { "ko-Kore", "Kore", SUPPORTED },
-        { "ko-Hang", "Hang", SUPPORTED },
-        { "zh-Hanb", "Hanb", SUPPORTED },
-
-        // Japanese supports Hiragana, Katakanara, etc.
-        { "ja-Jpan", "Hira", SUPPORTED },
-        { "ja-Jpan", "Kana", SUPPORTED },
-        { "ja-Jpan", "Hrkt", SUPPORTED },
-        { "ja-Hrkt", "Hira", SUPPORTED },
-        { "ja-Hrkt", "Kana", SUPPORTED },
-
-        // Chinese supports Han.
-        { "zh-Hans", "Hani", SUPPORTED },
-        { "zh-Hant", "Hani", SUPPORTED },
-        { "zh-Hanb", "Hani", SUPPORTED },
-
-        // Hanb supports Bopomofo.
-        { "zh-Hanb", "Bopo", SUPPORTED },
-
-        // Korean supports Hangul.
-        { "ko-Kore", "Hang", SUPPORTED },
-
-        // Different scripts
-        { "ja-Jpan", "Latn", NOT_SUPPORTED },
-        { "en-Latn", "Jpan", NOT_SUPPORTED },
-        { "ja-Jpan", "Hant", NOT_SUPPORTED },
-        { "zh-Hant", "Jpan", NOT_SUPPORTED },
-        { "ja-Jpan", "Hans", NOT_SUPPORTED },
-        { "zh-Hans", "Jpan", NOT_SUPPORTED },
-        { "ja-Jpan", "Kore", NOT_SUPPORTED },
-        { "ko-Kore", "Jpan", NOT_SUPPORTED },
-        { "zh-Hans", "Hant", NOT_SUPPORTED },
-        { "zh-Hant", "Hans", NOT_SUPPORTED },
-        { "zh-Hans", "Kore", NOT_SUPPORTED },
-        { "ko-Kore", "Hans", NOT_SUPPORTED },
-        { "zh-Hant", "Kore", NOT_SUPPORTED },
-        { "ko-Kore", "Hant", NOT_SUPPORTED },
-
-        // Hiragana doesn't support Japanese, etc.
-        { "ja-Hira", "Jpan", NOT_SUPPORTED },
-        { "ja-Kana", "Jpan", NOT_SUPPORTED },
-        { "ja-Hrkt", "Jpan", NOT_SUPPORTED },
-        { "ja-Hani", "Jpan", NOT_SUPPORTED },
-        { "ja-Hira", "Hrkt", NOT_SUPPORTED },
-        { "ja-Kana", "Hrkt", NOT_SUPPORTED },
-        { "ja-Hani", "Hrkt", NOT_SUPPORTED },
-        { "ja-Hani", "Hira", NOT_SUPPORTED },
-        { "ja-Hani", "Kana", NOT_SUPPORTED },
-
-        // Kanji doesn't support Chinese, etc.
-        { "zh-Hani", "Hant", NOT_SUPPORTED },
-        { "zh-Hani", "Hans", NOT_SUPPORTED },
-        { "zh-Hani", "Hanb", NOT_SUPPORTED },
-
-        // Hangul doesn't support Korean, etc.
-        { "ko-Hang", "Kore", NOT_SUPPORTED },
-        { "ko-Hani", "Kore", NOT_SUPPORTED },
-        { "ko-Hani", "Hang", NOT_SUPPORTED },
-        { "ko-Hang", "Hani", NOT_SUPPORTED },
-
-        // Han with botomofo doesn't support simplified Chinese, etc.
-        { "zh-Hanb", "Hant", NOT_SUPPORTED },
-        { "zh-Hanb", "Hans", NOT_SUPPORTED },
-        { "zh-Hanb", "Jpan", NOT_SUPPORTED },
-        { "zh-Hanb", "Kore", NOT_SUPPORTED },
-    };
-
-    for (auto testCase : testCases) {
-        hb_script_t script = hb_script_from_iso15924_tag(
-                HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
-                       testCase.requestedScript[2], testCase.requestedScript[3]));
-        if (testCase.isSupported) {
-            EXPECT_TRUE(
-                    createFontLanguage(testCase.baseScript).supportsHbScript(script))
-                    << testCase.baseScript << " should support " << testCase.requestedScript;
-        } else {
-            EXPECT_FALSE(
-                    createFontLanguage(testCase.baseScript).supportsHbScript(script))
-                    << testCase.baseScript << " shouldn't support " << testCase.requestedScript;
-        }
-    }
-}
-
-TEST_F(FontLanguagesTest, basicTests) {
-    FontLanguages emptyLangs;
-    EXPECT_EQ(0u, emptyLangs.size());
-
-    FontLanguage english = createFontLanguage("en");
-    const FontLanguages& singletonLangs = createFontLanguages("en");
-    EXPECT_EQ(1u, singletonLangs.size());
-    EXPECT_EQ(english, singletonLangs[0]);
-
-    FontLanguage french = createFontLanguage("fr");
-    const FontLanguages& twoLangs = createFontLanguages("en,fr");
-    EXPECT_EQ(2u, twoLangs.size());
-    EXPECT_EQ(english, twoLangs[0]);
-    EXPECT_EQ(french, twoLangs[1]);
-}
-
-TEST_F(FontLanguagesTest, unsupportedLanguageTests) {
-    const FontLanguages& oneUnsupported = createFontLanguages("abcd-example");
-    EXPECT_TRUE(oneUnsupported.empty());
-
-    const FontLanguages& twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
-    EXPECT_TRUE(twoUnsupporteds.empty());
-
-    FontLanguage english = createFontLanguage("en");
-    const FontLanguages& firstUnsupported = createFontLanguages("abcd-example,en");
-    EXPECT_EQ(1u, firstUnsupported.size());
-    EXPECT_EQ(english, firstUnsupported[0]);
-
-    const FontLanguages& lastUnsupported = createFontLanguages("en,abcd-example");
-    EXPECT_EQ(1u, lastUnsupported.size());
-    EXPECT_EQ(english, lastUnsupported[0]);
-}
-
-TEST_F(FontLanguagesTest, repeatedLanguageTests) {
-    FontLanguage english = createFontLanguage("en");
-    FontLanguage french = createFontLanguage("fr");
-    FontLanguage englishInLatn = createFontLanguage("en-Latn");
-    ASSERT_TRUE(english == englishInLatn);
-
-    const FontLanguages& langs = createFontLanguages("en,en-Latn");
-    EXPECT_EQ(1u, langs.size());
-    EXPECT_EQ(english, langs[0]);
-
-    // Country codes are ignored.
-    const FontLanguages& fr = createFontLanguages("fr,fr-CA,fr-FR");
-    EXPECT_EQ(1u, fr.size());
-    EXPECT_EQ(french, fr[0]);
-
-    // The order should be kept.
-    const FontLanguages& langs2 = createFontLanguages("en,fr,en-Latn");
-    EXPECT_EQ(2u, langs2.size());
-    EXPECT_EQ(english, langs2[0]);
-    EXPECT_EQ(french, langs2[1]);
-}
-
-TEST_F(FontLanguagesTest, undEmojiTests) {
-    FontLanguage emoji = createFontLanguage("und-Zsye");
-    EXPECT_TRUE(emoji.hasEmojiFlag());
-
-    FontLanguage und = createFontLanguage("und");
-    EXPECT_FALSE(und.hasEmojiFlag());
-    EXPECT_FALSE(emoji == und);
-
-    FontLanguage undExample = createFontLanguage("und-example");
-    EXPECT_FALSE(undExample.hasEmojiFlag());
-    EXPECT_FALSE(emoji == undExample);
-}
-
-TEST_F(FontLanguagesTest, registerLanguageListTest) {
-    EXPECT_EQ(0UL, FontStyle::registerLanguageList(""));
-    EXPECT_NE(0UL, FontStyle::registerLanguageList("en"));
-    EXPECT_NE(0UL, FontStyle::registerLanguageList("jp"));
-    EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans"));
-
-    EXPECT_EQ(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("en"));
-    EXPECT_NE(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("jp"));
-
-    EXPECT_EQ(FontStyle::registerLanguageList("en,zh-Hans"),
-              FontStyle::registerLanguageList("en,zh-Hans"));
-    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
-              FontStyle::registerLanguageList("zh-Hans,en"));
-    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
-              FontStyle::registerLanguageList("jp"));
-    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
-              FontStyle::registerLanguageList("en"));
-    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
-              FontStyle::registerLanguageList("en,zh-Hant"));
-}
-
-// The test font has following glyphs.
-// U+82A6
-// U+82A6 U+FE00 (VS1)
-// U+82A6 U+E0100 (VS17)
-// U+82A6 U+E0101 (VS18)
-// U+82A6 U+E0102 (VS19)
-// U+845B
-// U+845B U+FE00 (VS2)
-// U+845B U+E0101 (VS18)
-// U+845B U+E0102 (VS19)
-// U+845B U+E0103 (VS20)
-// U+537F
-// U+717D U+FE02 (VS3)
-// U+717D U+E0102 (VS19)
-// U+717D U+E0103 (VS20)
-const char kVsTestFont[] = kTestFontDir "VarioationSelectorTest-Regular.ttf";
-
-class FontFamilyTest : public ICUTestBase {
-public:
-    virtual void SetUp() override {
-        ICUTestBase::SetUp();
-        if (access(kVsTestFont, R_OK) != 0) {
-            FAIL() << "Unable to read " << kVsTestFont << ". "
-                   << "Please prepare the test data directory. "
-                   << "For more details, please see how_to_run.txt.";
-        }
-    }
-};
-
-// Asserts that the font family has glyphs for and only for specified codepoint
-// and variationSelector pairs.
-void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set<uint32_t>& vs) {
-    for (uint32_t i = 0xFE00; i <= 0xE01EF; ++i) {
-        // Move to variation selectors supplements after variation selectors.
-        if (i == 0xFF00) {
-            i = 0xE0100;
-        }
-        if (vs.find(i) == vs.end()) {
-            EXPECT_FALSE(family->hasGlyph(codepoint, i))
-                    << "Glyph for U+" << std::hex << codepoint << " U+" << i;
-        } else {
-            EXPECT_TRUE(family->hasGlyph(codepoint, i))
-                    << "Glyph for U+" << std::hex << codepoint << " U+" << i;
-        }
-
-    }
-}
-
-TEST_F(FontFamilyTest, hasVariationSelectorTest) {
-    MinikinAutoUnref<MinikinFontForTest> minikinFont(new MinikinFontForTest(kVsTestFont));
-    MinikinAutoUnref<FontFamily> family(new FontFamily);
-    family->addFont(minikinFont.get());
-
-    AutoMutex _l(gMinikinLock);
-
-    const uint32_t kVS1 = 0xFE00;
-    const uint32_t kVS2 = 0xFE01;
-    const uint32_t kVS3 = 0xFE02;
-    const uint32_t kVS17 = 0xE0100;
-    const uint32_t kVS18 = 0xE0101;
-    const uint32_t kVS19 = 0xE0102;
-    const uint32_t kVS20 = 0xE0103;
-
-    const uint32_t kSupportedChar1 = 0x82A6;
-    EXPECT_TRUE(family->getCoverage()->get(kSupportedChar1));
-    expectVSGlyphs(family.get(), 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}));
-
-    const uint32_t kNoVsSupportedChar = 0x537F;
-    EXPECT_TRUE(family->getCoverage()->get(kNoVsSupportedChar));
-    expectVSGlyphs(family.get(), 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}));
-
-    const uint32_t kNotSupportedChar = 0x845C;
-    EXPECT_FALSE(family->getCoverage()->get(kNotSupportedChar));
-    expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
-}
-
-TEST_F(FontFamilyTest, hasVSTableTest) {
-    struct TestCase {
-        const std::string fontPath;
-        bool hasVSTable;
-    } testCases[] = {
-        { kTestFontDir "Ja.ttf", true },
-        { kTestFontDir "ZhHant.ttf", true },
-        { kTestFontDir "ZhHans.ttf", true },
-        { kTestFontDir "Italic.ttf", false },
-        { kTestFontDir "Bold.ttf", false },
-        { kTestFontDir "BoldItalic.ttf", false },
-    };
-
-    for (auto testCase : testCases) {
-        SCOPED_TRACE(testCase.hasVSTable ?
-                "Font " + testCase.fontPath + " should have a variation sequence table." :
-                "Font " + testCase.fontPath + " shouldn't have a variation sequence table.");
-
-        MinikinAutoUnref<MinikinFontForTest> minikinFont(new MinikinFontForTest(testCase.fontPath));
-        MinikinAutoUnref<FontFamily> family(new FontFamily);
-        family->addFont(minikinFont.get());
-        AutoMutex _l(gMinikinLock);
-        family->getCoverage();
-
-        EXPECT_EQ(testCase.hasVSTable, family->hasVSTable());
-    }
-}
-
-}  // namespace android
diff --git a/tests/FontTestUtils.cpp b/tests/FontTestUtils.cpp
deleted file mode 100644
index fdc3ed6..0000000
--- a/tests/FontTestUtils.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-#include <libxml/tree.h>
-
-#include <minikin/FontCollection.h>
-#include <minikin/FontFamily.h>
-
-#include <cutils/log.h>
-
-#include "FontLanguage.h"
-#include "MinikinFontForTest.h"
-
-android::FontCollection* getFontCollection(const char* fontDir, const char* fontXml) {
-    xmlDoc* doc = xmlReadFile(fontXml, NULL, 0);
-    xmlNode* familySet = xmlDocGetRootElement(doc);
-
-    std::vector<android::FontFamily*> families;
-    for (xmlNode* familyNode = familySet->children; familyNode; familyNode = familyNode->next) {
-        if (xmlStrcmp(familyNode->name, (const xmlChar*)"family") != 0) {
-            continue;
-        }
-
-        xmlChar* variantXmlch = xmlGetProp(familyNode, (const xmlChar*)"variant");
-        int variant = android::VARIANT_DEFAULT;
-        if (variantXmlch) {
-            if (xmlStrcmp(variantXmlch, (const xmlChar*)"elegant") == 0) {
-                variant = android::VARIANT_ELEGANT;
-            } else if (xmlStrcmp(variantXmlch, (const xmlChar*)"compact") == 0) {
-                variant = android::VARIANT_COMPACT;
-            }
-        }
-
-        xmlChar* lang = xmlGetProp(familyNode, (const xmlChar*)"lang");
-        uint32_t langId = android::FontStyle::registerLanguageList(
-                std::string((const char*)lang, xmlStrlen(lang)));
-
-        android::FontFamily* family = new android::FontFamily(langId, variant);
-
-        for (xmlNode* fontNode = familyNode->children; fontNode; fontNode = fontNode->next) {
-            if (xmlStrcmp(fontNode->name, (const xmlChar*)"font") != 0) {
-                continue;
-            }
-
-            int weight = atoi((const char*)(xmlGetProp(fontNode, (const xmlChar*)"weight"))) / 100;
-            bool italic = xmlStrcmp(
-                    xmlGetProp(fontNode, (const xmlChar*)"style"), (const xmlChar*)"italic") == 0;
-
-            xmlChar* fontFileName = xmlNodeListGetString(doc, fontNode->xmlChildrenNode, 1);
-            std::string fontPath = fontDir + std::string((const char*)fontFileName);
-            xmlFree(fontFileName);
-
-            LOG_ALWAYS_FATAL_IF(access(fontPath.c_str(), R_OK) != 0,
-                    "%s is not found", fontPath.c_str());
-
-            family->addFont(new MinikinFontForTest(fontPath), android::FontStyle(weight, italic));
-        }
-        families.push_back(family);
-    }
-    xmlFreeDoc(doc);
-
-    android::FontCollection* collection = new android::FontCollection(families);
-    collection->Ref();
-    for (size_t i = 0; i < families.size(); ++i) {
-        families[i]->Unref();
-    }
-    return collection;
-}
diff --git a/tests/GraphemeBreakTests.cpp b/tests/GraphemeBreakTests.cpp
deleted file mode 100644
index 9dfd426..0000000
--- a/tests/GraphemeBreakTests.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-#include <UnicodeUtils.h>
-#include <minikin/GraphemeBreak.h>
-
-using namespace android;
-
-bool IsBreak(const char* src) {
-    const size_t BUF_SIZE = 256;
-    uint16_t buf[BUF_SIZE];
-    size_t offset;
-    size_t size;
-    ParseUnicode(buf, BUF_SIZE, src, &size, &offset);
-    return GraphemeBreak::isGraphemeBreak(buf, 0, size, offset);
-}
-
-TEST(GraphemeBreak, utf16) {
-    EXPECT_FALSE(IsBreak("U+D83C | U+DC31"));  // emoji, U+1F431
-
-    // tests for invalid UTF-16
-    EXPECT_TRUE(IsBreak("U+D800 | U+D800"));  // two leading surrogates
-    EXPECT_TRUE(IsBreak("U+DC00 | U+DC00"));  // two trailing surrogates
-    EXPECT_TRUE(IsBreak("'a' | U+D800"));  // lonely leading surrogate
-    EXPECT_TRUE(IsBreak("U+DC00 | 'a'"));  // lonely trailing surrogate
-    EXPECT_TRUE(IsBreak("U+D800 | 'a'"));  // leading surrogate followed by non-surrogate
-    EXPECT_TRUE(IsBreak("'a' | U+DC00"));  // non-surrogate followed by trailing surrogate
-}
-
-TEST(GraphemeBreak, rules) {
-    // Rule GB1, sot ÷; Rule GB2, ÷ eot
-    EXPECT_TRUE(IsBreak("| 'a'"));
-    EXPECT_TRUE(IsBreak("'a' |"));
-
-    // Rule GB3, CR x LF
-    EXPECT_FALSE(IsBreak("U+000D | U+000A"));  // CR x LF
-
-    // Rule GB4, (Control | CR | LF) ÷
-    EXPECT_TRUE(IsBreak("'a' | U+2028"));  // Line separator
-    EXPECT_TRUE(IsBreak("'a' | U+000D"));  // LF
-    EXPECT_TRUE(IsBreak("'a' | U+000A"));  // CR
-
-    // Rule GB5, ÷ (Control | CR | LF)
-    EXPECT_TRUE(IsBreak("U+2028 | 'a'"));  // Line separator
-    EXPECT_TRUE(IsBreak("U+000D | 'a'"));  // LF
-    EXPECT_TRUE(IsBreak("U+000A | 'a'"));  // CR
-
-    // Rule GB6, L x ( L | V | LV | LVT )
-    EXPECT_FALSE(IsBreak("U+1100 | U+1100"));  // L x L
-    EXPECT_FALSE(IsBreak("U+1100 | U+1161"));  // L x V
-    EXPECT_FALSE(IsBreak("U+1100 | U+AC00"));  // L x LV
-    EXPECT_FALSE(IsBreak("U+1100 | U+AC01"));  // L x LVT
-
-    // Rule GB7, ( LV | V ) x ( V | T )
-    EXPECT_FALSE(IsBreak("U+AC00 | U+1161"));  // LV x V
-    EXPECT_FALSE(IsBreak("U+1161 | U+1161"));  // V x V
-    EXPECT_FALSE(IsBreak("U+AC00 | U+11A8"));  // LV x T
-    EXPECT_FALSE(IsBreak("U+1161 | U+11A8"));  // V x T
-
-    // Rule GB8, ( LVT | T ) x T
-    EXPECT_FALSE(IsBreak("U+AC01 | U+11A8"));  // LVT x T
-    EXPECT_FALSE(IsBreak("U+11A8 | U+11A8"));  // T x T
-
-    // Other hangul pairs not counted above _are_ breaks (GB10)
-    EXPECT_TRUE(IsBreak("U+AC00 | U+1100"));  // LV x L
-    EXPECT_TRUE(IsBreak("U+AC01 | U+1100"));  // LVT x L
-    EXPECT_TRUE(IsBreak("U+11A8 | U+1100"));  // T x L
-    EXPECT_TRUE(IsBreak("U+11A8 | U+AC00"));  // T x LV
-    EXPECT_TRUE(IsBreak("U+11A8 | U+AC01"));  // T x LVT
-
-    // Rule GB8a, Regional_Indicator x Regional_Indicator
-    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8"));
-    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | U+1F1FA U+1F1F8")); // Regional indicator pair (flag)
-    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8 U+1F1FA U+1F1F8")); // Regional indicator pair (flag)
-    EXPECT_FALSE(IsBreak("U+1F1FA U+1F1F8 U+1F1FA | U+1F1F8")); // Regional indicator pair (flag)
-
-    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | U+1F1FA"));  // Regional indicator pair (flag)
-    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8 U+1F1FA"));  // Regional indicator pair (flag)
-
-    EXPECT_TRUE(IsBreak("'a' U+1F1FA U+1F1F8 | U+1F1FA"));  // Regional indicator pair (flag)
-    EXPECT_FALSE(IsBreak("'a' U+1F1FA | U+1F1F8 U+1F1FA"));  // Regional indicator pair (flag)
-
-    EXPECT_TRUE(
-            IsBreak("'a' U+1F1FA U+1F1F8 | U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
-    EXPECT_FALSE(
-            IsBreak("'a' U+1F1FA | U+1F1F8 U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
-    EXPECT_FALSE(
-            IsBreak("'a' U+1F1FA U+1F1F8 U+1F1FA | U+1F1F8"));  // Regional indicator pair (flag)
-
-    // Rule GB9, x Extend
-    EXPECT_FALSE(IsBreak("'a' | U+0301"));  // combining accent
-    // Rule GB9a, x SpacingMark
-    EXPECT_FALSE(IsBreak("U+0915 | U+093E"));  // KA, AA (spacing mark)
-    // Rule GB9b, Prepend x
-    // see tailoring test for prepend, as current ICU doesn't have any characters in the class
-
-    // Rule GB10, Any ÷ Any
-    EXPECT_TRUE(IsBreak("'a' | 'b'"));
-    EXPECT_TRUE(IsBreak("'f' | 'i'"));  // probable ligature
-    EXPECT_TRUE(IsBreak("U+0644 | U+0627"));  // probable ligature, lam + alef
-    EXPECT_TRUE(IsBreak("U+4E00 | U+4E00"));  // CJK ideographs
-    EXPECT_TRUE(IsBreak("'a' | U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
-    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | 'a'"));  // Regional indicator pair (flag)
-}
-
-TEST(GraphemeBreak, tailoring) {
-    // control characters that we interpret as "extend"
-    EXPECT_FALSE(IsBreak("'a' | U+00AD"));  // soft hyphen
-    EXPECT_FALSE(IsBreak("'a' | U+200B"));  // zwsp
-    EXPECT_FALSE(IsBreak("'a' | U+200E"));  // lrm
-    EXPECT_FALSE(IsBreak("'a' | U+202A"));  // lre
-    EXPECT_FALSE(IsBreak("'a' | U+E0041"));  // tag character
-
-    // UTC-approved characters for the Prepend class
-    EXPECT_FALSE(IsBreak("U+06DD | U+0661"));  // arabic subtending mark + digit one
-
-    EXPECT_TRUE(IsBreak("U+0E01 | U+0E33"));  // Thai sara am
-
-    // virama is not a grapheme break, but "pure killer" is
-    EXPECT_FALSE(IsBreak("U+0915 | U+094D U+0915"));  // Devanagari ka+virama+ka
-    EXPECT_FALSE(IsBreak("U+0915 U+094D | U+0915"));  // Devanagari ka+virama+ka
-    EXPECT_FALSE(IsBreak("U+0E01 | U+0E3A U+0E01"));  // thai phinthu = pure killer
-    EXPECT_TRUE(IsBreak("U+0E01 U+0E3A | U+0E01"));  // thai phinthu = pure killer
-
-    // suppress grapheme breaks in zwj emoji sequences, see
-    // http://www.unicode.org/emoji/charts/emoji-zwj-sequences.html
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+2764 U+FE0F U+200D | U+1F48B U+200D U+1F468"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D | U+1F468"));
-    EXPECT_FALSE(IsBreak("U+1F468 U+200D | U+1F469 U+200D U+1F466"));
-    EXPECT_FALSE(IsBreak("U+1F468 U+200D U+1F469 U+200D | U+1F466"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+1F469 U+200D U+1F467 U+200D U+1F466"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+1F469 U+200D | U+1F467 U+200D U+1F466"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+1F469 U+200D U+1F467 U+200D | U+1F466"));
-    EXPECT_FALSE(IsBreak("U+1F441 U+200D | U+1F5E8"));
-
-    // Do not break before and after zwj with all kind of emoji characters.
-    EXPECT_FALSE(IsBreak("U+1F431 | U+200D U+1F464"));
-    EXPECT_FALSE(IsBreak("U+1F431 U+200D | U+1F464"));
-
-    // ARABIC LETTER BEH + ZWJ + heart, not a zwj emoji sequence, so we preserve the break
-    EXPECT_TRUE(IsBreak("U+0628 U+200D | U+2764"));
-}
-
-TEST(GraphemeBreak, emojiModifiers) {
-    EXPECT_FALSE(IsBreak("U+261D | U+1F3FB"));  // white up pointing index + modifier
-    EXPECT_FALSE(IsBreak("U+270C | U+1F3FB"));  // victory hand + modifier
-    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FB"));  // boy + modifier
-    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FC"));  // boy + modifier
-    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FD"));  // boy + modifier
-    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FE"));  // boy + modifier
-    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FF"));  // boy + modifier
-    EXPECT_FALSE(IsBreak("U+1F918 | U+1F3FF"));  // sign of the horns + modifier
-    EXPECT_FALSE(IsBreak("U+1F933 | U+1F3FF"));  // selfie (Unicode 9) + modifier
-
-    // adding emoji style variation selector doesn't affect grapheme cluster
-    EXPECT_TRUE(IsBreak("U+270C U+FE0E | U+1F3FB"));  // victory hand + text style + modifier
-    EXPECT_FALSE(IsBreak("U+270C U+FE0F | U+1F3FB"));  // heart + emoji style + modifier
-
-    // heart is not an emoji base
-    EXPECT_TRUE(IsBreak("U+2764 | U+1F3FB"));  // heart + modifier
-    EXPECT_TRUE(IsBreak("U+2764 U+FE0E | U+1F3FB"));  // heart + emoji style + modifier
-    EXPECT_TRUE(IsBreak("U+2764 U+FE0F | U+1F3FB"));  // heart + emoji style + modifier
-    EXPECT_TRUE(IsBreak("U+1F3FB | U+1F3FB"));  // modifier + modifier
-
-    // rat is not an emoji modifer
-    EXPECT_TRUE(IsBreak("U+1F466 | U+1F400"));  // boy + rat
-
-}
-
-TEST(GraphemeBreak, genderBalancedEmoji) {
-    // U+1F469 is WOMAN, U+200D is ZWJ, U+1F4BC is BRIEFCASE.
-    EXPECT_FALSE(IsBreak("U+1F469 | U+200D U+1F4BC"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+1F4BC"));
-
-    // U+2695 has now emoji property, so should be part of ZWJ sequence.
-    EXPECT_FALSE(IsBreak("U+1F469 | U+200D U+2695"));
-    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+2695"));
-}
-
-TEST(GraphemeBreak, offsets) {
-    uint16_t string[] = { 0x0041, 0x06DD, 0x0045, 0x0301, 0x0049, 0x0301 };
-    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(string, 2, 3, 2));
-    EXPECT_FALSE(GraphemeBreak::isGraphemeBreak(string, 2, 3, 3));
-    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(string, 2, 3, 4));
-    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(string, 2, 3, 5));
-}
diff --git a/tests/HbFontCacheTest.cpp b/tests/HbFontCacheTest.cpp
deleted file mode 100644
index 2dee61a..0000000
--- a/tests/HbFontCacheTest.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016 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 "HbFontCache.h"
-
-#include <cutils/log.h>
-#include <hb.h>
-#include <utils/Mutex.h>
-
-#include "MinikinInternal.h"
-#include "MinikinFontForTest.h"
-#include <minikin/MinikinFont.h>
-
-namespace android {
-namespace {
-
-class HbFontCacheTest : public testing::Test {
-public:
-    virtual void TearDown() {
-        AutoMutex _l(gMinikinLock);
-        purgeHbFontCacheLocked();
-    }
-};
-
-TEST_F(HbFontCacheTest, getHbFontLockedTest) {
-    AutoMutex _l(gMinikinLock);
-
-    MinikinFontForTest fontA(kTestFontDir "Regular.ttf");
-    MinikinFontForTest fontB(kTestFontDir "Bold.ttf");
-    MinikinFontForTest fontC(kTestFontDir "BoldItalic.ttf");
-
-    // Never return NULL.
-    EXPECT_NE(nullptr, getHbFontLocked(&fontA));
-    EXPECT_NE(nullptr, getHbFontLocked(&fontB));
-    EXPECT_NE(nullptr, getHbFontLocked(&fontC));
-
-    EXPECT_NE(nullptr, getHbFontLocked(nullptr));
-
-    // Must return same object if same font object is passed.
-    EXPECT_EQ(getHbFontLocked(&fontA), getHbFontLocked(&fontA));
-    EXPECT_EQ(getHbFontLocked(&fontB), getHbFontLocked(&fontB));
-    EXPECT_EQ(getHbFontLocked(&fontC), getHbFontLocked(&fontC));
-
-    // Different object must be returned if the passed minikinFont has different ID.
-    EXPECT_NE(getHbFontLocked(&fontA), getHbFontLocked(&fontB));
-    EXPECT_NE(getHbFontLocked(&fontA), getHbFontLocked(&fontC));
-}
-
-TEST_F(HbFontCacheTest, purgeCacheTest) {
-    AutoMutex _l(gMinikinLock);
-    MinikinFontForTest minikinFont(kTestFontDir "Regular.ttf");
-
-    hb_font_t* font = getHbFontLocked(&minikinFont);
-    ASSERT_NE(nullptr, font);
-
-    // Set user data to identify the font object.
-    hb_user_data_key_t key;
-    void* data = (void*)0xdeadbeef;
-    hb_font_set_user_data(font, &key, data, NULL, false);
-    ASSERT_EQ(data, hb_font_get_user_data(font, &key));
-
-    purgeHbFontCacheLocked();
-
-    // By checking user data, confirm that the object after purge is different from previously
-    // created one. Do not compare the returned pointer here since memory allocator may assign
-    // same region for new object.
-    font = getHbFontLocked(&minikinFont);
-    EXPECT_EQ(nullptr, hb_font_get_user_data(font, &key));
-}
-
-}  // namespace
-}  // namespace android
diff --git a/tests/MinikinFontForTest.cpp b/tests/MinikinFontForTest.cpp
deleted file mode 100644
index 66dd4ea..0000000
--- a/tests/MinikinFontForTest.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.
- */
-
-#include "MinikinFontForTest.h"
-
-#include <minikin/MinikinFont.h>
-
-#include <SkTypeface.h>
-
-#include <cutils/log.h>
-
-MinikinFontForTest::MinikinFontForTest(const std::string& font_path) :
-    MinikinFontForTest(font_path, SkTypeface::CreateFromFile(font_path.c_str())) {
-}
-
-MinikinFontForTest::MinikinFontForTest(const std::string& font_path, SkTypeface* typeface) :
-    MinikinFont(typeface->uniqueID()),
-    mTypeface(typeface),
-    mFontPath(font_path) {
-}
-
-MinikinFontForTest::~MinikinFontForTest() {
-}
-
-float MinikinFontForTest::GetHorizontalAdvance(uint32_t /* glyph_id */,
-        const android::MinikinPaint& /* paint */) const {
-    LOG_ALWAYS_FATAL("MinikinFontForTest::GetHorizontalAdvance is not yet implemented");
-    return 0.0f;
-}
-
-void MinikinFontForTest::GetBounds(android::MinikinRect* /* bounds */, uint32_t /* glyph_id */,
-        const android::MinikinPaint& /* paint */) const {
-    LOG_ALWAYS_FATAL("MinikinFontForTest::GetBounds is not yet implemented");
-}
-
-const void* MinikinFontForTest::GetTable(uint32_t tag, size_t* size,
-        android::MinikinDestroyFunc* destroy) {
-    const size_t tableSize = mTypeface->getTableSize(tag);
-    *size = tableSize;
-    if (tableSize == 0) {
-        return nullptr;
-    }
-    void* buf = malloc(tableSize);
-    if (buf == nullptr) {
-        return nullptr;
-    }
-    mTypeface->getTableData(tag, 0, tableSize, buf);
-    *destroy = free;
-    return buf;
-}
diff --git a/tests/MinikinFontForTest.h b/tests/MinikinFontForTest.h
deleted file mode 100644
index e527d21..0000000
--- a/tests/MinikinFontForTest.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
-#define MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
-
-#include <minikin/MinikinFont.h>
-
-class SkTypeface;
-
-class MinikinFontForTest : public android::MinikinFont {
-public:
-    explicit MinikinFontForTest(const std::string& font_path);
-    MinikinFontForTest(const std::string& font_path, SkTypeface* typeface);
-    ~MinikinFontForTest();
-
-    // MinikinFont overrides.
-    float GetHorizontalAdvance(uint32_t glyph_id, const android::MinikinPaint &paint) const;
-    void GetBounds(android::MinikinRect* bounds, uint32_t glyph_id,
-            const android::MinikinPaint& paint) const;
-    const void* GetTable(uint32_t tag, size_t* size, android::MinikinDestroyFunc* destroy);
-
-    const std::string& fontPath() const { return mFontPath; }
-private:
-    SkTypeface *mTypeface;
-    const std::string mFontPath;
-};
-
-#endif  // MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
diff --git a/tests/MinikinInternalTest.cpp b/tests/MinikinInternalTest.cpp
deleted file mode 100644
index 9c1a1e5..0000000
--- a/tests/MinikinInternalTest.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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 "MinikinInternal.h"
-
-namespace android {
-
-TEST(MinikinInternalTest, isEmojiTest) {
-    EXPECT_TRUE(isEmoji(0x0023));  // NUMBER SIGN
-    EXPECT_TRUE(isEmoji(0x0035));  // DIGIT FIVE
-    EXPECT_TRUE(isEmoji(0x1F0CF));  // PLAYING CARD BLACK JOKER
-    EXPECT_TRUE(isEmoji(0x1F1E9));  // REGIONAL INDICATOR SYMBOL LETTER D
-
-    EXPECT_FALSE(isEmoji(0x0000));  // <control>
-    EXPECT_FALSE(isEmoji(0x0061));  // LATIN SMALL LETTER A
-    EXPECT_FALSE(isEmoji(0x29E3D));  // A han character.
-}
-
-}  // namespace android
diff --git a/tests/UnicodeUtils.cpp b/tests/UnicodeUtils.cpp
deleted file mode 100644
index 501fc9f..0000000
--- a/tests/UnicodeUtils.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-#include <unicode/utf.h>
-#include <cstdlib>
-
-// src is of the form "U+1F431 | 'h' 'i'". Position of "|" gets saved to offset if non-null.
-// Size is returned in an out parameter because gtest needs a void return for ASSERT to work.
-void ParseUnicode(uint16_t* buf, size_t buf_size, const char* src, size_t* result_size,
-        size_t* offset) {
-    size_t input_ix = 0;
-    size_t output_ix = 0;
-    bool seen_offset = false;
-
-    while (src[input_ix] != 0) {
-        switch (src[input_ix]) {
-        case '\'':
-            // single ASCII char
-            ASSERT_LT(src[input_ix], 0x80);
-            input_ix++;
-            ASSERT_NE(src[input_ix], 0);
-            ASSERT_LT(output_ix, buf_size);
-            buf[output_ix++] = (uint16_t)src[input_ix++];
-            ASSERT_EQ(src[input_ix], '\'');
-            input_ix++;
-            break;
-        case 'u':
-        case 'U': {
-            // Unicode codepoint in hex syntax
-            input_ix++;
-            ASSERT_EQ(src[input_ix], '+');
-            input_ix++;
-            char* endptr = (char*)src + input_ix;
-            unsigned long int codepoint = strtoul(src + input_ix, &endptr, 16);
-            size_t num_hex_digits = endptr - (src + input_ix);
-            ASSERT_GE(num_hex_digits, 4u);  // also triggers on invalid number syntax, digits = 0
-            ASSERT_LE(num_hex_digits, 6u);
-            ASSERT_LE(codepoint, 0x10FFFFu);
-            input_ix += num_hex_digits;
-            if (U16_LENGTH(codepoint) == 1) {
-                ASSERT_LE(output_ix + 1, buf_size);
-                buf[output_ix++] = codepoint;
-            } else {
-                // UTF-16 encoding
-                ASSERT_LE(output_ix + 2, buf_size);
-                buf[output_ix++] = U16_LEAD(codepoint);
-                buf[output_ix++] = U16_TRAIL(codepoint);
-            }
-            break;
-        }
-        case ' ':
-            input_ix++;
-            break;
-        case '|':
-            ASSERT_FALSE(seen_offset);
-            ASSERT_NE(offset, nullptr);
-            *offset = output_ix;
-            seen_offset = true;
-            input_ix++;
-            break;
-        default:
-            FAIL();  // unexpected character
-        }
-    }
-    ASSERT_NE(result_size, nullptr);
-    *result_size = output_ix;
-    ASSERT_TRUE(seen_offset || offset == nullptr);
-}
-
-TEST(UnicodeUtils, parse) {
-    const size_t BUF_SIZE = 256;
-    uint16_t buf[BUF_SIZE];
-    size_t offset;
-    size_t size;
-    ParseUnicode(buf, BUF_SIZE, "U+000D U+1F431 | 'a'", &size, &offset);
-    EXPECT_EQ(size, 4u);
-    EXPECT_EQ(offset, 3u);
-    EXPECT_EQ(buf[0], 0x000D);
-    EXPECT_EQ(buf[1], 0xD83D);
-    EXPECT_EQ(buf[2], 0xDC31);
-    EXPECT_EQ(buf[3], 'a');
-}
diff --git a/tests/data/MultiAxis.ttf b/tests/data/MultiAxis.ttf
new file mode 100644
index 0000000..1d687cb
--- /dev/null
+++ b/tests/data/MultiAxis.ttf
Binary files differ
diff --git a/tests/data/MultiAxis.ttx b/tests/data/MultiAxis.ttx
new file mode 100644
index 0000000..7c17198
--- /dev/null
+++ b/tests/data/MultiAxis.ttx
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="default"/>
+  </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="30"/>
+    <yMin value="-200"/>
+    <xMax value="629"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="659"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="30"/>
+    <xMaxExtent value="629"/>
+    <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="18"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="54"/>
+    <maxPoints value="73"/>
+    <maxContours value="10"/>
+    <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="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="default" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="default" />
+    </cmap_format_4>
+  </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="0" yMax="0">
+      <contour></contour><instructions><assembly></assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="default" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour></contour><instructions><assembly></assembly></instructions>
+    </TTGlyph>
+  </glyf>
+
+  <fvar>
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <MinValue>-1.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <NameID>256</NameID>
+    </Axis>
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <MinValue>-1.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <NameID>256</NameID>
+    </Axis>
+  </fvar>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxis
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      MultiAxisFontTest-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      MultiAxis
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFont Test
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MultiAxisFontTest-Regular
+    </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/NoCmapFormat14.ttf b/tests/data/NoCmapFormat14.ttf
new file mode 100644
index 0000000..2a0c46c
--- /dev/null
+++ b/tests/data/NoCmapFormat14.ttf
Binary files differ
diff --git a/tests/data/NoCmapFormat14.ttx b/tests/data/NoCmapFormat14.ttx
new file mode 100644
index 0000000..3c7411b
--- /dev/null
+++ b/tests/data/NoCmapFormat14.ttx
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2016 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>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="defaultGlyph"/>
+  </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="30"/>
+    <yMin value="-200"/>
+    <xMax value="629"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="659"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="30"/>
+    <xMaxExtent value="629"/>
+    <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="18"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="54"/>
+    <maxPoints value="73"/>
+    <maxContours value="10"/>
+    <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="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="defaultGlyph" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x5380" name="defaultGlyph" />
+    </cmap_format_4>
+    <!-- Do not add cmap_format_14 here since this font is desinged for testing purpose. -->
+  </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="0" yMax="0">
+      <contour></contour><instructions><assembly></assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="defaultGlyph" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour></contour><instructions><assembly></assembly></instructions>
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      No Cmap Format 14 Subtable Test
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      No Cmap Format 14 Subtable Test
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      No Cmap Format 14 SubtableTest-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      No Cmap Format 14 Subtable Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      No Cmap Format 14 Subtable Test
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      No Cmap Format 14 SubtableTest-Regular
+    </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/UnicodeBMPOnly.ttf b/tests/data/UnicodeBMPOnly.ttf
new file mode 100644
index 0000000..8196669
--- /dev/null
+++ b/tests/data/UnicodeBMPOnly.ttf
Binary files differ
diff --git a/tests/data/UnicodeBMPOnly.ttx b/tests/data/UnicodeBMPOnly.ttx
new file mode 100644
index 0000000..b50a3f0
--- /dev/null
+++ b/tests/data/UnicodeBMPOnly.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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="a"/>
+  </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:31: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="a" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="1" language="0">
+      <map code="0x0061" name="a" />
+    </cmap_format_4>
+  </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="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </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>
+  </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/UnicodeBMPOnly2.ttf b/tests/data/UnicodeBMPOnly2.ttf
new file mode 100644
index 0000000..c14b195
--- /dev/null
+++ b/tests/data/UnicodeBMPOnly2.ttf
Binary files differ
diff --git a/tests/data/UnicodeBMPOnly2.ttx b/tests/data/UnicodeBMPOnly2.ttx
new file mode 100644
index 0000000..e43ebf8
--- /dev/null
+++ b/tests/data/UnicodeBMPOnly2.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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="a"/>
+  </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:31: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="a" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="2" language="0">
+      <map code="0x0061" name="a" />
+    </cmap_format_4>
+  </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="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </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>
+  </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/UnicodeUCS4.ttf b/tests/data/UnicodeUCS4.ttf
new file mode 100644
index 0000000..354e1a3
--- /dev/null
+++ b/tests/data/UnicodeUCS4.ttf
Binary files differ
diff --git a/tests/data/UnicodeUCS4.ttx b/tests/data/UnicodeUCS4.ttx
new file mode 100644
index 0000000..da5575d
--- /dev/null
+++ b/tests/data/UnicodeUCS4.ttx
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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="a"/>
+  </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:31: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="a" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="1" language="0">
+      <map code="0x0061" name="a" />
+    </cmap_format_4>
+    <cmap_format_12 format="12" reserved="0" length="2" nGroups="1" platformID="0" platEncID="4" language="0">
+      <map code="0x0061" name="a" />
+      <map code="0x1F926" name="a" />
+    </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="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </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>
+  </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/VariationSelectorTest-Regular.ttf b/tests/data/VariationSelectorTest-Regular.ttf
new file mode 100644
index 0000000..0504c67
--- /dev/null
+++ b/tests/data/VariationSelectorTest-Regular.ttf
Binary files differ
diff --git a/tests/data/VarioationSelectorTest-Regular.ttx b/tests/data/VariationSelectorTest-Regular.ttx
similarity index 65%
rename from tests/data/VarioationSelectorTest-Regular.ttx
rename to tests/data/VariationSelectorTest-Regular.ttx
index a063a5e..f86f008 100644
--- a/tests/data/VarioationSelectorTest-Regular.ttx
+++ b/tests/data/VariationSelectorTest-Regular.ttx
@@ -18,18 +18,7 @@
   <GlyphOrder>
     <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
     <GlyphID id="0" name=".notdef"/>
-    <GlyphID id="1" name="BaseChar1"/>
-    <GlyphID id="2" name="BaseChar1_VS1"/>
-    <GlyphID id="3" name="BaseChar1_VS17"/>
-    <GlyphID id="4" name="BaseChar1_VS18"/>
-    <GlyphID id="5" name="BaseChar2"/>
-    <GlyphID id="6" name="BaseChar2_VS2"/>
-    <GlyphID id="7" name="BaseChar2_VS18"/>
-    <GlyphID id="8" name="BaseChar2_VS19"/>
-    <GlyphID id="9" name="BaseChar3"/>
-    <GlyphID id="10" name="BaseChar4_VS3"/>
-    <GlyphID id="11" name="BaseChar4_VS19"/>
-    <GlyphID id="12" name="BaseChar4_VS20"/>
+    <GlyphID id="1" name="defaultGlyph"/>
   </GlyphOrder>
 
   <head>
@@ -147,45 +136,36 @@
 
   <hmtx>
     <mtx name=".notdef" width="500" lsb="93"/>
-    <mtx name="BaseChar1" width="500" lsb="93"/>
-    <mtx name="BaseChar1_VS1" width="500" lsb="93"/>
-    <mtx name="BaseChar1_VS17" width="500" lsb="93"/>
-    <mtx name="BaseChar1_VS18" width="500" lsb="93"/>
-    <mtx name="BaseChar2" width="500" lsb="93"/>
-    <mtx name="BaseChar2_VS2" width="500" lsb="93"/>
-    <mtx name="BaseChar2_VS18" width="500" lsb="93"/>
-    <mtx name="BaseChar2_VS19" width="500" lsb="93"/>
-    <mtx name="BaseChar3" width="500" lsb="93"/>
-    <mtx name="BaseChar4_VS3" width="500" lsb="93"/>
-    <mtx name="BaseChar4_VS19" width="500" lsb="93"/>
-    <mtx name="BaseChar4_VS20" width="500" lsb="93"/>
+    <mtx name="defaultGlyph" width="500" lsb="93"/>
   </hmtx>
 
   <cmap>
     <tableVersion version="0"/>
     <cmap_format_4 platformID="3" platEncID="10" language="0">
-      <map code="0x82A6" name="BaseChar1" />
-      <map code="0x845B" name="BaseChar2" />
-      <map code="0x537F" name="BaseChar3" />
+      <map code="0x82A6" name="defaultGlyph" />
+      <map code="0x845B" name="defaultGlyph" />
+      <map code="0x537F" name="defaultGlyph" />
+      <map code="0x5380" name="defaultGlyph" />
     </cmap_format_4>
     <cmap_format_14 format="14" platformID="0" platEncID="5" length="40" numVarSelectorRecords="3">
-      <map uvs="0xFE00" uv="0x82A6" name="BaseChar1_VS1" />
-      <map uvs="0xE0100" uv="0x82A6" name="BaseChar1_VS17" />
-      <map uvs="0xE0101" uv="0x82A6" name="BaseChar1_VS18" />
+      <map uvs="0xFE00" uv="0x82A6" name="None" />
+      <map uvs="0xE0100" uv="0x82A6" name="None" />
+      <map uvs="0xE0101" uv="0x82A6" name="None" />
       <map uvs="0xE0102" uv="0x82A6" name="None" />
 
-      <map uvs="0xFE01" uv="0x845B" name="BaseChar2_VS2" />
-      <map uvs="0xE0101" uv="0x845B" name="BaseChar2_VS18" />
-      <map uvs="0xE0102" uv="0x845B" name="BaseChar2_VS19" />
+      <map uvs="0xFE01" uv="0x845B" name="None" />
+      <map uvs="0xE0101" uv="0x845B" name="None" />
+      <map uvs="0xE0102" uv="0x845B" name="None" />
       <map uvs="0xE0103" uv="0x845B" name="None" />
 
-      <map uvs="0xFE02" uv="0x717D" name="BaseChar4_VS3" />
-      <map uvs="0xE0102" uv="0x717D" name="BaseChar4_VS19" />
-      <map uvs="0xE0103" uv="0x717D" name="BaseChar4_VS20" />
+      <map uvs="0xFE02" uv="0x717D" name="defaultGlyph" />
+      <map uvs="0xE0102" uv="0x717D" name="defaultGlyph" />
+      <map uvs="0xE0103" uv="0x717D" name="defaultGlyph" />
       <!-- There is no default glyph for U+717D U+E0104 but there is a entry for
            default UVS entry.  hasVariationSelector should return false in this
            case.  -->
       <map uvs="0xE0104" uv="0x717D" name="None" />
+      <!-- Do not add entry for U+5380. -->
     </cmap_format_14>
   </cmap>
 
@@ -202,40 +182,7 @@
       <contour></contour><instructions><assembly></assembly></instructions>
     </TTGlyph>
 
-    <TTGlyph name="BaseChar1" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar1_VS1" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar1_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar1_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar2" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar2_VS2" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar2_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar2_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar3" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar4_VS3" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar4_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
-      <contour></contour><instructions><assembly></assembly></instructions>
-    </TTGlyph>
-    <TTGlyph name="BaseChar4_VS20" xMin="0" yMin="0" xMax="0" yMax="0">
+    <TTGlyph name="defaultGlyph" xMin="0" yMin="0" xMax="0" yMax="0">
       <contour></contour><instructions><assembly></assembly></instructions>
     </TTGlyph>
   </glyf>
diff --git a/tests/data/VarioationSelectorTest-Regular.ttf b/tests/data/VarioationSelectorTest-Regular.ttf
deleted file mode 100644
index dfb0b2d..0000000
--- a/tests/data/VarioationSelectorTest-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/tests/how_to_run.txt b/tests/how_to_run.txt
deleted file mode 100644
index bee367b..0000000
--- a/tests/how_to_run.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-mmm -j8 frameworks/minikin/tests &&
-adb push $OUT/data/nativetest/minikin_tests/minikin_tests \
-    /data/nativetest/minikin_tests/minikin_tests &&
-adb push frameworks/minikin/tests/data /data/nativetest/minikin_tests/ &&
-adb shell /data/nativetest/minikin_tests/minikin_tests
diff --git a/tests/perftests/Android.mk b/tests/perftests/Android.mk
new file mode 100644
index 0000000..c60123a
--- /dev/null
+++ b/tests/perftests/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+perftest_src_files := \
+  ../util/FileUtils.cpp \
+  ../util/FontTestUtils.cpp \
+  ../util/MinikinFontForTest.cpp \
+  ../util/UnicodeUtils.cpp \
+  FontCollection.cpp \
+  FontFamily.cpp \
+  FontLanguage.cpp \
+  GraphemeBreak.cpp \
+  Hyphenator.cpp \
+  WordBreaker.cpp \
+  main.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := minikin_perftests
+LOCAL_CPPFLAGS := -Werror -Wall -Wextra
+LOCAL_SRC_FILES := $(perftest_src_files)
+LOCAL_STATIC_LIBRARIES := \
+  libminikin \
+  libxml2
+
+LOCAL_SHARED_LIBRARIES := \
+  libharfbuzz_ng \
+  libicuuc \
+  liblog \
+  libskia
+
+LOCAL_C_INCLUDES := \
+  $(LOCAL_PATH)/../ \
+  $(LOCAL_PATH)/../../libs/minikin \
+  external/harfbuzz_ng/src \
+  external/libxml2/include
+
+include $(BUILD_NATIVE_BENCHMARK)
diff --git a/tests/perftests/FontCollection.cpp b/tests/perftests/FontCollection.cpp
new file mode 100644
index 0000000..fd95cf1
--- /dev/null
+++ b/tests/perftests/FontCollection.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include <memory>
+
+#include <minikin/FontCollection.h>
+#include <util/FontTestUtils.h>
+#include <util/UnicodeUtils.h>
+#include <MinikinInternal.h>
+
+namespace minikin {
+
+const char* SYSTEM_FONT_PATH = "/system/fonts/";
+const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml";
+
+static void BM_FontCollection_construct(benchmark::State& state) {
+    std::vector<std::shared_ptr<FontFamily>> families =
+            getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML);
+    while (state.KeepRunning()) {
+        std::make_shared<FontCollection>(families);
+    }
+}
+
+BENCHMARK(BM_FontCollection_construct);
+
+static void BM_FontCollection_hasVariationSelector(benchmark::State& state) {
+    std::shared_ptr<FontCollection> collection(
+            getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+
+    uint32_t baseCp = state.range(0);
+    uint32_t vsCp = state.range(1);
+
+    char titleBuffer[64];
+    snprintf(titleBuffer, 64, "hasVariationSelector U+%04X,U+%04X", baseCp, vsCp);
+    state.SetLabel(titleBuffer);
+
+    while (state.KeepRunning()) {
+        collection->hasVariationSelector(baseCp, vsCp);
+    }
+}
+
+// TODO: Rewrite with BENCHMARK_CAPTURE for better test name.
+BENCHMARK(BM_FontCollection_hasVariationSelector)
+      ->ArgPair(0x2708, 0xFE0F)
+      ->ArgPair(0x2708, 0xFE0E)
+      ->ArgPair(0x3402, 0xE0100);
+
+struct ItemizeTestCases {
+    std::string itemizeText;
+    std::string languageTag;
+    std::string labelText;
+} ITEMIZE_TEST_CASES[] = {
+    { "'A' 'n' 'd' 'r' 'o' 'i' 'd'", "en", "English" },
+    { "U+4E16", "zh-Hans", "CJK Ideograph" },
+    { "U+4E16", "zh-Hans,zh-Hant,ja,en,es,pt,fr,de", "CJK Ideograph with many language fallback" },
+    { "U+3402 U+E0100", "ja", "CJK Ideograph with variation selector" },
+    { "'A' 'n' U+0E1A U+0E31 U+0645 U+062D U+0648", "en", "Mixture of English, Thai and Arabic" },
+    { "U+2708 U+FE0E", "en", "Emoji with variation selector" },
+    { "U+0031 U+FE0F U+20E3", "en", "KEYCAP" },
+};
+
+static void BM_FontCollection_itemize(benchmark::State& state) {
+    std::shared_ptr<FontCollection> collection(
+            getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+
+    size_t testIndex = state.range(0);
+    state.SetLabel("Itemize: " + ITEMIZE_TEST_CASES[testIndex].labelText);
+
+    uint16_t buffer[64];
+    size_t utf16_length = 0;
+    ParseUnicode(
+            buffer, 64, ITEMIZE_TEST_CASES[testIndex].itemizeText.c_str(), &utf16_length, nullptr);
+    std::vector<FontCollection::Run> result;
+    FontStyle style(FontStyle::registerLanguageList(ITEMIZE_TEST_CASES[testIndex].languageTag));
+
+    android::AutoMutex _l(gMinikinLock);
+    while (state.KeepRunning()) {
+        result.clear();
+        collection->itemize(buffer, utf16_length, style, &result);
+    }
+}
+
+// TODO: Rewrite with BENCHMARK_CAPTURE once it is available in Android.
+BENCHMARK(BM_FontCollection_itemize)
+    ->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(5)->Arg(6);
+
+}  // namespace minikin
diff --git a/tests/perftests/FontFamily.cpp b/tests/perftests/FontFamily.cpp
new file mode 100644
index 0000000..9ab61e1
--- /dev/null
+++ b/tests/perftests/FontFamily.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 <benchmark/benchmark.h>
+
+#include <minikin/FontFamily.h>
+#include "../util/MinikinFontForTest.h"
+
+namespace minikin {
+
+static void BM_FontFamily_create(benchmark::State& state) {
+    std::shared_ptr<MinikinFontForTest> minikinFont =
+            std::make_shared<MinikinFontForTest>("/system/fonts/NotoSansCJK-Regular.ttc", 0);
+
+    while (state.KeepRunning()) {
+        std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(
+                std::vector<Font>({Font(minikinFont, FontStyle())}));
+    }
+}
+
+BENCHMARK(BM_FontFamily_create);
+
+}  // namespace minikin
diff --git a/tests/perftests/FontLanguage.cpp b/tests/perftests/FontLanguage.cpp
new file mode 100644
index 0000000..6c9c84d
--- /dev/null
+++ b/tests/perftests/FontLanguage.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include "FontLanguage.h"
+
+namespace minikin {
+
+static void BM_FontLanguage_en_US(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        FontLanguage language("en-US", 5);
+    }
+}
+BENCHMARK(BM_FontLanguage_en_US);
+
+static void BM_FontLanguage_en_Latn_US(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        FontLanguage language("en-Latn-US", 10);
+    }
+}
+BENCHMARK(BM_FontLanguage_en_Latn_US);
+
+static void BM_FontLanguage_en_Latn_US_u_em_emoji(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        FontLanguage language("en-Latn-US-u-em-emoji", 21);
+    }
+}
+BENCHMARK(BM_FontLanguage_en_Latn_US_u_em_emoji);
+
+}  // namespace minikin
diff --git a/tests/perftests/GraphemeBreak.cpp b/tests/perftests/GraphemeBreak.cpp
new file mode 100644
index 0000000..6d6cf5b
--- /dev/null
+++ b/tests/perftests/GraphemeBreak.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include <cutils/log.h>
+
+#include "minikin/GraphemeBreak.h"
+#include "util/UnicodeUtils.h"
+
+namespace minikin {
+
+const char* ASCII_TEST_STR = "'L' 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' '.'";
+// U+261D: WHITE UP POINTING INDEX
+// U+1F3FD: EMOJI MODIFIER FITZPATRICK TYPE-4
+const char* EMOJI_TEST_STR = "U+261D U+1F3FD U+261D U+1F3FD U+261D U+1F3FD U+261D U+1F3FD";
+// U+1F1FA: REGIONAL INDICATOR SYMBOL LETTER U
+// U+1F1F8: REGIONAL INDICATOR SYMBOL LETTER S
+const char* FLAGS_TEST_STR = "U+1F1FA U+1F1F8 U+1F1FA U+1F1F8 U+1F1FA U+1F1F8";
+
+// TODO: Migrate BENCHMARK_CAPTURE for parameterizing.
+static void BM_GraphemeBreak_Ascii(benchmark::State& state) {
+    size_t result_size;
+    uint16_t buffer[12];
+    ParseUnicode(buffer, 12, ASCII_TEST_STR, &result_size, nullptr);
+    LOG_ALWAYS_FATAL_IF(result_size != 12);
+    const size_t testIndex = state.range(0);
+    while (state.KeepRunning()) {
+        GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
+    }
+}
+BENCHMARK(BM_GraphemeBreak_Ascii)
+    ->Arg(0)  // Begining of the text.
+    ->Arg(1)  // Middle of the text.
+    ->Arg(12);  // End of the text.
+
+static void BM_GraphemeBreak_Emoji(benchmark::State& state) {
+    size_t result_size;
+    uint16_t buffer[12];
+    ParseUnicode(buffer, 12, EMOJI_TEST_STR, &result_size, nullptr);
+    LOG_ALWAYS_FATAL_IF(result_size != 12);
+    const size_t testIndex = state.range(0);
+    while (state.KeepRunning()) {
+        GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
+    }
+}
+BENCHMARK(BM_GraphemeBreak_Emoji)
+    ->Arg(1)  // Middle of emoji modifier sequence.
+    ->Arg(2)  // Middle of the surrogate pairs.
+    ->Arg(3);  // After emoji modifier sequence. Here is boundary of grapheme cluster.
+
+static void BM_GraphemeBreak_Emoji_Flags(benchmark::State& state) {
+    size_t result_size;
+    uint16_t buffer[12];
+    ParseUnicode(buffer, 12, FLAGS_TEST_STR, &result_size, nullptr);
+    LOG_ALWAYS_FATAL_IF(result_size != 12);
+    const size_t testIndex = state.range(0);
+    while (state.KeepRunning()) {
+        GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
+    }
+}
+BENCHMARK(BM_GraphemeBreak_Emoji_Flags)
+    ->Arg(2)  // Middle of flag sequence.
+    ->Arg(4)  // After flag sequence. Here is boundary of grapheme cluster.
+    ->Arg(10); // Middle of 3rd flag sequence.
+
+}  // namespace minikin
diff --git a/tests/perftests/Hyphenator.cpp b/tests/perftests/Hyphenator.cpp
new file mode 100644
index 0000000..2107e05
--- /dev/null
+++ b/tests/perftests/Hyphenator.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include <minikin/Hyphenator.h>
+#include <util/FileUtils.h>
+#include <util/UnicodeUtils.h>
+
+namespace minikin {
+
+const char* enUsHyph = "/system/usr/hyphen-data/hyph-en-us.hyb";
+const int enUsMinPrefix = 2;
+const int enUsMinSuffix = 3;
+const icu::Locale& usLocale = icu::Locale::getUS();
+
+static void BM_Hyphenator_short_word(benchmark::State& state) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(
+            readWholeFile(enUsHyph).data(), enUsMinPrefix, enUsMinSuffix);
+    std::vector<uint16_t> word = utf8ToUtf16("hyphen");
+    std::vector<HyphenationType> result;
+    while (state.KeepRunning()) {
+        hyphenator->hyphenate(&result, word.data(), word.size(), usLocale);
+    }
+    Hyphenator::loadBinary(nullptr, 2, 2);
+}
+
+// TODO: Use BENCHMARK_CAPTURE for parametrise.
+BENCHMARK(BM_Hyphenator_short_word);
+
+static void BM_Hyphenator_long_word(benchmark::State& state) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(
+            readWholeFile(enUsHyph).data(), enUsMinPrefix, enUsMinSuffix);
+    std::vector<uint16_t> word = utf8ToUtf16(
+            "Pneumonoultramicroscopicsilicovolcanoconiosis");
+    std::vector<HyphenationType> result;
+    while (state.KeepRunning()) {
+        hyphenator->hyphenate(&result, word.data(), word.size(), usLocale);
+    }
+    Hyphenator::loadBinary(nullptr, 2, 2);
+}
+
+// TODO: Use BENCHMARK_CAPTURE for parametrise.
+BENCHMARK(BM_Hyphenator_long_word);
+
+// TODO: Add more tests for other languages.
+
+}  // namespace minikin
diff --git a/tests/perftests/WordBreaker.cpp b/tests/perftests/WordBreaker.cpp
new file mode 100644
index 0000000..6758cf9
--- /dev/null
+++ b/tests/perftests/WordBreaker.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include "minikin/WordBreaker.h"
+#include "util/UnicodeUtils.h"
+
+namespace minikin {
+
+static void BM_WordBreaker_English(benchmark::State& state) {
+    const char* kLoremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+        "eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+
+    WordBreaker wb;
+    wb.setLocale(icu::Locale::getEnglish());
+    std::vector<uint16_t> text = utf8ToUtf16(kLoremIpsum);
+    while (state.KeepRunning()) {
+        wb.setText(text.data(), text.size());
+        while (wb.next() != -1) {}
+    }
+}
+BENCHMARK(BM_WordBreaker_English);
+
+// TODO: Add more tests for other languages.
+
+}  // namespace minikin
diff --git a/tests/perftests/how_to_run.txt b/tests/perftests/how_to_run.txt
new file mode 100644
index 0000000..f55a8ac
--- /dev/null
+++ b/tests/perftests/how_to_run.txt
@@ -0,0 +1,3 @@
+mmm -j8 frameworks/minikin/tests/perftests &&
+adb sync data &&
+adb shell /data/benchmarktest/minikin_perftests/minikin_perftests
diff --git a/tests/perftests/main.cpp b/tests/perftests/main.cpp
new file mode 100644
index 0000000..e6f9d14
--- /dev/null
+++ b/tests/perftests/main.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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 <benchmark/benchmark.h>
+
+#include <cutils/log.h>
+
+#include <unicode/uclean.h>
+#include <unicode/udata.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+int main(int argc, char** argv) {
+    const char* fn = "/system/usr/icu/" U_ICUDATA_NAME ".dat";
+    int fd = open(fn, O_RDONLY);
+    LOG_ALWAYS_FATAL_IF(fd == -1);
+    struct stat st;
+    LOG_ALWAYS_FATAL_IF(fstat(fd, &st) != 0);
+    void* data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
+    UErrorCode errorCode = U_ZERO_ERROR;
+    udata_setCommonData(data, &errorCode);
+    LOG_ALWAYS_FATAL_IF(U_FAILURE(errorCode));
+    u_init(&errorCode);
+    LOG_ALWAYS_FATAL_IF(U_FAILURE(errorCode));
+
+    benchmark::Initialize(&argc, argv);
+    benchmark::RunSpecifiedBenchmarks();
+
+    u_cleanup();
+    return 0;
+}
diff --git a/tests/stresstest/Android.mk b/tests/stresstest/Android.mk
new file mode 100644
index 0000000..b655a74
--- /dev/null
+++ b/tests/stresstest/Android.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2017 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.
+
+# see how_to_run.txt for instructions on running these tests
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_TEST_DATA := $(foreach f,$(LOCAL_TEST_DATA),frameworks/minikin/tests:$(f))
+
+LOCAL_MODULE := minikin_stress_tests
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_CLASS := NATIVE_TESTS
+
+LOCAL_STATIC_LIBRARIES := libminikin
+
+# Shared libraries which are dependencies of minikin; these are not automatically
+# pulled in by the build system (and thus sadly must be repeated).
+
+LOCAL_SHARED_LIBRARIES := \
+    libskia \
+    libft2 \
+    libharfbuzz_ng \
+    libicuuc \
+    liblog \
+    libutils \
+    libz
+
+LOCAL_STATIC_LIBRARIES += \
+    libxml2
+
+LOCAL_SRC_FILES += \
+    ../util/FontTestUtils.cpp \
+    ../util/MinikinFontForTest.cpp \
+    FontFamilyTest.cpp \
+    MultithreadTest.cpp \
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/../../libs/minikin/ \
+    $(LOCAL_PATH)/../util \
+    external/libxml2/include \
+
+LOCAL_CPPFLAGS += -Werror -Wall -Wextra
+
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/stresstest/FontFamilyTest.cpp b/tests/stresstest/FontFamilyTest.cpp
new file mode 100644
index 0000000..9d289e5
--- /dev/null
+++ b/tests/stresstest/FontFamilyTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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 "../util/FontTestUtils.h"
+#include "../util/MinikinFontForTest.h"
+#include "HbFontCache.h"
+#include "MinikinInternal.h"
+#include "minikin/FontCollection.h"
+#include "minikin/Layout.h"
+
+namespace minikin {
+
+typedef std::pair<std::string, int> TestParam;
+
+class FontFamilyHarfBuzzCompatibilityTest : public ::testing::TestWithParam<TestParam> {};
+
+TEST_P(FontFamilyHarfBuzzCompatibilityTest, CoverageTest) {
+    const std::string& fontPath = GetParam().first;
+    int ttcIndex = GetParam().second;
+
+    std::shared_ptr<MinikinFont> font(new MinikinFontForTest(fontPath, ttcIndex));
+    std::shared_ptr<FontFamily> family =
+            std::make_shared<FontFamily>(std::vector<Font>({Font(font, FontStyle())}));
+
+    android::AutoMutex _l(gMinikinLock);
+    hb_font_t* hbFont = getHbFontLocked(font.get());
+
+    for (uint32_t codePoint = 0; codePoint < MAX_UNICODE_CODE_POINT; ++codePoint) {
+        uint32_t unusedGlyph;
+        EXPECT_EQ(family->hasGlyph(codePoint, 0 /* variation selector */),
+                static_cast<bool>(hb_font_get_glyph(hbFont, codePoint, 0 /* variation selector */,
+                        &unusedGlyph)));
+    }
+
+    for (uint32_t vs = VS1; vs < VS256; ++vs) {
+        // Move to variation selectors supplements after variation selectors.
+        if (vs == VS16 + 1) {
+            vs = VS17;
+        }
+        for (uint32_t codePoint = 0; codePoint < MAX_UNICODE_CODE_POINT; ++codePoint) {
+            uint32_t unusedGlyph;
+            ASSERT_EQ(family->hasGlyph(codePoint, vs),
+                    static_cast<bool>(hb_font_get_glyph(hbFont, codePoint, vs, &unusedGlyph)))
+                << "Inconsistent Result: " << fontPath << "#" << ttcIndex
+                << ": U+" << std::hex << codePoint << " U+" << std::hex << vs
+                << " Minikin: " << family->hasGlyph(codePoint, vs)
+                << " HarfBuzz: "
+                << static_cast<bool>(hb_font_get_glyph(hbFont, codePoint, vs, &unusedGlyph));
+
+        }
+    }
+    hb_font_destroy(hbFont);
+}
+
+INSTANTIATE_TEST_CASE_P(FontFamilyTest,
+        FontFamilyHarfBuzzCompatibilityTest,
+        ::testing::Values(
+                TestParam("/system/fonts/NotoSansCJK-Regular.ttc", 0),
+                TestParam("/system/fonts/NotoColorEmoji.ttf", 0)));
+}  // namespace minikin
diff --git a/tests/stresstest/MultithreadTest.cpp b/tests/stresstest/MultithreadTest.cpp
new file mode 100644
index 0000000..08c94b9
--- /dev/null
+++ b/tests/stresstest/MultithreadTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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 <condition_variable>
+#include <mutex>
+#include <random>
+#include <thread>
+
+#include <cutils/log.h>
+
+#include "MinikinInternal.h"
+#include "minikin/FontCollection.h"
+#include "minikin/Layout.h"
+#include "../util/FontTestUtils.h"
+
+namespace minikin {
+
+const char* SYSTEM_FONT_PATH = "/system/fonts/";
+const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml";
+
+constexpr int LAYOUT_COUNT_PER_COLLECTION = 500;
+constexpr int COLLECTION_COUNT_PER_THREAD = 15;
+constexpr int NUM_THREADS = 10;
+
+std::mutex gMutex;
+std::condition_variable gCv;
+bool gReady = false;
+
+static std::vector<uint16_t> generateTestText(
+        std::mt19937* mt, int lettersInWord, int wordsInText) {
+    std::uniform_int_distribution<uint16_t> dist('A', 'Z');
+
+    std::vector<uint16_t> text;
+    text.reserve((lettersInWord + 1) * wordsInText - 1);
+    for (int i = 0; i < wordsInText; ++i) {
+        if (i != 0) {
+            text.emplace_back(' ');
+        }
+        for (int j = 0; j < lettersInWord; ++j) {
+            text.emplace_back(dist(*mt));
+        }
+    }
+    return text;
+}
+
+static void thread_main(int tid) {
+    {
+        // Wait until all threads are created.
+        std::unique_lock<std::mutex> lock(gMutex);
+        gCv.wait(lock, [] { return gReady; });
+    }
+
+    std::mt19937 mt(tid);
+    MinikinPaint paint;
+
+    for (int i = 0; i < COLLECTION_COUNT_PER_THREAD; ++i) {
+        std::shared_ptr<FontCollection> collection(
+                getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+
+        for (int j = 0; j < LAYOUT_COUNT_PER_COLLECTION; ++j) {
+            // Generates 10 of 3-letter words so that the word sometimes hit the cache.
+            Layout layout;
+            std::vector<uint16_t> text = generateTestText(&mt, 3, 10);
+            layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(),
+                    paint, collection);
+            std::vector<float> advances(text.size());
+            layout.getAdvances(advances.data());
+            for (size_t k = 0; k < advances.size(); ++k) {
+                // MinikinFontForTest always returns 10.0f for horizontal advance.
+                LOG_ALWAYS_FATAL_IF(advances[k] != 10.0f, "Memory corruption detected.");
+            }
+        }
+    }
+}
+
+TEST(MultithreadTest, ThreadSafeStressTest) {
+    std::vector<std::thread> threads;
+
+    {
+        std::unique_lock<std::mutex> lock(gMutex);
+        threads.reserve(NUM_THREADS);
+        for (int i = 0; i < NUM_THREADS; ++i) {
+            threads.emplace_back(&thread_main, i);
+        }
+        gReady = true;
+    }
+    gCv.notify_all();
+
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+}  // namespace minikin
diff --git a/tests/stresstest/how_to_run.txt b/tests/stresstest/how_to_run.txt
new file mode 100644
index 0000000..ba4dbdf
--- /dev/null
+++ b/tests/stresstest/how_to_run.txt
@@ -0,0 +1,3 @@
+mmm -j8 frameworks/minikin/tests/stresstest &&
+adb sync data &&
+adb shell /data/nativetest/minikin_tests/minikin_stress_tests
diff --git a/tests/Android.mk b/tests/unittest/Android.mk
similarity index 66%
rename from tests/Android.mk
rename to tests/unittest/Android.mk
index b33631e..b817c46 100644
--- a/tests/Android.mk
+++ b/tests/unittest/Android.mk
@@ -18,41 +18,36 @@
 
 include $(CLEAR_VARS)
 
-data_root_for_test_zip := $(TARGET_OUT_DATA)/DATA/
-minikin_tests_subpath_from_data := nativetest/minikin_tests
-minikin_tests_root_in_device := /data/$(minikin_tests_subpath_from_data)
-minikin_tests_root_for_test_zip := $(data_root_for_test_zip)/$(minikin_tests_subpath_from_data)
-
-font_src_files := \
-    data/BoldItalic.ttf \
+LOCAL_TEST_DATA := \
     data/Bold.ttf \
+    data/BoldItalic.ttf \
     data/ColorEmojiFont.ttf \
     data/ColorTextMixedEmojiFont.ttf \
     data/Emoji.ttf \
     data/Italic.ttf \
     data/Ja.ttf \
     data/Ko.ttf \
+    data/MultiAxis.ttf \
+    data/NoCmapFormat14.ttf \
     data/NoGlyphFont.ttf \
     data/Regular.ttf \
     data/TextEmojiFont.ttf \
-    data/VarioationSelectorTest-Regular.ttf \
+    data/UnicodeBMPOnly.ttf \
+    data/UnicodeBMPOnly2.ttf \
+    data/UnicodeUCS4.ttf \
+    data/VariationSelectorTest-Regular.ttf \
     data/ZhHans.ttf \
     data/ZhHant.ttf \
+    data/emoji.xml \
     data/itemize.xml \
-    data/emoji.xml
+
+LOCAL_TEST_DATA := $(foreach f,$(LOCAL_TEST_DATA),frameworks/minikin/tests:$(f))
 
 LOCAL_MODULE := minikin_tests
 LOCAL_MODULE_TAGS := tests
-
-GEN := $(addprefix $(minikin_tests_root_for_test_zip)/, $(font_src_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(minikin_tests_root_for_test_zip)/data/% : $(LOCAL_PATH)/data/%
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
+LOCAL_MODULE_CLASS := NATIVE_TESTS
 
 LOCAL_STATIC_LIBRARIES := libminikin
-LOCAL_PICKUP_FILES := $(data_root_for_test_zip)
 
 # Shared libraries which are dependencies of minikin; these are not automatically
 # pulled in by the build system (and thus sadly must be repeated).
@@ -70,26 +65,36 @@
     libxml2
 
 LOCAL_SRC_FILES += \
+    ../util/FileUtils.cpp \
+    ../util/FontTestUtils.cpp \
+    ../util/MinikinFontForTest.cpp \
+    ../util/UnicodeUtils.cpp \
+    CmapCoverageTest.cpp \
+    EmojiTest.cpp \
     FontCollectionTest.cpp \
     FontCollectionItemizeTest.cpp \
     FontFamilyTest.cpp \
     FontLanguageListCacheTest.cpp \
-    FontTestUtils.cpp \
     HbFontCacheTest.cpp \
-    MinikinFontForTest.cpp \
-    MinikinInternalTest.cpp \
+    HyphenatorTest.cpp \
     GraphemeBreakTests.cpp \
+    LayoutTest.cpp \
     LayoutUtilsTest.cpp \
-    UnicodeUtils.cpp \
+    MeasurementTests.cpp \
+    SparseBitSetTest.cpp \
+    UnicodeUtilsTest.cpp \
     WordBreakerTests.cpp
 
 LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/../libs/minikin/ \
+    $(LOCAL_PATH)/../../libs/minikin/ \
+    $(LOCAL_PATH)/../util \
     external/harfbuzz_ng/src \
     external/libxml2/include \
     external/skia/src/core
 
-LOCAL_CPPFLAGS += -Werror -Wall -Wextra \
-    -DkTestFontDir="\"$(minikin_tests_root_in_device)/data/\""
+LOCAL_CPPFLAGS += -Werror -Wall -Wextra
+
+LOCAL_CPPFLAGS_32 += -DkTestFontDir="\"/data/nativetest/minikin_tests/data/\""
+LOCAL_CPPFLAGS_64 += -DkTestFontDir="\"/data/nativetest64/minikin_tests/data/\""
 
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/unittest/CmapCoverageTest.cpp b/tests/unittest/CmapCoverageTest.cpp
new file mode 100644
index 0000000..fe2d7ba
--- /dev/null
+++ b/tests/unittest/CmapCoverageTest.cpp
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (C) 2017 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 <random>
+
+#include <log/log.h>
+#include <gtest/gtest.h>
+#include <minikin/CmapCoverage.h>
+#include <minikin/SparseBitSet.h>
+
+#include "MinikinInternal.h"
+
+namespace minikin {
+
+static constexpr uint16_t VS_PLATFORM_ID = 0;
+static constexpr uint16_t VS_ENCODING_ID = 5;
+
+size_t writeU8(uint8_t x, uint8_t* out, size_t offset) {
+    out[offset] = x;
+    return offset + 1;
+}
+
+size_t writeU16(uint16_t x, uint8_t* out, size_t offset) {
+    out[offset] = x >> 8;
+    out[offset + 1] = x;
+    return offset + 2;
+}
+
+size_t writeI16(int16_t sx, uint8_t* out, size_t offset) {
+    return writeU16(static_cast<uint16_t>(sx), out, offset);
+}
+
+size_t writeU24(uint32_t x, uint8_t* out, size_t offset) {
+    out[offset] = x >> 16;
+    out[offset + 1] = x >> 8;
+    out[offset + 2] = x;
+    return offset + 3;
+}
+
+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;
+}
+
+// Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
+// 'a' (U+0061) is mapped to Glyph ID = 0x0061).
+// 'range' should be specified with inclusive-inclusive values.
+static std::vector<uint8_t> buildCmapFormat4Table(const std::vector<uint16_t>& ranges) {
+    uint16_t segmentCount = ranges.size() / 2 + 1 /* +1 for end marker */;
+
+    const size_t numOfUint16 =
+        8 /* format, length, languages, segCountX2, searchRange, entrySelector, rangeShift, pad */ +
+        segmentCount * 4 /* endCount, startCount, idRange, idRangeOffset */;
+    const size_t finalLength = sizeof(uint16_t) * numOfUint16;
+
+    std::vector<uint8_t> out(finalLength);
+    size_t head = 0;
+    head = writeU16(4, out.data(), head);  // format
+    head = writeU16(finalLength, out.data(), head);  // length
+    head = writeU16(0, out.data(), head);  // langauge
+
+    const uint16_t searchRange = 2 * (1 << static_cast<int>(floor(log2(segmentCount))));
+
+    head = writeU16(segmentCount * 2, out.data(), head);  // segCountX2
+    head = writeU16(searchRange, out.data(), head);  // searchRange
+    head = writeU16(__builtin_ctz(searchRange) - 1, out.data(), head); // entrySelector
+    head = writeU16(segmentCount * 2 - searchRange, out.data(), head);  // rangeShift
+
+    size_t endCountHead = head;
+    size_t startCountHead = head + segmentCount * sizeof(uint16_t) + 2 /* padding */;
+    size_t idDeltaHead = startCountHead + segmentCount * sizeof(uint16_t);
+    size_t idRangeOffsetHead = idDeltaHead + segmentCount * sizeof(uint16_t);
+
+    for (size_t i = 0; i < ranges.size() / 2; ++i) {
+        const uint16_t begin = ranges[i * 2];
+        const uint16_t end = ranges[i * 2 + 1];
+        startCountHead = writeU16(begin, out.data(), startCountHead);
+        endCountHead = writeU16(end, out.data(), endCountHead);
+        // map glyph ID as the same value of the code point.
+        idDeltaHead = writeU16(0, out.data(), idDeltaHead);
+        idRangeOffsetHead = writeU16(0 /* we don't use this */, out.data(), idRangeOffsetHead);
+    }
+
+    // fill end marker
+    endCountHead = writeU16(0xFFFF, out.data(), endCountHead);
+    startCountHead = writeU16(0xFFFF, out.data(), startCountHead);
+    idDeltaHead = writeU16(1, out.data(), idDeltaHead);
+    idRangeOffsetHead = writeU16(0, out.data(), idRangeOffsetHead);
+    LOG_ALWAYS_FATAL_IF(endCountHead > finalLength);
+    LOG_ALWAYS_FATAL_IF(startCountHead > finalLength);
+    LOG_ALWAYS_FATAL_IF(idDeltaHead > finalLength);
+    LOG_ALWAYS_FATAL_IF(idRangeOffsetHead != finalLength);
+    return out;
+}
+
+// Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
+// 'a' (U+0061) is mapped to Glyph ID = 0x0061).
+// 'range' should be specified with inclusive-inclusive values.
+static std::vector<uint8_t> buildCmapFormat12Table(const std::vector<uint32_t>& ranges) {
+    uint32_t numGroups  = ranges.size() / 2;
+
+    const size_t finalLength = 2 /* format */ + 2 /* reserved */ + 4 /* length */ +
+        4 /* languages */ + 4 /* numGroups */ + 12 /* size of a group */ * numGroups;
+
+    std::vector<uint8_t> out(finalLength);
+    size_t head = 0;
+    head = writeU16(12, out.data(), head);  // format
+    head = writeU16(0, out.data(), head);  // reserved
+    head = writeU32(finalLength, out.data(), head);  // length
+    head = writeU32(0, out.data(), head);  // langauge
+    head = writeU32(numGroups, out.data(), head);  // numGroups
+
+    for (uint32_t i = 0; i < numGroups; ++i) {
+        const uint32_t start = ranges[2 * i];
+        const uint32_t end = ranges[2 * i + 1];
+        head = writeU32(start, out.data(), head);
+        head = writeU32(end, out.data(), head);
+        // map glyph ID as the same value of the code point.
+        // TODO: Use glyph IDs lower than 65535.
+        // Cmap can store 32 bit glyph ID but due to the size of numGlyph, a font file can contain
+        // up to 65535 glyphs in a file.
+        head = writeU32(start, out.data(), head);
+    }
+
+    LOG_ALWAYS_FATAL_IF(head != finalLength);
+    return out;
+}
+
+struct VariationSelectorRecord {
+    uint32_t codePoint;
+    std::vector<uint32_t> defaultUVSRanges;
+    std::vector<uint32_t> nonDefaultUVS;
+
+    std::vector<uint8_t> getDefaultUVSAsBinary() const {
+        if (defaultUVSRanges.empty()) {
+            return std::vector<uint8_t>();
+        }
+        const size_t numOfRanges = defaultUVSRanges.size() / 2;
+        const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
+            numOfRanges * 4 /* size of Unicode Range Table */;
+
+        std::vector<uint8_t> out(length);
+        size_t head = 0;
+        head = writeU32(numOfRanges, out.data(), head);
+        for (size_t i = 0; i < numOfRanges; ++i) {
+            const uint32_t startUnicodeValue = defaultUVSRanges[i * 2];
+            const uint32_t endUnicodeValue = defaultUVSRanges[i * 2 + 1];
+            head = writeU24(startUnicodeValue, out.data(), head);
+            head = writeU8(endUnicodeValue - startUnicodeValue, out.data(), head);
+        }
+        LOG_ALWAYS_FATAL_IF(head != length);
+        return out;
+    }
+
+    std::vector<uint8_t> getNonDefaultUVSAsBinary() const {
+        if (nonDefaultUVS.empty()) {
+            return std::vector<uint8_t>();
+        }
+        const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
+            nonDefaultUVS.size() * 5 /* size of UVS Mapping Record */;
+
+        std::vector<uint8_t> out(length);
+        size_t head = 0;
+        head = writeU32(nonDefaultUVS.size(), out.data(), head);
+        for (uint32_t codePoint : nonDefaultUVS) {
+            head = writeU24(codePoint, out.data(), head);
+            head = writeU16(4 /* fixed glyph id */, out.data(), head);
+        }
+        LOG_ALWAYS_FATAL_IF(head != length);
+        return out;
+    }
+};
+
+static std::vector<uint8_t> buildCmapFormat14Table(
+        const std::vector<VariationSelectorRecord>& vsRecords) {
+
+    const size_t headerLength = sizeof(uint16_t) /* format */ + sizeof(uint32_t) /* length */ +
+            sizeof(uint32_t) /* numVarSelectorRecords */ +
+            11 /* size of variation selector record */ * vsRecords.size();
+
+    std::vector<uint8_t> out(headerLength);
+    size_t head = 0;
+    head = writeU16(14, out.data(), head);  // format
+    head += sizeof(uint32_t);  // length will be filled later
+    head = writeU32(vsRecords.size(), out.data(), head);  // numVarSelectorRecords;
+
+    for (const auto& record : vsRecords) {
+        const uint32_t vsCodePoint = record.codePoint;
+        head = writeU24(vsCodePoint, out.data(), head);
+
+        std::vector<uint8_t> defaultUVS = record.getDefaultUVSAsBinary();
+        if (defaultUVS.empty()) {
+            head = writeU32(0, out.data(), head);
+        } else {
+            head = writeU32(out.size(), out.data(), head);
+            out.insert(out.end(), defaultUVS.begin(), defaultUVS.end());
+        }
+
+        std::vector<uint8_t> nonDefaultUVS = record.getNonDefaultUVSAsBinary();
+        if (nonDefaultUVS.empty()) {
+            head = writeU32(0, out.data(), head);
+        } else {
+            head = writeU32(out.size(), out.data(), head);
+            out.insert(out.end(), nonDefaultUVS.begin(), nonDefaultUVS.end());
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(head != headerLength);
+    writeU32(out.size(), out.data(), 2);  // fill the length.
+    return out;
+}
+
+class CmapBuilder {
+public:
+    static constexpr size_t kEncodingTableHead = 4;
+    static constexpr size_t kEncodingTableSize = 8;
+
+    CmapBuilder(int numTables) : mNumTables(numTables), mCurrentTableIndex(0) {
+        const size_t headerSize =
+            2 /* version */ + 2 /* numTables */ + kEncodingTableSize * numTables;
+        out.resize(headerSize);
+        writeU16(0, out.data(), 0);
+        writeU16(numTables, out.data(), 2);
+    }
+
+    void appendTable(uint16_t platformId, uint16_t encodingId,
+            const std::vector<uint8_t>& table) {
+        appendEncodingTable(platformId, encodingId, out.size());
+        out.insert(out.end(), table.begin(), table.end());
+    }
+
+    std::vector<uint8_t> build() {
+        LOG_ALWAYS_FATAL_IF(mCurrentTableIndex != mNumTables);
+        return out;
+    }
+
+    // Helper functions.
+    static std::vector<uint8_t> buildSingleFormat4Cmap(uint16_t platformId, uint16_t encodingId,
+            const std::vector<uint16_t>& ranges) {
+        CmapBuilder builder(1);
+        builder.appendTable(platformId, encodingId, buildCmapFormat4Table(ranges));
+        return builder.build();
+    }
+
+    static std::vector<uint8_t> buildSingleFormat12Cmap(uint16_t platformId, uint16_t encodingId,
+            const std::vector<uint32_t>& ranges) {
+        CmapBuilder builder(1);
+        builder.appendTable(platformId, encodingId, buildCmapFormat12Table(ranges));
+        return builder.build();
+    }
+
+private:
+    void appendEncodingTable(uint16_t platformId, uint16_t encodingId, uint32_t offset) {
+        LOG_ALWAYS_FATAL_IF(mCurrentTableIndex == mNumTables);
+
+        const size_t currentEncodingTableHead =
+                kEncodingTableHead + mCurrentTableIndex * kEncodingTableSize;
+        size_t head = writeU16(platformId, out.data(), currentEncodingTableHead);
+        head = writeU16(encodingId, out.data(), head);
+        head = writeU32(offset, out.data(), head);
+        LOG_ALWAYS_FATAL_IF((head - currentEncodingTableHead) != kEncodingTableSize);
+        mCurrentTableIndex++;
+    }
+
+    int mNumTables;
+    int mCurrentTableIndex;
+    std::vector<uint8_t> out;
+};
+
+TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("Reading beyond buffer size - Too small cmap size");
+        std::vector<uint8_t> cmap =
+                CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
+
+        SparseBitSet coverage =
+                CmapCoverage::getCoverage(cmap.data(), 3 /* too small */, &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reading beyond buffer size - space needed for tables goes beyond cmap size");
+        std::vector<uint8_t> cmap =
+                CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
+
+        writeU16(1000, cmap.data(), 2 /* offset of num tables in cmap header */);
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reading beyond buffer size - Invalid offset in encoding table");
+        std::vector<uint8_t> cmap =
+                CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
+
+        writeU16(1000, cmap.data(), 8 /* offset of the offset in the first encoding record */);
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>(
+                {'b', 'b', 'a', 'a'}));
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range - partially readable");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>(
+                { 'a', 'a', 'c', 'c', 'b', 'b'}));
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, SingleFormat4) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    struct TestCast {
+        std::string testTitle;
+        uint16_t platformId;
+        uint16_t encodingId;
+    } TEST_CASES[] = {
+        { "Platform 0, Encoding 0", 0, 0 },
+        { "Platform 0, Encoding 1", 0, 1 },
+        { "Platform 0, Encoding 2", 0, 2 },
+        { "Platform 0, Encoding 3", 0, 3 },
+        { "Platform 3, Encoding 1", 3, 1 },
+    };
+
+    for (const auto& testCase : TEST_CASES) {
+        SCOPED_TRACE(testCase.testTitle.c_str());
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
+                testCase.platformId, testCase.encodingId, std::vector<uint16_t>({'a', 'a'}));
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));
+        EXPECT_FALSE(coverage.get('b'));
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, SingleFormat12) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+
+    struct TestCast {
+        std::string testTitle;
+        uint16_t platformId;
+        uint16_t encodingId;
+    } TEST_CASES[] = {
+        { "Platform 0, Encoding 4", 0, 4 },
+        { "Platform 0, Encoding 6", 0, 6 },
+        { "Platform 3, Encoding 10", 3, 10 },
+    };
+
+    for (const auto& testCase : TEST_CASES) {
+        SCOPED_TRACE(testCase.testTitle.c_str());
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                testCase.platformId, testCase.encodingId, std::vector<uint32_t>({'a', 'a'}));
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));
+        EXPECT_FALSE(coverage.get('b'));
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("Starting range is out of Unicode code point. Should be ignored.");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                0, 0, std::vector<uint32_t>({'a', 'a', 0x110000, 0x110000}));
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));
+        EXPECT_FALSE(coverage.get(0x110000));
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Ending range is out of Unicode code point. Should be ignored.");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                0, 0, std::vector<uint32_t>({'a', 'a', 0x10FF00, 0x110000}));
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));
+        EXPECT_TRUE(coverage.get(0x10FF00));
+        EXPECT_TRUE(coverage.get(0x10FFFF));
+        EXPECT_FALSE(coverage.get(0x110000));
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, notSupportedEncodings) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+
+    struct TestCast {
+        std::string testTitle;
+        uint16_t platformId;
+        uint16_t encodingId;
+    } TEST_CASES[] = {
+        // Any encodings with platform 2 is not supported.
+        { "Platform 2, Encoding 0", 2, 0 },
+        { "Platform 2, Encoding 1", 2, 1 },
+        { "Platform 2, Encoding 2", 2, 2 },
+        { "Platform 2, Encoding 3", 2, 3 },
+        // UCS-2 or UCS-4 are supported on Platform == 3. Others are not supported.
+        { "Platform 3, Encoding 0", 3, 0 },  // Symbol
+        { "Platform 3, Encoding 2", 3, 2 },  // ShiftJIS
+        { "Platform 3, Encoding 3", 3, 3 },  // RPC
+        { "Platform 3, Encoding 4", 3, 4 },  // Big5
+        { "Platform 3, Encoding 5", 3, 5 },  // Wansung
+        { "Platform 3, Encoding 6", 3, 6 },  // Johab
+        { "Platform 3, Encoding 7", 3, 7 },  // Reserved
+        { "Platform 3, Encoding 8", 3, 8 },  // Reserved
+        { "Platform 3, Encoding 9", 3, 9 },  // Reserved
+        // Uknown platforms
+        { "Platform 4, Encoding 0", 4, 0 },
+        { "Platform 5, Encoding 1", 5, 1 },
+        { "Platform 6, Encoding 0", 6, 0 },
+        { "Platform 7, Encoding 1", 7, 1 },
+    };
+
+    for (const auto& testCase : TEST_CASES) {
+        SCOPED_TRACE(testCase.testTitle.c_str());
+        CmapBuilder builder(1);
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
+                testCase.platformId, testCase.encodingId, std::vector<uint16_t>({'a', 'a'}));
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, brokenFormat4Table) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("Too small table cmap size");
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
+        table.resize(2);  // Remove trailing data.
+
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too many segments");
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
+        writeU16(5000, table.data(), 6 /* segment count offset */);  // 5000 segments.
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Inversed range");
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
+        // Put smaller end code point to inverse the range.
+        writeU16('a', table.data(), 14 /* the first element of endCount offset */);
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, brokenFormat12Table) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("Too small cmap size");
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+        table.resize(2);  // Remove trailing data.
+
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too many groups");
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+        writeU32(5000, table.data(), 12 /* num group offset */);  // 5000 groups.
+
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Inversed range.");
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+        // Put larger start code point to inverse the range.
+        writeU32('b', table.data(), 16 /* start code point offset in the first  group */);
+
+        CmapBuilder builder(1);
+        builder.appendTable(0, 0, table);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too large code point");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                0, 0, std::vector<uint32_t>({0x110000, 0x110000}));
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                0, 0, std::vector<uint32_t>({'b', 'b', 'a', 'a'}));
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range - partially readable");
+        std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
+                0, 0, std::vector<uint32_t>({'a', 'a', 'c', 'c', 'b', 'b'}));
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_EQ(0U, coverage.length());
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, TableSelection_Priority) {
+    std::vector<uint8_t> highestFormat12Table =
+            buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+    std::vector<uint8_t> highestFormat4Table =
+            buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
+    std::vector<uint8_t> format4 = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
+    std::vector<uint8_t> format12 = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
+
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("(platform, encoding) = (3, 10) is the highest priority.");
+
+        struct LowerPriorityTable {
+            uint16_t platformId;
+            uint16_t encodingId;
+            const std::vector<uint8_t>& table;
+        } LOWER_PRIORITY_TABLES[] = {
+            { 0, 0, format4 },
+            { 0, 1, format4 },
+            { 0, 2, format4 },
+            { 0, 3, format4 },
+            { 0, 4, format12 },
+            { 0, 6, format12 },
+            { 3, 1, format4 },
+        };
+
+        for (const auto& table : LOWER_PRIORITY_TABLES) {
+            CmapBuilder builder(2);
+            builder.appendTable(table.platformId, table.encodingId, table.table);
+            builder.appendTable(3, 10, highestFormat12Table);
+            std::vector<uint8_t> cmap = builder.build();
+
+            SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+            EXPECT_TRUE(coverage.get('a'));  // comes from highest table
+            EXPECT_FALSE(coverage.get('b'));  // should not use other table.
+            EXPECT_TRUE(vsTables.empty());
+        }
+    }
+    {
+        SCOPED_TRACE("(platform, encoding) = (3, 1) case");
+
+        struct LowerPriorityTable {
+            uint16_t platformId;
+            uint16_t encodingId;
+            const std::vector<uint8_t>& table;
+        } LOWER_PRIORITY_TABLES[] = {
+            { 0, 0, format4 },
+            { 0, 1, format4 },
+            { 0, 2, format4 },
+            { 0, 3, format4 },
+        };
+
+        for (const auto& table : LOWER_PRIORITY_TABLES) {
+            CmapBuilder builder(2);
+            builder.appendTable(table.platformId, table.encodingId, table.table);
+            builder.appendTable(3, 1, highestFormat4Table);
+            std::vector<uint8_t> cmap = builder.build();
+
+            SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+            EXPECT_TRUE(coverage.get('a'));  // comes from highest table
+            EXPECT_FALSE(coverage.get('b'));  // should not use other table.
+            EXPECT_TRUE(vsTables.empty());
+        }
+    }
+}
+
+TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
+    std::vector<uint8_t> validTable = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    {
+        SCOPED_TRACE("Unsupported format");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
+        writeU16(0, table.data(), 0 /* format offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid language");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
+        writeU16(1, table.data(), 4 /* language offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid length");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
+        writeU16(5000, table.data(), 2 /* length offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat12Table) {
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    std::vector<uint8_t> validTable =
+            buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+    {
+        SCOPED_TRACE("Unsupported format");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
+        writeU16(0, table.data(), 0 /* format offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid language");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
+        writeU32(1, table.data(), 8 /* language offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid length");
+        CmapBuilder builder(2);
+        std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
+        writeU32(5000, table.data(), 4 /* length offset */);
+        builder.appendTable(3, 1, table);
+        builder.appendTable(0, 0, validTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        EXPECT_TRUE(coverage.get('a'));  // comes from valid table
+        EXPECT_FALSE(coverage.get('b'));  // should not use invalid table.
+        EXPECT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, TableSelection_VSTable) {
+    std::vector<uint8_t> smallLetterTable =
+            buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
+    std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+        { 0xFE0E, { 'a', 'b' }, {} /* no non-default UVS table */ },
+        { 0xFE0F, {} /* no default UVS table */, { 'a', 'b'} },
+        { 0xE0100, { 'a', 'a' }, { 'b'} },
+    }));
+    CmapBuilder builder(2);
+    builder.appendTable(3, 1, smallLetterTable);
+    builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+    std::vector<uint8_t> cmap = builder.build();
+
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+    EXPECT_TRUE(coverage.get('a'));
+    ASSERT_FALSE(vsTables.empty());
+
+    const uint16_t vs15Index = getVsIndex(0xFE0E);
+    ASSERT_LT(vs15Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs15Index]);
+    EXPECT_TRUE(vsTables[vs15Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs15Index]->get('b'));
+
+    const uint16_t vs16Index = getVsIndex(0xFE0F);
+    ASSERT_LT(vs16Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs16Index]);
+    EXPECT_TRUE(vsTables[vs16Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+
+    const uint16_t vs17Index = getVsIndex(0xE0100);
+    ASSERT_LT(vs17Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs17Index]);
+    EXPECT_TRUE(vsTables[vs17Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs17Index]->get('b'));
+}
+
+TEST(CmapCoverageTest, TableSelection_InterSection) {
+    std::vector<uint8_t> smallLetterTable =
+            buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
+    std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+        { 0xFE0E, { 'a', 'e' }, { 'c', 'd', } },
+        { 0xFE0F, { 'c', 'e'} , { 'a', 'b', 'c', 'd', 'e'} },
+        { 0xE0100, { 'a', 'c' }, { 'b', 'c', 'd' } },
+        { 0xE0101, { 'b', 'd'} , { 'a', 'b', 'c', 'd'} },
+        { 0xE0102, { 'a', 'c', 'd', 'g'} , { 'b', 'c', 'd', 'e', 'f', 'g', 'h'} },
+        { 0xE0103, { 'a', 'f'} , { 'b', 'd', } },
+    }));
+    CmapBuilder builder(2);
+    builder.appendTable(3, 1, smallLetterTable);
+    builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+    std::vector<uint8_t> cmap = builder.build();
+
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+    EXPECT_TRUE(coverage.get('a'));
+    ASSERT_FALSE(vsTables.empty());
+
+    const uint16_t vs15Index = getVsIndex(0xFE0E);
+    ASSERT_LT(vs15Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs15Index]);
+    EXPECT_TRUE(vsTables[vs15Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs15Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs15Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs15Index]->get('d'));
+    EXPECT_TRUE(vsTables[vs15Index]->get('e'));
+
+    const uint16_t vs16Index = getVsIndex(0xFE0F);
+    ASSERT_LT(vs16Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs16Index]);
+    EXPECT_TRUE(vsTables[vs16Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs16Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs16Index]->get('d'));
+    EXPECT_TRUE(vsTables[vs16Index]->get('e'));
+
+    const uint16_t vs17Index = getVsIndex(0xE0100);
+    ASSERT_LT(vs17Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs17Index]);
+    EXPECT_TRUE(vsTables[vs17Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs17Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs17Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs17Index]->get('d'));
+
+    const uint16_t vs18Index = getVsIndex(0xE0101);
+    ASSERT_LT(vs18Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs18Index]);
+    EXPECT_TRUE(vsTables[vs18Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs18Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs18Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs18Index]->get('d'));
+
+    const uint16_t vs19Index = getVsIndex(0xE0102);
+    ASSERT_LT(vs19Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs19Index]);
+    EXPECT_TRUE(vsTables[vs19Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('d'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('e'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('f'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('g'));
+    EXPECT_TRUE(vsTables[vs19Index]->get('h'));
+
+    const uint16_t vs20Index = getVsIndex(0xE0103);
+    ASSERT_LT(vs20Index, vsTables.size());
+    ASSERT_TRUE(vsTables[vs20Index]);
+    EXPECT_TRUE(vsTables[vs20Index]->get('a'));
+    EXPECT_TRUE(vsTables[vs20Index]->get('b'));
+    EXPECT_TRUE(vsTables[vs20Index]->get('c'));
+    EXPECT_TRUE(vsTables[vs20Index]->get('d'));
+    EXPECT_TRUE(vsTables[vs20Index]->get('e'));
+    EXPECT_TRUE(vsTables[vs20Index]->get('f'));
+}
+
+TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
+    std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
+    {
+        SCOPED_TRACE("Too small cmap size");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0E, { 'a', 'a' }, { 'b' } }
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(
+                cmap.data(), 3 /* too small size */, &vsTables);
+        EXPECT_FALSE(coverage.get('a'));
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too many variation records");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } }
+        }));
+        writeU32(5000, vsTable.data(), 6 /* numVarSelectorRecord offset */);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid default UVS offset in variation records");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } }
+        }));
+        writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the first record */);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Invalid non default UVS offset in variation records");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } }
+        }));
+        writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too many ranges entry in default UVS table");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } }
+        }));
+        // 21 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
+        writeU32(5000, vsTable.data(), 21);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Too many ranges entry in non default UVS table");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } }
+        }));
+        // 29 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
+        writeU32(5000, vsTable.data(), 29);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range in default UVS table");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'b', 'b', 'a', 'a' }, { } }
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed range in default UVS table - partially readable");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a', 'c', 'c', 'b', 'b' }, { } }
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed mapping entries in non default UVS table");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { }, { 'b', 'a' } }
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+    {
+        SCOPED_TRACE("Reversed mapping entries in non default UVS table");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { }, { 'a', 'c', 'b' } }
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+        ASSERT_TRUE(vsTables.empty());
+    }
+}
+
+TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
+    std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
+    {
+        SCOPED_TRACE("Invalid default UVS offset in variation records");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0E, { 'a', 'a' }, { 'b' } },
+            { 0xFE0F, { 'a', 'a' }, { 'b' } },
+        }));
+        writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the record for 0xFE0E */);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+
+        const uint16_t vs16Index = getVsIndex(0xFE0F);
+        ASSERT_LT(vs16Index, vsTables.size());
+        ASSERT_TRUE(vsTables[vs16Index]);
+        EXPECT_TRUE(vsTables[vs16Index]->get('a'));
+        EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+
+        const uint16_t vs15Index = getVsIndex(0xFE0E);
+        EXPECT_FALSE(vsTables[vs15Index]);
+    }
+    {
+        SCOPED_TRACE("Invalid non default UVS offset in variation records");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0E, { 'a', 'a' }, { 'b' } },
+            { 0xFE0F, { 'a', 'a' }, { 'b' } },
+        }));
+        writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+
+        const uint16_t vs16Index = getVsIndex(0xFE0F);
+        ASSERT_LT(vs16Index, vsTables.size());
+        ASSERT_TRUE(vsTables[vs16Index]);
+        EXPECT_TRUE(vsTables[vs16Index]->get('a'));
+        EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+
+        const uint16_t vs15Index = getVsIndex(0xFE0E);
+        EXPECT_FALSE(vsTables[vs15Index]);
+    }
+    {
+        SCOPED_TRACE("Unknown variation selectors.");
+        std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+            { 0xFE0F, { 'a', 'a' }, { 'b' } },
+            { 0xEFFFF, { 'a', 'a' }, { 'b' } },
+        }));
+        CmapBuilder builder(2);
+        builder.appendTable(3, 1, cmap12Table);
+        builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+        std::vector<uint8_t> cmap = builder.build();
+
+        std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+        SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+
+        const uint16_t vs16Index = getVsIndex(0xFE0F);
+        ASSERT_LT(vs16Index, vsTables.size());
+        ASSERT_TRUE(vsTables[vs16Index]);
+        EXPECT_TRUE(vsTables[vs16Index]->get('a'));
+        EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+    }
+}
+
+// Used only for better looking of range definition.
+#define RANGE(x, y) x, y
+
+TEST(CmapCoverageTest, TableSelection_defaultUVSPointMissingGlyph) {
+    std::vector<uint8_t> baseTable = buildCmapFormat12Table(std::vector<uint32_t>(
+            {RANGE('a', 'e'), RANGE('g', 'h'), RANGE('j', 'j'), RANGE('m', 'z')}));
+    std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+        { 0xFE0F, { 'a', 'z' }, { } }
+    }));
+
+    CmapBuilder builder(2);
+    builder.appendTable(3, 1, baseTable);
+    builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+    std::vector<uint8_t> cmap = builder.build();
+
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+    const uint16_t vsIndex = getVsIndex(0xFE0F);
+    ASSERT_LT(vsIndex, vsTables.size());
+    ASSERT_TRUE(vsTables[vsIndex]);
+
+    for (char c = 'a'; c <= 'z'; ++c)  {
+        // Default UVS table points the variation sequence to the glyph of the base code point.
+        // Thus, if the base code point is not supported, we should exclude them.
+        EXPECT_EQ(coverage.get(c), vsTables[vsIndex]->get(c)) << c;
+    }
+}
+
+#undef RANGE
+
+TEST(CmapCoverageTest, TableSelection_vsTableOnly) {
+    std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
+        { 0xFE0F, { }, { 'a' } }
+    }));
+
+    CmapBuilder builder(1);
+    builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
+    std::vector<uint8_t> cmap = builder.build();
+
+    std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+    SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
+    const uint16_t vsIndex = getVsIndex(0xFE0F);
+    ASSERT_LT(vsIndex, vsTables.size());
+    ASSERT_TRUE(vsTables[vsIndex]);
+    EXPECT_TRUE(vsTables[vsIndex]->get('a'));
+}
+}  // namespace minikin
diff --git a/tests/unittest/EmojiTest.cpp b/tests/unittest/EmojiTest.cpp
new file mode 100644
index 0000000..e7d0f56
--- /dev/null
+++ b/tests/unittest/EmojiTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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 <unicode/uchar.h>
+
+#include <minikin/Emoji.h>
+
+namespace minikin {
+
+TEST(EmojiTest, isEmojiTest) {
+    EXPECT_TRUE(isEmoji(0x0023));  // NUMBER SIGN
+    EXPECT_TRUE(isEmoji(0x0035));  // DIGIT FIVE
+    EXPECT_TRUE(isEmoji(0x2640));  // FEMALE SIGN
+    EXPECT_TRUE(isEmoji(0x2642));  // MALE SIGN
+    EXPECT_TRUE(isEmoji(0x2695));  // STAFF OF AESCULAPIUS
+    EXPECT_TRUE(isEmoji(0x1F0CF));  // PLAYING CARD BLACK JOKER
+    EXPECT_TRUE(isEmoji(0x1F1E9));  // REGIONAL INDICATOR SYMBOL LETTER D
+    EXPECT_TRUE(isEmoji(0x1F6F7));  // SLED
+    EXPECT_TRUE(isEmoji(0x1F9E6));  // SOCKS
+
+    EXPECT_FALSE(isEmoji(0x0000));  // <control>
+    EXPECT_FALSE(isEmoji(0x0061));  // LATIN SMALL LETTER A
+    EXPECT_FALSE(isEmoji(0x1F93B));  // MODERN PENTATHLON
+    EXPECT_FALSE(isEmoji(0x1F946));  // RIFLE
+    EXPECT_FALSE(isEmoji(0x29E3D));  // A han character.
+}
+
+TEST(EmojiTest, isEmojiModifierTest) {
+    EXPECT_TRUE(isEmojiModifier(0x1F3FB));  // EMOJI MODIFIER FITZPATRICK TYPE-1-2
+    EXPECT_TRUE(isEmojiModifier(0x1F3FC));  // EMOJI MODIFIER FITZPATRICK TYPE-3
+    EXPECT_TRUE(isEmojiModifier(0x1F3FD));  // EMOJI MODIFIER FITZPATRICK TYPE-4
+    EXPECT_TRUE(isEmojiModifier(0x1F3FE));  // EMOJI MODIFIER FITZPATRICK TYPE-5
+    EXPECT_TRUE(isEmojiModifier(0x1F3FF));  // EMOJI MODIFIER FITZPATRICK TYPE-6
+
+    EXPECT_FALSE(isEmojiModifier(0x0000));  // <control>
+    EXPECT_FALSE(isEmojiModifier(0x1F3FA));  // AMPHORA
+    EXPECT_FALSE(isEmojiModifier(0x1F400));  // RAT
+    EXPECT_FALSE(isEmojiModifier(0x29E3D));  // A han character.
+}
+
+TEST(EmojiTest, isEmojiBaseTest) {
+    EXPECT_TRUE(isEmojiBase(0x261D));  // WHITE UP POINTING INDEX
+    EXPECT_TRUE(isEmojiBase(0x270D));  // WRITING HAND
+    EXPECT_TRUE(isEmojiBase(0x1F385));  // FATHER CHRISTMAS
+    EXPECT_TRUE(isEmojiBase(0x1F3C2));  // SNOWBOARDER
+    EXPECT_TRUE(isEmojiBase(0x1F3C7));  // HORSE RACING
+    EXPECT_TRUE(isEmojiBase(0x1F3CC));  // GOLFER
+    EXPECT_TRUE(isEmojiBase(0x1F574));  // MAN IN BUSINESS SUIT LEVITATING
+    EXPECT_TRUE(isEmojiBase(0x1F6CC));  // SLEEPING ACCOMMODATION
+    EXPECT_TRUE(isEmojiBase(0x1F91D));  // HANDSHAKE (removed from Emoji 4.0, but we need it)
+    EXPECT_TRUE(isEmojiBase(0x1F91F));  // I LOVE YOU HAND SIGN
+    EXPECT_TRUE(isEmojiBase(0x1F931));  // BREAST-FEEDING
+    EXPECT_TRUE(isEmojiBase(0x1F932));  // PALMS UP TOGETHER
+    EXPECT_TRUE(isEmojiBase(0x1F93C));  // WRESTLERS (removed from Emoji 4.0, but we need it)
+    EXPECT_TRUE(isEmojiBase(0x1F9D1));  // ADULT
+    EXPECT_TRUE(isEmojiBase(0x1F9DD));  // ELF
+
+    EXPECT_FALSE(isEmojiBase(0x0000));  // <control>
+    EXPECT_FALSE(isEmojiBase(0x261C));  // WHITE LEFT POINTING INDEX
+    EXPECT_FALSE(isEmojiBase(0x1F384));  // CHRISTMAS TREE
+    EXPECT_FALSE(isEmojiBase(0x1F9DE));  // GENIE
+    EXPECT_FALSE(isEmojiBase(0x29E3D));  // A han character.
+}
+
+TEST(EmojiTest, emojiBidiOverrideTest) {
+    EXPECT_EQ(U_RIGHT_TO_LEFT, emojiBidiOverride(nullptr, 0x05D0));  // HEBREW LETTER ALEF
+    EXPECT_EQ(U_LEFT_TO_RIGHT,
+            emojiBidiOverride(nullptr, 0x1F170));  // NEGATIVE SQUARED LATIN CAPITAL LETTER A
+    EXPECT_EQ(U_OTHER_NEUTRAL, emojiBidiOverride(nullptr, 0x1F6F7));  // SLED
+    EXPECT_EQ(U_OTHER_NEUTRAL, emojiBidiOverride(nullptr, 0x1F9E6));  // SOCKS
+}
+
+}  // namespace minikin
diff --git a/tests/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
similarity index 79%
rename from tests/FontCollectionItemizeTest.cpp
rename to tests/unittest/FontCollectionItemizeTest.cpp
index 468b4a2..78bfa3b 100644
--- a/tests/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -16,6 +16,8 @@
 
 #include <gtest/gtest.h>
 
+#include <memory>
+
 #include "FontLanguageListCache.h"
 #include "FontLanguage.h"
 #include "FontTestUtils.h"
@@ -25,16 +27,7 @@
 #include "UnicodeUtils.h"
 #include "minikin/FontFamily.h"
 
-using android::AutoMutex;
-using android::FontCollection;
-using android::FontFamily;
-using android::FontLanguage;
-using android::FontLanguages;
-using android::FontLanguageListCache;
-using android::FontStyle;
-using android::MinikinAutoUnref;
-using android::MinikinFont;
-using android::gMinikinLock;
+namespace minikin {
 
 const char kItemizeFontXml[] = kTestFontDir "itemize.xml";
 const char kEmojiFont[] = kTestFontDir "Emoji.ttf";
@@ -53,10 +46,13 @@
 const char kTextEmojiFont[] = kTestFontDir "TextEmojiFont.ttf";
 const char kMixedEmojiFont[] = kTestFontDir "ColorTextMixedEmojiFont.ttf";
 
+const char kHasCmapFormat14Font[] =  kTestFontDir "NoCmapFormat14.ttf";
+const char kNoCmapFormat14Font[] =  kTestFontDir "VariationSelectorTest-Regular.ttf";
+
 typedef ICUTestBase FontCollectionItemizeTest;
 
 // Utility function for calling itemize function.
-void itemize(FontCollection* collection, const char* str, FontStyle style,
+void itemize(const std::shared_ptr<FontCollection>& collection, const char* str, FontStyle style,
         std::vector<FontCollection::Run>* result) {
     const size_t BUF_SIZE = 256;
     uint16_t buf[BUF_SIZE];
@@ -64,7 +60,7 @@
 
     result->clear();
     ParseUnicode(buf, BUF_SIZE, str, &len, NULL);
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     collection->itemize(buf, len, style, result);
 }
 
@@ -76,12 +72,12 @@
 
 // Utility function to obtain FontLanguages from string.
 const FontLanguages& registerAndGetFontLanguages(const std::string& lang_string) {
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     return FontLanguageListCache::getById(FontLanguageListCache::getId(lang_string));
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_latin) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kRegularStyle = FontStyle();
@@ -89,7 +85,7 @@
     const FontStyle kBoldStyle = FontStyle(7, false);
     const FontStyle kBoldItalicStyle = FontStyle(7, true);
 
-    itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kRegularStyle, &runs);
+    itemize(collection, "'a' 'b' 'c' 'd' 'e'", kRegularStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -97,7 +93,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kItalicStyle, &runs);
+    itemize(collection, "'a' 'b' 'c' 'd' 'e'", kItalicStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -105,7 +101,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kBoldStyle, &runs);
+    itemize(collection, "'a' 'b' 'c' 'd' 'e'", kBoldStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -113,7 +109,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kBoldItalicStyle, &runs);
+    itemize(collection, "'a' 'b' 'c' 'd' 'e'", kBoldItalicStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -123,7 +119,7 @@
 
     // Continue if the specific characters (e.g. hyphen, comma, etc.) is
     // followed.
-    itemize(collection.get(), "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
+    itemize(collection, "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -131,7 +127,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
+    itemize(collection, "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -141,7 +137,7 @@
 
     // U+0301(COMBINING ACUTE ACCENT) must be in the same run with preceding
     // chars if the font supports it.
-    itemize(collection.get(), "'a' U+0301", kRegularStyle, &runs);
+    itemize(collection, "'a' U+0301", kRegularStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -151,10 +147,10 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_emoji) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
-    itemize(collection.get(), "U+1F469 U+1F467", FontStyle(), &runs);
+    itemize(collection, "U+1F469 U+1F467", FontStyle(), &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
@@ -164,7 +160,7 @@
 
     // U+20E3(COMBINING ENCLOSING KEYCAP) must be in the same run with preceding
     // character if the font supports.
-    itemize(collection.get(), "'0' U+20E3", FontStyle(), &runs);
+    itemize(collection, "'0' U+20E3", FontStyle(), &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -172,7 +168,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "U+1F470 U+20E3", FontStyle(), &runs);
+    itemize(collection, "U+1F470 U+20E3", FontStyle(), &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
@@ -180,7 +176,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
-    itemize(collection.get(), "U+242EE U+1F470 U+20E3", FontStyle(), &runs);
+    itemize(collection, "U+242EE U+1F470 U+20E3", FontStyle(), &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -196,7 +192,7 @@
 
     // Currently there is no fonts which has a glyph for 'a' + U+20E3, so they
     // are splitted into two.
-    itemize(collection.get(), "'a' U+20E3", FontStyle(), &runs);
+    itemize(collection, "'a' U+20E3", FontStyle(), &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -212,7 +208,7 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_non_latin) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     FontStyle kJAStyle = FontStyle(FontStyle::registerLanguageList("ja_JP"));
@@ -220,7 +216,7 @@
     FontStyle kZH_HansStyle = FontStyle(FontStyle::registerLanguageList("zh_Hans"));
 
     // All Japanese Hiragana characters.
-    itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kUSStyle, &runs);
+    itemize(collection, "U+3042 U+3044 U+3046 U+3048 U+304A", kUSStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -229,7 +225,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
     // All Korean Hangul characters.
-    itemize(collection.get(), "U+B300 U+D55C U+BBFC U+AD6D", kUSStyle, &runs);
+    itemize(collection, "U+B300 U+D55C U+BBFC U+AD6D", kUSStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
@@ -239,7 +235,7 @@
 
     // All Han characters ja, zh-Hans font having.
     // Japanese font should be selected if the specified language is Japanese.
-    itemize(collection.get(), "U+81ED U+82B1 U+5FCD", kJAStyle, &runs);
+    itemize(collection, "U+81ED U+82B1 U+5FCD", kJAStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
@@ -249,7 +245,7 @@
 
     // Simplified Chinese font should be selected if the specified language is Simplified
     // Chinese.
-    itemize(collection.get(), "U+81ED U+82B1 U+5FCD", kZH_HansStyle, &runs);
+    itemize(collection, "U+81ED U+82B1 U+5FCD", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
@@ -259,7 +255,7 @@
 
     // Fallbacks to other fonts if there is no glyph in the specified language's
     // font. There is no character U+4F60 in Japanese.
-    itemize(collection.get(), "U+81ED U+4F60 U+5FCD", kJAStyle, &runs);
+    itemize(collection, "U+81ED U+4F60 U+5FCD", kJAStyle, &runs);
     ASSERT_EQ(3U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -280,7 +276,7 @@
     EXPECT_FALSE(runs[2].fakedFont.fakery.isFakeItalic());
 
     // Tone mark.
-    itemize(collection.get(), "U+4444 U+302D", FontStyle(), &runs);
+    itemize(collection, "U+4444 U+302D", FontStyle(), &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -291,7 +287,7 @@
     // Both zh-Hant and ja fonts support U+242EE, but zh-Hans doesn't.
     // Here, ja and zh-Hant font should have the same score but ja should be selected since it is
     // listed before zh-Hant.
-    itemize(collection.get(), "U+242EE", kZH_HansStyle, &runs);
+    itemize(collection, "U+242EE", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -301,12 +297,12 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_mixed) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     FontStyle kUSStyle = FontStyle(FontStyle::registerLanguageList("en_US"));
 
-    itemize(collection.get(), "'a' U+4F60 'b' U+4F60 'c'", kUSStyle, &runs);
+    itemize(collection, "'a' U+4F60 'b' U+4F60 'c'", kUSStyle, &runs);
     ASSERT_EQ(5U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -340,7 +336,7 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_variationSelector) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     // A glyph for U+4FAE is provided by both Japanese font and Simplified
@@ -352,19 +348,19 @@
 
     // U+4FAE is available in both zh_Hans and ja font, but U+4FAE,U+FE00 is
     // only available in ja font.
-    itemize(collection.get(), "U+4FAE", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
     EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+4FAE U+FE00", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE U+FE00", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+4FAE U+4FAE U+FE00", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE U+4FAE U+FE00", kZH_HansStyle, &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -373,7 +369,7 @@
     EXPECT_EQ(3, runs[1].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[1]));
 
-    itemize(collection.get(), "U+4FAE U+4FAE U+FE00 U+4FAE", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE U+4FAE U+FE00 U+4FAE", kZH_HansStyle, &runs);
     ASSERT_EQ(3U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -386,14 +382,14 @@
     EXPECT_EQ(kZH_HansFont, getFontPath(runs[2]));
 
     // Validation selector after validation selector.
-    itemize(collection.get(), "U+4FAE U+FE00 U+FE00", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE U+FE00 U+FE00", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[1]));
 
     // No font supports U+242EE U+FE0E.
-    itemize(collection.get(), "U+4FAE U+FE0E", kZH_HansStyle, &runs);
+    itemize(collection, "U+4FAE U+FE0E", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -402,19 +398,19 @@
     // Surrogate pairs handling.
     // U+242EE is available in ja font and zh_Hant font.
     // U+242EE U+FE00 is available only in ja font.
-    itemize(collection.get(), "U+242EE", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+242EE U+FE00", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+FE00", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+242EE U+242EE U+FE00", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+242EE U+FE00", kZH_HantStyle, &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -423,7 +419,7 @@
     EXPECT_EQ(5, runs[1].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[1]));
 
-    itemize(collection.get(), "U+242EE U+242EE U+FE00 U+242EE", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+242EE U+FE00 U+242EE", kZH_HantStyle, &runs);
     ASSERT_EQ(3U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -436,27 +432,27 @@
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[2]));
 
     // Validation selector after validation selector.
-    itemize(collection.get(), "U+242EE U+FE00 U+FE00", kZH_HansStyle, &runs);
+    itemize(collection, "U+242EE U+FE00 U+FE00", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
     // No font supports U+242EE U+FE0E
-    itemize(collection.get(), "U+242EE U+FE0E", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+FE0E", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
 
     // Isolated variation selector supplement.
-    itemize(collection.get(), "U+FE00", FontStyle(), &runs);
+    itemize(collection, "U+FE00", FontStyle(), &runs);
     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 == getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+FE00", kZH_HantStyle, &runs);
+    itemize(collection, "U+FE00", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -464,14 +460,14 @@
 
     // 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.
-    itemize(collection.get(), "U+203C U+FE0F", kZH_HantStyle, &runs);
+    itemize(collection, "U+203C U+FE0F", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
 
     // First font family (Regular.ttf) supports U+203C U+FE0E.
-    itemize(collection.get(), "U+203C U+FE0E", kZH_HantStyle, &runs);
+    itemize(collection, "U+203C U+FE0E", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -479,7 +475,7 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_variationSelectorSupplement) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     // A glyph for U+845B is provided by both Japanese font and Simplified
@@ -491,19 +487,19 @@
 
     // U+845B is available in both zh_Hans and ja font, but U+845B,U+E0100 is
     // only available in ja font.
-    itemize(collection.get(), "U+845B", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
     EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+845B U+E0100", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B U+E0100", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+845B U+845B U+E0100", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B U+845B U+E0100", kZH_HansStyle, &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -512,7 +508,7 @@
     EXPECT_EQ(4, runs[1].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[1]));
 
-    itemize(collection.get(), "U+845B U+845B U+E0100 U+845B", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B U+845B U+E0100 U+845B", kZH_HansStyle, &runs);
     ASSERT_EQ(3U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
@@ -525,14 +521,14 @@
     EXPECT_EQ(kZH_HansFont, getFontPath(runs[2]));
 
     // Validation selector after validation selector.
-    itemize(collection.get(), "U+845B U+E0100 U+E0100", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B U+E0100 U+E0100", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
     // No font supports U+845B U+E01E0.
-    itemize(collection.get(), "U+845B U+E01E0", kZH_HansStyle, &runs);
+    itemize(collection, "U+845B U+E01E0", kZH_HansStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
@@ -542,19 +538,19 @@
     // Surrogate pairs handling.
     // U+242EE is available in ja font and zh_Hant font.
     // U+242EE U+E0100 is available only in ja font.
-    itemize(collection.get(), "U+242EE", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+242EE U+E0101", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+E0101", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+242EE U+242EE U+E0101", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+242EE U+E0101", kZH_HantStyle, &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -563,7 +559,7 @@
     EXPECT_EQ(6, runs[1].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[1]));
 
-    itemize(collection.get(), "U+242EE U+242EE U+E0101 U+242EE", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+242EE U+E0101 U+242EE", kZH_HantStyle, &runs);
     ASSERT_EQ(3U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -576,27 +572,27 @@
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[2]));
 
     // Validation selector after validation selector.
-    itemize(collection.get(), "U+242EE U+E0100 U+E0100", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+E0100 U+E0100", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(6, runs[0].end);
     EXPECT_EQ(kJAFont, getFontPath(runs[0]));
 
     // No font supports U+242EE U+E01E0.
-    itemize(collection.get(), "U+242EE U+E01E0", kZH_HantStyle, &runs);
+    itemize(collection, "U+242EE U+E01E0", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
     EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
 
     // Isolated variation selector supplement.
-    itemize(collection.get(), "U+E0100", FontStyle(), &runs);
+    itemize(collection, "U+E0100", FontStyle(), &runs);
     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 == getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+E0100", kZH_HantStyle, &runs);
+    itemize(collection, "U+E0100", kZH_HantStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -604,31 +600,31 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_no_crash) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     // Broken Surrogate pairs. Check only not crashing.
-    itemize(collection.get(), "'a' U+D83D 'a'", FontStyle(), &runs);
-    itemize(collection.get(), "'a' U+DC69 'a'", FontStyle(), &runs);
-    itemize(collection.get(), "'a' U+D83D U+D83D 'a'", FontStyle(), &runs);
-    itemize(collection.get(), "'a' U+DC69 U+DC69 'a'", FontStyle(), &runs);
+    itemize(collection, "'a' U+D83D 'a'", FontStyle(), &runs);
+    itemize(collection, "'a' U+DC69 'a'", FontStyle(), &runs);
+    itemize(collection, "'a' U+D83D U+D83D 'a'", FontStyle(), &runs);
+    itemize(collection, "'a' U+DC69 U+DC69 'a'", FontStyle(), &runs);
 
     // Isolated variation selector. Check only not crashing.
-    itemize(collection.get(), "U+FE00 U+FE00", FontStyle(), &runs);
-    itemize(collection.get(), "U+E0100 U+E0100", FontStyle(), &runs);
-    itemize(collection.get(), "U+FE00 U+E0100", FontStyle(), &runs);
-    itemize(collection.get(), "U+E0100 U+FE00", FontStyle(), &runs);
+    itemize(collection, "U+FE00 U+FE00", FontStyle(), &runs);
+    itemize(collection, "U+E0100 U+E0100", FontStyle(), &runs);
+    itemize(collection, "U+FE00 U+E0100", FontStyle(), &runs);
+    itemize(collection, "U+E0100 U+FE00", FontStyle(), &runs);
 
     // Tone mark only. Check only not crashing.
-    itemize(collection.get(), "U+302D", FontStyle(), &runs);
-    itemize(collection.get(), "U+302D U+302D", FontStyle(), &runs);
+    itemize(collection, "U+302D", FontStyle(), &runs);
+    itemize(collection, "U+302D U+302D", FontStyle(), &runs);
 
     // Tone mark and variation selector mixed. Check only not crashing.
-    itemize(collection.get(), "U+FE00 U+302D U+E0100", FontStyle(), &runs);
+    itemize(collection, "U+FE00 U+302D U+E0100", FontStyle(), &runs);
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_fakery) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
     std::vector<FontCollection::Run> runs;
 
     FontStyle kJABoldStyle = FontStyle(FontStyle::registerLanguageList("ja_JP"), 0, 7, false);
@@ -640,7 +636,7 @@
     // the differences between desired and actual font style.
 
     // All Japanese Hiragana characters.
-    itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldStyle, &runs);
+    itemize(collection, "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -649,7 +645,7 @@
     EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
 
     // All Japanese Hiragana characters.
-    itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJAItalicStyle, &runs);
+    itemize(collection, "U+3042 U+3044 U+3046 U+3048 U+304A", kJAItalicStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -658,7 +654,7 @@
     EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic());
 
     // All Japanese Hiragana characters.
-    itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldItalicStyle, &runs);
+    itemize(collection, "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldItalicStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
@@ -671,29 +667,28 @@
     // kVSTestFont supports U+717D U+FE02 but doesn't support U+717D.
     // kVSTestFont should be selected for U+717D U+FE02 even if it does not support the base code
     // point.
-    const std::string kVSTestFont = kTestFontDir "VarioationSelectorTest-Regular.ttf";
+    const std::string kVSTestFont = kTestFontDir "VariationSelectorTest-Regular.ttf";
 
-    std::vector<android::FontFamily*> families;
-    FontFamily* family1 = new FontFamily(android::VARIANT_DEFAULT);
-    family1->addFont(new MinikinFontForTest(kLatinFont));
+    std::vector<std::shared_ptr<FontFamily>> families;
+    std::shared_ptr<MinikinFont> font(new MinikinFontForTest(kLatinFont));
+    std::shared_ptr<FontFamily> family1(new FontFamily(VARIANT_DEFAULT,
+            std::vector<Font>{ Font(font, FontStyle()) }));
     families.push_back(family1);
 
-    FontFamily* family2 = new FontFamily(android::VARIANT_DEFAULT);
-    family2->addFont(new MinikinFontForTest(kVSTestFont));
+    std::shared_ptr<MinikinFont> font2(new MinikinFontForTest(kVSTestFont));
+    std::shared_ptr<FontFamily> family2(new FontFamily(VARIANT_DEFAULT,
+            std::vector<Font>{ Font(font2, FontStyle()) }));
     families.push_back(family2);
 
-    FontCollection collection(families);
+    std::shared_ptr<FontCollection> collection(new FontCollection(families));
 
     std::vector<FontCollection::Run> runs;
 
-    itemize(&collection, "U+717D U+FE02", FontStyle(), &runs);
+    itemize(collection, "U+717D U+FE02", FontStyle(), &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kVSTestFont, getFontPath(runs[0]));
-
-    family1->Unref();
-    family2->Unref();
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_LanguageScore) {
@@ -784,6 +779,20 @@
         // Language match with unified subscript bits.
         { "zh-Hanb", { "zh-Hant", "zh-Bopo", "ja-Hant,ja-Bopo", "zh-Hant,zh-Bopo"}, 3 },
         { "zh-Hanb", { "zh-Hant", "zh-Bopo", "ja-Hant,zh-Bopo", "zh-Hant,zh-Bopo"}, 3 },
+
+        // Two elements subtag matching: language and subtag or language or script.
+        { "ja-Kana-u-em-emoji", { "zh-Hant", "ja-Kana"}, 1 },
+        { "ja-Kana-u-em-emoji", { "zh-Hant", "ja-Kana", "ja-Zsye"}, 2 },
+        { "ja-Zsym-u-em-emoji", { "ja-Kana", "ja-Zsym", "ja-Zsye"}, 2 },
+
+        // One element subtag matching: subtag only or script only.
+        { "en-Latn-u-em-emoji", { "ja-Latn", "ja-Zsye"}, 1 },
+        { "en-Zsym-u-em-emoji", { "ja-Zsym", "ja-Zsye"}, 1 },
+        { "en-Zsye-u-em-text", { "ja-Zsym", "ja-Zsye"}, 0 },
+
+        // Multiple languages list with subtags.
+        { "en-Latn,ja-Jpan-u-em-text", { "en-Latn", "en-Zsye", "en-Zsym"}, 0 },
+        { "en-Latn,en-Zsye,ja-Jpan-u-em-text", { "zh", "en-Zsye", "en-Zsym"}, 1 },
     };
 
     for (auto testCase : testCases) {
@@ -798,13 +807,14 @@
         SCOPED_TRACE("Test of user preferred languages: \"" + testCase.userPreferredLanguages +
                      "\" with font languages: " + fontLanguagesStr);
 
-        std::vector<FontFamily*> families;
+        std::vector<std::shared_ptr<FontFamily>> families;
 
         // Prepare first font which doesn't supports U+9AA8
-        FontFamily* firstFamily = new FontFamily(
-                FontStyle::registerLanguageList("und"), 0 /* variant */);
-        MinikinFont* firstFamilyMinikinFont = new MinikinFontForTest(kNoGlyphFont);
-        firstFamily->addFont(firstFamilyMinikinFont);
+        std::shared_ptr<MinikinFont> firstFamilyMinikinFont(
+                new MinikinFontForTest(kNoGlyphFont));
+        std::shared_ptr<FontFamily> firstFamily(new FontFamily(
+                FontStyle::registerLanguageList("und"), 0 /* variant */,
+                std::vector<Font>({ Font(firstFamilyMinikinFont, FontStyle()) })));
         families.push_back(firstFamily);
 
         // Prepare font families
@@ -813,29 +823,25 @@
         std::unordered_map<MinikinFont*, int> fontLangIdxMap;
 
         for (size_t i = 0; i < testCase.fontLanguages.size(); ++i) {
-            FontFamily* family = new FontFamily(
-                    FontStyle::registerLanguageList(testCase.fontLanguages[i]), 0 /* variant */);
-            MinikinFont* minikin_font = new MinikinFontForTest(kJAFont);
-            family->addFont(minikin_font);
+            std::shared_ptr<MinikinFont> minikin_font(new MinikinFontForTest(kJAFont));
+            std::shared_ptr<FontFamily> family(new FontFamily(
+                    FontStyle::registerLanguageList(testCase.fontLanguages[i]), 0 /* variant */,
+                    std::vector<Font>({ Font(minikin_font, FontStyle()) })));
             families.push_back(family);
-            fontLangIdxMap.insert(std::make_pair(minikin_font, i));
+            fontLangIdxMap.insert(std::make_pair(minikin_font.get(), i));
         }
-        FontCollection collection(families);
-        for (auto family : families) {
-            family->Unref();
-        }
-
+        std::shared_ptr<FontCollection> collection(new FontCollection(families));
         // Do itemize
         const FontStyle style = FontStyle(
                 FontStyle::registerLanguageList(testCase.userPreferredLanguages));
         std::vector<FontCollection::Run> runs;
-        itemize(&collection, "U+9AA8", style, &runs);
+        itemize(collection, "U+9AA8", style, &runs);
         ASSERT_EQ(1U, runs.size());
         ASSERT_NE(nullptr, runs[0].fakedFont.font);
 
         // First family doesn't support U+9AA8 and others support it, so the first font should not
         // be selected.
-        EXPECT_NE(firstFamilyMinikinFont, runs[0].fakedFont.font);
+        EXPECT_NE(firstFamilyMinikinFont.get(), runs[0].fakedFont.font);
 
         // Lookup used font family by MinikinFont*.
         const int usedLangIndex = fontLangIdxMap[runs[0].fakedFont.font];
@@ -1135,7 +1141,7 @@
         { "U+1F469", "zh-Hant,ja-Jpan,zh-Hans", kEmojiFont },
     };
 
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
 
     for (auto testCase : testCases) {
         SCOPED_TRACE("Test for \"" + testCase.testString + "\" with languages " +
@@ -1144,21 +1150,21 @@
         std::vector<FontCollection::Run> runs;
         const FontStyle style =
                 FontStyle(FontStyle::registerLanguageList(testCase.requestedLanguages));
-        itemize(collection.get(), testCase.testString.c_str(), style, &runs);
+        itemize(collection, testCase.testString.c_str(), style, &runs);
         ASSERT_EQ(1U, runs.size());
         EXPECT_EQ(testCase.expectedFont, getFontPath(runs[0]));
     }
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kDefaultFontStyle;
 
     // U+00A9 is a text default emoji which is only available in TextEmojiFont.ttf.
     // TextEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+00A9 U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+00A9 U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1166,7 +1172,7 @@
 
     // U+00A9 is a text default emoji which is only available in ColorEmojiFont.ttf.
     // ColorEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+00AE U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+00AE U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1175,7 +1181,7 @@
 
     // U+203C is a text default emoji which is available in both TextEmojiFont.ttf and
     // ColorEmojiFont.ttf. TextEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+203C U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+203C U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1183,7 +1189,7 @@
 
     // U+2049 is a text default emoji which is not available either TextEmojiFont.ttf or
     // ColorEmojiFont.ttf. No font should be selected.
-    itemize(collection.get(), "U+2049 U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+2049 U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1191,7 +1197,7 @@
 
     // U+231A is a emoji default emoji which is available only in TextEmojifFont.
     // TextEmojiFont.ttf sohuld be selected.
-    itemize(collection.get(), "U+231A U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+231A U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1199,7 +1205,7 @@
 
     // U+231B is a emoji default emoji which is available only in ColorEmojiFont.ttf.
     // ColorEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+231B U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+231B U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1209,7 +1215,7 @@
     // U+23E9 is a emoji default emoji which is available in both TextEmojiFont.ttf and
     // ColorEmojiFont.ttf. TextEmojiFont.ttf should be selected even if U+23E9 is emoji default
     // emoji since U+FE0E is appended.
-    itemize(collection.get(), "U+23E9 U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+23E9 U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1217,7 +1223,7 @@
 
     // U+23EA is a emoji default emoji but which is not available in either TextEmojiFont.ttf or
     // ColorEmojiFont.ttf. No font should be selected.
-    itemize(collection.get(), "U+23EA U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+23EA U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1225,7 +1231,7 @@
 
     // U+26FA U+FE0E is specified but ColorTextMixedEmojiFont has a variation sequence U+26F9 U+FE0F
     // in its cmap, so ColorTextMixedEmojiFont should be selected instaed of ColorEmojiFont.
-    itemize(collection.get(), "U+26FA U+FE0E", kDefaultFontStyle, &runs);
+    itemize(collection, "U+26FA U+FE0E", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1233,14 +1239,14 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kDefaultFontStyle;
 
     // U+00A9 is a text default emoji which is available only in TextEmojiFont.ttf.
     // TextEmojiFont.ttf shoudl be selected.
-    itemize(collection.get(), "U+00A9 U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+00A9 U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1249,7 +1255,7 @@
 
     // U+00AE is a text default emoji which is available only in ColorEmojiFont.ttf.
     // ColorEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+00AE U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+00AE U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1258,7 +1264,7 @@
     // U+203C is a text default emoji which is available in both TextEmojiFont.ttf and
     // ColorEmojiFont.ttf. ColorEmojiFont.ttf should be selected even if U+203C is a text default
     // emoji since U+FF0F is appended.
-    itemize(collection.get(), "U+203C U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+203C U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1266,7 +1272,7 @@
 
     // U+2049 is a text default emoji which is not available in either TextEmojiFont.ttf or
     // ColorEmojiFont.ttf. No font should be selected.
-    itemize(collection.get(), "U+2049 U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+2049 U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1274,7 +1280,7 @@
 
     // U+231A is a emoji default emoji which is available only in TextEmojiFont.ttf.
     // TextEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+231A U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+231A U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1283,7 +1289,7 @@
 
     // U+231B is a emoji default emoji which is available only in ColorEmojiFont.ttf.
     // ColorEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+231B U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+231B U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1291,7 +1297,7 @@
 
     // U+23E9 is a emoji default emoji which is available in both TextEmojiFont.ttf and
     // ColorEmojiFont.ttf. ColorEmojiFont.ttf should be selected.
-    itemize(collection.get(), "U+23E9 U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+23E9 U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1299,7 +1305,7 @@
 
     // U+23EA is a emoji default emoji which is not available in either TextEmojiFont.ttf or
     // ColorEmojiFont.ttf. No font should be selected.
-    itemize(collection.get(), "U+23EA U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+23EA U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1307,7 +1313,7 @@
 
     // U+26F9 U+FE0F is specified but ColorTextMixedEmojiFont has a variation sequence U+26F9 U+FE0F
     // in its cmap, so ColorTextMixedEmojiFont should be selected instaed of ColorEmojiFont.
-    itemize(collection.get(), "U+26F9 U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+26F9 U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1315,27 +1321,27 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_with_skinTone) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kDefaultFontStyle;
 
     // TextEmoji font is selected since it is listed before ColorEmoji font.
-    itemize(collection.get(), "U+261D", kDefaultFontStyle, &runs);
+    itemize(collection, "U+261D", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(1, runs[0].end);
     EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0]));
 
     // If skin tone is specified, it should be colored.
-    itemize(collection.get(), "U+261D U+1F3FD", kDefaultFontStyle, &runs);
+    itemize(collection, "U+261D U+1F3FD", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(3, runs[0].end);
     EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
 
     // Still color font is selected if an emoji variation selector is specified.
-    itemize(collection.get(), "U+261D U+FE0F U+1F3FD", kDefaultFontStyle, &runs);
+    itemize(collection, "U+261D U+FE0F U+1F3FD", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
@@ -1343,7 +1349,7 @@
 
     // Text font should be selected if a text variation selector is specified and skin tone is
     // rendered by itself.
-    itemize(collection.get(), "U+261D U+FE0E U+1F3FD", kDefaultFontStyle, &runs);
+    itemize(collection, "U+261D U+FE0E U+1F3FD", kDefaultFontStyle, &runs);
     ASSERT_EQ(2U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
@@ -1354,19 +1360,19 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_PrivateUseArea) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kDefaultFontStyle;
 
     // Should not set nullptr to the result run. (Issue 26808815)
-    itemize(collection.get(), "U+FEE10", kDefaultFontStyle, &runs);
+    itemize(collection, "U+FEE10", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(2, runs[0].end);
     EXPECT_EQ(kNoGlyphFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+FEE40 U+FE4C5", kDefaultFontStyle, &runs);
+    itemize(collection, "U+FEE40 U+FE4C5", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
@@ -1374,26 +1380,96 @@
 }
 
 TEST_F(FontCollectionItemizeTest, itemize_genderBalancedEmoji) {
-    MinikinAutoUnref<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
     std::vector<FontCollection::Run> runs;
 
     const FontStyle kDefaultFontStyle;
 
-    itemize(collection.get(), "U+1F469 U+200D U+1F373", kDefaultFontStyle, &runs);
+    itemize(collection, "U+1F469 U+200D U+1F373", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
     EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+1F469 U+200D U+2695 U+FE0F", kDefaultFontStyle, &runs);
+    itemize(collection, "U+1F469 U+200D U+2695 U+FE0F", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(5, runs[0].end);
     EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
 
-    itemize(collection.get(), "U+1F469 U+200D U+2695", kDefaultFontStyle, &runs);
+    itemize(collection, "U+1F469 U+200D U+2695", kDefaultFontStyle, &runs);
     ASSERT_EQ(1U, runs.size());
     EXPECT_EQ(0, runs[0].start);
     EXPECT_EQ(4, runs[0].end);
     EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
 }
+
+// For b/29585939
+TEST_F(FontCollectionItemizeTest, itemizeShouldKeepOrderForVS) {
+    const FontStyle kDefaultFontStyle;
+
+    std::shared_ptr<MinikinFont> dummyFont(new MinikinFontForTest(kNoGlyphFont));
+    std::shared_ptr<MinikinFont> fontA(new MinikinFontForTest(kZH_HansFont));
+    std::shared_ptr<MinikinFont> fontB(new MinikinFontForTest(kZH_HansFont));
+
+    std::shared_ptr<FontFamily> dummyFamily(new FontFamily(
+            std::vector<Font>({ Font(dummyFont, FontStyle()) })));
+    std::shared_ptr<FontFamily> familyA(new FontFamily(
+            std::vector<Font>({ Font(fontA, FontStyle()) })));
+    std::shared_ptr<FontFamily> familyB(new FontFamily(
+            std::vector<Font>({ Font(fontB, FontStyle()) })));
+
+    std::vector<std::shared_ptr<FontFamily>> families =
+            { dummyFamily, familyA, familyB };
+    std::vector<std::shared_ptr<FontFamily>> reversedFamilies =
+            { dummyFamily, familyB, familyA };
+
+    std::shared_ptr<FontCollection> collection(new FontCollection(families));
+    std::shared_ptr<FontCollection> reversedCollection(new FontCollection(reversedFamilies));
+
+    // Both fontA/fontB support U+35A8 but don't support U+35A8 U+E0100. The first font should be
+    // selected.
+    std::vector<FontCollection::Run> runs;
+    itemize(collection, "U+35A8 U+E0100", kDefaultFontStyle, &runs);
+    EXPECT_EQ(fontA.get(), runs[0].fakedFont.font);
+
+    itemize(reversedCollection, "U+35A8 U+E0100", kDefaultFontStyle, &runs);
+    EXPECT_EQ(fontB.get(), runs[0].fakedFont.font);
+}
+
+// For b/29585939
+TEST_F(FontCollectionItemizeTest, itemizeShouldKeepOrderForVS2) {
+    const FontStyle kDefaultFontStyle;
+
+    std::shared_ptr<MinikinFont> dummyFont(new MinikinFontForTest(kNoGlyphFont));
+    std::shared_ptr<MinikinFont> hasCmapFormat14Font(
+            new MinikinFontForTest(kHasCmapFormat14Font));
+    std::shared_ptr<MinikinFont> noCmapFormat14Font(
+            new MinikinFontForTest(kNoCmapFormat14Font));
+
+    std::shared_ptr<FontFamily> dummyFamily(new FontFamily(
+            std::vector<Font>({ Font(dummyFont, FontStyle()) })));
+    std::shared_ptr<FontFamily> hasCmapFormat14Family(new FontFamily(
+            std::vector<Font>({ Font(hasCmapFormat14Font, FontStyle()) })));
+    std::shared_ptr<FontFamily> noCmapFormat14Family(new FontFamily(
+            std::vector<Font>({ Font(noCmapFormat14Font, FontStyle()) })));
+
+    std::vector<std::shared_ptr<FontFamily>> families =
+            { dummyFamily, hasCmapFormat14Family, noCmapFormat14Family };
+    std::vector<std::shared_ptr<FontFamily>> reversedFamilies =
+            { dummyFamily, noCmapFormat14Family, hasCmapFormat14Family };
+
+    std::shared_ptr<FontCollection> collection(new FontCollection(families));
+    std::shared_ptr<FontCollection> reversedCollection(new FontCollection(reversedFamilies));
+
+    // Both hasCmapFormat14Font/noCmapFormat14Font support U+5380 but don't support U+5380 U+E0100.
+    // The first font should be selected.
+    std::vector<FontCollection::Run> runs;
+    itemize(collection, "U+5380 U+E0100", kDefaultFontStyle, &runs);
+    EXPECT_EQ(hasCmapFormat14Font.get(), runs[0].fakedFont.font);
+
+    itemize(reversedCollection, "U+5380 U+E0100", kDefaultFontStyle, &runs);
+    EXPECT_EQ(noCmapFormat14Font.get(), runs[0].fakedFont.font);
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/FontCollectionTest.cpp b/tests/unittest/FontCollectionTest.cpp
new file mode 100644
index 0000000..bef1c63
--- /dev/null
+++ b/tests/unittest/FontCollectionTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <minikin/FontCollection.h>
+#include "FontTestUtils.h"
+#include "MinikinFontForTest.h"
+#include "MinikinInternal.h"
+
+namespace minikin {
+
+// The test font has following glyphs.
+// U+82A6
+// U+82A6 U+FE00 (VS1)
+// U+82A6 U+E0100 (VS17)
+// U+82A6 U+E0101 (VS18)
+// U+82A6 U+E0102 (VS19)
+// U+845B
+// U+845B U+FE01 (VS2)
+// U+845B U+E0101 (VS18)
+// U+845B U+E0102 (VS19)
+// U+845B U+E0103 (VS20)
+// U+537F
+// U+717D U+FE02 (VS3)
+// U+717D U+E0102 (VS19)
+// U+717D U+E0103 (VS20)
+const char kVsTestFont[] = kTestFontDir "/VariationSelectorTest-Regular.ttf";
+
+void expectVSGlyphs(const FontCollection* fc, uint32_t codepoint, const std::set<uint32_t>& vsSet) {
+    for (uint32_t vs = 0xFE00; vs <= 0xE01EF; ++vs) {
+        // Move to variation selectors supplements after variation selectors.
+        if (vs == 0xFF00) {
+            vs = 0xE0100;
+        }
+        if (vsSet.find(vs) == vsSet.end()) {
+            EXPECT_FALSE(fc->hasVariationSelector(codepoint, vs))
+                << "Glyph for U+" << std::hex << codepoint << " U+" << vs;
+        } else {
+            EXPECT_TRUE(fc->hasVariationSelector(codepoint, vs))
+                << "Glyph for U+" << std::hex << codepoint << " U+" << vs;
+        }
+    }
+}
+
+TEST(FontCollectionTest, hasVariationSelectorTest) {
+  std::shared_ptr<MinikinFont> font(new MinikinFontForTest(kVsTestFont));
+  std::shared_ptr<FontFamily> family(new FontFamily(
+          std::vector<Font>({ Font(font, FontStyle()) })));
+  std::vector<std::shared_ptr<FontFamily>> families({ family });
+  std::shared_ptr<FontCollection> fc(new FontCollection(families));
+
+  EXPECT_FALSE(fc->hasVariationSelector(0x82A6, 0));
+  expectVSGlyphs(fc.get(), 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}));
+
+  EXPECT_FALSE(fc->hasVariationSelector(0x537F, 0));
+  expectVSGlyphs(fc.get(), 0x537F, std::set<uint32_t>({0xFE0E}));
+
+  EXPECT_FALSE(fc->hasVariationSelector(0x717D, 0));
+  expectVSGlyphs(fc.get(), 0x717D, std::set<uint32_t>({0xFE02, 0xE0102, 0xE0103}));
+}
+
+const char kEmojiXmlFile[] = kTestFontDir "emoji.xml";
+
+TEST(FontCollectionTest, hasVariationSelectorTest_emoji) {
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+
+    // Both text/color font have cmap format 14 subtable entry for VS15/VS16 respectively.
+    EXPECT_TRUE(collection->hasVariationSelector(0x2623, 0xFE0E));
+    EXPECT_TRUE(collection->hasVariationSelector(0x2623, 0xFE0F));
+
+    // The text font has cmap format 14 subtable entry for VS15 but the color font doesn't have for
+    // VS16
+    EXPECT_TRUE(collection->hasVariationSelector(0x2626, 0xFE0E));
+    EXPECT_FALSE(collection->hasVariationSelector(0x2626, 0xFE0F));
+
+    // The color font has cmap format 14 subtable entry for VS16 but the text font doesn't have for
+    // VS15.
+    EXPECT_TRUE(collection->hasVariationSelector(0x262A, 0xFE0E));
+    EXPECT_TRUE(collection->hasVariationSelector(0x262A, 0xFE0F));
+
+    // Neither text/color font have cmap format 14 subtable entry for VS15/VS16.
+    EXPECT_TRUE(collection->hasVariationSelector(0x262E, 0xFE0E));
+    EXPECT_FALSE(collection->hasVariationSelector(0x262E, 0xFE0F));
+
+    // Text font doesn't support U+1F3FD. Only the color emoji fonts has. So VS15 is not supported.
+    EXPECT_FALSE(collection->hasVariationSelector(0x1F3FD, 0xFE0E));
+
+    // Text font doesn't have U+262F U+FE0E or even its base code point U+262F.
+    EXPECT_FALSE(collection->hasVariationSelector(0x262F, 0xFE0E));
+
+    // None of the fonts support U+2229.
+    EXPECT_FALSE(collection->hasVariationSelector(0x2229, 0xFE0E));
+    EXPECT_FALSE(collection->hasVariationSelector(0x2229, 0xFE0F));
+
+}
+
+TEST(FontCollectionTest, newEmojiTest) {
+    std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kEmojiXmlFile));
+
+    // U+2695, U+2640, U+2642 are not in emoji catrgory in Unicode 9 but they are now in emoji
+    // category. Should return true even if U+FE0E was appended.
+    // These three emojis are only avalilable in TextEmoji.ttf but U+2695 is excluded here since it
+    // is used in other tests.
+    EXPECT_TRUE(collection->hasVariationSelector(0x2640, 0xFE0E));
+    EXPECT_FALSE(collection->hasVariationSelector(0x2640, 0xFE0F));
+    EXPECT_TRUE(collection->hasVariationSelector(0x2642, 0xFE0E));
+    EXPECT_FALSE(collection->hasVariationSelector(0x2642, 0xFE0F));
+}
+
+TEST(FontCollectionTest, createWithVariations) {
+    // This font has 'wdth' and 'wght' axes.
+    const char kMultiAxisFont[] = kTestFontDir "/MultiAxis.ttf";
+    const char kNoAxisFont[] = kTestFontDir "/Regular.ttf";
+
+    std::shared_ptr<MinikinFont> multiAxisFont(new MinikinFontForTest(kMultiAxisFont));
+    std::shared_ptr<FontFamily> multiAxisFamily(new FontFamily(
+            std::vector<Font>({ Font(multiAxisFont, FontStyle()) })));
+    std::vector<std::shared_ptr<FontFamily>> multiAxisFamilies({multiAxisFamily});
+    std::shared_ptr<FontCollection> multiAxisFc(new FontCollection(multiAxisFamilies));
+
+    std::shared_ptr<MinikinFont> noAxisFont(new MinikinFontForTest(kNoAxisFont));
+    std::shared_ptr<FontFamily> noAxisFamily(new FontFamily(
+            std::vector<Font>({ Font(noAxisFont, FontStyle()) })));
+    std::vector<std::shared_ptr<FontFamily>> noAxisFamilies({noAxisFamily});
+    std::shared_ptr<FontCollection> noAxisFc(new FontCollection(noAxisFamilies));
+
+    {
+        // Do not ceate new instance if none of variations are specified.
+        EXPECT_EQ(nullptr,
+                multiAxisFc->createCollectionWithVariation(std::vector<FontVariation>()));
+        EXPECT_EQ(nullptr,
+                noAxisFc->createCollectionWithVariation(std::vector<FontVariation>()));
+    }
+    {
+        // New instance should be used for supported variation.
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f }
+        };
+        std::shared_ptr<FontCollection> newFc(
+                multiAxisFc->createCollectionWithVariation(variations));
+        EXPECT_NE(nullptr, newFc.get());
+        EXPECT_NE(multiAxisFc.get(), newFc.get());
+
+        EXPECT_EQ(nullptr, noAxisFc->createCollectionWithVariation(variations));
+    }
+    {
+        // New instance should be used for supported variation (multiple variations case).
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
+                { MinikinFont::MakeTag('w', 'g', 'h', 't'), 1.0f }
+        };
+        std::shared_ptr<FontCollection> newFc(
+                multiAxisFc->createCollectionWithVariation(variations));
+        EXPECT_NE(nullptr, newFc.get());
+        EXPECT_NE(multiAxisFc.get(), newFc.get());
+
+        EXPECT_EQ(nullptr, noAxisFc->createCollectionWithVariation(variations));
+    }
+    {
+        // Do not ceate new instance if none of variations are supported.
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
+        };
+        EXPECT_EQ(nullptr, multiAxisFc->createCollectionWithVariation(variations));
+        EXPECT_EQ(nullptr, noAxisFc->createCollectionWithVariation(variations));
+    }
+    {
+        // At least one axis is supported, should create new instance.
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
+                { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
+        };
+        std::shared_ptr<FontCollection> newFc(
+                multiAxisFc->createCollectionWithVariation(variations));
+        EXPECT_NE(nullptr, newFc.get());
+        EXPECT_NE(multiAxisFc.get(), newFc.get());
+
+        EXPECT_EQ(nullptr, noAxisFc->createCollectionWithVariation(variations));
+    }
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
new file mode 100644
index 0000000..90e2a64
--- /dev/null
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -0,0 +1,682 @@
+/*
+ * 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.
+ */
+
+#include <minikin/FontFamily.h>
+
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+#include "FontLanguageListCache.h"
+#include "ICUTestBase.h"
+#include "MinikinFontForTest.h"
+#include "MinikinInternal.h"
+
+namespace minikin {
+
+typedef ICUTestBase FontLanguagesTest;
+typedef ICUTestBase FontLanguageTest;
+
+static const FontLanguages& createFontLanguages(const std::string& input) {
+    android::AutoMutex _l(gMinikinLock);
+    uint32_t langId = FontLanguageListCache::getId(input);
+    return FontLanguageListCache::getById(langId);
+}
+
+static FontLanguage createFontLanguage(const std::string& input) {
+    android::AutoMutex _l(gMinikinLock);
+    uint32_t langId = FontLanguageListCache::getId(input);
+    return FontLanguageListCache::getById(langId)[0];
+}
+
+static FontLanguage createFontLanguageWithoutICUSanitization(const std::string& input) {
+    return FontLanguage(input.c_str(), input.size());
+}
+
+std::shared_ptr<FontFamily> makeFamily(const std::string& fontPath) {
+    std::shared_ptr<MinikinFont> font(new MinikinFontForTest(fontPath));
+    return std::make_shared<FontFamily>(
+            std::vector<Font>({Font(font, FontStyle())}));
+}
+
+TEST_F(FontLanguageTest, basicTests) {
+    FontLanguage defaultLang;
+    FontLanguage emptyLang("", 0);
+    FontLanguage english = createFontLanguage("en");
+    FontLanguage french = createFontLanguage("fr");
+    FontLanguage und = createFontLanguage("und");
+    FontLanguage undZsye = createFontLanguage("und-Zsye");
+
+    EXPECT_EQ(english, english);
+    EXPECT_EQ(french, french);
+
+    EXPECT_TRUE(defaultLang != defaultLang);
+    EXPECT_TRUE(emptyLang != emptyLang);
+    EXPECT_TRUE(defaultLang != emptyLang);
+    EXPECT_TRUE(defaultLang != und);
+    EXPECT_TRUE(emptyLang != und);
+    EXPECT_TRUE(english != defaultLang);
+    EXPECT_TRUE(english != emptyLang);
+    EXPECT_TRUE(english != french);
+    EXPECT_TRUE(english != undZsye);
+    EXPECT_TRUE(und != undZsye);
+    EXPECT_TRUE(english != und);
+
+    EXPECT_TRUE(defaultLang.isUnsupported());
+    EXPECT_TRUE(emptyLang.isUnsupported());
+
+    EXPECT_FALSE(english.isUnsupported());
+    EXPECT_FALSE(french.isUnsupported());
+    EXPECT_FALSE(und.isUnsupported());
+    EXPECT_FALSE(undZsye.isUnsupported());
+}
+
+TEST_F(FontLanguageTest, getStringTest) {
+    EXPECT_EQ("en-Latn-US", createFontLanguage("en").getString());
+    EXPECT_EQ("en-Latn-US", createFontLanguage("en-Latn").getString());
+
+    // Capitalized language code or lowercased script should be normalized.
+    EXPECT_EQ("en-Latn-US", createFontLanguage("EN-LATN").getString());
+    EXPECT_EQ("en-Latn-US", createFontLanguage("EN-latn").getString());
+    EXPECT_EQ("en-Latn-US", createFontLanguage("en-latn").getString());
+
+    // Invalid script should be kept.
+    EXPECT_EQ("en-Xyzt-US", createFontLanguage("en-xyzt").getString());
+
+    EXPECT_EQ("en-Latn-US", createFontLanguage("en-Latn-US").getString());
+    EXPECT_EQ("ja-Jpan-JP", createFontLanguage("ja").getString());
+    EXPECT_EQ("zh-Hant-TW", createFontLanguage("zh-TW").getString());
+    EXPECT_EQ("zh-Hant-HK", createFontLanguage("zh-HK").getString());
+    EXPECT_EQ("zh-Hant-MO", createFontLanguage("zh-MO").getString());
+    EXPECT_EQ("zh-Hans-CN", createFontLanguage("zh").getString());
+    EXPECT_EQ("zh-Hans-CN", createFontLanguage("zh-CN").getString());
+    EXPECT_EQ("zh-Hans-SG", createFontLanguage("zh-SG").getString());
+    EXPECT_EQ("und", createFontLanguage("und").getString());
+    EXPECT_EQ("und", createFontLanguage("UND").getString());
+    EXPECT_EQ("und", createFontLanguage("Und").getString());
+    EXPECT_EQ("und-Zsye", createFontLanguage("und-Zsye").getString());
+    EXPECT_EQ("und-Zsye", createFontLanguage("Und-ZSYE").getString());
+    EXPECT_EQ("und-Zsye", createFontLanguage("Und-zsye").getString());
+
+    EXPECT_EQ("de-Latn-DE", createFontLanguage("de-1901").getString());
+
+    EXPECT_EQ("es-Latn-419", createFontLanguage("es-Latn-419").getString());
+
+    // Emoji subtag is dropped from getString().
+    EXPECT_EQ("es-Latn-419", createFontLanguage("es-419-u-em-emoji").getString());
+    EXPECT_EQ("es-Latn-419", createFontLanguage("es-Latn-419-u-em-emoji").getString());
+
+    // This is not a necessary desired behavior, just known behavior.
+    EXPECT_EQ("en-Latn-US", createFontLanguage("und-Abcdefgh").getString());
+}
+
+TEST_F(FontLanguageTest, testReconstruction) {
+    EXPECT_EQ("en", createFontLanguageWithoutICUSanitization("en").getString());
+    EXPECT_EQ("fil", createFontLanguageWithoutICUSanitization("fil").getString());
+    EXPECT_EQ("und", createFontLanguageWithoutICUSanitization("und").getString());
+
+    EXPECT_EQ("en-Latn", createFontLanguageWithoutICUSanitization("en-Latn").getString());
+    EXPECT_EQ("fil-Taga", createFontLanguageWithoutICUSanitization("fil-Taga").getString());
+    EXPECT_EQ("und-Zsye", createFontLanguageWithoutICUSanitization("und-Zsye").getString());
+
+    EXPECT_EQ("en-US", createFontLanguageWithoutICUSanitization("en-US").getString());
+    EXPECT_EQ("fil-PH", createFontLanguageWithoutICUSanitization("fil-PH").getString());
+    EXPECT_EQ("es-419", createFontLanguageWithoutICUSanitization("es-419").getString());
+
+    EXPECT_EQ("en-Latn-US", createFontLanguageWithoutICUSanitization("en-Latn-US").getString());
+    EXPECT_EQ("fil-Taga-PH", createFontLanguageWithoutICUSanitization("fil-Taga-PH").getString());
+    EXPECT_EQ("es-Latn-419", createFontLanguageWithoutICUSanitization("es-Latn-419").getString());
+
+    // Possible minimum/maximum values.
+    EXPECT_EQ("aa", createFontLanguageWithoutICUSanitization("aa").getString());
+    EXPECT_EQ("zz", createFontLanguageWithoutICUSanitization("zz").getString());
+    EXPECT_EQ("aa-Aaaa", createFontLanguageWithoutICUSanitization("aa-Aaaa").getString());
+    EXPECT_EQ("zz-Zzzz", createFontLanguageWithoutICUSanitization("zz-Zzzz").getString());
+    EXPECT_EQ("aaa-Aaaa-AA", createFontLanguageWithoutICUSanitization("aaa-Aaaa-AA").getString());
+    EXPECT_EQ("zzz-Zzzz-ZZ", createFontLanguageWithoutICUSanitization("zzz-Zzzz-ZZ").getString());
+    EXPECT_EQ("aaa-Aaaa-000", createFontLanguageWithoutICUSanitization("aaa-Aaaa-000").getString());
+    EXPECT_EQ("zzz-Zzzz-999", createFontLanguageWithoutICUSanitization("zzz-Zzzz-999").getString());
+}
+
+TEST_F(FontLanguageTest, ScriptEqualTest) {
+    EXPECT_TRUE(createFontLanguage("en").isEqualScript(createFontLanguage("en")));
+    EXPECT_TRUE(createFontLanguage("en-Latn").isEqualScript(createFontLanguage("en")));
+    EXPECT_TRUE(createFontLanguage("jp-Latn").isEqualScript(createFontLanguage("en-Latn")));
+    EXPECT_TRUE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Jpan")));
+
+    EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hira")));
+    EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hani")));
+}
+
+TEST_F(FontLanguageTest, ScriptMatchTest) {
+    const bool SUPPORTED = true;
+    const bool NOT_SUPPORTED = false;
+
+    struct TestCase {
+        const std::string baseScript;
+        const std::string requestedScript;
+        bool isSupported;
+    } testCases[] = {
+        // Same scripts
+        { "en-Latn", "Latn", SUPPORTED },
+        { "ja-Jpan", "Jpan", SUPPORTED },
+        { "ja-Hira", "Hira", SUPPORTED },
+        { "ja-Kana", "Kana", SUPPORTED },
+        { "ja-Hrkt", "Hrkt", SUPPORTED },
+        { "zh-Hans", "Hans", SUPPORTED },
+        { "zh-Hant", "Hant", SUPPORTED },
+        { "zh-Hani", "Hani", SUPPORTED },
+        { "ko-Kore", "Kore", SUPPORTED },
+        { "ko-Hang", "Hang", SUPPORTED },
+        { "zh-Hanb", "Hanb", SUPPORTED },
+
+        // Japanese supports Hiragana, Katakanara, etc.
+        { "ja-Jpan", "Hira", SUPPORTED },
+        { "ja-Jpan", "Kana", SUPPORTED },
+        { "ja-Jpan", "Hrkt", SUPPORTED },
+        { "ja-Hrkt", "Hira", SUPPORTED },
+        { "ja-Hrkt", "Kana", SUPPORTED },
+
+        // Chinese supports Han.
+        { "zh-Hans", "Hani", SUPPORTED },
+        { "zh-Hant", "Hani", SUPPORTED },
+        { "zh-Hanb", "Hani", SUPPORTED },
+
+        // Hanb supports Bopomofo.
+        { "zh-Hanb", "Bopo", SUPPORTED },
+
+        // Korean supports Hangul.
+        { "ko-Kore", "Hang", SUPPORTED },
+
+        // Different scripts
+        { "ja-Jpan", "Latn", NOT_SUPPORTED },
+        { "en-Latn", "Jpan", NOT_SUPPORTED },
+        { "ja-Jpan", "Hant", NOT_SUPPORTED },
+        { "zh-Hant", "Jpan", NOT_SUPPORTED },
+        { "ja-Jpan", "Hans", NOT_SUPPORTED },
+        { "zh-Hans", "Jpan", NOT_SUPPORTED },
+        { "ja-Jpan", "Kore", NOT_SUPPORTED },
+        { "ko-Kore", "Jpan", NOT_SUPPORTED },
+        { "zh-Hans", "Hant", NOT_SUPPORTED },
+        { "zh-Hant", "Hans", NOT_SUPPORTED },
+        { "zh-Hans", "Kore", NOT_SUPPORTED },
+        { "ko-Kore", "Hans", NOT_SUPPORTED },
+        { "zh-Hant", "Kore", NOT_SUPPORTED },
+        { "ko-Kore", "Hant", NOT_SUPPORTED },
+
+        // Hiragana doesn't support Japanese, etc.
+        { "ja-Hira", "Jpan", NOT_SUPPORTED },
+        { "ja-Kana", "Jpan", NOT_SUPPORTED },
+        { "ja-Hrkt", "Jpan", NOT_SUPPORTED },
+        { "ja-Hani", "Jpan", NOT_SUPPORTED },
+        { "ja-Hira", "Hrkt", NOT_SUPPORTED },
+        { "ja-Kana", "Hrkt", NOT_SUPPORTED },
+        { "ja-Hani", "Hrkt", NOT_SUPPORTED },
+        { "ja-Hani", "Hira", NOT_SUPPORTED },
+        { "ja-Hani", "Kana", NOT_SUPPORTED },
+
+        // Kanji doesn't support Chinese, etc.
+        { "zh-Hani", "Hant", NOT_SUPPORTED },
+        { "zh-Hani", "Hans", NOT_SUPPORTED },
+        { "zh-Hani", "Hanb", NOT_SUPPORTED },
+
+        // Hangul doesn't support Korean, etc.
+        { "ko-Hang", "Kore", NOT_SUPPORTED },
+        { "ko-Hani", "Kore", NOT_SUPPORTED },
+        { "ko-Hani", "Hang", NOT_SUPPORTED },
+        { "ko-Hang", "Hani", NOT_SUPPORTED },
+
+        // Han with botomofo doesn't support simplified Chinese, etc.
+        { "zh-Hanb", "Hant", NOT_SUPPORTED },
+        { "zh-Hanb", "Hans", NOT_SUPPORTED },
+        { "zh-Hanb", "Jpan", NOT_SUPPORTED },
+        { "zh-Hanb", "Kore", NOT_SUPPORTED },
+    };
+
+    for (auto testCase : testCases) {
+        hb_script_t script = hb_script_from_iso15924_tag(
+                HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
+                       testCase.requestedScript[2], testCase.requestedScript[3]));
+        if (testCase.isSupported) {
+            EXPECT_TRUE(
+                    createFontLanguage(testCase.baseScript).supportsHbScript(script))
+                    << testCase.baseScript << " should support " << testCase.requestedScript;
+        } else {
+            EXPECT_FALSE(
+                    createFontLanguage(testCase.baseScript).supportsHbScript(script))
+                    << testCase.baseScript << " shouldn't support " << testCase.requestedScript;
+        }
+    }
+}
+
+TEST_F(FontLanguagesTest, basicTests) {
+    FontLanguages emptyLangs;
+    EXPECT_EQ(0u, emptyLangs.size());
+
+    FontLanguage english = createFontLanguage("en");
+    const FontLanguages& singletonLangs = createFontLanguages("en");
+    EXPECT_EQ(1u, singletonLangs.size());
+    EXPECT_EQ(english, singletonLangs[0]);
+
+    FontLanguage french = createFontLanguage("fr");
+    const FontLanguages& twoLangs = createFontLanguages("en,fr");
+    EXPECT_EQ(2u, twoLangs.size());
+    EXPECT_EQ(english, twoLangs[0]);
+    EXPECT_EQ(french, twoLangs[1]);
+}
+
+TEST_F(FontLanguagesTest, unsupportedLanguageTests) {
+    const FontLanguages& oneUnsupported = createFontLanguages("abcd-example");
+    EXPECT_TRUE(oneUnsupported.empty());
+
+    const FontLanguages& twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
+    EXPECT_TRUE(twoUnsupporteds.empty());
+
+    FontLanguage english = createFontLanguage("en");
+    const FontLanguages& firstUnsupported = createFontLanguages("abcd-example,en");
+    EXPECT_EQ(1u, firstUnsupported.size());
+    EXPECT_EQ(english, firstUnsupported[0]);
+
+    const FontLanguages& lastUnsupported = createFontLanguages("en,abcd-example");
+    EXPECT_EQ(1u, lastUnsupported.size());
+    EXPECT_EQ(english, lastUnsupported[0]);
+}
+
+TEST_F(FontLanguagesTest, repeatedLanguageTests) {
+    FontLanguage english = createFontLanguage("en");
+    FontLanguage french = createFontLanguage("fr");
+    FontLanguage canadianFrench = createFontLanguage("fr-CA");
+    FontLanguage englishInLatn = createFontLanguage("en-Latn");
+    ASSERT_TRUE(english == englishInLatn);
+
+    const FontLanguages& langs = createFontLanguages("en,en-Latn");
+    EXPECT_EQ(1u, langs.size());
+    EXPECT_EQ(english, langs[0]);
+
+    const FontLanguages& fr = createFontLanguages("fr,fr-FR,fr-Latn-FR");
+    EXPECT_EQ(1u, fr.size());
+    EXPECT_EQ(french, fr[0]);
+
+    // ICU appends FR to fr. The third language is dropped which is same as the first language.
+    const FontLanguages& fr2 = createFontLanguages("fr,fr-CA,fr-FR");
+    EXPECT_EQ(2u, fr2.size());
+    EXPECT_EQ(french, fr2[0]);
+    EXPECT_EQ(canadianFrench, fr2[1]);
+
+    // The order should be kept.
+    const FontLanguages& langs2 = createFontLanguages("en,fr,en-Latn");
+    EXPECT_EQ(2u, langs2.size());
+    EXPECT_EQ(english, langs2[0]);
+    EXPECT_EQ(french, langs2[1]);
+}
+
+TEST_F(FontLanguagesTest, identifierTest) {
+    EXPECT_EQ(createFontLanguage("en-Latn-US"), createFontLanguage("en-Latn-US"));
+    EXPECT_EQ(createFontLanguage("zh-Hans-CN"), createFontLanguage("zh-Hans-CN"));
+    EXPECT_EQ(createFontLanguage("en-Zsye-US"), createFontLanguage("en-Zsye-US"));
+
+    EXPECT_NE(createFontLanguage("en-Latn-US"), createFontLanguage("en-Latn-GB"));
+    EXPECT_NE(createFontLanguage("en-Latn-US"), createFontLanguage("en-Zsye-US"));
+    EXPECT_NE(createFontLanguage("es-Latn-US"), createFontLanguage("en-Latn-US"));
+    EXPECT_NE(createFontLanguage("zh-Hant-HK"), createFontLanguage("zh-Hant-TW"));
+}
+
+TEST_F(FontLanguagesTest, undEmojiTests) {
+    FontLanguage emoji = createFontLanguage("und-Zsye");
+    EXPECT_EQ(FontLanguage::EMSTYLE_EMOJI, emoji.getEmojiStyle());
+
+    FontLanguage und = createFontLanguage("und");
+    EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, und.getEmojiStyle());
+    EXPECT_FALSE(emoji == und);
+
+    FontLanguage undExample = createFontLanguage("und-example");
+    EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, undExample.getEmojiStyle());
+    EXPECT_FALSE(emoji == undExample);
+}
+
+TEST_F(FontLanguagesTest, subtagEmojiTest) {
+    std::string subtagEmojiStrings[] = {
+        // Duplicate subtag case.
+        "und-Latn-u-em-emoji-u-em-text",
+
+        // Strings that contain language.
+        "und-u-em-emoji",
+        "en-u-em-emoji",
+
+        // Strings that contain the script.
+        "und-Jpan-u-em-emoji",
+        "en-Latn-u-em-emoji",
+        "und-Zsym-u-em-emoji",
+        "und-Zsye-u-em-emoji",
+        "en-Zsym-u-em-emoji",
+        "en-Zsye-u-em-emoji",
+
+        // Strings that contain the county.
+        "und-US-u-em-emoji",
+        "en-US-u-em-emoji",
+        "es-419-u-em-emoji",
+        "und-Latn-US-u-em-emoji",
+        "en-Zsym-US-u-em-emoji",
+        "en-Zsye-US-u-em-emoji",
+        "es-Zsye-419-u-em-emoji",
+    };
+
+    for (auto subtagEmojiString : subtagEmojiStrings) {
+        SCOPED_TRACE("Test for \"" + subtagEmojiString + "\"");
+        FontLanguage subtagEmoji = createFontLanguage(subtagEmojiString);
+        EXPECT_EQ(FontLanguage::EMSTYLE_EMOJI, subtagEmoji.getEmojiStyle());
+    }
+}
+
+TEST_F(FontLanguagesTest, subtagTextTest) {
+    std::string subtagTextStrings[] = {
+        // Duplicate subtag case.
+        "und-Latn-u-em-text-u-em-emoji",
+
+        // Strings that contain language.
+        "und-u-em-text",
+        "en-u-em-text",
+
+        // Strings that contain the script.
+        "und-Latn-u-em-text",
+        "en-Jpan-u-em-text",
+        "und-Zsym-u-em-text",
+        "und-Zsye-u-em-text",
+        "en-Zsym-u-em-text",
+        "en-Zsye-u-em-text",
+
+        // Strings that contain the county.
+        "und-US-u-em-text",
+        "en-US-u-em-text",
+        "es-419-u-em-text",
+        "und-Latn-US-u-em-text",
+        "en-Zsym-US-u-em-text",
+        "en-Zsye-US-u-em-text",
+        "es-Zsye-419-u-em-text",
+    };
+
+    for (auto subtagTextString : subtagTextStrings) {
+        SCOPED_TRACE("Test for \"" + subtagTextString + "\"");
+        FontLanguage subtagText = createFontLanguage(subtagTextString);
+        EXPECT_EQ(FontLanguage::EMSTYLE_TEXT, subtagText.getEmojiStyle());
+    }
+}
+
+// TODO: add more "und" language cases whose language and script are
+//       unexpectedly translated to en-Latn by ICU.
+TEST_F(FontLanguagesTest, subtagDefaultTest) {
+    std::string subtagDefaultStrings[] = {
+        // Duplicate subtag case.
+        "en-Latn-u-em-default-u-em-emoji",
+        "en-Latn-u-em-default-u-em-text",
+
+        // Strings that contain language.
+        "und-u-em-default",
+        "en-u-em-default",
+
+        // Strings that contain the script.
+        "en-Latn-u-em-default",
+        "en-Zsym-u-em-default",
+        "en-Zsye-u-em-default",
+
+        // Strings that contain the county.
+        "en-US-u-em-default",
+        "en-Latn-US-u-em-default",
+        "es-Latn-419-u-em-default",
+        "en-Zsym-US-u-em-default",
+        "en-Zsye-US-u-em-default",
+        "es-Zsye-419-u-em-default",
+    };
+
+    for (auto subtagDefaultString : subtagDefaultStrings) {
+        SCOPED_TRACE("Test for \"" + subtagDefaultString + "\"");
+        FontLanguage subtagDefault = createFontLanguage(subtagDefaultString);
+        EXPECT_EQ(FontLanguage::EMSTYLE_DEFAULT, subtagDefault.getEmojiStyle());
+    }
+}
+
+TEST_F(FontLanguagesTest, subtagEmptyTest) {
+    std::string subtagEmptyStrings[] = {
+        "und",
+        "jp",
+        "en-US",
+        "en-Latn",
+        "en-Latn-US",
+        "en-Latn-US-u-em",
+        "en-Latn-US-u-em-defaultemoji",
+    };
+
+    for (auto subtagEmptyString : subtagEmptyStrings) {
+        SCOPED_TRACE("Test for \"" + subtagEmptyString + "\"");
+        FontLanguage subtagEmpty = createFontLanguage(subtagEmptyString);
+        EXPECT_EQ(FontLanguage::EMSTYLE_EMPTY, subtagEmpty.getEmojiStyle());
+    }
+}
+
+TEST_F(FontLanguagesTest, registerLanguageListTest) {
+    EXPECT_EQ(0UL, FontStyle::registerLanguageList(""));
+    EXPECT_NE(0UL, FontStyle::registerLanguageList("en"));
+    EXPECT_NE(0UL, FontStyle::registerLanguageList("jp"));
+    EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans"));
+
+    EXPECT_EQ(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("en"));
+    EXPECT_NE(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("jp"));
+
+    EXPECT_EQ(FontStyle::registerLanguageList("en,zh-Hans"),
+              FontStyle::registerLanguageList("en,zh-Hans"));
+    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
+              FontStyle::registerLanguageList("zh-Hans,en"));
+    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
+              FontStyle::registerLanguageList("jp"));
+    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
+              FontStyle::registerLanguageList("en"));
+    EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
+              FontStyle::registerLanguageList("en,zh-Hant"));
+}
+
+// The test font has following glyphs.
+// U+82A6
+// U+82A6 U+FE00 (VS1)
+// U+82A6 U+E0100 (VS17)
+// U+82A6 U+E0101 (VS18)
+// U+82A6 U+E0102 (VS19)
+// U+845B
+// U+845B U+FE00 (VS2)
+// U+845B U+E0101 (VS18)
+// U+845B U+E0102 (VS19)
+// U+845B U+E0103 (VS20)
+// U+537F
+// U+717D U+FE02 (VS3)
+// U+717D U+E0102 (VS19)
+// U+717D U+E0103 (VS20)
+const char kVsTestFont[] = kTestFontDir "VariationSelectorTest-Regular.ttf";
+
+class FontFamilyTest : public ICUTestBase {
+public:
+    virtual void SetUp() override {
+        ICUTestBase::SetUp();
+        if (access(kVsTestFont, R_OK) != 0) {
+            FAIL() << "Unable to read " << kVsTestFont << ". "
+                   << "Please prepare the test data directory. "
+                   << "For more details, please see how_to_run.txt.";
+        }
+    }
+};
+
+// Asserts that the font family has glyphs for and only for specified codepoint
+// and variationSelector pairs.
+void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set<uint32_t>& vs) {
+    for (uint32_t i = 0xFE00; i <= 0xE01EF; ++i) {
+        // Move to variation selectors supplements after variation selectors.
+        if (i == 0xFF00) {
+            i = 0xE0100;
+        }
+        if (vs.find(i) == vs.end()) {
+            EXPECT_FALSE(family->hasGlyph(codepoint, i))
+                    << "Glyph for U+" << std::hex << codepoint << " U+" << i;
+        } else {
+            EXPECT_TRUE(family->hasGlyph(codepoint, i))
+                    << "Glyph for U+" << std::hex << codepoint << " U+" << i;
+        }
+
+    }
+}
+
+TEST_F(FontFamilyTest, hasVariationSelectorTest) {
+    std::shared_ptr<MinikinFont> minikinFont(new MinikinFontForTest(kVsTestFont));
+    std::shared_ptr<FontFamily> family(
+            new FontFamily(std::vector<Font>{ Font(minikinFont, FontStyle()) }));
+
+    const uint32_t kVS1 = 0xFE00;
+    const uint32_t kVS2 = 0xFE01;
+    const uint32_t kVS3 = 0xFE02;
+    const uint32_t kVS17 = 0xE0100;
+    const uint32_t kVS18 = 0xE0101;
+    const uint32_t kVS19 = 0xE0102;
+    const uint32_t kVS20 = 0xE0103;
+
+    const uint32_t kSupportedChar1 = 0x82A6;
+    EXPECT_TRUE(family->getCoverage().get(kSupportedChar1));
+    expectVSGlyphs(family.get(), 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}));
+
+    const uint32_t kNoVsSupportedChar = 0x537F;
+    EXPECT_TRUE(family->getCoverage().get(kNoVsSupportedChar));
+    expectVSGlyphs(family.get(), 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}));
+
+    const uint32_t kNotSupportedChar = 0x845C;
+    EXPECT_FALSE(family->getCoverage().get(kNotSupportedChar));
+    expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
+}
+
+TEST_F(FontFamilyTest, hasVSTableTest) {
+    struct TestCase {
+        const std::string fontPath;
+        bool hasVSTable;
+    } testCases[] = {
+        { kTestFontDir "Ja.ttf", true },
+        { kTestFontDir "ZhHant.ttf", true },
+        { kTestFontDir "ZhHans.ttf", true },
+        { kTestFontDir "Italic.ttf", false },
+        { kTestFontDir "Bold.ttf", false },
+        { kTestFontDir "BoldItalic.ttf", false },
+    };
+
+    for (auto testCase : testCases) {
+        SCOPED_TRACE(testCase.hasVSTable ?
+                "Font " + testCase.fontPath + " should have a variation sequence table." :
+                "Font " + testCase.fontPath + " shouldn't have a variation sequence table.");
+
+        std::shared_ptr<MinikinFont> minikinFont(
+                new MinikinFontForTest(testCase.fontPath));
+        std::shared_ptr<FontFamily> family(new FontFamily(
+                std::vector<Font>{ Font(minikinFont, FontStyle()) }));
+        EXPECT_EQ(testCase.hasVSTable, family->hasVSTable());
+    }
+}
+
+TEST_F(FontFamilyTest, createFamilyWithVariationTest) {
+    // This font has 'wdth' and 'wght' axes.
+    const char kMultiAxisFont[] = kTestFontDir "/MultiAxis.ttf";
+    const char kNoAxisFont[] = kTestFontDir "/Regular.ttf";
+
+    std::shared_ptr<FontFamily> multiAxisFamily = makeFamily(kMultiAxisFont);
+    std::shared_ptr<FontFamily> noAxisFamily = makeFamily(kNoAxisFont);
+
+    {
+        // Do not ceate new instance if none of variations are specified.
+        EXPECT_EQ(nullptr,
+                multiAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
+        EXPECT_EQ(nullptr,
+                noAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
+    }
+    {
+        // New instance should be used for supported variation.
+        std::vector<FontVariation> variations = {{MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f}};
+        std::shared_ptr<FontFamily> newFamily(
+                multiAxisFamily->createFamilyWithVariation(variations));
+        EXPECT_NE(nullptr, newFamily.get());
+        EXPECT_NE(multiAxisFamily.get(), newFamily.get());
+        EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
+    }
+    {
+        // New instance should be used for supported variation. (multiple variations case)
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
+                { MinikinFont::MakeTag('w', 'g', 'h', 't'), 1.0f }
+        };
+        std::shared_ptr<FontFamily> newFamily(
+                multiAxisFamily->createFamilyWithVariation(variations));
+        EXPECT_NE(nullptr, newFamily.get());
+        EXPECT_NE(multiAxisFamily.get(), newFamily.get());
+        EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
+    }
+    {
+        // Do not ceate new instance if none of variations are supported.
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
+        };
+        EXPECT_EQ(nullptr, multiAxisFamily->createFamilyWithVariation(variations));
+        EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
+    }
+    {
+        // At least one axis is supported, should create new instance.
+        std::vector<FontVariation> variations = {
+                { MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f },
+                { MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f }
+        };
+        std::shared_ptr<FontFamily> newFamily(
+                multiAxisFamily->createFamilyWithVariation(variations));
+        EXPECT_NE(nullptr, newFamily.get());
+        EXPECT_NE(multiAxisFamily.get(), newFamily.get());
+        EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
+    }
+}
+
+TEST_F(FontFamilyTest, coverageTableSelectionTest) {
+    // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
+    // encoding ID is 1.
+    const char kUnicodeEncoding1Font[] = kTestFontDir "UnicodeBMPOnly.ttf";
+
+    // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
+    // encoding ID is 3.
+    const char kUnicodeEncoding3Font[] = kTestFontDir "UnicodeBMPOnly2.ttf";
+
+    // This font has both cmap format 4 subtable which platform ID is 0 and encoding ID is 1
+    // and cmap format 14 subtable which platform ID is 0 and encoding ID is 10.
+    // U+0061 is listed in both subtable but U+1F926 is only listed in latter.
+    const char kUnicodeEncoding4Font[] = kTestFontDir "UnicodeUCS4.ttf";
+
+    std::shared_ptr<FontFamily> unicodeEnc1Font = makeFamily(kUnicodeEncoding1Font);
+    std::shared_ptr<FontFamily> unicodeEnc3Font = makeFamily(kUnicodeEncoding3Font);
+    std::shared_ptr<FontFamily> unicodeEnc4Font = makeFamily(kUnicodeEncoding4Font);
+
+    android::AutoMutex _l(gMinikinLock);
+
+    EXPECT_TRUE(unicodeEnc1Font->hasGlyph(0x0061, 0));
+    EXPECT_TRUE(unicodeEnc3Font->hasGlyph(0x0061, 0));
+    EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x0061, 0));
+
+    EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x1F926, 0));
+}
+
+}  // namespace minikin
diff --git a/tests/FontLanguageListCacheTest.cpp b/tests/unittest/FontLanguageListCacheTest.cpp
similarity index 95%
rename from tests/FontLanguageListCacheTest.cpp
rename to tests/unittest/FontLanguageListCacheTest.cpp
index 2a04671..81d84a8 100644
--- a/tests/FontLanguageListCacheTest.cpp
+++ b/tests/unittest/FontLanguageListCacheTest.cpp
@@ -22,7 +22,7 @@
 #include "ICUTestBase.h"
 #include "MinikinInternal.h"
 
-namespace android {
+namespace minikin {
 
 typedef ICUTestBase FontLanguageListCacheTest;
 
@@ -31,7 +31,7 @@
     EXPECT_NE(0UL, FontStyle::registerLanguageList("jp"));
     EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans"));
 
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     EXPECT_EQ(0UL, FontLanguageListCache::getId(""));
 
     EXPECT_EQ(FontLanguageListCache::getId("en"), FontLanguageListCache::getId("en"));
@@ -50,7 +50,7 @@
 }
 
 TEST_F(FontLanguageListCacheTest, getById) {
-    AutoMutex _l(gMinikinLock);
+    android::AutoMutex _l(gMinikinLock);
     uint32_t enLangId = FontLanguageListCache::getId("en");
     uint32_t jpLangId = FontLanguageListCache::getId("jp");
     FontLanguage english = FontLanguageListCache::getById(enLangId)[0];
@@ -70,4 +70,4 @@
     EXPECT_EQ(japanese, langs2[1]);
 }
 
-}  // android
+}  // namespace minikin
diff --git a/tests/unittest/GraphemeBreakTests.cpp b/tests/unittest/GraphemeBreakTests.cpp
new file mode 100644
index 0000000..6720df6
--- /dev/null
+++ b/tests/unittest/GraphemeBreakTests.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <UnicodeUtils.h>
+#include <minikin/GraphemeBreak.h>
+
+namespace minikin {
+
+bool IsBreak(const char* src) {
+    const size_t BUF_SIZE = 256;
+    uint16_t buf[BUF_SIZE];
+    size_t offset;
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, src, &size, &offset);
+    return GraphemeBreak::isGraphemeBreak(nullptr, buf, 0, size, offset);
+}
+
+bool IsBreakWithAdvances(const float* advances, const char* src) {
+    const size_t BUF_SIZE = 256;
+    uint16_t buf[BUF_SIZE];
+    size_t offset;
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, src, &size, &offset);
+    return GraphemeBreak::isGraphemeBreak(advances, buf, 0, size, offset);
+}
+
+TEST(GraphemeBreak, utf16) {
+    EXPECT_FALSE(IsBreak("U+D83C | U+DC31"));  // emoji, U+1F431
+
+    // tests for invalid UTF-16
+    EXPECT_TRUE(IsBreak("U+D800 | U+D800"));  // two leading surrogates
+    EXPECT_TRUE(IsBreak("U+DC00 | U+DC00"));  // two trailing surrogates
+    EXPECT_TRUE(IsBreak("'a' | U+D800"));  // lonely leading surrogate
+    EXPECT_TRUE(IsBreak("U+DC00 | 'a'"));  // lonely trailing surrogate
+    EXPECT_TRUE(IsBreak("U+D800 | 'a'"));  // leading surrogate followed by non-surrogate
+    EXPECT_TRUE(IsBreak("'a' | U+DC00"));  // non-surrogate followed by trailing surrogate
+}
+
+TEST(GraphemeBreak, rules) {
+    // Rule GB1, sot ÷; Rule GB2, ÷ eot
+    EXPECT_TRUE(IsBreak("| 'a'"));
+    EXPECT_TRUE(IsBreak("'a' |"));
+
+    // Rule GB3, CR x LF
+    EXPECT_FALSE(IsBreak("U+000D | U+000A"));  // CR x LF
+
+    // Rule GB4, (Control | CR | LF) ÷
+    EXPECT_TRUE(IsBreak("'a' | U+2028"));  // Line separator
+    EXPECT_TRUE(IsBreak("'a' | U+000D"));  // LF
+    EXPECT_TRUE(IsBreak("'a' | U+000A"));  // CR
+
+    // Rule GB5, ÷ (Control | CR | LF)
+    EXPECT_TRUE(IsBreak("U+2028 | 'a'"));  // Line separator
+    EXPECT_TRUE(IsBreak("U+000D | 'a'"));  // LF
+    EXPECT_TRUE(IsBreak("U+000A | 'a'"));  // CR
+
+    // Rule GB6, L x ( L | V | LV | LVT )
+    EXPECT_FALSE(IsBreak("U+1100 | U+1100"));  // L x L
+    EXPECT_FALSE(IsBreak("U+1100 | U+1161"));  // L x V
+    EXPECT_FALSE(IsBreak("U+1100 | U+AC00"));  // L x LV
+    EXPECT_FALSE(IsBreak("U+1100 | U+AC01"));  // L x LVT
+
+    // Rule GB7, ( LV | V ) x ( V | T )
+    EXPECT_FALSE(IsBreak("U+AC00 | U+1161"));  // LV x V
+    EXPECT_FALSE(IsBreak("U+1161 | U+1161"));  // V x V
+    EXPECT_FALSE(IsBreak("U+AC00 | U+11A8"));  // LV x T
+    EXPECT_FALSE(IsBreak("U+1161 | U+11A8"));  // V x T
+
+    // Rule GB8, ( LVT | T ) x T
+    EXPECT_FALSE(IsBreak("U+AC01 | U+11A8"));  // LVT x T
+    EXPECT_FALSE(IsBreak("U+11A8 | U+11A8"));  // T x T
+
+    // Other hangul pairs not counted above _are_ breaks (GB10)
+    EXPECT_TRUE(IsBreak("U+AC00 | U+1100"));  // LV x L
+    EXPECT_TRUE(IsBreak("U+AC01 | U+1100"));  // LVT x L
+    EXPECT_TRUE(IsBreak("U+11A8 | U+1100"));  // T x L
+    EXPECT_TRUE(IsBreak("U+11A8 | U+AC00"));  // T x LV
+    EXPECT_TRUE(IsBreak("U+11A8 | U+AC01"));  // T x LVT
+
+    // Rule GB12 and Rule GB13, Regional_Indicator x Regional_Indicator
+    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8"));
+    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | U+1F1FA U+1F1F8")); // Regional indicator pair (flag)
+    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8 U+1F1FA U+1F1F8")); // Regional indicator pair (flag)
+    EXPECT_FALSE(IsBreak("U+1F1FA U+1F1F8 U+1F1FA | U+1F1F8")); // Regional indicator pair (flag)
+
+    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | U+1F1FA"));  // Regional indicator pair (flag)
+    EXPECT_FALSE(IsBreak("U+1F1FA | U+1F1F8 U+1F1FA"));  // Regional indicator pair (flag)
+    // Same case as the two above, knowing that the first two characters ligate, which is what
+    // would typically happen.
+    const float firstPairLigated[] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0}; // Two entries per codepoint
+    EXPECT_TRUE(IsBreakWithAdvances(firstPairLigated, "U+1F1FA U+1F1F8 | U+1F1FA"));
+    EXPECT_FALSE(IsBreakWithAdvances(firstPairLigated, "U+1F1FA | U+1F1F8 U+1F1FA"));
+    // Repeat the tests, But now the font doesn't have a ligature for the first two characters,
+    // while it does have a ligature for the last two. This could happen for fonts that do not
+    // support some (potentially encoded later than they were developed) flags.
+    const float secondPairLigated[] = {1.0, 0.0, 1.0, 0.0, 0.0, 0.0};
+    EXPECT_FALSE(IsBreakWithAdvances(secondPairLigated, "U+1F1FA U+1F1F8 | U+1F1FA"));
+    EXPECT_TRUE(IsBreakWithAdvances(secondPairLigated, "U+1F1FA | U+1F1F8 U+1F1FA"));
+
+    EXPECT_TRUE(IsBreak("'a' U+1F1FA U+1F1F8 | U+1F1FA"));  // Regional indicator pair (flag)
+    EXPECT_FALSE(IsBreak("'a' U+1F1FA | U+1F1F8 U+1F1FA"));  // Regional indicator pair (flag)
+
+    EXPECT_TRUE(
+            IsBreak("'a' U+1F1FA U+1F1F8 | U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
+    EXPECT_FALSE(
+            IsBreak("'a' U+1F1FA | U+1F1F8 U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
+    EXPECT_FALSE(
+            IsBreak("'a' U+1F1FA U+1F1F8 U+1F1FA | U+1F1F8"));  // Regional indicator pair (flag)
+
+    // Rule GB9, x (Extend | ZWJ)
+    EXPECT_FALSE(IsBreak("'a' | U+0301"));  // combining accent
+    EXPECT_FALSE(IsBreak("'a' | U+200D"));  // ZWJ
+    // Rule GB9a, x SpacingMark
+    EXPECT_FALSE(IsBreak("U+0915 | U+093E"));  // KA, AA (spacing mark)
+    // Rule GB9b, Prepend x
+    // see tailoring test for prepend, as current ICU doesn't have any characters in the class
+
+    // Rule GB999, Any ÷ Any
+    EXPECT_TRUE(IsBreak("'a' | 'b'"));
+    EXPECT_TRUE(IsBreak("'f' | 'i'"));  // probable ligature
+    EXPECT_TRUE(IsBreak("U+0644 | U+0627"));  // probable ligature, lam + alef
+    EXPECT_TRUE(IsBreak("U+4E00 | U+4E00"));  // CJK ideographs
+    EXPECT_TRUE(IsBreak("'a' | U+1F1FA U+1F1F8"));  // Regional indicator pair (flag)
+    EXPECT_TRUE(IsBreak("U+1F1FA U+1F1F8 | 'a'"));  // Regional indicator pair (flag)
+
+    // Extended rule for emoji tag sequence.
+    EXPECT_TRUE(IsBreak("'a' | U+1F3F4 'a'"));
+    EXPECT_TRUE(IsBreak("'a' U+1F3F4 | 'a'"));
+
+    // Immediate tag_term after tag_base.
+    EXPECT_TRUE(IsBreak("'a' | U+1F3F4 U+E007F 'a'"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 | U+E007F"));
+    EXPECT_TRUE(IsBreak("'a' U+1F3F4 U+E007F | 'a'"));
+
+    // Flag sequence
+    // U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F is emoji tag sequence for the flag
+    // of Scotland.
+    // U+1F3F4 is WAVING BLACK FLAG. This can be a tag_base character.
+    // U+E0067 is TAG LATIN SMALL LETTER G. This can be a part of tag_spec.
+    // U+E0062 is TAG LATIN SMALL LETTER B. This can be a part of tag_spec.
+    // U+E0073 is TAG LATIN SMALL LETTER S. This can be a part of tag_spec.
+    // U+E0063 is TAG LATIN SMALL LETTER C. This can be a part of tag_spec.
+    // U+E0074 is TAG LATIN SMALL LETTER T. This can be a part of tag_spec.
+    // U+E007F is CANCEL TAG. This is a tag_term character.
+    EXPECT_TRUE(IsBreak("'a' | U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 | U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 U+E0067 | U+E0062 U+E0073 U+E0063 U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 U+E0067 U+E0062 | U+E0073 U+E0063 U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 U+E0067 U+E0062 U+E0073 | U+E0063 U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 | U+E0074 U+E007F"));
+    EXPECT_FALSE(IsBreak("U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 | U+E007F"));
+    EXPECT_TRUE(IsBreak("U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F | 'a'"));
+}
+
+TEST(GraphemeBreak, tailoring) {
+    // control characters that we interpret as "extend"
+    EXPECT_FALSE(IsBreak("'a' | U+00AD"));  // soft hyphen
+    EXPECT_FALSE(IsBreak("'a' | U+200B"));  // zwsp
+    EXPECT_FALSE(IsBreak("'a' | U+200E"));  // lrm
+    EXPECT_FALSE(IsBreak("'a' | U+202A"));  // lre
+    EXPECT_FALSE(IsBreak("'a' | U+E0041"));  // tag character
+
+    // UTC-approved characters for the Prepend class
+    EXPECT_FALSE(IsBreak("U+06DD | U+0661"));  // arabic subtending mark + digit one
+
+    EXPECT_TRUE(IsBreak("U+0E01 | U+0E33"));  // Thai sara am
+
+    // virama is not a grapheme break, but "pure killer" is
+    EXPECT_FALSE(IsBreak("U+0915 | U+094D U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_FALSE(IsBreak("U+0915 U+094D | U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_FALSE(IsBreak("U+0E01 | U+0E3A U+0E01"));  // thai phinthu = pure killer
+    EXPECT_TRUE(IsBreak("U+0E01 U+0E3A | U+0E01"));  // thai phinthu = pure killer
+
+    // Repetition of above tests, but with a given advances array that implies everything
+    // became just one cluster.
+    const float conjoined[] = {1.0, 0.0, 0.0};
+    EXPECT_FALSE(IsBreakWithAdvances(conjoined,
+            "U+0915 | U+094D U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_FALSE(IsBreakWithAdvances(conjoined,
+            "U+0915 U+094D | U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_FALSE(IsBreakWithAdvances(conjoined,
+            "U+0E01 | U+0E3A U+0E01"));  // thai phinthu = pure killer
+    EXPECT_TRUE(IsBreakWithAdvances(conjoined,
+            "U+0E01 U+0E3A | U+0E01"));  // thai phinthu = pure killer
+
+    // Repetition of above tests, but with a given advances array that the virama did not
+    // form a cluster with the following consonant. The difference is that there is now
+    // a grapheme break after the virama in ka+virama+ka.
+    const float separate[] = {1.0, 0.0, 1.0};
+    EXPECT_FALSE(IsBreakWithAdvances(separate,
+            "U+0915 | U+094D U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_TRUE(IsBreakWithAdvances(separate,
+            "U+0915 U+094D | U+0915"));  // Devanagari ka+virama+ka
+    EXPECT_FALSE(IsBreakWithAdvances(separate,
+            "U+0E01 | U+0E3A U+0E01"));  // thai phinthu = pure killer
+    EXPECT_TRUE(IsBreakWithAdvances(separate,
+            "U+0E01 U+0E3A | U+0E01"));  // thai phinthu = pure killer
+
+    // suppress grapheme breaks in zwj emoji sequences
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+2764 U+FE0F U+200D | U+1F48B U+200D U+1F468"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D | U+1F468"));
+    EXPECT_FALSE(IsBreak("U+1F468 U+200D | U+1F469 U+200D U+1F466"));
+    EXPECT_FALSE(IsBreak("U+1F468 U+200D U+1F469 U+200D | U+1F466"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+1F469 U+200D U+1F467 U+200D U+1F466"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+1F469 U+200D | U+1F467 U+200D U+1F466"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D U+1F469 U+200D U+1F467 U+200D | U+1F466"));
+    EXPECT_FALSE(IsBreak("U+1F441 U+200D | U+1F5E8"));
+
+    // Do not break before and after zwj with all kind of emoji characters.
+    EXPECT_FALSE(IsBreak("U+1F431 | U+200D U+1F464"));
+    EXPECT_FALSE(IsBreak("U+1F431 U+200D | U+1F464"));
+
+    // ARABIC LETTER BEH + ZWJ + heart, not a zwj emoji sequence, so we preserve the break
+    EXPECT_TRUE(IsBreak("U+0628 U+200D | U+2764"));
+}
+
+TEST(GraphemeBreak, emojiModifiers) {
+    EXPECT_FALSE(IsBreak("U+261D | U+1F3FB"));  // white up pointing index + modifier
+    EXPECT_FALSE(IsBreak("U+270C | U+1F3FB"));  // victory hand + modifier
+    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FB"));  // boy + modifier
+    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FC"));  // boy + modifier
+    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FD"));  // boy + modifier
+    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FE"));  // boy + modifier
+    EXPECT_FALSE(IsBreak("U+1F466 | U+1F3FF"));  // boy + modifier
+    EXPECT_FALSE(IsBreak("U+1F918 | U+1F3FF"));  // sign of the horns + modifier
+    EXPECT_FALSE(IsBreak("U+1F933 | U+1F3FF"));  // selfie (Unicode 9) + modifier
+    // Reptition of the tests above, with the knowledge that they are ligated.
+    const float ligated1_2[] = {1.0, 0.0, 0.0};
+    const float ligated2_2[] = {1.0, 0.0, 0.0, 0.0};
+    EXPECT_FALSE(IsBreakWithAdvances(ligated1_2, "U+261D | U+1F3FB"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated1_2, "U+270C | U+1F3FB"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F466 | U+1F3FB"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F466 | U+1F3FC"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F466 | U+1F3FD"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F466 | U+1F3FE"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F466 | U+1F3FF"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F918 | U+1F3FF"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated2_2, "U+1F933 | U+1F3FF"));
+    // Reptition of the tests above, with the knowledge that they are not ligated.
+    const float unligated1_2[] = {1.0, 1.0, 0.0};
+    const float unligated2_2[] = {1.0, 0.0, 1.0, 0.0};
+    EXPECT_TRUE(IsBreakWithAdvances(unligated1_2, "U+261D | U+1F3FB"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated1_2, "U+270C | U+1F3FB"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F466 | U+1F3FB"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F466 | U+1F3FC"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F466 | U+1F3FD"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F466 | U+1F3FE"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F466 | U+1F3FF"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F918 | U+1F3FF"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_2, "U+1F933 | U+1F3FF"));
+
+    // adding extend characters between emoji base and modifier doesn't affect grapheme cluster
+    EXPECT_FALSE(IsBreak("U+270C U+FE0E | U+1F3FB"));  // victory hand + text style + modifier
+    EXPECT_FALSE(IsBreak("U+270C U+FE0F | U+1F3FB"));  // heart + emoji style + modifier
+    // Reptition of the two tests above, with the knowledge that they are ligated.
+    const float ligated1_1_2[] = {1.0, 0.0, 0.0, 0.0};
+    EXPECT_FALSE(IsBreakWithAdvances(ligated1_1_2, "U+270C U+FE0E | U+1F3FB"));
+    EXPECT_FALSE(IsBreakWithAdvances(ligated1_1_2, "U+270C U+FE0F | U+1F3FB"));
+    // Reptition of the first two tests, with the knowledge that they are not ligated.
+    const float unligated1_1_2[] = {1.0, 0.0, 1.0, 0.0};
+    EXPECT_TRUE(IsBreakWithAdvances(unligated1_1_2, "U+270C U+FE0E | U+1F3FB"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated1_1_2, "U+270C U+FE0F | U+1F3FB"));
+
+    // heart is not an emoji base
+    EXPECT_TRUE(IsBreak("U+2764 | U+1F3FB"));  // heart + modifier
+    EXPECT_TRUE(IsBreak("U+2764 U+FE0E | U+1F3FB"));  // heart + emoji style + modifier
+    EXPECT_TRUE(IsBreak("U+2764 U+FE0F | U+1F3FB"));  // heart + emoji style + modifier
+    EXPECT_TRUE(IsBreak("U+1F3FB | U+1F3FB"));  // modifier + modifier
+
+    // rat is not an emoji modifer
+    EXPECT_TRUE(IsBreak("U+1F466 | U+1F400"));  // boy + rat
+}
+
+TEST(GraphemeBreak, genderBalancedEmoji) {
+    // U+1F469 is WOMAN, U+200D is ZWJ, U+1F4BC is BRIEFCASE.
+    EXPECT_FALSE(IsBreak("U+1F469 | U+200D U+1F4BC"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+1F4BC"));
+    // The above two cases, when the ligature is not supported in the font. We now expect a break
+    // between them.
+    const float unligated2_1_2[] = {1.0, 0.0, 0.0, 1.0, 0.0};
+    EXPECT_FALSE(IsBreakWithAdvances(unligated2_1_2, "U+1F469 | U+200D U+1F4BC"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_1_2, "U+1F469 U+200D | U+1F4BC"));
+
+    // U+2695 has now emoji property, so should be part of ZWJ sequence.
+    EXPECT_FALSE(IsBreak("U+1F469 | U+200D U+2695"));
+    EXPECT_FALSE(IsBreak("U+1F469 U+200D | U+2695"));
+    // The above two cases, when the ligature is not supported in the font. We now expect a break
+    // between them.
+    const float unligated2_1_1[] = {1.0, 0.0, 0.0, 1.0};
+    EXPECT_FALSE(IsBreakWithAdvances(unligated2_1_1, "U+1F469 | U+200D U+2695"));
+    EXPECT_TRUE(IsBreakWithAdvances(unligated2_1_1, "U+1F469 U+200D | U+2695"));
+}
+
+TEST(GraphemeBreak, offsets) {
+    uint16_t string[] = { 0x0041, 0x06DD, 0x0045, 0x0301, 0x0049, 0x0301 };
+    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(nullptr, string, 2, 3, 2));
+    EXPECT_FALSE(GraphemeBreak::isGraphemeBreak(nullptr, string, 2, 3, 3));
+    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(nullptr, string, 2, 3, 4));
+    EXPECT_TRUE(GraphemeBreak::isGraphemeBreak(nullptr, string, 2, 3, 5));
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/HbFontCacheTest.cpp b/tests/unittest/HbFontCacheTest.cpp
new file mode 100644
index 0000000..a5581a2
--- /dev/null
+++ b/tests/unittest/HbFontCacheTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 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 "HbFontCache.h"
+
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+#include <hb.h>
+
+#include "MinikinInternal.h"
+#include "MinikinFontForTest.h"
+#include <minikin/MinikinFont.h>
+
+namespace minikin {
+
+class HbFontCacheTest : public testing::Test {
+public:
+    virtual void TearDown() {
+        android::AutoMutex _l(gMinikinLock);
+        purgeHbFontCacheLocked();
+    }
+};
+
+TEST_F(HbFontCacheTest, getHbFontLockedTest) {
+    std::shared_ptr<MinikinFontForTest> fontA(
+            new MinikinFontForTest(kTestFontDir "Regular.ttf"));
+
+    std::shared_ptr<MinikinFontForTest> fontB(
+            new MinikinFontForTest(kTestFontDir "Bold.ttf"));
+
+    std::shared_ptr<MinikinFontForTest> fontC(
+            new MinikinFontForTest(kTestFontDir "BoldItalic.ttf"));
+
+    android::AutoMutex _l(gMinikinLock);
+    // Never return NULL.
+    EXPECT_NE(nullptr, getHbFontLocked(fontA.get()));
+    EXPECT_NE(nullptr, getHbFontLocked(fontB.get()));
+    EXPECT_NE(nullptr, getHbFontLocked(fontC.get()));
+
+    EXPECT_NE(nullptr, getHbFontLocked(nullptr));
+
+    // Must return same object if same font object is passed.
+    EXPECT_EQ(getHbFontLocked(fontA.get()), getHbFontLocked(fontA.get()));
+    EXPECT_EQ(getHbFontLocked(fontB.get()), getHbFontLocked(fontB.get()));
+    EXPECT_EQ(getHbFontLocked(fontC.get()), getHbFontLocked(fontC.get()));
+
+    // Different object must be returned if the passed minikinFont has different ID.
+    EXPECT_NE(getHbFontLocked(fontA.get()), getHbFontLocked(fontB.get()));
+    EXPECT_NE(getHbFontLocked(fontA.get()), getHbFontLocked(fontC.get()));
+}
+
+TEST_F(HbFontCacheTest, purgeCacheTest) {
+    std::shared_ptr<MinikinFontForTest> minikinFont(
+            new MinikinFontForTest(kTestFontDir "Regular.ttf"));
+
+    android::AutoMutex _l(gMinikinLock);
+    hb_font_t* font = getHbFontLocked(minikinFont.get());
+    ASSERT_NE(nullptr, font);
+
+    // Set user data to identify the font object.
+    hb_user_data_key_t key;
+    void* data = (void*)0xdeadbeef;
+    hb_font_set_user_data(font, &key, data, NULL, false);
+    ASSERT_EQ(data, hb_font_get_user_data(font, &key));
+
+    purgeHbFontCacheLocked();
+
+    // By checking user data, confirm that the object after purge is different from previously
+    // created one. Do not compare the returned pointer here since memory allocator may assign
+    // same region for new object.
+    font = getHbFontLocked(minikinFont.get());
+    EXPECT_EQ(nullptr, hb_font_get_user_data(font, &key));
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/HyphenatorTest.cpp b/tests/unittest/HyphenatorTest.cpp
new file mode 100644
index 0000000..ecd58a2
--- /dev/null
+++ b/tests/unittest/HyphenatorTest.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2017 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 "ICUTestBase.h"
+#include <minikin/Hyphenator.h>
+#include <FileUtils.h>
+
+#ifndef NELEM
+#define NELEM(x) ((sizeof(x) / sizeof((x)[0])))
+#endif
+
+namespace minikin {
+
+const char* usHyph = "/system/usr/hyphen-data/hyph-en-us.hyb";
+const char* malayalamHyph = "/system/usr/hyphen-data/hyph-ml.hyb";
+
+typedef ICUTestBase HyphenatorTest;
+
+const icu::Locale catalanLocale("ca", "ES", nullptr, nullptr);
+const icu::Locale polishLocale("pl", "PL", nullptr, nullptr);
+const icu::Locale& usLocale = icu::Locale::getUS();
+
+const uint16_t HYPHEN_MINUS = 0x002D;
+const uint16_t SOFT_HYPHEN = 0x00AD;
+const uint16_t MIDDLE_DOT = 0x00B7;
+const uint16_t GREEK_LOWER_ALPHA = 0x03B1;
+const uint16_t ARMENIAN_AYB = 0x0531;
+const uint16_t HEBREW_ALEF = 0x05D0;
+const uint16_t ARABIC_ALEF = 0x0627;
+const uint16_t ARABIC_BEH = 0x0628;
+const uint16_t ARABIC_ZWARAKAY = 0x0659;
+const uint16_t MALAYALAM_KA = 0x0D15;
+const uint16_t UCAS_E = 0x1401;
+const uint16_t HYPHEN = 0x2010;
+const uint16_t EN_DASH = 0x2013;
+
+// Simple test for US English. This tests "table", which happens to be the in the exceptions list.
+TEST_F(HyphenatorTest, usEnglishAutomaticHyphenation) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(readWholeFile(usHyph).data(), 2, 3);
+    const uint16_t word[] = {'t', 'a', 'b', 'l', 'e'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 5, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[2]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[3]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[4]);
+}
+
+// Catalan l·l should break as l-/l
+TEST_F(HyphenatorTest, catalanMiddleDot) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'l', 'l', MIDDLE_DOT, 'l', 'l'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), catalanLocale);
+    EXPECT_EQ((size_t) 5, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[2]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_REPLACE_WITH_HYPHEN, result[3]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[4]);
+}
+
+// Catalan l·l should not break if the word is too short.
+TEST_F(HyphenatorTest, catalanMiddleDotShortWord) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'l', MIDDLE_DOT, 'l'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), catalanLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[2]);
+}
+
+// If we break on a hyphen in Polish, the hyphen should be repeated on the next line.
+TEST_F(HyphenatorTest, polishHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'x', HYPHEN, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), polishLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE, result[2]);
+}
+
+// If the language is Polish but the script is not Latin, don't use Polish rules for hyphenation.
+TEST_F(HyphenatorTest, polishHyphenButNonLatinWord) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {GREEK_LOWER_ALPHA, HYPHEN, GREEK_LOWER_ALPHA};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), polishLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+}
+
+// Polish en dash doesn't repeat on next line (as far as we know), but just provides a break
+// opportunity.
+TEST_F(HyphenatorTest, polishEnDash) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'x', EN_DASH, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), polishLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+}
+
+// In Latin script text, soft hyphens should insert a visible hyphen if broken at.
+TEST_F(HyphenatorTest, latinSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'x', SOFT_HYPHEN, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[2]);
+}
+
+// Soft hyphens at the beginning of a word are not useful in linebreaking.
+TEST_F(HyphenatorTest, latinSoftHyphenStartingTheWord) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {SOFT_HYPHEN, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 2, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+}
+
+// In Malayalam script text, soft hyphens should not insert a visible hyphen if broken at.
+TEST_F(HyphenatorTest, malayalamSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {MALAYALAM_KA, SOFT_HYPHEN, MALAYALAM_KA};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+}
+
+// In automatically hyphenated Malayalam script text, we should not insert a visible hyphen.
+TEST_F(HyphenatorTest, malayalamAutomaticHyphenation) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(readWholeFile(malayalamHyph).data(), 2, 2);
+    const uint16_t word[] = {
+            MALAYALAM_KA, MALAYALAM_KA, MALAYALAM_KA, MALAYALAM_KA, MALAYALAM_KA};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 5, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[3]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[4]);
+}
+
+// In Armenian script text, soft hyphens should insert an Armenian hyphen if broken at.
+TEST_F(HyphenatorTest, aremenianSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARMENIAN_AYB, SOFT_HYPHEN, ARMENIAN_AYB};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_ARMENIAN_HYPHEN, result[2]);
+}
+
+// In Hebrew script text, soft hyphens should insert a normal hyphen if broken at, for now.
+// We may need to change this to maqaf later.
+TEST_F(HyphenatorTest, hebrewSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {HEBREW_ALEF, SOFT_HYPHEN, HEBREW_ALEF};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[2]);
+}
+
+// Soft hyphen between two Arabic letters that join should keep the joining
+// behavior when broken across lines.
+TEST_F(HyphenatorTest, arabicSoftHyphenConnecting) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARABIC_BEH, SOFT_HYPHEN, ARABIC_BEH};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ, result[2]);
+}
+
+// Arabic letters may be joining on one side, but if it's the wrong side, we
+// should use the normal hyphen.
+TEST_F(HyphenatorTest, arabicSoftHyphenNonConnecting) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARABIC_ALEF, SOFT_HYPHEN, ARABIC_BEH};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[2]);
+}
+
+// Skip transparent characters until you find a non-transparent one.
+TEST_F(HyphenatorTest, arabicSoftHyphenSkipTransparents) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARABIC_BEH, ARABIC_ZWARAKAY, SOFT_HYPHEN, ARABIC_ZWARAKAY, ARABIC_BEH};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 5, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[2]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ, result[3]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[4]);
+}
+
+// Skip transparent characters until you find a non-transparent one. If we get to one end without
+// finding anything, we are still non-joining.
+TEST_F(HyphenatorTest, arabicSoftHyphenTransparentsAtEnd) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARABIC_BEH, ARABIC_ZWARAKAY, SOFT_HYPHEN, ARABIC_ZWARAKAY};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 4, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[2]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[3]);
+}
+
+// Skip transparent characters until you find a non-transparent one. If we get to one end without
+// finding anything, we are still non-joining.
+TEST_F(HyphenatorTest, arabicSoftHyphenTransparentsAtStart) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {ARABIC_ZWARAKAY, SOFT_HYPHEN, ARABIC_ZWARAKAY, ARABIC_BEH};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 4, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_HYPHEN, result[2]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[3]);
+}
+
+// In Unified Canadian Aboriginal script (UCAS) text, soft hyphens should insert a UCAS hyphen.
+TEST_F(HyphenatorTest, ucasSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {UCAS_E, SOFT_HYPHEN, UCAS_E};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN, result[2]);
+}
+
+// Presently, soft hyphen looks at the character after it to determine hyphenation type. This is a
+// little arbitrary, but let's test it anyway.
+TEST_F(HyphenatorTest, mixedScriptSoftHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'a', SOFT_HYPHEN, UCAS_E};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN, result[2]);
+}
+
+// Hard hyphens provide a breaking opportunity with nothing extra inserted.
+TEST_F(HyphenatorTest, hardHyphen) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'x', HYPHEN, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+}
+
+// Hyphen-minuses also provide a breaking opportunity with nothing extra inserted.
+TEST_F(HyphenatorTest, hyphenMinus) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {'x', HYPHEN_MINUS, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 3, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+    EXPECT_EQ(HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN, result[2]);
+}
+
+// If the word starts with a hard hyphen or hyphen-minus, it doesn't make sense to break
+// it at that point.
+TEST_F(HyphenatorTest, startingHyphenMinus) {
+    Hyphenator* hyphenator = Hyphenator::loadBinary(nullptr, 2, 2);
+    const uint16_t word[] = {HYPHEN_MINUS, 'y'};
+    std::vector<HyphenationType> result;
+    hyphenator->hyphenate(&result, word, NELEM(word), usLocale);
+    EXPECT_EQ((size_t) 2, result.size());
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[0]);
+    EXPECT_EQ(HyphenationType::DONT_BREAK, result[1]);
+}
+
+}  // namespace minikin
+
diff --git a/tests/ICUTestBase.h b/tests/unittest/ICUTestBase.h
similarity index 96%
rename from tests/ICUTestBase.h
rename to tests/unittest/ICUTestBase.h
index 3bcfaf3..f915cf8 100644
--- a/tests/ICUTestBase.h
+++ b/tests/unittest/ICUTestBase.h
@@ -26,6 +26,8 @@
 #include <sys/stat.h>
 #include <sys/mman.h>
 
+namespace minikin {
+
 class ICUTestBase : public testing::Test {
 protected:
     virtual void SetUp() override {
@@ -48,5 +50,5 @@
     }
 };
 
-
+}  // namespace minikin
 #endif  //  MINIKIN_TEST_ICU_TEST_BASE_H
diff --git a/tests/unittest/LayoutTest.cpp b/tests/unittest/LayoutTest.cpp
new file mode 100644
index 0000000..1770d3a
--- /dev/null
+++ b/tests/unittest/LayoutTest.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2016 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 "ICUTestBase.h"
+#include "minikin/FontCollection.h"
+#include "minikin/Layout.h"
+#include "../util/FontTestUtils.h"
+#include "../util/UnicodeUtils.h"
+
+const char* SYSTEM_FONT_PATH = "/system/fonts/";
+const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml";
+
+namespace minikin {
+
+const float UNTOUCHED_MARKER = 1e+38;
+
+static void expectAdvances(std::vector<float> expected, float* advances, size_t length) {
+    EXPECT_LE(expected.size(), length);
+    for (size_t i = 0; i < expected.size(); ++i) {
+        EXPECT_EQ(expected[i], advances[i])
+                << i << "th element is different. Expected: " << expected[i]
+                << ", Actual: " << advances[i];
+    }
+    EXPECT_EQ(UNTOUCHED_MARKER, advances[expected.size()]);
+}
+
+static void resetAdvances(float* advances, size_t length) {
+    for (size_t i = 0; i < length; ++i) {
+        advances[i] = UNTOUCHED_MARKER;
+    }
+}
+
+class LayoutTest : public ICUTestBase {
+protected:
+    LayoutTest() : mCollection(nullptr) {
+    }
+
+    virtual ~LayoutTest() {}
+
+    virtual void SetUp() override {
+        mCollection = std::shared_ptr<FontCollection>(
+                getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+    }
+
+    virtual void TearDown() override {
+    }
+
+    std::shared_ptr<FontCollection> mCollection;
+};
+
+TEST_F(LayoutTest, doLayoutTest) {
+    MinikinPaint paint;
+    MinikinRect rect;
+    const size_t kMaxAdvanceLength = 32;
+    float advances[kMaxAdvanceLength];
+    std::vector<float> expectedValues;
+
+    Layout layout;
+    std::vector<uint16_t> text;
+
+    // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph.
+    {
+        SCOPED_TRACE("one word");
+        text = utf8ToUtf16("oneword");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(70.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(70.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two words");
+        text = utf8ToUtf16("two words");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(90.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(90.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("three words");
+        text = utf8ToUtf16("three words test");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(160.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(160.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two spaces");
+        text = utf8ToUtf16("two  spaces");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(110.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(110.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+}
+
+TEST_F(LayoutTest, doLayoutTest_wordSpacing) {
+    MinikinPaint paint;
+    MinikinRect rect;
+    const size_t kMaxAdvanceLength = 32;
+    float advances[kMaxAdvanceLength];
+    std::vector<float> expectedValues;
+    std::vector<uint16_t> text;
+
+    Layout layout;
+
+    paint.wordSpacing = 5.0f;
+
+    // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph.
+    {
+        SCOPED_TRACE("one word");
+        text = utf8ToUtf16("oneword");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(70.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(70.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two words");
+        text = utf8ToUtf16("two words");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(95.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(95.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        EXPECT_EQ(UNTOUCHED_MARKER, advances[text.size()]);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[3] = 15.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("three words test");
+        text = utf8ToUtf16("three words test");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(170.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(170.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[5] = 15.0f;
+        expectedValues[11] = 15.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two spaces");
+        text = utf8ToUtf16("two  spaces");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(120.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(120.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[3] = 15.0f;
+        expectedValues[4] = 15.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+}
+
+TEST_F(LayoutTest, doLayoutTest_negativeWordSpacing) {
+    MinikinPaint paint;
+    MinikinRect rect;
+    const size_t kMaxAdvanceLength = 32;
+    float advances[kMaxAdvanceLength];
+    std::vector<float> expectedValues;
+
+    Layout layout;
+    std::vector<uint16_t> text;
+
+    // Negative word spacing also should work.
+    paint.wordSpacing = -5.0f;
+
+    {
+        SCOPED_TRACE("one word");
+        text = utf8ToUtf16("oneword");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(70.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(70.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two words");
+        text = utf8ToUtf16("two words");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(85.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(85.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[3] = 5.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("three words");
+        text = utf8ToUtf16("three word test");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(140.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(140.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[5] = 5.0f;
+        expectedValues[10] = 5.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+    {
+        SCOPED_TRACE("two spaces");
+        text = utf8ToUtf16("two  spaces");
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(100.0f, layout.getAdvance());
+        layout.getBounds(&rect);
+        EXPECT_EQ(0.0f, rect.mLeft);
+        EXPECT_EQ(0.0f, rect.mTop);
+        EXPECT_EQ(100.0f, rect.mRight);
+        EXPECT_EQ(10.0f, rect.mBottom);
+        resetAdvances(advances, kMaxAdvanceLength);
+        layout.getAdvances(advances);
+        expectedValues.resize(text.size());
+        for (size_t i = 0; i < expectedValues.size(); ++i) {
+            expectedValues[i] = 10.0f;
+        }
+        expectedValues[3] = 5.0f;
+        expectedValues[4] = 5.0f;
+        expectAdvances(expectedValues, advances, kMaxAdvanceLength);
+    }
+}
+
+TEST_F(LayoutTest, doLayoutTest_rtlTest) {
+    MinikinPaint paint;
+
+    std::vector<uint16_t> text = parseUnicodeString("'a' 'b' U+3042 U+3043 'c' 'd'");
+
+    Layout ltrLayout;
+    ltrLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+            mCollection);
+
+    Layout rtlLayout;
+    rtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_RTL, FontStyle(), paint,
+            mCollection);
+
+    ASSERT_EQ(ltrLayout.nGlyphs(), rtlLayout.nGlyphs());
+    ASSERT_EQ(6u, ltrLayout.nGlyphs());
+
+    size_t nGlyphs = ltrLayout.nGlyphs();
+    for (size_t i = 0; i < nGlyphs; ++i) {
+        EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(nGlyphs - i - 1));
+        EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(nGlyphs - i - 1));
+    }
+}
+
+TEST_F(LayoutTest, hyphenationTest) {
+    Layout layout;
+    std::vector<uint16_t> text;
+
+    // The mock implementation returns 10.0f advance for all glyphs.
+    {
+        SCOPED_TRACE("one word with no hyphen edit");
+        text = utf8ToUtf16("oneword");
+        MinikinPaint paint;
+        paint.hyphenEdit = HyphenEdit::NO_EDIT;
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(70.0f, layout.getAdvance());
+    }
+    {
+        SCOPED_TRACE("one word with hyphen insertion at the end");
+        text = utf8ToUtf16("oneword");
+        MinikinPaint paint;
+        paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_END;
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(80.0f, layout.getAdvance());
+    }
+    {
+        SCOPED_TRACE("one word with hyphen replacement at the end");
+        text = utf8ToUtf16("oneword");
+        MinikinPaint paint;
+        paint.hyphenEdit = HyphenEdit::REPLACE_WITH_HYPHEN_AT_END;
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(70.0f, layout.getAdvance());
+    }
+    {
+        SCOPED_TRACE("one word with hyphen insertion at the start");
+        text = utf8ToUtf16("oneword");
+        MinikinPaint paint;
+        paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_START;
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(80.0f, layout.getAdvance());
+    }
+    {
+        SCOPED_TRACE("one word with hyphen insertion at the both ends");
+        text = utf8ToUtf16("oneword");
+        MinikinPaint paint;
+        paint.hyphenEdit = HyphenEdit::INSERT_HYPHEN_AT_START | HyphenEdit::INSERT_HYPHEN_AT_END;
+        layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
+                mCollection);
+        EXPECT_EQ(90.0f, layout.getAdvance());
+    }
+}
+
+// TODO: Add more test cases, e.g. measure text, letter spacing.
+
+}  // namespace minikin
diff --git a/tests/LayoutUtilsTest.cpp b/tests/unittest/LayoutUtilsTest.cpp
similarity index 99%
rename from tests/LayoutUtilsTest.cpp
rename to tests/unittest/LayoutUtilsTest.cpp
index f4fbb18..e7e6c27 100644
--- a/tests/LayoutUtilsTest.cpp
+++ b/tests/unittest/LayoutUtilsTest.cpp
@@ -19,7 +19,7 @@
 
 #include "LayoutUtils.h"
 
-namespace {
+namespace minikin {
 
 void ExpectNextWordBreakForCache(size_t offset_in, const char* query_str) {
     const size_t BUF_SIZE = 256U;
@@ -507,4 +507,4 @@
     ExpectPrevWordBreakForCache(1000, "U+4444 U+302D U+302D | U+4444");
 }
 
-}  // namespace
+}  // namespace minikin
diff --git a/tests/unittest/MeasurementTests.cpp b/tests/unittest/MeasurementTests.cpp
new file mode 100644
index 0000000..7fedecb
--- /dev/null
+++ b/tests/unittest/MeasurementTests.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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 <UnicodeUtils.h>
+#include <minikin/Measurement.h>
+
+namespace minikin {
+
+float getAdvance(const float* advances, const char* src) {
+    const size_t BUF_SIZE = 256;
+    uint16_t buf[BUF_SIZE];
+    size_t offset;
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, src, &size, &offset);
+    return getRunAdvance(advances, buf, 0, size, offset);
+}
+
+// Latin fi
+TEST(Measurement, getRunAdvance_fi) {
+    const float unligated[] = {30.0, 20.0};
+    EXPECT_EQ(0.0, getAdvance(unligated, "| 'f' 'i'"));
+    EXPECT_EQ(30.0, getAdvance(unligated, "'f' | 'i'"));
+    EXPECT_EQ(50.0, getAdvance(unligated, "'f' 'i' |"));
+
+    const float ligated[] = {40.0, 0.0};
+    EXPECT_EQ(0.0, getAdvance(ligated, "| 'f' 'i'"));
+    EXPECT_EQ(20.0, getAdvance(ligated, "'f' | 'i'"));
+    EXPECT_EQ(40.0, getAdvance(ligated, "'f' 'i' |"));
+}
+
+// Devanagari ka+virama+ka
+TEST(Measurement, getRunAdvance_kka) {
+    const float unligated[] = {30.0, 0.0, 30.0};
+    EXPECT_EQ(0.0, getAdvance(unligated, "| U+0915 U+094D U+0915"));
+    EXPECT_EQ(30.0, getAdvance(unligated, "U+0915 | U+094D U+0915"));
+    EXPECT_EQ(30.0, getAdvance(unligated, "U+0915 U+094D | U+0915"));
+    EXPECT_EQ(60.0, getAdvance(unligated, "U+0915 U+094D U+0915 |"));
+
+    const float ligated[] = {30.0, 0.0, 0.0};
+    EXPECT_EQ(0.0, getAdvance(ligated, "| U+0915 U+094D U+0915"));
+    EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 | U+094D U+0915"));
+    EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 U+094D | U+0915"));
+    EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 U+094D U+0915 |"));
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/SparseBitSetTest.cpp b/tests/unittest/SparseBitSetTest.cpp
new file mode 100644
index 0000000..39c9e1b
--- /dev/null
+++ b/tests/unittest/SparseBitSetTest.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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 <random>
+
+#include <gtest/gtest.h>
+#include <minikin/SparseBitSet.h>
+
+namespace minikin {
+
+TEST(SparseBitSetTest, randomTest) {
+    const uint32_t kTestRangeNum = 4096;
+
+    std::mt19937 mt;  // Fix seeds to be able to reproduce the result.
+    std::uniform_int_distribution<uint16_t> distribution(1, 512);
+
+    std::vector<uint32_t> range { distribution(mt) };
+    for (size_t i = 1; i < kTestRangeNum * 2; ++i) {
+        range.push_back((range.back() - 1) + distribution(mt));
+    }
+
+    SparseBitSet bitset(range.data(), range.size() / 2);
+
+    uint32_t ch = 0;
+    for (size_t i = 0; i < range.size() / 2; ++i) {
+        uint32_t start = range[i * 2];
+        uint32_t end = range[i * 2 + 1];
+
+        for (; ch < start; ch++) {
+            ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
+        }
+        for (; ch < end; ch++) {
+            ASSERT_TRUE(bitset.get(ch)) << std::hex << ch;
+        }
+    }
+    for (; ch < 0x1FFFFFF; ++ch) {
+        ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
+    }
+}
+
+}  // namespace minikin
diff --git a/tests/unittest/UnicodeUtilsTest.cpp b/tests/unittest/UnicodeUtilsTest.cpp
new file mode 100644
index 0000000..9932723
--- /dev/null
+++ b/tests/unittest/UnicodeUtilsTest.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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 "UnicodeUtils.h"
+
+namespace minikin {
+
+TEST(UnicodeUtils, parse) {
+    const size_t BUF_SIZE = 256;
+    uint16_t buf[BUF_SIZE];
+    size_t offset;
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, "U+000D U+1F431 | 'a'", &size, &offset);
+    EXPECT_EQ(size, 4u);
+    EXPECT_EQ(offset, 3u);
+    EXPECT_EQ(buf[0], 0x000D);
+    EXPECT_EQ(buf[1], 0xD83D);
+    EXPECT_EQ(buf[2], 0xDC31);
+    EXPECT_EQ(buf[3], 'a');
+}
+
+} // namespace minikin
diff --git a/tests/WordBreakerTests.cpp b/tests/unittest/WordBreakerTests.cpp
similarity index 77%
rename from tests/WordBreakerTests.cpp
rename to tests/unittest/WordBreakerTests.cpp
index 8ed87cc..13e0420 100644
--- a/tests/WordBreakerTests.cpp
+++ b/tests/unittest/WordBreakerTests.cpp
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "Minikin"
+
+#include <android/log.h>
 #include <gtest/gtest.h>
+
 #include "ICUTestBase.h"
 #include "UnicodeUtils.h"
 #include <minikin/WordBreaker.h>
@@ -22,23 +26,20 @@
 #include <unicode/uclean.h>
 #include <unicode/udata.h>
 
-#define LOG_TAG "Minikin"
-#include <cutils/log.h>
-
 #ifndef NELEM
 #define NELEM(x) ((sizeof(x) / sizeof((x)[0])))
 #endif
 
 #define UTF16(codepoint) U16_LEAD(codepoint), U16_TRAIL(codepoint)
 
-using namespace android;
+namespace minikin {
 
 typedef ICUTestBase WordBreakerTest;
 
 TEST_F(WordBreakerTest, basic) {
     uint16_t buf[] = {'h', 'e', 'l', 'l' ,'o', ' ', 'w', 'o', 'r', 'l', 'd'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(6, breaker.next());  // after "hello "
@@ -56,7 +57,7 @@
 TEST_F(WordBreakerTest, softHyphen) {
     uint16_t buf[] = {'h', 'e', 'l', 0x00AD, 'l' ,'o', ' ', 'w', 'o', 'r', 'l', 'd'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(7, breaker.next());  // after "hel{SOFT HYPHEN}lo "
@@ -69,10 +70,23 @@
     EXPECT_EQ(0, breaker.breakBadness());
 }
 
+TEST_F(WordBreakerTest, hardHyphen) {
+    // Hyphens should not allow breaks anymore.
+    uint16_t buf[] = {'s', 'u', 'g', 'a', 'r', '-', 'f', 'r', 'e', 'e'};
+    WordBreaker breaker;
+    breaker.setLocale(icu::Locale::getUS());
+    breaker.setText(buf, NELEM(buf));
+    EXPECT_EQ(0, breaker.current());
+    EXPECT_EQ((ssize_t)NELEM(buf), breaker.next());
+    EXPECT_EQ(0, breaker.wordStart());
+    EXPECT_EQ((ssize_t)NELEM(buf), breaker.wordEnd());
+    EXPECT_EQ(0, breaker.breakBadness());
+}
+
 TEST_F(WordBreakerTest, postfixAndPrefix) {
     uint16_t buf[] = {'U', 'S', 0x00A2, ' ', 'J', 'P', 0x00A5}; // US¢ JP¥
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
 
@@ -85,7 +99,7 @@
     EXPECT_EQ((ssize_t)NELEM(buf), breaker.wordEnd());
 }
 
-TEST_F(WordBreakerTest, MyanmarKinzi) {
+TEST_F(WordBreakerTest, myanmarKinzi) {
     uint16_t buf[] = {0x1004, 0x103A, 0x1039, 0x1000, 0x102C};  // NGA, ASAT, VIRAMA, KA, UU
     WordBreaker breaker;
     icu::Locale burmese("my");
@@ -110,7 +124,7 @@
         UTF16(0x1F431), 0x200D, UTF16(0x1F464),
     };
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(7, breaker.next());  // after man + zwj + heart + zwj + man
@@ -133,10 +147,10 @@
         0x270C, 0xFE0F, UTF16(0x1F3FF)  // victory hand + emoji style + type 6 fitzpatrick modifier
     };
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
-    EXPECT_EQ(4, breaker.next());  // after man + type 6 fitzpatrick modifier
+    EXPECT_EQ(4, breaker.next());  // after boy + type 1-2 fitzpatrick modifier
     EXPECT_EQ(0, breaker.wordStart());
     EXPECT_EQ(4, breaker.wordEnd());
     EXPECT_EQ((ssize_t)NELEM(buf), breaker.next());  // end
@@ -144,11 +158,108 @@
     EXPECT_EQ(8, breaker.wordEnd());
 }
 
+TEST_F(WordBreakerTest, unicode10Emoji) {
+    // Should break between emojis.
+    uint16_t buf[] = {
+        // SLED + SLED
+        UTF16(0x1F6F7), UTF16(0x1F6F7),
+        // SLED + VS15 + SLED
+        UTF16(0x1F6F7), 0xFE0E, UTF16(0x1F6F7),
+        // WHITE SMILING FACE + SLED
+        0x263A, UTF16(0x1F6F7),
+        // WHITE SMILING FACE + VS16 + SLED
+        0x263A, 0xFE0F, UTF16(0x1F6F7),
+    };
+    WordBreaker breaker;
+    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setText(buf, NELEM(buf));
+    EXPECT_EQ(0, breaker.current());
+    EXPECT_EQ(2, breaker.next());
+    EXPECT_EQ(0, breaker.wordStart());
+    EXPECT_EQ(2, breaker.wordEnd());
+
+    EXPECT_EQ(4, breaker.next());
+    EXPECT_EQ(2, breaker.wordStart());
+    EXPECT_EQ(4, breaker.wordEnd());
+
+    EXPECT_EQ(7, breaker.next());
+    EXPECT_EQ(4, breaker.wordStart());
+    EXPECT_EQ(7, breaker.wordEnd());
+
+    EXPECT_EQ(9, breaker.next());
+    EXPECT_EQ(7, breaker.wordStart());
+    EXPECT_EQ(9, breaker.wordEnd());
+
+    EXPECT_EQ(10, breaker.next());
+    EXPECT_EQ(9, breaker.wordStart());
+    EXPECT_EQ(10, breaker.wordEnd());
+
+    EXPECT_EQ(12, breaker.next());
+    EXPECT_EQ(10, breaker.wordStart());
+    EXPECT_EQ(12, breaker.wordEnd());
+
+    EXPECT_EQ(14, breaker.next());
+    EXPECT_EQ(12, breaker.wordStart());
+    EXPECT_EQ(14, breaker.wordEnd());
+
+    EXPECT_EQ(16, breaker.next());
+    EXPECT_EQ(14, breaker.wordStart());
+    EXPECT_EQ(16, breaker.wordEnd());
+}
+
+TEST_F(WordBreakerTest, flagsSequenceSingleFlag) {
+    const std::string kFlag = "U+1F3F4";
+    const std::string flags = kFlag + " " + kFlag;
+
+    const int kFlagLength = 2;
+    const size_t BUF_SIZE = kFlagLength * 2;
+
+    uint16_t buf[BUF_SIZE];
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, flags.c_str(), &size, nullptr);
+
+    WordBreaker breaker;
+    breaker.setLocale(icu::Locale::getUS());
+    breaker.setText(buf, size);
+    EXPECT_EQ(0, breaker.current());
+    EXPECT_EQ(kFlagLength, breaker.next());  // end of the first flag
+    EXPECT_EQ(0, breaker.wordStart());
+    EXPECT_EQ(kFlagLength, breaker.wordEnd());
+    EXPECT_EQ(static_cast<ssize_t>(size), breaker.next());
+    EXPECT_EQ(kFlagLength, breaker.wordStart());
+    EXPECT_EQ(kFlagLength * 2, breaker.wordEnd());
+}
+
+TEST_F(WordBreakerTest, flagsSequence) {
+    // U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F is emoji tag sequence for the flag
+    // of Scotland.
+    const std::string kFlagSequence = "U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F";
+    const std::string flagSequence = kFlagSequence + " " + kFlagSequence;
+
+    const int kFlagLength = 14;
+    const size_t BUF_SIZE = kFlagLength * 2;
+
+    uint16_t buf[BUF_SIZE];
+    size_t size;
+    ParseUnicode(buf, BUF_SIZE, flagSequence.c_str(), &size, nullptr);
+
+    WordBreaker breaker;
+    breaker.setLocale(icu::Locale::getUS());
+    breaker.setText(buf, size);
+    EXPECT_EQ(0, breaker.current());
+    EXPECT_EQ(kFlagLength, breaker.next());  // end of the first flag sequence
+    EXPECT_EQ(0, breaker.wordStart());
+    EXPECT_EQ(kFlagLength, breaker.wordEnd());
+    EXPECT_EQ(static_cast<ssize_t>(size), breaker.next());
+    EXPECT_EQ(kFlagLength, breaker.wordStart());
+    EXPECT_EQ(kFlagLength * 2, breaker.wordEnd());
+}
+
 TEST_F(WordBreakerTest, punct) {
     uint16_t buf[] = {0x00A1, 0x00A1, 'h', 'e', 'l', 'l' ,'o', ',', ' ', 'w', 'o', 'r', 'l', 'd',
         '!', '!'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(9, breaker.next());  // after "¡¡hello, "
@@ -165,7 +276,7 @@
     uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm',
         ' ', 'x'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(11, breaker.next());  // after "foo@example"
@@ -184,7 +295,7 @@
     uint16_t buf[] = {'m', 'a', 'i', 'l', 't', 'o', ':', 'f', 'o', 'o', '@',
         'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm', ' ', 'x'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(7, breaker.next());  // after "mailto:"
@@ -208,7 +319,7 @@
     uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm',
         0x4E00};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(11, breaker.next());  // after "foo@example"
@@ -227,7 +338,7 @@
     uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm',
         0x0303, ' ', 'x'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(11, breaker.next());  // after "foo@example"
@@ -245,7 +356,7 @@
 TEST_F(WordBreakerTest, lonelyAt) {
     uint16_t buf[] = {'a', ' ', '@', ' ', 'b'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(2, breaker.next());  // after "a "
@@ -265,7 +376,7 @@
     uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'e', 'x', 'a', 'm', 'p', 'l', 'e',
         '.', 'c', 'o', 'm', ' ', 'x'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(5, breaker.next());  // after "http:"
@@ -291,7 +402,7 @@
     uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '.', 'b', '/', '~', 'c', ',', 'd',
         '-', 'e', '?', 'f', '=', 'g', '&', 'h', '#', 'i', '%', 'j', '_', 'k', '/', 'l'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(5, breaker.next());  // after "http:"
@@ -350,7 +461,7 @@
 TEST_F(WordBreakerTest, urlNoHyphenBreak) {
     uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '-', '/', 'b'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(5, breaker.next());  // after "http:"
@@ -366,7 +477,7 @@
 TEST_F(WordBreakerTest, urlEndsWithSlash) {
     uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '/'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ(5, breaker.next());  // after "http:"
@@ -382,9 +493,11 @@
 TEST_F(WordBreakerTest, emailStartsWithSlash) {
     uint16_t buf[] = {'/', 'a', '@', 'b'};
     WordBreaker breaker;
-    breaker.setLocale(icu::Locale::getEnglish());
+    breaker.setLocale(icu::Locale::getUS());
     breaker.setText(buf, NELEM(buf));
     EXPECT_EQ(0, breaker.current());
     EXPECT_EQ((ssize_t)NELEM(buf), breaker.next());  // end
     EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
 }
+
+}  // namespace minikin
diff --git a/tests/unittest/how_to_run.txt b/tests/unittest/how_to_run.txt
new file mode 100644
index 0000000..20aa5ab
--- /dev/null
+++ b/tests/unittest/how_to_run.txt
@@ -0,0 +1,3 @@
+mmm -j8 frameworks/minikin/tests/unittest &&
+adb sync data &&
+adb shell /data/nativetest/minikin_tests/minikin_tests
diff --git a/tests/util/FileUtils.cpp b/tests/util/FileUtils.cpp
new file mode 100644
index 0000000..68cc45c
--- /dev/null
+++ b/tests/util/FileUtils.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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 <cutils/log.h>
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include <string>
+#include <vector>
+
+std::vector<uint8_t> readWholeFile(const std::string& filePath) {
+    FILE* fp = fopen(filePath.c_str(), "r");
+    LOG_ALWAYS_FATAL_IF(fp == nullptr);
+    struct stat st;
+    LOG_ALWAYS_FATAL_IF(fstat(fileno(fp), &st) != 0);
+
+    std::vector<uint8_t> result(st.st_size);
+    LOG_ALWAYS_FATAL_IF(fread(result.data(), 1, st.st_size, fp) != static_cast<size_t>(st.st_size));
+    fclose(fp);
+    return result;
+}
diff --git a/tests/UnicodeUtils.h b/tests/util/FileUtils.h
similarity index 84%
rename from tests/UnicodeUtils.h
rename to tests/util/FileUtils.h
index 4f1b06a..1e66d1b 100644
--- a/tests/UnicodeUtils.h
+++ b/tests/util/FileUtils.h
@@ -14,5 +14,5 @@
  * limitations under the License.
  */
 
- void ParseUnicode(uint16_t* buf, size_t buf_size, const char* src, size_t* result_size,
-        size_t* offset);
+std::vector<uint8_t> readWholeFile(const std::string& filePath);
+
diff --git a/tests/util/FontTestUtils.cpp b/tests/util/FontTestUtils.cpp
new file mode 100644
index 0000000..13360d4
--- /dev/null
+++ b/tests/util/FontTestUtils.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <libxml/tree.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "FontLanguage.h"
+#include "MinikinFontForTest.h"
+#include <minikin/FontCollection.h>
+#include <minikin/FontFamily.h>
+
+namespace minikin {
+
+std::vector<std::shared_ptr<FontFamily>> getFontFamilies(const char* fontDir, const char* fontXml) {
+    xmlDoc* doc = xmlReadFile(fontXml, NULL, 0);
+    xmlNode* familySet = xmlDocGetRootElement(doc);
+
+    std::vector<std::shared_ptr<FontFamily>> families;
+    for (xmlNode* familyNode = familySet->children; familyNode; familyNode = familyNode->next) {
+        if (xmlStrcmp(familyNode->name, (const xmlChar*)"family") != 0) {
+            continue;
+        }
+
+        xmlChar* variantXmlch = xmlGetProp(familyNode, (const xmlChar*)"variant");
+        int variant = VARIANT_DEFAULT;
+        if (variantXmlch) {
+            if (xmlStrcmp(variantXmlch, (const xmlChar*)"elegant") == 0) {
+                variant = VARIANT_ELEGANT;
+            } else if (xmlStrcmp(variantXmlch, (const xmlChar*)"compact") == 0) {
+                variant = VARIANT_COMPACT;
+            }
+        }
+
+        std::vector<Font> fonts;
+        for (xmlNode* fontNode = familyNode->children; fontNode; fontNode = fontNode->next) {
+            if (xmlStrcmp(fontNode->name, (const xmlChar*)"font") != 0) {
+                continue;
+            }
+
+            int weight = atoi((const char*)(xmlGetProp(fontNode, (const xmlChar*)"weight"))) / 100;
+            bool italic = xmlStrcmp(
+                    xmlGetProp(fontNode, (const xmlChar*)"style"), (const xmlChar*)"italic") == 0;
+            xmlChar* index = xmlGetProp(familyNode, (const xmlChar*)"index");
+
+            xmlChar* fontFileName = xmlNodeListGetString(doc, fontNode->xmlChildrenNode, 1);
+            std::string fontPath = fontDir + std::string((const char*)fontFileName);
+            xmlFree(fontFileName);
+
+            if (access(fontPath.c_str(), R_OK) != 0) {
+                ALOGW("%s is not found.", fontPath.c_str());
+                continue;
+            }
+
+            if (index == nullptr) {
+                std::shared_ptr<MinikinFont> minikinFont =
+                        std::make_shared<MinikinFontForTest>(fontPath);
+                fonts.push_back(Font(minikinFont, FontStyle(weight, italic)));
+            } else {
+                std::shared_ptr<MinikinFont> minikinFont =
+                        std::make_shared<MinikinFontForTest>(fontPath, atoi((const char*)index));
+                fonts.push_back(Font(minikinFont, FontStyle(weight, italic)));
+            }
+        }
+
+        xmlChar* lang = xmlGetProp(familyNode, (const xmlChar*)"lang");
+        std::shared_ptr<FontFamily> family;
+        if (lang == nullptr) {
+            family = std::make_shared<FontFamily>(variant, std::move(fonts));
+        } else {
+            uint32_t langId = FontStyle::registerLanguageList(
+                    std::string((const char*)lang, xmlStrlen(lang)));
+            family = std::make_shared<FontFamily>(langId, variant, std::move(fonts));
+        }
+        families.push_back(family);
+    }
+    xmlFreeDoc(doc);
+    return families;
+}
+std::shared_ptr<FontCollection> getFontCollection(const char* fontDir, const char* fontXml) {
+    return std::make_shared<FontCollection>(getFontFamilies(fontDir, fontXml));
+}
+
+}  // namespace minikin
diff --git a/tests/FontTestUtils.h b/tests/util/FontTestUtils.h
similarity index 60%
rename from tests/FontTestUtils.h
rename to tests/util/FontTestUtils.h
index 5258a76..dd5e586 100644
--- a/tests/FontTestUtils.h
+++ b/tests/util/FontTestUtils.h
@@ -19,14 +19,28 @@
 
 #include <minikin/FontCollection.h>
 
+#include <memory>
+
+namespace minikin {
+
 /**
- * Returns FontCollection from installed fonts.
+ * Returns list of FontFamily from installed fonts.
  *
- * This function reads /system/etc/fonts.xml and make font families and
- * collections of them. MinikinFontForTest is used for FontFamily creation.
+ * This function reads an XML file and makes font families.
  *
  * Caller must unref the returned pointer.
  */
-android::FontCollection* getFontCollection(const char* fontDir, const char* fontXml);
+std::vector<std::shared_ptr<FontFamily>> getFontFamilies(const char* fontDir, const char* fontXml);
 
+/**
+ * Returns FontCollection from installed fonts.
+ *
+ * This function reads an XML file and makes font families and collections of them.
+ * MinikinFontForTest is used for FontFamily creation.
+ *
+ * Caller must unref the returned pointer.
+ */
+std::shared_ptr<FontCollection> getFontCollection(const char* fontDir, const char* fontXml);
+
+}  // namespace minikin
 #endif  // MINIKIN_FONT_TEST_UTILS_H
diff --git a/tests/util/MinikinFontForTest.cpp b/tests/util/MinikinFontForTest.cpp
new file mode 100644
index 0000000..723e86a
--- /dev/null
+++ b/tests/util/MinikinFontForTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include "MinikinFontForTest.h"
+
+#include <minikin/MinikinFont.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <log/log.h>
+
+namespace minikin {
+
+static int uniqueId = 0;  // TODO: make thread safe if necessary.
+
+MinikinFontForTest::MinikinFontForTest(const std::string& font_path, int index,
+        const std::vector<FontVariation>& variations) :
+        MinikinFont(uniqueId++),
+        mFontPath(font_path),
+        mVariations(variations),
+        mFontIndex(index) {
+    int fd = open(font_path.c_str(), O_RDONLY);
+    LOG_ALWAYS_FATAL_IF(fd == -1);
+    struct stat st = {};
+    LOG_ALWAYS_FATAL_IF(fstat(fd, &st) != 0);
+    mFontSize = st.st_size;
+    mFontData = mmap(NULL, mFontSize, PROT_READ, MAP_SHARED, fd, 0);
+    LOG_ALWAYS_FATAL_IF(mFontData == nullptr);
+    close(fd);
+}
+
+MinikinFontForTest::~MinikinFontForTest() {
+    munmap(mFontData, mFontSize);
+}
+
+float MinikinFontForTest::GetHorizontalAdvance(uint32_t /* glyph_id */,
+        const MinikinPaint& /* paint */) const {
+    // TODO: Make mock value configurable if necessary.
+    return 10.0f;
+}
+
+void MinikinFontForTest::GetBounds(MinikinRect* bounds, uint32_t /* glyph_id */,
+        const MinikinPaint& /* paint */) const {
+    // TODO: Make mock values configurable if necessary.
+    bounds->mLeft = 0.0f;
+    bounds->mTop = 0.0f;
+    bounds->mRight = 10.0f;
+    bounds->mBottom = 10.0f;
+}
+
+std::shared_ptr<MinikinFont> MinikinFontForTest::createFontWithVariation(
+        const std::vector<FontVariation>& variations) const {
+    return std::shared_ptr<MinikinFont>(new MinikinFontForTest(mFontPath, mFontIndex, variations));
+}
+
+}  // namespace minikin
diff --git a/tests/util/MinikinFontForTest.h b/tests/util/MinikinFontForTest.h
new file mode 100644
index 0000000..6e230e1
--- /dev/null
+++ b/tests/util/MinikinFontForTest.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
+#define MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
+
+#include <minikin/MinikinFont.h>
+
+class SkTypeface;
+
+namespace minikin {
+
+class MinikinFontForTest : public MinikinFont {
+public:
+    MinikinFontForTest(const std::string& font_path, int index,
+            const std::vector<FontVariation>& variations);
+    MinikinFontForTest(const std::string& font_path, int index)
+            : MinikinFontForTest(font_path, index, std::vector<FontVariation>()) {}
+    MinikinFontForTest(const std::string& font_path) : MinikinFontForTest(font_path, 0) {}
+    virtual ~MinikinFontForTest();
+
+    // MinikinFont overrides.
+    float GetHorizontalAdvance(uint32_t glyph_id, const MinikinPaint &paint) const;
+    void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
+            const MinikinPaint& paint) const;
+
+    const std::string& fontPath() const { return mFontPath; }
+
+    const void* GetFontData() const { return mFontData; }
+    size_t GetFontSize() const { return mFontSize; }
+    int GetFontIndex() const { return mFontIndex; }
+    const std::vector<minikin::FontVariation>& GetAxes() const { return mVariations; }
+    std::shared_ptr<MinikinFont> createFontWithVariation(
+            const std::vector<FontVariation>& variations) const;
+private:
+    MinikinFontForTest() = delete;
+    MinikinFontForTest(const MinikinFontForTest&) = delete;
+    MinikinFontForTest& operator=(MinikinFontForTest&) = delete;
+
+    const std::string mFontPath;
+    const std::vector<FontVariation> mVariations;
+    const int mFontIndex;
+    void* mFontData;
+    size_t mFontSize;
+};
+
+}  // namespace minikin
+
+#endif  // MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
diff --git a/tests/util/UnicodeUtils.cpp b/tests/util/UnicodeUtils.cpp
new file mode 100644
index 0000000..e66ff93
--- /dev/null
+++ b/tests/util/UnicodeUtils.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include <unicode/utf.h>
+#include <unicode/utf8.h>
+#include <cstdlib>
+#include <cutils/log.h>
+#include <vector>
+#include <string>
+
+namespace minikin {
+
+// src is of the form "U+1F431 | 'h' 'i'". Position of "|" gets saved to offset if non-null.
+// Size is returned in an out parameter because gtest needs a void return for ASSERT to work.
+void ParseUnicode(uint16_t* buf, size_t buf_size, const char* src, size_t* result_size,
+        size_t* offset) {
+    size_t input_ix = 0;
+    size_t output_ix = 0;
+    bool seen_offset = false;
+
+    while (src[input_ix] != 0) {
+        switch (src[input_ix]) {
+        case '\'':
+            // single ASCII char
+            LOG_ALWAYS_FATAL_IF(static_cast<uint8_t>(src[input_ix]) >= 0x80);
+            input_ix++;
+            LOG_ALWAYS_FATAL_IF(src[input_ix] == 0);
+            LOG_ALWAYS_FATAL_IF(output_ix >= buf_size);
+            buf[output_ix++] = (uint16_t)src[input_ix++];
+            LOG_ALWAYS_FATAL_IF(src[input_ix] != '\'');
+            input_ix++;
+            break;
+        case 'u':
+        case 'U': {
+            // Unicode codepoint in hex syntax
+            input_ix++;
+            LOG_ALWAYS_FATAL_IF(src[input_ix] != '+');
+            input_ix++;
+            char* endptr = (char*)src + input_ix;
+            unsigned long int codepoint = strtoul(src + input_ix, &endptr, 16);
+            size_t num_hex_digits = endptr - (src + input_ix);
+
+            // also triggers on invalid number syntax, digits = 0
+            LOG_ALWAYS_FATAL_IF(num_hex_digits < 4u);
+            LOG_ALWAYS_FATAL_IF(num_hex_digits > 6u);
+            LOG_ALWAYS_FATAL_IF(codepoint > 0x10FFFFu);
+            input_ix += num_hex_digits;
+            if (U16_LENGTH(codepoint) == 1) {
+                LOG_ALWAYS_FATAL_IF(output_ix + 1 > buf_size);
+                buf[output_ix++] = codepoint;
+            } else {
+                // UTF-16 encoding
+                LOG_ALWAYS_FATAL_IF(output_ix + 2 > buf_size);
+                buf[output_ix++] = U16_LEAD(codepoint);
+                buf[output_ix++] = U16_TRAIL(codepoint);
+            }
+            break;
+        }
+        case ' ':
+            input_ix++;
+            break;
+        case '|':
+            LOG_ALWAYS_FATAL_IF(seen_offset);
+            LOG_ALWAYS_FATAL_IF(offset == nullptr);
+            *offset = output_ix;
+            seen_offset = true;
+            input_ix++;
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Unexpected Character");
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(result_size == nullptr);
+    *result_size = output_ix;
+    LOG_ALWAYS_FATAL_IF(!seen_offset && offset != nullptr);
+}
+
+std::vector<uint16_t> parseUnicodeStringWithOffset(const std::string& in, size_t* offset) {
+    std::unique_ptr<uint16_t[]> buffer(new uint16_t[in.size()]);
+    size_t result_size = 0;
+    ParseUnicode(buffer.get(), in.size(), in.c_str(), &result_size, offset);
+    return std::vector<uint16_t>(buffer.get(), buffer.get() + result_size);
+}
+
+std::vector<uint16_t> parseUnicodeString(const std::string& in) {
+    return parseUnicodeStringWithOffset(in, nullptr);
+}
+
+std::vector<uint16_t> utf8ToUtf16(const std::string& text) {
+    std::vector<uint16_t> result;
+    int32_t i = 0;
+    const int32_t textLength = static_cast<int32_t>(text.size());
+    uint32_t c = 0;
+    while (i < textLength) {
+        U8_NEXT(text.c_str(), i, textLength, c);
+        if (U16_LENGTH(c) == 1) {
+            result.push_back(c);
+        } else {
+            result.push_back(U16_LEAD(c));
+            result.push_back(U16_TRAIL(c));
+        }
+    }
+    return result;
+}
+
+}  // namespace minikin
diff --git a/tests/UnicodeUtils.h b/tests/util/UnicodeUtils.h
similarity index 62%
copy from tests/UnicodeUtils.h
copy to tests/util/UnicodeUtils.h
index 4f1b06a..6ce2fcb 100644
--- a/tests/UnicodeUtils.h
+++ b/tests/util/UnicodeUtils.h
@@ -14,5 +14,15 @@
  * limitations under the License.
  */
 
- void ParseUnicode(uint16_t* buf, size_t buf_size, const char* src, size_t* result_size,
+namespace minikin {
+
+void ParseUnicode(uint16_t* buf, size_t buf_size, const char* src, size_t* result_size,
         size_t* offset);
+
+std::vector<uint16_t> parseUnicodeStringWithOffset(const std::string& in, size_t* offset);
+std::vector<uint16_t> parseUnicodeString(const std::string& in);
+
+// Converts UTF-8 to UTF-16.
+std::vector<uint16_t> utf8ToUtf16(const std::string& text);
+
+}  // namespace minikin
diff --git a/tools/mk_hyb_file.py b/tools/mk_hyb_file.py
index 978c082..a9b8932 100755
--- a/tools/mk_hyb_file.py
+++ b/tools/mk_hyb_file.py
@@ -539,6 +539,12 @@
     patterns = []
     exceptions = []
     traverse_trie(0, '', trie_data, ch_map, pattern_data, patterns, exceptions)
+
+    # EXCEPTION for Bulgarian (bg), which contains an ineffectual line of <0, U+044C, 0>
+    if u'\u044c' in patterns:
+        patterns.remove(u'\u044c')
+        patterns.append(u'0\u044c0')
+
     assert verify_file_sorted(patterns, pat_fn), 'pattern table not verified'
     assert verify_file_sorted(exceptions, hyp_fn), 'exception table not verified'