From 8b585499667b5c18a1e11594dad53d2703ef14cb Mon Sep 17 00:00:00 2001 From: Bart Sears Date: Tue, 22 Dec 2015 09:06:03 +0000 Subject: Revert "Save all kind of script tags into FontLanguage." This reverts commit 5e995fb850c2b32631914c3815dfb421855fba9b. Change-Id: I761e0e41906742fbe3d3ac34170af3101e18042a --- include/minikin/FontFamily.h | 64 ++++++++- libs/minikin/Android.mk | 1 - libs/minikin/FontCollection.cpp | 10 +- libs/minikin/FontFamily.cpp | 120 ++++++++++++++++- libs/minikin/FontLanguage.cpp | 135 ------------------- libs/minikin/FontLanguage.h | 89 ------------- libs/minikin/FontLanguageListCache.cpp | 90 +------------ libs/minikin/FontLanguageListCache.h | 1 - libs/minikin/Layout.cpp | 13 +- tests/FontCollectionItemizeTest.cpp | 32 ++--- tests/FontFamilyTest.cpp | 232 ++++----------------------------- tests/FontLanguageListCacheTest.cpp | 18 +-- tests/FontTestUtils.cpp | 7 +- tests/ICUTestBase.h | 52 -------- 14 files changed, 225 insertions(+), 639 deletions(-) delete mode 100644 libs/minikin/FontLanguage.cpp delete mode 100644 libs/minikin/FontLanguage.h delete mode 100644 tests/ICUTestBase.h diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h index aa2e0ac..00130e6 100644 --- a/include/minikin/FontFamily.h +++ b/include/minikin/FontFamily.h @@ -30,6 +30,62 @@ namespace android { class MinikinFont; +// FontLanguage is a compact representation of a bcp-47 language tag. It +// does not capture all possible information, only what directly affects +// font rendering. +class FontLanguage { + friend class FontStyle; + friend class FontLanguages; +public: + FontLanguage() : mBits(0) { } + + // Parse from string + FontLanguage(const char* buf, size_t size); + + bool operator==(const FontLanguage other) const { + return mBits != kUnsupportedLanguage && mBits == other.mBits; + } + operator bool() const { return mBits != 0; } + + bool isUnsupported() const { return mBits == kUnsupportedLanguage; } + bool hasEmojiFlag() const { return isUnsupported() ? false : (mBits & kEmojiFlag); } + + std::string getString() const; + + // 0 = no match, 1 = language matches + int match(const FontLanguage other) const; + +private: + explicit FontLanguage(uint32_t bits) : mBits(bits) { } + + uint32_t bits() const { return mBits; } + + static const uint32_t kUnsupportedLanguage = 0xFFFFFFFFu; + static const uint32_t kBaseLangMask = 0xFFFFFFu; + static const uint32_t kHansFlag = 1u << 24; + static const uint32_t kHantFlag = 1u << 25; + static const uint32_t kEmojiFlag = 1u << 26; + static const uint32_t kScriptMask = kHansFlag | kHantFlag | kEmojiFlag; + 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 two 32-bit words // so it can be efficiently copied, embedded in other objects, etc. @@ -102,9 +158,7 @@ class FontFamily : public MinikinRefCounted { public: FontFamily() : mHbFont(nullptr) { } - FontFamily(int variant); - - FontFamily(uint32_t langId, int variant) : mLangId(langId), mVariant(variant), mHbFont(nullptr) { + FontFamily(FontLanguage lang, int variant) : mLang(lang), mVariant(variant), mHbFont(nullptr) { } ~FontFamily(); @@ -115,7 +169,7 @@ public: void addFont(MinikinFont* typeface, FontStyle style); FakedFont getClosestMatch(FontStyle style) const; - uint32_t langId() const { return mLangId; } + FontLanguage lang() const { return mLang; } int variant() const { return mVariant; } // API's for enumerating the fonts in a family. These don't guarantee any particular order @@ -146,7 +200,7 @@ private: MinikinFont* typeface; FontStyle style; }; - uint32_t mLangId; + FontLanguage mLang; int mVariant; std::vector mFonts; diff --git a/libs/minikin/Android.mk b/libs/minikin/Android.mk index 42fdca3..4c945a6 100644 --- a/libs/minikin/Android.mk +++ b/libs/minikin/Android.mk @@ -21,7 +21,6 @@ minikin_src_files := \ CmapCoverage.cpp \ FontCollection.cpp \ FontFamily.cpp \ - FontLanguage.cpp \ FontLanguageListCache.cpp \ GraphemeBreak.cpp \ HbFaceCache.cpp \ diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index 62c7031..bba2ef9 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -22,7 +22,6 @@ #include "unicode/unistr.h" #include "unicode/unorm2.h" -#include "FontLanguage.h" #include "FontLanguageListCache.h" #include "MinikinInternal.h" #include @@ -151,17 +150,14 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs, // always use it. return family; } - - // TODO use all language in the list. - FontLanguage fontLang = FontLanguageListCache::getById(family->langId())[0]; - int score = lang.match(fontLang) * 2; + int score = lang.match(family->lang()) * 2; if (family->variant() == 0 || family->variant() == variant) { score++; } if (hasVSGlyph) { score += 8; - } else if (((vs == 0xFE0F) && fontLang.hasEmojiFlag()) || - ((vs == 0xFE0E) && !fontLang.hasEmojiFlag())) { + } else if (((vs == 0xFE0F) && family->lang().hasEmojiFlag()) || + ((vs == 0xFE0E) && !family->lang().hasEmojiFlag())) { score += 4; } if (score > bestScore) { diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp index 1405789..7639831 100644 --- a/libs/minikin/FontFamily.cpp +++ b/libs/minikin/FontFamily.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "Minikin" +#include + #include #include #include @@ -26,7 +28,6 @@ #include -#include "FontLanguage.h" #include "FontLanguageListCache.h" #include "HbFaceCache.h" #include "MinikinInternal.h" @@ -40,6 +41,117 @@ using std::vector; namespace android { +// Parse bcp-47 language identifier into internal structure +FontLanguage::FontLanguage(const char* buf, size_t size) { + uint32_t bits = 0; + size_t i; + for (i = 0; i < size; i++) { + uint16_t c = buf[i]; + if (c == '-' || c == '_') break; + } + if (i == 2) { + bits = uint8_t(buf[0]) | (uint8_t(buf[1]) << 8); + } else if (i == 3) { + bits = uint8_t(buf[0]) | (uint8_t(buf[1]) << 8) | (uint8_t(buf[2]) << 16); + } else { + mBits = kUnsupportedLanguage; + // We don't understand anything other than two-letter or three-letter + // language codes, so we skip parsing the rest of the string. + return; + } + size_t next; + for (i++; i < size; i = next + 1) { + for (next = i; next < size; next++) { + uint16_t c = buf[next]; + if (c == '-' || c == '_') break; + } + if (next - i == 4) { + if (buf[i] == 'H' && buf[i+1] == 'a' && buf[i+2] == 'n') { + if (buf[i+3] == 's') { + bits |= kHansFlag; + } else if (buf[i+3] == 't') { + bits |= kHantFlag; + } + } else if (buf[i] == 'Q' && buf[i+1] == 'a' && buf[i+2] == 'a'&& buf[i+3] == 'e') { + bits |= kEmojiFlag; + } + } + // TODO: this might be a good place to infer script from country (zh_TW -> Hant), + // but perhaps it's up to the client to do that, before passing a string. + } + mBits = bits; +} + +std::string FontLanguage::getString() const { + if (mBits == kUnsupportedLanguage) { + return "und"; + } + char buf[16]; + size_t i = 0; + if (mBits & kBaseLangMask) { + buf[i++] = mBits & 0xFFu; + buf[i++] = (mBits >> 8) & 0xFFu; + char third_letter = (mBits >> 16) & 0xFFu; + if (third_letter != 0) buf[i++] = third_letter; + } + if (mBits & kScriptMask) { + if (!i) { + // This should not happen, but as it apparently has, we fill the language code part + // with "und". + buf[i++] = 'u'; + buf[i++] = 'n'; + buf[i++] = 'd'; + } + buf[i++] = '-'; + if (mBits & kEmojiFlag) { + buf[i++] = 'Q'; + buf[i++] = 'a'; + buf[i++] = 'a'; + buf[i++] = 'e'; + } else { + buf[i++] = 'H'; + buf[i++] = 'a'; + buf[i++] = 'n'; + buf[i++] = (mBits & kHansFlag) ? 's' : 't'; + } + } + return std::string(buf, i); +} + +int FontLanguage::match(const FontLanguage other) const { + return *this == other; +} + +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; + } +} + FontStyle::FontStyle(int variant, int weight, bool italic) : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) { } @@ -65,9 +177,6 @@ uint32_t FontStyle::pack(int variant, int weight, bool italic) { return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift); } -FontFamily::FontFamily(int variant) : FontFamily(FontLanguageListCache::kEmptyListId, variant) { -} - FontFamily::~FontFamily() { for (size_t i = 0; i < mFonts.size(); i++) { mFonts[i].typeface->UnrefLocked(); @@ -101,8 +210,7 @@ void FontFamily::addFont(MinikinFont* typeface, FontStyle style) { addFontLocked(typeface, style); } -void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { - typeface->RefLocked(); +void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { typeface->RefLocked(); mFonts.push_back(Font(typeface, style)); mCoverageValid = false; } diff --git a/libs/minikin/FontLanguage.cpp b/libs/minikin/FontLanguage.cpp deleted file mode 100644 index 3c12e06..0000000 --- a/libs/minikin/FontLanguage.cpp +++ /dev/null @@ -1,135 +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. - */ - -#define LOG_TAG "Minikin" - -#include "FontLanguage.h" - -#include -#include - -namespace android { - -#define SCRIPT_TAG(c1, c2, c3, c4) \ - ((uint32_t)(c1)) << 24 | ((uint32_t)(c2)) << 16 | ((uint32_t)(c3)) << 8 | ((uint32_t)(c4)) - -// 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); - } 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 (next - i == 4 && 'A' <= buf[i] && buf[i] <= 'Z') { - mScript = SCRIPT_TAG(buf[i], buf[i + 1], buf[i + 2], buf[i + 3]); - } - } - - mSubScriptBits = scriptToSubScriptBits(mScript); -} - -//static -uint8_t FontLanguage::scriptToSubScriptBits(uint32_t script) { - uint8_t subScriptBits = 0u; - switch (script) { - case SCRIPT_TAG('H', 'a', 'n', 'g'): - subScriptBits = kHangulFlag; - break; - case SCRIPT_TAG('H', 'a', 'n', 'i'): - subScriptBits = kHanFlag; - break; - case SCRIPT_TAG('H', 'a', 'n', 's'): - subScriptBits = kHanFlag | kSimplifiedChineseFlag; - break; - case SCRIPT_TAG('H', 'a', 'n', 't'): - subScriptBits = kHanFlag | kTraditionalChineseFlag; - break; - case SCRIPT_TAG('H', 'i', 'r', 'a'): - subScriptBits = kHiraganaFlag; - break; - case SCRIPT_TAG('H', 'r', 'k', 't'): - subScriptBits = kKatakanaFlag | kHiraganaFlag; - break; - case SCRIPT_TAG('J', 'p', 'a', 'n'): - subScriptBits = kHanFlag | kKatakanaFlag | kHiraganaFlag; - break; - case SCRIPT_TAG('K', 'a', 'n', 'a'): - subScriptBits = kKatakanaFlag; - break; - case SCRIPT_TAG('K', 'o', 'r', 'e'): - subScriptBits = kHanFlag | kHangulFlag; - break; - case SCRIPT_TAG('Q', 'a', 'a', 'e'): - subScriptBits = kEmojiFlag; - break; - } - return subScriptBits; -} - -std::string FontLanguage::getString() const { - if (mLanguage == 0ul) { - 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; - if (mScript != 0) { - buf[i++] = '-'; - buf[i++] = (mScript >> 24) & 0xFFu; - buf[i++] = (mScript >> 16) & 0xFFu; - buf[i++] = (mScript >> 8) & 0xFFu; - buf[i++] = mScript & 0xFFu; - } - return std::string(buf, i); -} - -bool FontLanguage::isEqualScript(const FontLanguage other) const { - return other.mScript == mScript; -} - -bool FontLanguage::supportsHbScript(hb_script_t script) const { - static_assert(SCRIPT_TAG('J', 'p', 'a', 'n') == HB_TAG('J', 'p', 'a', 'n'), - "The Minikin script and HarfBuzz hb_script_t have different encodings."); - if (script == mScript) return true; - uint8_t requestedBits = scriptToSubScriptBits(script); - return requestedBits != 0 && (mSubScriptBits & requestedBits) == requestedBits; -} - -int FontLanguage::match(const FontLanguage other) const { - // TODO: Use script for matching. - return *this == other; -} - -#undef SCRIPT_TAG -} // namespace android diff --git a/libs/minikin/FontLanguage.h b/libs/minikin/FontLanguage.h deleted file mode 100644 index abe7d13..0000000 --- a/libs/minikin/FontLanguage.h +++ /dev/null @@ -1,89 +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_FONT_LANGUAGE_H -#define MINIKIN_FONT_LANGUAGE_H - -#include -#include - -#include - -namespace android { - -// FontLanguage is a compact representation of a BCP 47 language tag. It -// does not capture all possible information, only what directly affects -// font rendering. -struct FontLanguage { -public: - // Default constructor creates the unsupported language. - FontLanguage() : mScript(0ul), mLanguage(0ul), mSubScriptBits(0ul) {} - - // Parse from string - FontLanguage(const char* buf, size_t length); - - bool operator==(const FontLanguage other) const { - return !isUnsupported() && isEqualScript(other) && isEqualLanguage(other); - } - - bool operator!=(const FontLanguage other) const { - return !(*this == other); - } - - bool isUnsupported() const { return mLanguage == 0ul; } - bool hasEmojiFlag() const { return mSubScriptBits & kEmojiFlag; } - - bool isEqualLanguage(const FontLanguage other) const { return mLanguage == other.mLanguage; } - bool isEqualScript(const FontLanguage other) const; - - // Returns true if this script supports the given script. For example, ja-Jpan supports Hira, - // ja-Hira doesn't support Jpan. - bool supportsHbScript(hb_script_t script) const; - - std::string getString() const; - - // 0 = no match, 1 = language matches - int match(const FontLanguage other) const; - - uint64_t getIdentifier() const { return (uint64_t)mScript << 32 | (uint64_t)mLanguage; } - -private: - // ISO 15924 compliant script code. The 4 chars script code are packed into a 32 bit integer. - 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. - // mLanguage = 0 means the FontLanguage is unsupported. - uint32_t mLanguage; - - // For faster comparing, use 7 bits for specific scripts. - static const uint8_t kEmojiFlag = 1u; - 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; - - static uint8_t scriptToSubScriptBits(uint32_t script); -}; - -typedef std::vector FontLanguages; - -} // namespace android - -#endif // MINIKIN_FONT_LANGUAGE_H diff --git a/libs/minikin/FontLanguageListCache.cpp b/libs/minikin/FontLanguageListCache.cpp index 2d64998..e1c2343 100644 --- a/libs/minikin/FontLanguageListCache.cpp +++ b/libs/minikin/FontLanguageListCache.cpp @@ -19,92 +19,13 @@ #include "FontLanguageListCache.h" #include -#include -#include #include "MinikinInternal.h" -#include "FontLanguage.h" namespace android { const uint32_t FontLanguageListCache::kEmptyListId; -// Returns the text length of output. -static size_t toLanguageTag(char* output, size_t outSize, const std::string& locale) { - output[0] = '\0'; - if (locale.empty()) { - return 0; - } - - size_t outLength = 0; - UErrorCode uErr = U_ZERO_ERROR; - outLength = uloc_canonicalize(locale.c_str(), output, outSize, &uErr); - if (U_FAILURE(uErr)) { - // unable to build a proper language identifier - ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale.c_str(), u_errorName(uErr)); - output[0] = '\0'; - return 0; - } - - // Preserve "und" and "und-****" since uloc_addLikelySubtags changes "und" to "en-Latn-US". - if (strncmp(output, "und", 3) == 0 && - (outLength == 3 || (outLength == 8 && output[3] == '_'))) { - return outLength; - } - - char likelyChars[ULOC_FULLNAME_CAPACITY]; - uErr = U_ZERO_ERROR; - uloc_addLikelySubtags(output, likelyChars, ULOC_FULLNAME_CAPACITY, &uErr); - if (U_FAILURE(uErr)) { - // unable to build a proper language identifier - ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", output, u_errorName(uErr)); - output[0] = '\0'; - return 0; - } - - uErr = U_ZERO_ERROR; - outLength = uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr); - if (U_FAILURE(uErr)) { - // unable to build a proper language identifier - ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, u_errorName(uErr)); - output[0] = '\0'; - return 0; - } -#ifdef VERBOSE_DEBUG - ALOGD("ICU normalized '%s' to '%s'", locale.c_str(), output); -#endif - return outLength; -} - -static FontLanguages constructFontLanguages(const std::string& input) { - FontLanguages result; - size_t currentIdx = 0; - size_t commaLoc = 0; - char langTag[ULOC_FULLNAME_CAPACITY]; - std::unordered_set seen; - std::string locale(input.size(), 0); - - while ((commaLoc = input.find_first_of(',', currentIdx)) != std::string::npos) { - locale.assign(input, currentIdx, commaLoc - currentIdx); - currentIdx = commaLoc + 1; - size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); - FontLanguage lang(langTag, length); - uint64_t identifier = lang.getIdentifier(); - if (!lang.isUnsupported() && seen.count(identifier) == 0) { - result.push_back(lang); - seen.insert(identifier); - } - } - locale.assign(input, currentIdx, input.size() - currentIdx); - size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); - FontLanguage lang(langTag, length); - uint64_t identifier = lang.getIdentifier(); - if (!lang.isUnsupported() && seen.count(identifier) == 0) { - result.push_back(lang); - } - return result; -} - // static uint32_t FontLanguageListCache::getId(const std::string& languages) { FontLanguageListCache* inst = FontLanguageListCache::getInstance(); @@ -116,11 +37,7 @@ uint32_t FontLanguageListCache::getId(const std::string& languages) { // Given language list is not in cache. Insert it and return newly assigned ID. const uint32_t nextId = inst->mLanguageLists.size(); - FontLanguages fontLanguages = constructFontLanguages(languages); - if (fontLanguages.empty()) { - return kEmptyListId; - } - inst->mLanguageLists.push_back(fontLanguages); + inst->mLanguageLists.push_back(FontLanguages(languages.c_str(), languages.size())); inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId)); return nextId; } @@ -139,9 +56,8 @@ FontLanguageListCache* FontLanguageListCache::getInstance() { if (instance == nullptr) { instance = new FontLanguageListCache(); - // Insert an empty language list for mapping default language list to kEmptyListId. - // The default language list has only one FontLanguage and it is the unsupported language. - instance->mLanguageLists.push_back(FontLanguages({FontLanguage()})); + // Insert an empty language list for mapping empty language list to kEmptyListId. + instance->mLanguageLists.push_back(FontLanguages()); instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId)); } return instance; diff --git a/libs/minikin/FontLanguageListCache.h b/libs/minikin/FontLanguageListCache.h index c961882..7d627b5 100644 --- a/libs/minikin/FontLanguageListCache.h +++ b/libs/minikin/FontLanguageListCache.h @@ -20,7 +20,6 @@ #include #include -#include "FontLanguage.h" namespace android { diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp index 2e206a2..af5e6fe 100644 --- a/libs/minikin/Layout.cpp +++ b/libs/minikin/Layout.cpp @@ -34,7 +34,6 @@ #include #include -#include "FontLanguage.h" #include "FontLanguageListCache.h" #include "LayoutUtils.h" #include "HbFaceCache.h" @@ -747,15 +746,9 @@ void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t const FontLanguages& langList = FontLanguageListCache::getById(ctx->style.getLanguageListId()); if (langList.size() != 0) { - const FontLanguage* hbLanguage = &langList[0]; - for (size_t i = 0; i < langList.size(); ++i) { - if (langList[i].supportsHbScript(script)) { - hbLanguage = &langList[i]; - break; - } - } - hb_buffer_set_language(buffer, - hb_language_from_string(hbLanguage->getString().c_str(), -1)); + // TODO: use all languages in langList. + string lang = langList[0].getString(); + hb_buffer_set_language(buffer, hb_language_from_string(lang.c_str(), -1)); } hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart); if (ctx->paint.hyphenEdit.hasHyphen() && srunend > srunstart) { diff --git a/tests/FontCollectionItemizeTest.cpp b/tests/FontCollectionItemizeTest.cpp index cf9b704..85d76af 100644 --- a/tests/FontCollectionItemizeTest.cpp +++ b/tests/FontCollectionItemizeTest.cpp @@ -16,9 +16,7 @@ #include -#include "FontLanguage.h" #include "FontTestUtils.h" -#include "ICUTestBase.h" #include "MinikinFontForTest.h" #include "UnicodeUtils.h" @@ -44,8 +42,6 @@ const char kColorEmojiFont[] = kTestFontDir "ColorEmojiFont.ttf"; const char kTextEmojiFont[] = kTestFontDir "TextEmojiFont.ttf"; const char kMixedEmojiFont[] = kTestFontDir "ColorTextMixedEmojiFont.ttf"; -typedef ICUTestBase FontCollectionItemizeTest; - // Utility function for calling itemize function. void itemize(FontCollection* collection, const char* str, FontStyle style, std::vector* result) { @@ -64,7 +60,7 @@ const std::string& getFontPath(const FontCollection::Run& run) { return ((MinikinFontForTest*)run.fakedFont.font)->fontPath(); } -TEST_F(FontCollectionItemizeTest, itemize_latin) { +TEST(FontCollectionItemizeTest, itemize_latin) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -134,7 +130,7 @@ TEST_F(FontCollectionItemizeTest, itemize_latin) { EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic()); } -TEST_F(FontCollectionItemizeTest, itemize_emoji) { +TEST(FontCollectionItemizeTest, itemize_emoji) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -195,7 +191,7 @@ TEST_F(FontCollectionItemizeTest, itemize_emoji) { EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeItalic()); } -TEST_F(FontCollectionItemizeTest, itemize_non_latin) { +TEST(FontCollectionItemizeTest, itemize_non_latin) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -284,7 +280,7 @@ TEST_F(FontCollectionItemizeTest, itemize_non_latin) { EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic()); } -TEST_F(FontCollectionItemizeTest, itemize_mixed) { +TEST(FontCollectionItemizeTest, itemize_mixed) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -323,7 +319,7 @@ TEST_F(FontCollectionItemizeTest, itemize_mixed) { EXPECT_FALSE(runs[4].fakedFont.fakery.isFakeItalic()); } -TEST_F(FontCollectionItemizeTest, itemize_variationSelector) { +TEST(FontCollectionItemizeTest, itemize_variationSelector) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -462,7 +458,7 @@ TEST_F(FontCollectionItemizeTest, itemize_variationSelector) { EXPECT_EQ(kLatinFont, getFontPath(runs[0])); } -TEST_F(FontCollectionItemizeTest, itemize_variationSelectorSupplement) { +TEST(FontCollectionItemizeTest, itemize_variationSelectorSupplement) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -587,7 +583,7 @@ TEST_F(FontCollectionItemizeTest, itemize_variationSelectorSupplement) { EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontPath(runs[0])); } -TEST_F(FontCollectionItemizeTest, itemize_no_crash) { +TEST(FontCollectionItemizeTest, itemize_no_crash) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -611,7 +607,7 @@ TEST_F(FontCollectionItemizeTest, itemize_no_crash) { itemize(collection.get(), "U+FE00 U+302D U+E0100", FontStyle(), &runs); } -TEST_F(FontCollectionItemizeTest, itemize_fakery) { +TEST(FontCollectionItemizeTest, itemize_fakery) { std::unique_ptr collection = getFontCollection(kTestFontDir, kItemizeFontXml); std::vector runs; @@ -651,18 +647,18 @@ TEST_F(FontCollectionItemizeTest, itemize_fakery) { EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic()); } -TEST_F(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) { +TEST(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) { // 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"; std::vector families; - FontFamily* family1 = new FontFamily(android::VARIANT_DEFAULT); + FontFamily* family1 = new FontFamily(FontLanguage(), android::VARIANT_DEFAULT); family1->addFont(new MinikinFontForTest(kLatinFont)); families.push_back(family1); - FontFamily* family2 = new FontFamily(android::VARIANT_DEFAULT); + FontFamily* family2 = new FontFamily(FontLanguage(), android::VARIANT_DEFAULT); family2->addFont(new MinikinFontForTest(kVSTestFont)); families.push_back(family2); @@ -680,7 +676,7 @@ TEST_F(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) { family2->Unref(); } -TEST_F(FontCollectionItemizeTest, itemize_emojiSelection) { +TEST(FontCollectionItemizeTest, itemize_emojiSelection) { std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); std::vector runs; @@ -752,7 +748,7 @@ TEST_F(FontCollectionItemizeTest, itemize_emojiSelection) { EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0])); } -TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) { +TEST(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) { std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); std::vector runs; @@ -834,7 +830,7 @@ TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) { EXPECT_EQ(kMixedEmojiFont, getFontPath(runs[0])); } -TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) { +TEST(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) { std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); std::vector runs; diff --git a/tests/FontFamilyTest.cpp b/tests/FontFamilyTest.cpp index ac7616f..fc44e6c 100644 --- a/tests/FontFamilyTest.cpp +++ b/tests/FontFamilyTest.cpp @@ -17,261 +17,74 @@ #include #include - -#include - #include "FontLanguageListCache.h" -#include "ICUTestBase.h" #include "MinikinFontForTest.h" #include "MinikinInternal.h" namespace android { -typedef ICUTestBase FontLanguagesTest; -typedef ICUTestBase FontLanguageTest; - -static FontLanguages createFontLanguages(const std::string& input) { - uint32_t langId = FontLanguageListCache::getId(input); - return FontLanguageListCache::getById(langId); -} - -static FontLanguage createFontLanguage(const std::string& input) { - 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 undQaae = createFontLanguage("und-Qaae"); - - 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 != undQaae); - EXPECT_TRUE(und != undQaae); - 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(undQaae.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-Qaae", createFontLanguage("und-Qaae").getString()); - EXPECT_EQ("und-Qaae", createFontLanguage("Und-QAAE").getString()); - EXPECT_EQ("und-Qaae", createFontLanguage("Und-qaae").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 }, - - // 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 }, - - // 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 }, - - // 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 }, - }; - - 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) { +TEST(FontLanguagesTest, basicTests) { FontLanguages emptyLangs; EXPECT_EQ(0u, emptyLangs.size()); - FontLanguage english = createFontLanguage("en"); - FontLanguages singletonLangs = createFontLanguages("en"); + FontLanguage english("en", 2); + FontLanguages singletonLangs("en", 2); EXPECT_EQ(1u, singletonLangs.size()); EXPECT_EQ(english, singletonLangs[0]); - FontLanguage french = createFontLanguage("fr"); - FontLanguages twoLangs = createFontLanguages("en,fr"); + 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_F(FontLanguagesTest, unsupportedLanguageTests) { - FontLanguage unsupportedLang = createFontLanguage("abcd"); +TEST(FontLanguagesTest, unsupportedLanguageTests) { + FontLanguage unsupportedLang("x-example", 9); ASSERT_TRUE(unsupportedLang.isUnsupported()); - FontLanguages oneUnsupported = createFontLanguages("abcd-example"); + FontLanguages oneUnsupported("x-example", 9); EXPECT_EQ(1u, oneUnsupported.size()); EXPECT_TRUE(oneUnsupported[0].isUnsupported()); - FontLanguages twoUnsupporteds = createFontLanguages("abcd-example,abcd-example"); + FontLanguages twoUnsupporteds("x-example,x-example", 19); EXPECT_EQ(1u, twoUnsupporteds.size()); EXPECT_TRUE(twoUnsupporteds[0].isUnsupported()); - FontLanguage english = createFontLanguage("en"); - FontLanguages firstUnsupported = createFontLanguages("abcd-example,en"); + FontLanguage english("en", 2); + FontLanguages firstUnsupported("x-example,en", 12); EXPECT_EQ(1u, firstUnsupported.size()); EXPECT_EQ(english, firstUnsupported[0]); - FontLanguages lastUnsupported = createFontLanguages("en,abcd-example"); + FontLanguages lastUnsupported("en,x-example", 12); 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"); +TEST(FontLanguagesTest, repeatedLanguageTests) { + FontLanguage english("en", 2); + FontLanguage englishInLatn("en-Latn", 2); ASSERT_TRUE(english == englishInLatn); - FontLanguages langs = createFontLanguages("en,en-Latn"); + FontLanguages langs("en,en-Latn", 10); EXPECT_EQ(1u, langs.size()); EXPECT_EQ(english, langs[0]); - - // Country codes are ignored. - FontLanguages fr = createFontLanguages("fr,fr-CA,fr-FR"); - EXPECT_EQ(1u, fr.size()); - EXPECT_EQ(french, fr[0]); - - // The order should be kept. - langs = createFontLanguages("en,fr,en-Latn"); - EXPECT_EQ(2u, langs.size()); - EXPECT_EQ(english, langs[0]); - EXPECT_EQ(french, langs[1]); } -TEST_F(FontLanguagesTest, undEmojiTests) { - FontLanguage emoji = createFontLanguage("und-Qaae"); +TEST(FontLanguagesTest, undEmojiTests) { + FontLanguage emoji("und-Qaae", 8); EXPECT_TRUE(emoji.hasEmojiFlag()); - FontLanguage und = createFontLanguage("und"); + FontLanguage und("und", 3); EXPECT_FALSE(und.hasEmojiFlag()); EXPECT_FALSE(emoji == und); - FontLanguage undExample = createFontLanguage("und-example"); + FontLanguage undExample("und-example", 10); EXPECT_FALSE(undExample.hasEmojiFlag()); EXPECT_FALSE(emoji == undExample); } -TEST_F(FontLanguagesTest, registerLanguageListTest) { +TEST(FontLanguagesTest, registerLanguageListTest) { EXPECT_EQ(0UL, FontStyle::registerLanguageList("")); EXPECT_NE(0UL, FontStyle::registerLanguageList("en")); EXPECT_NE(0UL, FontStyle::registerLanguageList("jp")); @@ -309,10 +122,9 @@ TEST_F(FontLanguagesTest, registerLanguageListTest) { // U+717D U+E0103 (VS20) const char kVsTestFont[] = kTestFontDir "VarioationSelectorTest-Regular.ttf"; -class FontFamilyTest : public ICUTestBase { +class FontFamilyTest : public testing::Test { public: virtual void SetUp() override { - ICUTestBase::SetUp(); if (access(kVsTestFont, R_OK) != 0) { FAIL() << "Unable to read " << kVsTestFont << ". " << "Please prepare the test data directory. " diff --git a/tests/FontLanguageListCacheTest.cpp b/tests/FontLanguageListCacheTest.cpp index f83988c..29757fe 100644 --- a/tests/FontLanguageListCacheTest.cpp +++ b/tests/FontLanguageListCacheTest.cpp @@ -17,15 +17,11 @@ #include #include - #include "FontLanguageListCache.h" -#include "ICUTestBase.h" namespace android { -typedef ICUTestBase FontLanguageListCacheTest; - -TEST_F(FontLanguageListCacheTest, getId) { +TEST(FontLanguageListCacheTest, getId) { EXPECT_EQ(0UL, FontLanguageListCache::getId("")); EXPECT_NE(0UL, FontStyle::registerLanguageList("en")); EXPECT_NE(0UL, FontStyle::registerLanguageList("jp")); @@ -46,15 +42,11 @@ TEST_F(FontLanguageListCacheTest, getId) { FontLanguageListCache::getId("en,zh-Hant")); } -TEST_F(FontLanguageListCacheTest, getById) { - uint32_t enLangId = FontLanguageListCache::getId("en"); - uint32_t jpLangId = FontLanguageListCache::getId("jp"); - FontLanguage english = FontLanguageListCache::getById(enLangId)[0]; - FontLanguage japanese = FontLanguageListCache::getById(jpLangId)[0]; +TEST(FontLanguageListCacheTest, getById) { + FontLanguage english("en", 2); + FontLanguage japanese("jp", 2); - FontLanguages defLangs = FontLanguageListCache::getById(0); - EXPECT_EQ(1UL, defLangs.size()); - EXPECT_TRUE(defLangs[0].isUnsupported()); + EXPECT_EQ(0UL, FontLanguageListCache::getById(0).size()); FontLanguages langs = FontLanguageListCache::getById(FontLanguageListCache::getId("en")); ASSERT_EQ(1UL, langs.size()); diff --git a/tests/FontTestUtils.cpp b/tests/FontTestUtils.cpp index 98dab51..8e1d184 100644 --- a/tests/FontTestUtils.cpp +++ b/tests/FontTestUtils.cpp @@ -20,8 +20,6 @@ #include #include - -#include "FontLanguage.h" #include "MinikinFontForTest.h" std::unique_ptr getFontCollection( @@ -46,10 +44,9 @@ std::unique_ptr getFontCollection( } 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); + android::FontFamily* family = new android::FontFamily( + android::FontLanguage((const char*)lang, xmlStrlen(lang)), variant); for (xmlNode* fontNode = familyNode->children; fontNode; fontNode = fontNode->next) { if (xmlStrcmp(fontNode->name, (const xmlChar*)"font") != 0) { diff --git a/tests/ICUTestBase.h b/tests/ICUTestBase.h deleted file mode 100644 index 3bcfaf3..0000000 --- a/tests/ICUTestBase.h +++ /dev/null @@ -1,52 +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_ICU_TEST_BASE_H -#define MINIKIN_TEST_ICU_TEST_BASE_H - -#include -#include -#include - -// low level file access for mapping ICU data -#include -#include -#include - -class ICUTestBase : public testing::Test { -protected: - virtual void SetUp() override { - const char* fn = "/system/usr/icu/" U_ICUDATA_NAME ".dat"; - int fd = open(fn, O_RDONLY); - ASSERT_NE(-1, fd); - struct stat sb; - ASSERT_EQ(0, fstat(fd, &sb)); - void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); - - UErrorCode errorCode = U_ZERO_ERROR; - udata_setCommonData(data, &errorCode); - ASSERT_TRUE(U_SUCCESS(errorCode)); - u_init(&errorCode); - ASSERT_TRUE(U_SUCCESS(errorCode)); - } - - virtual void TearDown() override { - u_cleanup(); - } -}; - - -#endif // MINIKIN_TEST_ICU_TEST_BASE_H -- cgit v1.2.3 From 3a2a4a99ce0189d9f28e5f7c2cbab8076f6fadeb Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Tue, 26 Jan 2016 00:44:02 -0800 Subject: Disable unsigned integer overflow sanitization until libc++ is fixed. Bug: http://b/26781196 Bug: http://b/25884483 Bug: http://b/26432628 Although this issue was first only manifesting on Fugu, it now affects N9 and N6p as well. This change disables unsigned overflow sanitization on all platforms. The real fix for libc++ (r257368) can't be committed until we have updated Clang at least one more time. Change-Id: I71e9c50d25ae4566d4c06f348183c4b22a4bb60a --- libs/minikin/Android.mk | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libs/minikin/Android.mk b/libs/minikin/Android.mk index 67ac9f7..769b5fc 100644 --- a/libs/minikin/Android.mk +++ b/libs/minikin/Android.mk @@ -64,9 +64,7 @@ LOCAL_SHARED_LIBRARIES := $(minikin_shared_libraries) LOCAL_CLANG := true LOCAL_SANITIZE := signed-integer-overflow # b/26432628. -ifeq ($(filter x86%,$(TARGET_ARCH)),) - LOCAL_SANITIZE += unsigned-integer-overflow -endif +#LOCAL_SANITIZE += unsigned-integer-overflow include $(BUILD_SHARED_LIBRARY) @@ -82,9 +80,7 @@ LOCAL_SHARED_LIBRARIES := $(minikin_shared_libraries) LOCAL_CLANG := true LOCAL_SANITIZE := signed-integer-overflow # b/26432628. -ifeq ($(filter x86%,$(TARGET_ARCH)),) - LOCAL_SANITIZE += unsigned-integer-overflow -endif +#LOCAL_SANITIZE += unsigned-integer-overflow include $(BUILD_STATIC_LIBRARY) -- cgit v1.2.3 From f6273869d929bf83e72f687303b816714744541f Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 18 Feb 2016 10:27:38 -0800 Subject: Disable hyphenation when word overlaps style boundary In cases when a word (as defined by the ICU break iterator) overlaps a style boundary, the returned wordStart can be extend before the range currently being measured for layout. When we try to hyphenate the resulting substrings, we get a negative range, which crashes. This patch disables hyphenation in this case. Bug: 27237112 Change-Id: I76d04b39dd3b4d6d267aaaf4bebc9ab361891646 --- libs/minikin/LineBreaker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/minikin/LineBreaker.cpp b/libs/minikin/LineBreaker.cpp index 9cf07d5..22c3954 100644 --- a/libs/minikin/LineBreaker.cpp +++ b/libs/minikin/LineBreaker.cpp @@ -174,7 +174,8 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, const FontCollection* typefa if (paint != nullptr && mHyphenator != nullptr && mHyphenationFrequency != kHyphenationFrequency_None && !wordEndsInHyphen && !temporarilySkipHyphenation && - wordEnd > wordStart && wordEnd - wordStart <= LONGEST_HYPHENATED_WORD) { + wordStart >= start && wordEnd > wordStart && + wordEnd - wordStart <= LONGEST_HYPHENATED_WORD) { mHyphenator->hyphenate(&mHyphBuf, &mTextBuf[wordStart], wordEnd - wordStart); #if VERBOSE_DEBUG std::string hyphenatedString; -- cgit v1.2.3 From febefe05b35aa4b7a58e9a13fd83b264832bccb4 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Mon, 11 Apr 2016 17:53:34 +0900 Subject: Fix minikin_unittests This CL fixes following test cases in minikin_tests - FontFamilyTest.hasVariationSelectorTest - HbFontCacheTest.getHbFontLockedTest - HbFontCacheTest.purgeCacheTest For the fix of FontFamilyTest.hasVariationSelectorTest, removing virtual from GetUniqueId() in MinikinFont. After [1], MinikinFont's destructor started calling purgeHbCache() which calls virtual method, MinikinFont::GetUniqueId(). Fortunately, the SkTypeface::uniqueID() returns just internal value, so we can store it at the construction time and use it instead of calling SkTypeface::uniqueID() every time. This patch also changes purgeHbFont to purgeHbFontLocked, as all uses of it were already under global mutex. This change avoids deadlock on explicit unref, as when invoked by a Java finalizer from the Java object that holds a reference to the font. Some of the tests needed to change to using the ref counting protocol rather than explicitly destructing font objects, as well. [1] 9afcc6e2bd4d89e4e1deb6e18c3c4daca4e114fd Bug: 28105730 Bug: 28105688 Change-Id: Ie5983c4869147dacabdca81af1605066cd680b3f --- include/minikin/MinikinFont.h | 8 ++++++-- include/minikin/MinikinFontFreeType.h | 3 --- include/minikin/MinikinRefCounted.h | 17 ++++++++++++++++ libs/minikin/HbFontCache.cpp | 4 ++-- libs/minikin/HbFontCache.h | 2 +- libs/minikin/MinikinFont.cpp | 2 +- libs/minikin/MinikinFontFreeType.cpp | 6 +----- sample/MinikinSkia.cpp | 5 +---- sample/MinikinSkia.h | 2 -- tests/FontFamilyTest.cpp | 37 ++++++++++++++++++----------------- tests/MinikinFontForTest.cpp | 14 +++++++------ tests/MinikinFontForTest.h | 2 +- tests/how_to_run.txt | 2 +- 13 files changed, 58 insertions(+), 46 deletions(-) diff --git a/include/minikin/MinikinFont.h b/include/minikin/MinikinFont.h index 0298235..4951514 100644 --- a/include/minikin/MinikinFont.h +++ b/include/minikin/MinikinFont.h @@ -99,6 +99,8 @@ typedef void (*MinikinDestroyFunc) (void* data); class MinikinFont : public MinikinRefCounted { public: + MinikinFont(int32_t uniqueId) : mUniqueId(uniqueId) {} + virtual ~MinikinFont(); virtual float GetHorizontalAdvance(uint32_t glyph_id, @@ -125,12 +127,14 @@ public: return 0; } - virtual int32_t GetUniqueId() const = 0; - static uint32_t MakeTag(char c1, char c2, char c3, char c4) { return ((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) | ((uint32_t)c3 << 8) | (uint32_t)c4; } + + int32_t GetUniqueId() const { return mUniqueId; } +private: + const int32_t mUniqueId; }; } // namespace android diff --git a/include/minikin/MinikinFontFreeType.h b/include/minikin/MinikinFontFreeType.h index 535c249..baa08df 100644 --- a/include/minikin/MinikinFontFreeType.h +++ b/include/minikin/MinikinFontFreeType.h @@ -52,8 +52,6 @@ public: // TODO: provide access to raw data, as an optimization. - int32_t GetUniqueId() const; - // Not a virtual method, as the protocol to access rendered // glyph bitmaps is probably different depending on the // backend. @@ -64,7 +62,6 @@ public: private: FT_Face mTypeface; - int32_t mUniqueId; static int32_t sIdCounter; }; diff --git a/include/minikin/MinikinRefCounted.h b/include/minikin/MinikinRefCounted.h index 74d27fe..603aff0 100644 --- a/include/minikin/MinikinRefCounted.h +++ b/include/minikin/MinikinRefCounted.h @@ -37,6 +37,23 @@ 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 +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/libs/minikin/HbFontCache.cpp b/libs/minikin/HbFontCache.cpp index 9544dc2..3be942d 100644 --- a/libs/minikin/HbFontCache.cpp +++ b/libs/minikin/HbFontCache.cpp @@ -91,8 +91,8 @@ void purgeHbFontCacheLocked() { getFontCacheLocked()->clear(); } -void purgeHbFont(const MinikinFont* minikinFont) { - AutoMutex _l(gMinikinLock); +void purgeHbFontLocked(const MinikinFont* minikinFont) { + assertMinikinLocked(); const int32_t fontId = minikinFont->GetUniqueId(); getFontCacheLocked()->remove(fontId); } diff --git a/libs/minikin/HbFontCache.h b/libs/minikin/HbFontCache.h index 92e465a..449b354 100644 --- a/libs/minikin/HbFontCache.h +++ b/libs/minikin/HbFontCache.h @@ -23,7 +23,7 @@ namespace android { class MinikinFont; void purgeHbFontCacheLocked(); -void purgeHbFont(const MinikinFont* minikinFont); +void purgeHbFontLocked(const MinikinFont* minikinFont); hb_font_t* getHbFontLocked(MinikinFont* minikinFont); } // namespace android diff --git a/libs/minikin/MinikinFont.cpp b/libs/minikin/MinikinFont.cpp index d56ec9c..ef42e9b 100644 --- a/libs/minikin/MinikinFont.cpp +++ b/libs/minikin/MinikinFont.cpp @@ -20,7 +20,7 @@ namespace android { MinikinFont::~MinikinFont() { - purgeHbFont(this); + purgeHbFontLocked(this); } } // namespace android diff --git a/libs/minikin/MinikinFontFreeType.cpp b/libs/minikin/MinikinFontFreeType.cpp index b9ea5d7..4a1b115 100644 --- a/libs/minikin/MinikinFontFreeType.cpp +++ b/libs/minikin/MinikinFontFreeType.cpp @@ -30,8 +30,8 @@ namespace android { int32_t MinikinFontFreeType::sIdCounter = 0; MinikinFontFreeType::MinikinFontFreeType(FT_Face typeface) : + MinikinFont(sIdCounter++), mTypeface(typeface) { - mUniqueId = sIdCounter++; } MinikinFontFreeType::~MinikinFontFreeType() { @@ -72,10 +72,6 @@ const void* MinikinFontFreeType::GetTable(uint32_t tag, size_t* size, MinikinDes return buf; } -int32_t MinikinFontFreeType::GetUniqueId() const { - return mUniqueId; -} - bool MinikinFontFreeType::Render(uint32_t glyph_id, const MinikinPaint& /* paint */, GlyphBitmap *result) { FT_Error error; diff --git a/sample/MinikinSkia.cpp b/sample/MinikinSkia.cpp index c4971bb..e2ecde0 100644 --- a/sample/MinikinSkia.cpp +++ b/sample/MinikinSkia.cpp @@ -7,6 +7,7 @@ namespace android { MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) : + MinikinFont(typeface->uniqueID()), mTypeface(typeface) { } @@ -67,8 +68,4 @@ SkTypeface *MinikinFontSkia::GetSkTypeface() { return mTypeface; } -int32_t MinikinFontSkia::GetUniqueId() const { - return mTypeface->uniqueID(); -} - } diff --git a/sample/MinikinSkia.h b/sample/MinikinSkia.h index e1d7bf6..6eb9065 100644 --- a/sample/MinikinSkia.h +++ b/sample/MinikinSkia.h @@ -14,8 +14,6 @@ public: const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy); - int32_t GetUniqueId() const; - SkTypeface *GetSkTypeface(); private: diff --git a/tests/FontFamilyTest.cpp b/tests/FontFamilyTest.cpp index 907f395..194063f 100644 --- a/tests/FontFamilyTest.cpp +++ b/tests/FontFamilyTest.cpp @@ -350,9 +350,9 @@ void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set minikinFont(new MinikinFontForTest(kVsTestFont)); + MinikinAutoUnref family(new FontFamily); + family->addFont(minikinFont.get()); AutoMutex _l(gMinikinLock); @@ -365,24 +365,24 @@ TEST_F(FontFamilyTest, hasVariationSelectorTest) { const uint32_t kVS20 = 0xE0103; const uint32_t kSupportedChar1 = 0x82A6; - EXPECT_TRUE(family.getCoverage()->get(kSupportedChar1)); - expectVSGlyphs(&family, kSupportedChar1, std::set({kVS1, kVS17, kVS18, kVS19})); + EXPECT_TRUE(family->getCoverage()->get(kSupportedChar1)); + expectVSGlyphs(family.get(), kSupportedChar1, std::set({kVS1, kVS17, kVS18, kVS19})); const uint32_t kSupportedChar2 = 0x845B; - EXPECT_TRUE(family.getCoverage()->get(kSupportedChar2)); - expectVSGlyphs(&family, kSupportedChar2, std::set({kVS2, kVS18, kVS19, kVS20})); + EXPECT_TRUE(family->getCoverage()->get(kSupportedChar2)); + expectVSGlyphs(family.get(), kSupportedChar2, std::set({kVS2, kVS18, kVS19, kVS20})); const uint32_t kNoVsSupportedChar = 0x537F; - EXPECT_TRUE(family.getCoverage()->get(kNoVsSupportedChar)); - expectVSGlyphs(&family, kNoVsSupportedChar, std::set()); + EXPECT_TRUE(family->getCoverage()->get(kNoVsSupportedChar)); + expectVSGlyphs(family.get(), kNoVsSupportedChar, std::set()); const uint32_t kVsOnlySupportedChar = 0x717D; - EXPECT_FALSE(family.getCoverage()->get(kVsOnlySupportedChar)); - expectVSGlyphs(&family, kVsOnlySupportedChar, std::set({kVS3, kVS19, kVS20})); + EXPECT_FALSE(family->getCoverage()->get(kVsOnlySupportedChar)); + expectVSGlyphs(family.get(), kVsOnlySupportedChar, std::set({kVS3, kVS19, kVS20})); const uint32_t kNotSupportedChar = 0x845C; - EXPECT_FALSE(family.getCoverage()->get(kNotSupportedChar)); - expectVSGlyphs(&family, kNotSupportedChar, std::set()); + EXPECT_FALSE(family->getCoverage()->get(kNotSupportedChar)); + expectVSGlyphs(family.get(), kNotSupportedChar, std::set()); } TEST_F(FontFamilyTest, hasVSTableTest) { @@ -403,12 +403,13 @@ TEST_F(FontFamilyTest, hasVSTableTest) { "Font " + testCase.fontPath + " should have a variation sequence table." : "Font " + testCase.fontPath + " shouldn't have a variation sequence table."); - MinikinFontForTest minikinFont(testCase.fontPath); - FontFamily family; - family.addFont(&minikinFont); - family.getCoverage(); + MinikinAutoUnref minikinFont(new MinikinFontForTest(testCase.fontPath)); + MinikinAutoUnref family(new FontFamily); + family->addFont(minikinFont.get()); + AutoMutex _l(gMinikinLock); + family->getCoverage(); - EXPECT_EQ(testCase.hasVSTable, family.hasVSTable()); + EXPECT_EQ(testCase.hasVSTable, family->hasVSTable()); } } diff --git a/tests/MinikinFontForTest.cpp b/tests/MinikinFontForTest.cpp index 96f3326..66dd4ea 100644 --- a/tests/MinikinFontForTest.cpp +++ b/tests/MinikinFontForTest.cpp @@ -22,8 +22,14 @@ #include -MinikinFontForTest::MinikinFontForTest(const std::string& font_path) : mFontPath(font_path) { - mTypeface = SkTypeface::CreateFromFile(font_path.c_str()); +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() { @@ -55,7 +61,3 @@ const void* MinikinFontForTest::GetTable(uint32_t tag, size_t* size, *destroy = free; return buf; } - -int32_t MinikinFontForTest::GetUniqueId() const { - return mTypeface->uniqueID(); -} diff --git a/tests/MinikinFontForTest.h b/tests/MinikinFontForTest.h index 4686f7a..e527d21 100644 --- a/tests/MinikinFontForTest.h +++ b/tests/MinikinFontForTest.h @@ -24,6 +24,7 @@ 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. @@ -31,7 +32,6 @@ public: 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); - int32_t GetUniqueId() const; const std::string& fontPath() const { return mFontPath; } private: diff --git a/tests/how_to_run.txt b/tests/how_to_run.txt index a135c30..bee367b 100644 --- a/tests/how_to_run.txt +++ b/tests/how_to_run.txt @@ -1,5 +1,5 @@ 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/data && +adb push frameworks/minikin/tests/data /data/nativetest/minikin_tests/ && adb shell /data/nativetest/minikin_tests/minikin_tests -- cgit v1.2.3