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 <cutils/log.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include <hb.h>
25 #include <hb-ot.h>
26
27 #include <utils/JenkinsHash.h>
28
29 #include "FontLanguage.h"
30 #include "FontLanguageListCache.h"
31 #include "HbFontCache.h"
32 #include "MinikinInternal.h"
33 #include <minikin/MinikinFont.h>
34 #include <minikin/AnalyzeStyle.h>
35 #include <minikin/CmapCoverage.h>
36 #include <minikin/FontFamily.h>
37 #include <UniquePtr.h>
38
39 using std::vector;
40
41 namespace android {
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 hash_t FontStyle::hash() const {
52 uint32_t hash = JenkinsHashMix(0, bits);
53 hash = JenkinsHashMix(hash, mLanguageListId);
54 return JenkinsHashWhiten(hash);
55 }
56
57 // static
registerLanguageList(const std::string & languages)58 uint32_t FontStyle::registerLanguageList(const std::string& languages) {
59 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
FontFamily()68 FontFamily::FontFamily() : FontFamily(0 /* variant */) {
69 }
70
FontFamily(int variant)71 FontFamily::FontFamily(int variant) : FontFamily(FontLanguageListCache::kEmptyListId, variant) {
72 }
73
~FontFamily()74 FontFamily::~FontFamily() {
75 for (size_t i = 0; i < mFonts.size(); i++) {
76 mFonts[i].typeface->UnrefLocked();
77 }
78 }
79
addFont(MinikinFont * typeface)80 bool FontFamily::addFont(MinikinFont* typeface) {
81 AutoMutex _l(gMinikinLock);
82 const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
83 HbBlob os2Table(getFontTable(typeface, os2Tag));
84 if (os2Table.get() == nullptr) return false;
85 int weight;
86 bool italic;
87 if (analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
88 //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
89 FontStyle style(weight, italic);
90 addFontLocked(typeface, style);
91 return true;
92 } else {
93 ALOGD("failed to analyze style");
94 }
95 return false;
96 }
97
addFont(MinikinFont * typeface,FontStyle style)98 void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
99 AutoMutex _l(gMinikinLock);
100 addFontLocked(typeface, style);
101 }
102
addFontLocked(MinikinFont * typeface,FontStyle style)103 void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) {
104 typeface->RefLocked();
105 mFonts.push_back(Font(typeface, style));
106 mCoverageValid = false;
107 }
108
109 // Compute a matching metric between two styles - 0 is an exact match
computeMatch(FontStyle style1,FontStyle style2)110 static int computeMatch(FontStyle style1, FontStyle style2) {
111 if (style1 == style2) return 0;
112 int score = abs(style1.getWeight() - style2.getWeight());
113 if (style1.getItalic() != style2.getItalic()) {
114 score += 2;
115 }
116 return score;
117 }
118
computeFakery(FontStyle wanted,FontStyle actual)119 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
120 // If desired weight is semibold or darker, and 2 or more grades
121 // higher than actual (for example, medium 500 -> bold 700), then
122 // select fake bold.
123 int wantedWeight = wanted.getWeight();
124 bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
125 bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
126 return FontFakery(isFakeBold, isFakeItalic);
127 }
128
getClosestMatch(FontStyle style) const129 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
130 const Font* bestFont = NULL;
131 int bestMatch = 0;
132 for (size_t i = 0; i < mFonts.size(); i++) {
133 const Font& font = mFonts[i];
134 int match = computeMatch(font.style, style);
135 if (i == 0 || match < bestMatch) {
136 bestFont = &font;
137 bestMatch = match;
138 }
139 }
140 FakedFont result;
141 if (bestFont == NULL) {
142 result.font = NULL;
143 } else {
144 result.font = bestFont->typeface;
145 result.fakery = computeFakery(style, bestFont->style);
146 }
147 return result;
148 }
149
getNumFonts() const150 size_t FontFamily::getNumFonts() const {
151 return mFonts.size();
152 }
153
getFont(size_t index) const154 MinikinFont* FontFamily::getFont(size_t index) const {
155 return mFonts[index].typeface;
156 }
157
getStyle(size_t index) const158 FontStyle FontFamily::getStyle(size_t index) const {
159 return mFonts[index].style;
160 }
161
isColorEmojiFamily() const162 bool FontFamily::isColorEmojiFamily() const {
163 const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
164 for (size_t i = 0; i < languageList.size(); ++i) {
165 if (languageList[i].hasEmojiFlag()) {
166 return true;
167 }
168 }
169 return false;
170 }
171
getCoverage()172 const SparseBitSet* FontFamily::getCoverage() {
173 if (!mCoverageValid) {
174 const FontStyle defaultStyle;
175 MinikinFont* typeface = getClosestMatch(defaultStyle).font;
176 const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
177 HbBlob cmapTable(getFontTable(typeface, cmapTag));
178 if (cmapTable.get() == nullptr) {
179 ALOGE("Could not get cmap table size!\n");
180 // Note: This means we will retry on the next call to getCoverage, as we can't store
181 // the failure. This is fine, as we assume this doesn't really happen in practice.
182 return nullptr;
183 }
184 // TODO: Error check?
185 CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable);
186 #ifdef VERBOSE_DEBUG
187 ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(),
188 mCoverage.nextSetBit(0));
189 #endif
190 mCoverageValid = true;
191 }
192 return &mCoverage;
193 }
194
hasGlyph(uint32_t codepoint,uint32_t variationSelector)195 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) {
196 assertMinikinLocked();
197 if (variationSelector != 0 && !mHasVSTable) {
198 // Early exit if the variation selector is specified but the font doesn't have a cmap format
199 // 14 subtable.
200 return false;
201 }
202
203 const FontStyle defaultStyle;
204 MinikinFont* minikinFont = getClosestMatch(defaultStyle).font;
205 hb_font_t* font = getHbFontLocked(minikinFont);
206 uint32_t unusedGlyph;
207 bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
208 hb_font_destroy(font);
209 return result;
210 }
211
hasVSTable() const212 bool FontFamily::hasVSTable() const {
213 LOG_ALWAYS_FATAL_IF(!mCoverageValid, "Do not call this method before getCoverage() call");
214 return mHasVSTable;
215 }
216
217 } // namespace android
218