提交 6c4c098c 编写于 作者: B Bart Sears

Revert "Save all kind of script tags into FontLanguage."

This reverts commit 5e995fb8.

Change-Id: I761e0e41906742fbe3d3ac34170af3101e18042a
上级 5e995fb8
......@@ -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<FontLanguage> 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<Font> mFonts;
......
......@@ -21,7 +21,6 @@ minikin_src_files := \
CmapCoverage.cpp \
FontCollection.cpp \
FontFamily.cpp \
FontLanguage.cpp \
FontLanguageListCache.cpp \
GraphemeBreak.cpp \
HbFaceCache.cpp \
......
......@@ -22,7 +22,6 @@
#include "unicode/unistr.h"
#include "unicode/unorm2.h"
#include "FontLanguage.h"
#include "FontLanguageListCache.h"
#include "MinikinInternal.h"
#include <minikin/FontCollection.h>
......@@ -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) {
......
......@@ -16,6 +16,8 @@
#define LOG_TAG "Minikin"
#include <unordered_set>
#include <cutils/log.h>
#include <stdlib.h>
#include <stdint.h>
......@@ -26,7 +28,6 @@
#include <utils/JenkinsHash.h>
#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<uint32_t> seen;
mLangs.clear();
const char* bufEnd = buf + size;
const char* lastStart = buf;
bool isLastLang = false;
while (true) {
const char* commaLoc = static_cast<const char*>(
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;
}
......
/*
* 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 <hb.h>
#include <unicode/uloc.h>
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
/*
* 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 <string>
#include <vector>
#include <hb.h>
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<FontLanguage> FontLanguages;
} // namespace android
#endif // MINIKIN_FONT_LANGUAGE_H
......@@ -19,92 +19,13 @@
#include "FontLanguageListCache.h"
#include <cutils/log.h>
#include <unicode/uloc.h>
#include <unordered_set>
#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<uint64_t> 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;
......
......@@ -20,7 +20,6 @@
#include <unordered_map>
#include <minikin/FontFamily.h>
#include "FontLanguage.h"
namespace android {
......
......@@ -34,7 +34,6 @@
#include <hb-icu.h>
#include <hb-ot.h>
#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) {
......
......@@ -16,9 +16,7 @@
#include <gtest/gtest.h>
#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<FontCollection::Run>* 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> 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<android::FontFamily*> 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<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile);
std::vector<FontCollection::Run> 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<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile);
std::vector<FontCollection::Run> runs;
......
......@@ -17,261 +17,74 @@
#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 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. "
......
......@@ -17,15 +17,11 @@
#include <gtest/gtest.h>
#include <minikin/FontFamily.h>
#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());
......
......@@ -20,8 +20,6 @@
#include <minikin/FontFamily.h>
#include <cutils/log.h>
#include "FontLanguage.h"
#include "MinikinFontForTest.h"
std::unique_ptr<android::FontCollection> getFontCollection(
......@@ -46,10 +44,9 @@ std::unique_ptr<android::FontCollection> 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) {
......
/*
* 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 <gtest/gtest.h>
#include <unicode/uclean.h>
#include <unicode/udata.h>
// low level file access for mapping ICU data
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册