/* * 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 "FontLanguageListCache.h" #include #include #include #include "MinikinInternal.h" #include "FontLanguage.h" namespace android { const uint32_t FontLanguageListCache::kEmptyListId; // Returns the text length of output. static size_t toLanguageTag(char* output, size_t outSize, const std::string& locale) { output[0] = '\0'; if (locale.empty()) { return 0; } size_t outLength = 0; UErrorCode uErr = U_ZERO_ERROR; outLength = uloc_canonicalize(locale.c_str(), output, outSize, &uErr); if (U_FAILURE(uErr)) { // unable to build a proper language identifier ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale.c_str(), u_errorName(uErr)); output[0] = '\0'; return 0; } // Preserve "und" and "und-****" since uloc_addLikelySubtags changes "und" to "en-Latn-US". if (strncmp(output, "und", 3) == 0 && (outLength == 3 || (outLength == 8 && output[3] == '_'))) { return outLength; } char likelyChars[ULOC_FULLNAME_CAPACITY]; uErr = U_ZERO_ERROR; uloc_addLikelySubtags(output, likelyChars, ULOC_FULLNAME_CAPACITY, &uErr); if (U_FAILURE(uErr)) { // unable to build a proper language identifier ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", output, u_errorName(uErr)); output[0] = '\0'; return 0; } uErr = U_ZERO_ERROR; outLength = uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr); if (U_FAILURE(uErr)) { // unable to build a proper language identifier ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, u_errorName(uErr)); output[0] = '\0'; return 0; } #ifdef VERBOSE_DEBUG ALOGD("ICU normalized '%s' to '%s'", locale.c_str(), output); #endif return outLength; } static std::vector parseLanguageList(const std::string& input) { std::vector result; size_t currentIdx = 0; size_t commaLoc = 0; char langTag[ULOC_FULLNAME_CAPACITY]; std::unordered_set seen; std::string locale(input.size(), 0); while ((commaLoc = input.find_first_of(',', currentIdx)) != std::string::npos) { locale.assign(input, currentIdx, commaLoc - currentIdx); currentIdx = commaLoc + 1; size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); FontLanguage lang(langTag, length); uint64_t identifier = lang.getIdentifier(); if (!lang.isUnsupported() && seen.count(identifier) == 0) { result.push_back(lang); if (result.size() == FONT_LANGUAGES_LIMIT) { break; } seen.insert(identifier); } } if (result.size() < FONT_LANGUAGES_LIMIT) { 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(); std::unordered_map::const_iterator it = inst->mLanguageListLookupTable.find(languages); if (it != inst->mLanguageListLookupTable.end()) { return it->second; } // Given language list is not in cache. Insert it and return newly assigned ID. const uint32_t nextId = inst->mLanguageLists.size(); FontLanguages fontLanguages(parseLanguageList(languages)); if (fontLanguages.empty()) { return kEmptyListId; } inst->mLanguageLists.push_back(std::move(fontLanguages)); inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId)); return nextId; } // static const FontLanguages& FontLanguageListCache::getById(uint32_t id) { FontLanguageListCache* inst = FontLanguageListCache::getInstance(); LOG_ALWAYS_FATAL_IF(id >= inst->mLanguageLists.size(), "Lookup by unknown language list ID."); return inst->mLanguageLists[id]; } // static FontLanguageListCache* FontLanguageListCache::getInstance() { assertMinikinLocked(); static FontLanguageListCache* instance = nullptr; 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()); instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId)); } return instance; } } // namespace android