/* * 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('B', 'o', 'p', 'o'): subScriptBits = kBopomofoFlag; break; case SCRIPT_TAG('H', 'a', 'n', 'g'): subScriptBits = kHangulFlag; break; case SCRIPT_TAG('H', 'a', 'n', 'b'): // Bopomofo is almost exclusively used in Taiwan. subScriptBits = kHanFlag | kBopomofoFlag; 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('Z', 's', 'y', '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; } // static bool FontLanguage::supportsScript(uint8_t providedBits, uint8_t requestedBits) { return requestedBits != 0 && (providedBits & requestedBits) == requestedBits; } 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; return supportsScript(mSubScriptBits, scriptToSubScriptBits(script)); } int FontLanguage::calcScoreFor(const FontLanguages& supported) const { int score = 0; for (size_t i = 0; i < supported.size(); ++i) { if (isEqualScript(supported[i]) || supportsScript(supported[i].mSubScriptBits, mSubScriptBits)) { if (mLanguage == supported[i].mLanguage) { return 2; } else { score = 1; } } } if (score == 1) { return score; } if (supportsScript(supported.getUnionOfSubScriptBits(), mSubScriptBits)) { // Gives score of 2 only if the language matches all of the font languages except for the // exact match case handled above. return (mLanguage == supported[0].mLanguage && supported.isAllTheSameLanguage()) ? 2 : 1; } return 0; } FontLanguages::FontLanguages(std::vector&& languages) : mLanguages(std::move(languages)) { if (mLanguages.empty()) { return; } const FontLanguage& lang = mLanguages[0]; mIsAllTheSameLanguage = true; mUnionOfSubScriptBits = lang.mSubScriptBits; for (size_t i = 1; i < mLanguages.size(); ++i) { mUnionOfSubScriptBits |= mLanguages[i].mSubScriptBits; if (mIsAllTheSameLanguage && lang.mLanguage != mLanguages[i].mLanguage) { mIsAllTheSameLanguage = false; } } } #undef SCRIPT_TAG } // namespace android