Revert "Save all kind of script tags into FontLanguage."
This reverts commit bb5c10092c.
Change-Id: I761e0e41906742fbe3d3ac34170af3101e18042a
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 <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
|
||||
@@ -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 <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) {
|
||||
|
||||
@@ -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 <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
|
||||
Reference in New Issue
Block a user