From 9a4c3535bef65b72cf8b4b7da3b19d5995e2c137 Mon Sep 17 00:00:00 2001 From: Roozbeh Pournader Date: Mon, 19 Oct 2015 22:16:08 -0700 Subject: [PATCH] Basic scaffolding for handling a language list. The behavior hasn't changed much yet: all languages are ignored for rendering text, except the very first supported language. Change-Id: I1695fb985927ae5e28e4f59c1b531e4993af8688 --- .../src/flutter/include/minikin/FontFamily.h | 23 +++++++++ .../src/flutter/libs/minikin/FontFamily.cpp | 33 +++++++++++++ engine/src/flutter/tests/FontFamilyTest.cpp | 48 +++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/engine/src/flutter/include/minikin/FontFamily.h b/engine/src/flutter/include/minikin/FontFamily.h index b1994042e3c..fc05e3b2196 100644 --- a/engine/src/flutter/include/minikin/FontFamily.h +++ b/engine/src/flutter/include/minikin/FontFamily.h @@ -35,6 +35,7 @@ class MinikinFont; // font rendering. class FontLanguage { friend class FontStyle; + friend class FontLanguages; public: FontLanguage() : mBits(0) { } @@ -46,6 +47,8 @@ public: } operator bool() const { return mBits != 0; } + bool isUnsupported() const { return mBits == kUnsupportedLanguage; } + std::string getString() const; // 0 = no match, 1 = language matches, 2 = language and script match @@ -64,6 +67,23 @@ private: uint32_t mBits; }; +// A list of zero or more instances of FontLanguage, in the order of +// preference. Used for further resolution of rendering results. +class FontLanguages { +public: + FontLanguages() { mLangs.clear(); } + + // Parse from string, which is a comma-separated list of languages + FontLanguages(const char* buf, size_t size); + + const FontLanguage& operator[](size_t index) const { return mLangs.at(index); } + + size_t size() const { return mLangs.size(); } + +private: + std::vector mLangs; +}; + // FontStyle represents all style information needed to select an actual font // from a collection. The implementation is packed into a single 32-bit word // so it can be efficiently copied, embedded in other objects, etc. @@ -76,6 +96,9 @@ public: bits = (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift) | (lang.bits() << kLangShift); } + FontStyle(FontLanguages langs, int variant = 0, int weight = 4, bool italic = false) : + // TODO: Use all the languages in langs + FontStyle(langs[0], variant, weight, italic) { } int getWeight() const { return bits & kWeightMask; } bool getItalic() const { return (bits & kItalicMask) != 0; } int getVariant() const { return (bits >> kVariantShift) & kVariantMask; } diff --git a/engine/src/flutter/libs/minikin/FontFamily.cpp b/engine/src/flutter/libs/minikin/FontFamily.cpp index a6632e03398..dfd37fe5930 100644 --- a/engine/src/flutter/libs/minikin/FontFamily.cpp +++ b/engine/src/flutter/libs/minikin/FontFamily.cpp @@ -16,9 +16,12 @@ #define LOG_TAG "Minikin" +#include + #include #include #include +#include #include #include @@ -115,6 +118,36 @@ int FontLanguage::match(const FontLanguage other) const { return result; } +FontLanguages::FontLanguages(const char* buf, size_t size) { + std::unordered_set seen; + mLangs.clear(); + const char* bufEnd = buf + size; + const char* lastStart = buf; + bool isLastLang = false; + while (true) { + const char* commaLoc = static_cast( + memchr(lastStart, ',', bufEnd - lastStart)); + if (commaLoc == NULL) { + commaLoc = bufEnd; + isLastLang = true; + } + FontLanguage lang(lastStart, commaLoc - lastStart); + if (isLastLang && mLangs.size() == 0) { + // Make sure the list has at least one member + mLangs.push_back(lang); + return; + } + uint32_t bits = lang.bits(); + if (bits != FontLanguage::kUnsupportedLanguage && seen.count(bits) == 0) { + mLangs.push_back(lang); + if (isLastLang) return; + seen.insert(bits); + } + if (isLastLang) return; + lastStart = commaLoc + 1; + } +} + FontFamily::~FontFamily() { for (size_t i = 0; i < mFonts.size(); i++) { mFonts[i].typeface->UnrefLocked(); diff --git a/engine/src/flutter/tests/FontFamilyTest.cpp b/engine/src/flutter/tests/FontFamilyTest.cpp index a6813f013b3..78f286c0ca6 100644 --- a/engine/src/flutter/tests/FontFamilyTest.cpp +++ b/engine/src/flutter/tests/FontFamilyTest.cpp @@ -22,6 +22,54 @@ namespace android { +TEST(FontLanguagesTest, basicTests) { + FontLanguages emptyLangs; + EXPECT_EQ(0u, emptyLangs.size()); + + FontLanguage english("en", 2); + FontLanguages singletonLangs("en", 2); + EXPECT_EQ(1u, singletonLangs.size()); + EXPECT_EQ(english, singletonLangs[0]); + + FontLanguage french("fr", 2); + FontLanguages twoLangs("en,fr", 5); + EXPECT_EQ(2u, twoLangs.size()); + EXPECT_EQ(english, twoLangs[0]); + EXPECT_EQ(french, twoLangs[1]); +} + +TEST(FontLanguagesTest, unsupportedLanguageTests) { + FontLanguage unsupportedLang("x-example", 9); + ASSERT_TRUE(unsupportedLang.isUnsupported()); + + FontLanguages oneUnsupported("x-example", 9); + EXPECT_EQ(1u, oneUnsupported.size()); + EXPECT_TRUE(oneUnsupported[0].isUnsupported()); + + FontLanguages twoUnsupporteds("x-example,x-example", 19); + EXPECT_EQ(1u, twoUnsupporteds.size()); + EXPECT_TRUE(twoUnsupporteds[0].isUnsupported()); + + FontLanguage english("en", 2); + FontLanguages firstUnsupported("x-example,en", 12); + EXPECT_EQ(1u, firstUnsupported.size()); + EXPECT_EQ(english, firstUnsupported[0]); + + FontLanguages lastUnsupported("en,x-example", 12); + EXPECT_EQ(1u, lastUnsupported.size()); + EXPECT_EQ(english, lastUnsupported[0]); +} + +TEST(FontLanguagesTest, repeatedLanguageTests) { + FontLanguage english("en", 2); + FontLanguage englishInLatn("en-Latn", 2); + ASSERT_TRUE(english == englishInLatn); + + FontLanguages langs("en,en-Latn", 10); + EXPECT_EQ(1u, langs.size()); + EXPECT_EQ(english, langs[0]); +} + // The test font has following glyphs. // U+82A6 // U+82A6 U+FE00 (VS1)