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