19cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien/* 29cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Copyright (C) 2013 The Android Open Source Project 39cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * 49cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Licensed under the Apache License, Version 2.0 (the "License"); 59cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * you may not use this file except in compliance with the License. 69cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * You may obtain a copy of the License at 79cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * 89cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * http://www.apache.org/licenses/LICENSE-2.0 99cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * 109cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Unless required by applicable law or agreed to in writing, software 119cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * distributed under the License is distributed on an "AS IS" BASIS, 129cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * See the License for the specific language governing permissions and 149cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * limitations under the License. 159cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien */ 169cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 179cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#define LOG_TAG "Minikin" 189cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 199cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <cutils/log.h> 209cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <stdlib.h> 219cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <stdint.h> 224b723bf22b1bbc5fcdaa9bd96178c921199905d5Roozbeh Pournader#include <string.h> 23c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien 240f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka#include <hb.h> 250f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka#include <hb-ot.h> 260f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka 276d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include <utils/JenkinsHash.h> 286d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 29198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#include "FontLanguage.h" 306d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include "FontLanguageListCache.h" 3189e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka#include "HbFontCache.h" 32c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien#include "MinikinInternal.h" 33bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien#include <minikin/MinikinFont.h> 349cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <minikin/AnalyzeStyle.h> 3513f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien#include <minikin/CmapCoverage.h> 369cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <minikin/FontFamily.h> 371279a3bf5ec6131efefbc51d52d24850fd81f676Kenny Root#include <UniquePtr.h> 389cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 399cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienusing std::vector; 409cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 419cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Leviennamespace android { 429cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 436d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo NonakaFontStyle::FontStyle(int variant, int weight, bool italic) 446d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) { 456d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 466d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 476d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo NonakaFontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic) 486d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) { 496d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 506d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 516d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakahash_t FontStyle::hash() const { 526d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka uint32_t hash = JenkinsHashMix(0, bits); 536d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka hash = JenkinsHashMix(hash, mLanguageListId); 546d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return JenkinsHashWhiten(hash); 556d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 566d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 576d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka// static 586d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakauint32_t FontStyle::registerLanguageList(const std::string& languages) { 596d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka AutoMutex _l(gMinikinLock); 606d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return FontLanguageListCache::getId(languages); 616d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 626d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 636d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka// static 646d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakauint32_t FontStyle::pack(int variant, int weight, bool italic) { 656d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift); 666d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka} 676d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka 68994aa84f7b18466806fe552ea57da1852b909f24Seigo NonakaFontFamily::FontFamily() : FontFamily(0 /* variant */) { 69994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka} 70994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka 71198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo NonakaFontFamily::FontFamily(int variant) : FontFamily(FontLanguageListCache::kEmptyListId, variant) { 72198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka} 73198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka 74b80c1f19c58b927820a8a24bf2218e5645724608Raph LevienFontFamily::~FontFamily() { 75b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien for (size_t i = 0; i < mFonts.size(); i++) { 76b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien mFonts[i].typeface->UnrefLocked(); 77b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien } 78b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien} 79b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien 80bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levienbool FontFamily::addFont(MinikinFont* typeface) { 81c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien AutoMutex _l(gMinikinLock); 82bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2'); 83aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien HbBlob os2Table(getFontTable(typeface, os2Tag)); 84aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien if (os2Table.get() == nullptr) return false; 859cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien int weight; 869cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien bool italic; 87aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien if (analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) { 889cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false"); 899cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien FontStyle style(weight, italic); 90c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien addFontLocked(typeface, style); 919cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return true; 929cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien } else { 939cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien ALOGD("failed to analyze style"); 949cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien } 959cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return false; 969cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 979cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 98bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levienvoid FontFamily::addFont(MinikinFont* typeface, FontStyle style) { 99c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien AutoMutex _l(gMinikinLock); 100c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien addFontLocked(typeface, style); 101c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien} 102c31e3883456e018d742e9f29815ba5ff8b315ea1Raph Levien 103198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonakavoid FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { 104198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka typeface->RefLocked(); 1059cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien mFonts.push_back(Font(typeface, style)); 10613f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien mCoverageValid = false; 1079cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1089cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 1099cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien// Compute a matching metric between two styles - 0 is an exact match 1109a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levienstatic int computeMatch(FontStyle style1, FontStyle style2) { 1119cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien if (style1 == style2) return 0; 1129cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien int score = abs(style1.getWeight() - style2.getWeight()); 1139cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien if (style1.getItalic() != style2.getItalic()) { 1149cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien score += 2; 1159cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien } 1169cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return score; 1179cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1189cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 1199a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levienstatic FontFakery computeFakery(FontStyle wanted, FontStyle actual) { 120d5804e3937a961736e5cef0e8a70eacf91ee00bbRaph Levien // If desired weight is semibold or darker, and 2 or more grades 121d5804e3937a961736e5cef0e8a70eacf91ee00bbRaph Levien // higher than actual (for example, medium 500 -> bold 700), then 122d5804e3937a961736e5cef0e8a70eacf91ee00bbRaph Levien // select fake bold. 1239f9f3b1ef40f7358dca6acd9dfef686cedefb6aaRaph Levien int wantedWeight = wanted.getWeight(); 124d5804e3937a961736e5cef0e8a70eacf91ee00bbRaph Levien bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2; 1259a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien bool isFakeItalic = wanted.getItalic() && !actual.getItalic(); 1269a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien return FontFakery(isFakeBold, isFakeItalic); 1279a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien} 1289a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien 1299a5f713add8cfb91ac2c9ed5c917309053201ab6Raph LevienFakedFont FontFamily::getClosestMatch(FontStyle style) const { 1309cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien const Font* bestFont = NULL; 1319cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien int bestMatch = 0; 1329cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien for (size_t i = 0; i < mFonts.size(); i++) { 1339cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien const Font& font = mFonts[i]; 1349cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien int match = computeMatch(font.style, style); 1359cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien if (i == 0 || match < bestMatch) { 1369cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien bestFont = &font; 1379cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien bestMatch = match; 1389cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien } 1399cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien } 1409a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien FakedFont result; 1419a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien if (bestFont == NULL) { 1429a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien result.font = NULL; 1439a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien } else { 1449a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien result.font = bestFont->typeface; 1459a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien result.fakery = computeFakery(style, bestFont->style); 1469a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien } 1479a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien return result; 1489cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1499cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 1509cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Leviensize_t FontFamily::getNumFonts() const { 1519cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return mFonts.size(); 1529cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1539cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 154bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph LevienMinikinFont* FontFamily::getFont(size_t index) const { 1559cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return mFonts[index].typeface; 1569cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1579cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 1589cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph LevienFontStyle FontFamily::getStyle(size_t index) const { 1599cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien return mFonts[index].style; 1609cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} 1619cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien 162994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonakabool FontFamily::isColorEmojiFamily() const { 163994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka const FontLanguages& languageList = FontLanguageListCache::getById(mLangId); 164994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka for (size_t i = 0; i < languageList.size(); ++i) { 165994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka if (languageList[i].hasEmojiFlag()) { 166994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka return true; 167994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka } 168994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka } 169994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka return false; 170994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka} 171994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka 17213f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levienconst SparseBitSet* FontFamily::getCoverage() { 17313f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien if (!mCoverageValid) { 17413f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien const FontStyle defaultStyle; 17513f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien MinikinFont* typeface = getClosestMatch(defaultStyle).font; 17613f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p'); 177aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien HbBlob cmapTable(getFontTable(typeface, cmapTag)); 178aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien if (cmapTable.get() == nullptr) { 179cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe ALOGE("Could not get cmap table size!\n"); 180cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe // Note: This means we will retry on the next call to getCoverage, as we can't store 181cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe // the failure. This is fine, as we assume this doesn't really happen in practice. 182cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe return nullptr; 183cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe } 1846b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka // TODO: Error check? 185aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable); 18613f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien#ifdef VERBOSE_DEBUG 187bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(), 188bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka mCoverage.nextSetBit(0)); 18913f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien#endif 19013f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien mCoverageValid = true; 19113f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien } 19213f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien return &mCoverage; 19313f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien} 19413f1aae02bacd475722bc8ea3fc2cf6abc1a82e3Raph Levien 195994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonakabool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) { 1960f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka assertMinikinLocked(); 197994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka if (variationSelector != 0 && !mHasVSTable) { 198994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka // Early exit if the variation selector is specified but the font doesn't have a cmap format 199994aa84f7b18466806fe552ea57da1852b909f24Seigo Nonaka // 14 subtable. 2006b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka return false; 2016b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka } 2026b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka 20389e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka const FontStyle defaultStyle; 20489e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka MinikinFont* minikinFont = getClosestMatch(defaultStyle).font; 20589e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka hb_font_t* font = getHbFontLocked(minikinFont); 2060f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka uint32_t unusedGlyph; 207aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph); 208aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien hb_font_destroy(font); 209aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien return result; 2100f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka} 2110f2a025d135f9ca52cc3cf917fffc29d6c126094Seigo Nonaka 2126b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonakabool FontFamily::hasVSTable() const { 2136b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka LOG_ALWAYS_FATAL_IF(!mCoverageValid, "Do not call this method before getCoverage() call"); 2146b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka return mHasVSTable; 2156b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka} 2166b1c227da6492a435f0341d7fe95d9992669920eSeigo Nonaka 2179cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien} // namespace android 218