1 /*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "Minikin"
18
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <log/log.h>
24 #include <utils/JenkinsHash.h>
25
26 #include <hb.h>
27 #include <hb-ot.h>
28
29 #include "FontLanguage.h"
30 #include "FontLanguageListCache.h"
31 #include "FontUtils.h"
32 #include "HbFontCache.h"
33 #include "MinikinInternal.h"
34 #include <minikin/CmapCoverage.h>
35 #include <minikin/MinikinFont.h>
36 #include <minikin/FontFamily.h>
37 #include <minikin/MinikinFont.h>
38
39 using std::vector;
40
41 namespace minikin {
42
FontStyle(int variant,int weight,bool italic)43 FontStyle::FontStyle(int variant, int weight, bool italic)
44 : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
45 }
46
FontStyle(uint32_t languageListId,int variant,int weight,bool italic)47 FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic)
48 : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
49 }
50
hash() const51 android::hash_t FontStyle::hash() const {
52 uint32_t hash = android::JenkinsHashMix(0, bits);
53 hash = android::JenkinsHashMix(hash, mLanguageListId);
54 return android::JenkinsHashWhiten(hash);
55 }
56
57 // static
registerLanguageList(const std::string & languages)58 uint32_t FontStyle::registerLanguageList(const std::string& languages) {
59 android::AutoMutex _l(gMinikinLock);
60 return FontLanguageListCache::getId(languages);
61 }
62
63 // static
pack(int variant,int weight,bool italic)64 uint32_t FontStyle::pack(int variant, int weight, bool italic) {
65 return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
66 }
67
Font(const std::shared_ptr<MinikinFont> & typeface,FontStyle style)68 Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
69 : typeface(typeface), style(style) {
70 }
71
Font(std::shared_ptr<MinikinFont> && typeface,FontStyle style)72 Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
73 : typeface(typeface), style(style) {
74 }
75
getSupportedAxesLocked() const76 std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
77 const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
78 HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
79 if (fvarTable.size() == 0) {
80 return std::unordered_set<AxisTag>();
81 }
82
83 std::unordered_set<AxisTag> supportedAxes;
84 analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
85 return supportedAxes;
86 }
87
Font(Font && o)88 Font::Font(Font&& o) {
89 typeface = std::move(o.typeface);
90 style = o.style;
91 o.typeface = nullptr;
92 }
93
Font(const Font & o)94 Font::Font(const Font& o) {
95 typeface = o.typeface;
96 style = o.style;
97 }
98
99 // static
FontFamily(std::vector<Font> && fonts)100 FontFamily::FontFamily(std::vector<Font>&& fonts) : FontFamily(0 /* variant */, std::move(fonts)) {
101 }
102
FontFamily(int variant,std::vector<Font> && fonts)103 FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
104 : FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) {
105 }
106
FontFamily(uint32_t langId,int variant,std::vector<Font> && fonts)107 FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
108 : mLangId(langId), mVariant(variant), mFonts(std::move(fonts)) {
109 computeCoverage();
110 }
111
analyzeStyle(const std::shared_ptr<MinikinFont> & typeface,int * weight,bool * italic)112 bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
113 bool* italic) {
114 android::AutoMutex _l(gMinikinLock);
115 const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
116 HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
117 if (os2Table.get() == nullptr) return false;
118 return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight, italic);
119 }
120
121 // Compute a matching metric between two styles - 0 is an exact match
computeMatch(FontStyle style1,FontStyle style2)122 static int computeMatch(FontStyle style1, FontStyle style2) {
123 if (style1 == style2) return 0;
124 int score = abs(style1.getWeight() - style2.getWeight());
125 if (style1.getItalic() != style2.getItalic()) {
126 score += 2;
127 }
128 return score;
129 }
130
computeFakery(FontStyle wanted,FontStyle actual)131 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
132 // If desired weight is semibold or darker, and 2 or more grades
133 // higher than actual (for example, medium 500 -> bold 700), then
134 // select fake bold.
135 int wantedWeight = wanted.getWeight();
136 bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
137 bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
138 return FontFakery(isFakeBold, isFakeItalic);
139 }
140
getClosestMatch(FontStyle style) const141 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
142 const Font* bestFont = nullptr;
143 int bestMatch = 0;
144 for (size_t i = 0; i < mFonts.size(); i++) {
145 const Font& font = mFonts[i];
146 int match = computeMatch(font.style, style);
147 if (i == 0 || match < bestMatch) {
148 bestFont = &font;
149 bestMatch = match;
150 }
151 }
152 if (bestFont != nullptr) {
153 return FakedFont{ bestFont->typeface.get(), computeFakery(style, bestFont->style) };
154 }
155 return FakedFont{ nullptr, FontFakery() };
156 }
157
isColorEmojiFamily() const158 bool FontFamily::isColorEmojiFamily() const {
159 const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
160 for (size_t i = 0; i < languageList.size(); ++i) {
161 if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
162 return true;
163 }
164 }
165 return false;
166 }
167
computeCoverage()168 void FontFamily::computeCoverage() {
169 android::AutoMutex _l(gMinikinLock);
170 const FontStyle defaultStyle;
171 const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
172 const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
173 HbBlob cmapTable(getFontTable(typeface, cmapTag));
174 if (cmapTable.get() == nullptr) {
175 ALOGE("Could not get cmap table size!\n");
176 return;
177 }
178 mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
179
180 for (size_t i = 0; i < mFonts.size(); ++i) {
181 std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxesLocked();
182 mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
183 }
184 }
185
hasGlyph(uint32_t codepoint,uint32_t variationSelector) const186 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
187 if (variationSelector == 0) {
188 return mCoverage.get(codepoint);
189 }
190
191 if (mCmapFmt14Coverage.empty()) {
192 return false;
193 }
194
195 const uint16_t vsIndex = getVsIndex(variationSelector);
196
197 if (vsIndex >= mCmapFmt14Coverage.size()) {
198 // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
199 // be at the maximum end of the range.
200 return false;
201 }
202
203 const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
204 if (bitset.get() == nullptr) {
205 return false;
206 }
207
208 return bitset->get(codepoint);
209 }
210
createFamilyWithVariation(const std::vector<FontVariation> & variations) const211 std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
212 const std::vector<FontVariation>& variations) const {
213 if (variations.empty() || mSupportedAxes.empty()) {
214 return nullptr;
215 }
216
217 bool hasSupportedAxis = false;
218 for (const FontVariation& variation : variations) {
219 if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
220 hasSupportedAxis = true;
221 break;
222 }
223 }
224 if (!hasSupportedAxis) {
225 // None of variation axes are suppored by this family.
226 return nullptr;
227 }
228
229 std::vector<Font> fonts;
230 for (const Font& font : mFonts) {
231 bool supportedVariations = false;
232 android::AutoMutex _l(gMinikinLock);
233 std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
234 if (!supportedAxes.empty()) {
235 for (const FontVariation& variation : variations) {
236 if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
237 supportedVariations = true;
238 break;
239 }
240 }
241 }
242 std::shared_ptr<MinikinFont> minikinFont;
243 if (supportedVariations) {
244 minikinFont = font.typeface->createFontWithVariation(variations);
245 }
246 if (minikinFont == nullptr) {
247 minikinFont = font.typeface;
248 }
249 fonts.push_back(Font(std::move(minikinFont), font.style));
250 }
251
252 return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
253 }
254
255 } // namespace minikin
256