16d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka/* 26d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * Copyright (C) 2015 The Android Open Source Project 36d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * 46d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License"); 56d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * you may not use this file except in compliance with the License. 66d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * You may obtain a copy of the License at 76d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * 86d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * http://www.apache.org/licenses/LICENSE-2.0 96d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * 106d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * Unless required by applicable law or agreed to in writing, software 116d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS, 126d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * See the License for the specific language governing permissions and 146d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka * limitations under the License. 156d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka */ 166d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 176d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#define LOG_TAG "Minikin" 186d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 196d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include "FontLanguageListCache.h" 206d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 216d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include <cutils/log.h> 22198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#include <unicode/uloc.h> 23198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#include <unordered_set> 246d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 256d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include "MinikinInternal.h" 26198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#include "FontLanguage.h" 276d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 286d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakanamespace android { 296d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 306d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakaconst uint32_t FontLanguageListCache::kEmptyListId; 316d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 32198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka// Returns the text length of output. 33198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonakastatic size_t toLanguageTag(char* output, size_t outSize, const std::string& locale) { 34198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka output[0] = '\0'; 35198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (locale.empty()) { 36198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return 0; 37198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 38198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 39198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka size_t outLength = 0; 40198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka UErrorCode uErr = U_ZERO_ERROR; 41198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka outLength = uloc_canonicalize(locale.c_str(), output, outSize, &uErr); 42198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (U_FAILURE(uErr)) { 43198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // unable to build a proper language identifier 44198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale.c_str(), u_errorName(uErr)); 45198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka output[0] = '\0'; 46198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return 0; 47198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 48198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 49198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // Preserve "und" and "und-****" since uloc_addLikelySubtags changes "und" to "en-Latn-US". 50198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (strncmp(output, "und", 3) == 0 && 51198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka (outLength == 3 || (outLength == 8 && output[3] == '_'))) { 52198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return outLength; 53198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 54198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 55198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka char likelyChars[ULOC_FULLNAME_CAPACITY]; 56198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka uErr = U_ZERO_ERROR; 57198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka uloc_addLikelySubtags(output, likelyChars, ULOC_FULLNAME_CAPACITY, &uErr); 58198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (U_FAILURE(uErr)) { 59198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // unable to build a proper language identifier 60198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", output, u_errorName(uErr)); 61198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka output[0] = '\0'; 62198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return 0; 63198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 64198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 65198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka uErr = U_ZERO_ERROR; 66198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka outLength = uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr); 67198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (U_FAILURE(uErr)) { 68198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // unable to build a proper language identifier 69198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, u_errorName(uErr)); 70198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka output[0] = '\0'; 71198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return 0; 72198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 73198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#ifdef VERBOSE_DEBUG 74198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka ALOGD("ICU normalized '%s' to '%s'", locale.c_str(), output); 75198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#endif 76198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return outLength; 77198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka} 78198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 79f3afe92def0fff022889fd036d68451223aac146Seigo Nonakastatic std::vector<FontLanguage> parseLanguageList(const std::string& input) { 80f3afe92def0fff022889fd036d68451223aac146Seigo Nonaka std::vector<FontLanguage> result; 81198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka size_t currentIdx = 0; 82198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka size_t commaLoc = 0; 83198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka char langTag[ULOC_FULLNAME_CAPACITY]; 84198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka std::unordered_set<uint64_t> seen; 85198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka std::string locale(input.size(), 0); 86198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 87198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka while ((commaLoc = input.find_first_of(',', currentIdx)) != std::string::npos) { 88198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka locale.assign(input, currentIdx, commaLoc - currentIdx); 89198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka currentIdx = commaLoc + 1; 90198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); 91198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka FontLanguage lang(langTag, length); 92198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka uint64_t identifier = lang.getIdentifier(); 93198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (!lang.isUnsupported() && seen.count(identifier) == 0) { 94198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka result.push_back(lang); 956f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka if (result.size() == FONT_LANGUAGES_LIMIT) { 966f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka break; 976f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka } 98198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka seen.insert(identifier); 99198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 100198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 1016f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka if (result.size() < FONT_LANGUAGES_LIMIT) { 1026f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka locale.assign(input, currentIdx, input.size() - currentIdx); 1036f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); 1046f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka FontLanguage lang(langTag, length); 1056f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka uint64_t identifier = lang.getIdentifier(); 1066f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka if (!lang.isUnsupported() && seen.count(identifier) == 0) { 1076f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka result.push_back(lang); 1086f9966ea7c1910fd780cf7779cc59701c9b98a2bSeigo Nonaka } 109198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 110198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return result; 111198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka} 112198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 1136d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka// static 1146d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakauint32_t FontLanguageListCache::getId(const std::string& languages) { 1156d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka FontLanguageListCache* inst = FontLanguageListCache::getInstance(); 1166d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka std::unordered_map<std::string, uint32_t>::const_iterator it = 1176d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka inst->mLanguageListLookupTable.find(languages); 1186d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka if (it != inst->mLanguageListLookupTable.end()) { 1196d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return it->second; 1206d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka } 1216d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 1226d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka // Given language list is not in cache. Insert it and return newly assigned ID. 1236d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka const uint32_t nextId = inst->mLanguageLists.size(); 124f3afe92def0fff022889fd036d68451223aac146Seigo Nonaka FontLanguages fontLanguages(parseLanguageList(languages)); 125198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka if (fontLanguages.empty()) { 126198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka return kEmptyListId; 127198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka } 128f3afe92def0fff022889fd036d68451223aac146Seigo Nonaka inst->mLanguageLists.push_back(std::move(fontLanguages)); 1296d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId)); 1306d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return nextId; 1316d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 1326d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 1336d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka// static 1346d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakaconst FontLanguages& FontLanguageListCache::getById(uint32_t id) { 1356d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka FontLanguageListCache* inst = FontLanguageListCache::getInstance(); 1366d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka LOG_ALWAYS_FATAL_IF(id >= inst->mLanguageLists.size(), "Lookup by unknown language list ID."); 1376d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return inst->mLanguageLists[id]; 1386d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 1396d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 1406d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka// static 1416d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo NonakaFontLanguageListCache* FontLanguageListCache::getInstance() { 1426d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka assertMinikinLocked(); 1436d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka static FontLanguageListCache* instance = nullptr; 1446d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka if (instance == nullptr) { 1456d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka instance = new FontLanguageListCache(); 1466d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 147198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // Insert an empty language list for mapping default language list to kEmptyListId. 148198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka // The default language list has only one FontLanguage and it is the unsupported language. 149f3afe92def0fff022889fd036d68451223aac146Seigo Nonaka instance->mLanguageLists.push_back(FontLanguages()); 1506d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId)); 1516d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka } 1526d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return instance; 1536d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 1546d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 1556d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} // namespace android 156