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 "minikin/FontFamily.h"
20 
21 #include <cstdint>
22 #include <vector>
23 
24 #include <hb-ot.h>
25 #include <hb.h>
26 #include <log/log.h>
27 
28 #include "minikin/CmapCoverage.h"
29 #include "minikin/FamilyVariant.h"
30 #include "minikin/HbUtils.h"
31 #include "minikin/MinikinFont.h"
32 
33 #include "FontUtils.h"
34 #include "Locale.h"
35 #include "LocaleListCache.h"
36 #include "MinikinInternal.h"
37 
38 namespace minikin {
39 
build()40 Font Font::Builder::build() {
41     if (mIsWeightSet && mIsSlantSet) {
42         // No need to read OS/2 header of the font file.
43         return Font(std::move(mTypeface), FontStyle(mWeight, mSlant), prepareFont(mTypeface));
44     }
45 
46     HbFontUniquePtr font = prepareFont(mTypeface);
47     FontStyle styleFromFont = analyzeStyle(font);
48     if (!mIsWeightSet) {
49         mWeight = styleFromFont.weight();
50     }
51     if (!mIsSlantSet) {
52         mSlant = styleFromFont.slant();
53     }
54     return Font(std::move(mTypeface), FontStyle(mWeight, mSlant), std::move(font));
55 }
56 
57 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)58 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
59     const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
60     size_t size = typeface->GetFontSize();
61     uint32_t ttcIndex = typeface->GetFontIndex();
62 
63     HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
64     HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
65     HbFontUniquePtr parent(hb_font_create(face.get()));
66     hb_ot_font_set_funcs(parent.get());
67 
68     uint32_t upem = hb_face_get_upem(face.get());
69     hb_font_set_scale(parent.get(), upem, upem);
70 
71     HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
72     std::vector<hb_variation_t> variations;
73     variations.reserve(typeface->GetAxes().size());
74     for (const FontVariation& variation : typeface->GetAxes()) {
75         variations.push_back({variation.axisTag, variation.value});
76     }
77     hb_font_set_variations(font.get(), variations.data(), variations.size());
78     return font;
79 }
80 
81 // static
analyzeStyle(const HbFontUniquePtr & font)82 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
83     HbBlob os2Table(font, MinikinFont::MakeTag('O', 'S', '/', '2'));
84     if (!os2Table) {
85         return FontStyle();
86     }
87 
88     int weight;
89     bool italic;
90     if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
91         return FontStyle();
92     }
93     // TODO: Update weight/italic based on fvar value.
94     return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
95 }
96 
getSupportedAxes() const97 std::unordered_set<AxisTag> Font::getSupportedAxes() const {
98     HbBlob fvarTable(mBaseFont, MinikinFont::MakeTag('f', 'v', 'a', 'r'));
99     if (!fvarTable) {
100         return std::unordered_set<AxisTag>();
101     }
102     std::unordered_set<AxisTag> supportedAxes;
103     analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
104     return supportedAxes;
105 }
106 
FontFamily(std::vector<Font> && fonts)107 FontFamily::FontFamily(std::vector<Font>&& fonts)
108         : FontFamily(FamilyVariant::DEFAULT, std::move(fonts)) {}
109 
FontFamily(FamilyVariant variant,std::vector<Font> && fonts)110 FontFamily::FontFamily(FamilyVariant variant, std::vector<Font>&& fonts)
111         : FontFamily(LocaleListCache::kEmptyListId, variant, std::move(fonts),
112                      false /* isCustomFallback */) {}
113 
FontFamily(uint32_t localeListId,FamilyVariant variant,std::vector<Font> && fonts,bool isCustomFallback)114 FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant, std::vector<Font>&& fonts,
115                        bool isCustomFallback)
116         : mLocaleListId(localeListId),
117           mVariant(variant),
118           mFonts(std::move(fonts)),
119           mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
120                         EmojiStyle::EMOJI),
121           mIsCustomFallback(isCustomFallback) {
122     MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
123     computeCoverage();
124 }
125 
126 // Compute a matching metric between two styles - 0 is an exact match
computeMatch(FontStyle style1,FontStyle style2)127 static int computeMatch(FontStyle style1, FontStyle style2) {
128     if (style1 == style2) return 0;
129     int score = abs(style1.weight() / 100 - style2.weight() / 100);
130     if (style1.slant() != style2.slant()) {
131         score += 2;
132     }
133     return score;
134 }
135 
computeFakery(FontStyle wanted,FontStyle actual)136 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
137     // If desired weight is semibold or darker, and 2 or more grades
138     // higher than actual (for example, medium 500 -> bold 700), then
139     // select fake bold.
140     bool isFakeBold = wanted.weight() >= 600 && (wanted.weight() - actual.weight()) >= 200;
141     bool isFakeItalic = wanted.slant() == FontStyle::Slant::ITALIC &&
142                         actual.slant() == FontStyle::Slant::UPRIGHT;
143     return FontFakery(isFakeBold, isFakeItalic);
144 }
145 
getClosestMatch(FontStyle style) const146 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
147     const Font* bestFont = &mFonts[0];
148     int bestMatch = computeMatch(bestFont->style(), style);
149     for (size_t i = 1; i < mFonts.size(); i++) {
150         const Font& font = mFonts[i];
151         int match = computeMatch(font.style(), style);
152         if (i == 0 || match < bestMatch) {
153             bestFont = &font;
154             bestMatch = match;
155         }
156     }
157     return FakedFont{bestFont, computeFakery(style, bestFont->style())};
158 }
159 
computeCoverage()160 void FontFamily::computeCoverage() {
161     const Font* font = getClosestMatch(FontStyle()).font;
162     HbBlob cmapTable(font->baseFont(), MinikinFont::MakeTag('c', 'm', 'a', 'p'));
163     if (cmapTable.get() == nullptr) {
164         ALOGE("Could not get cmap table size!\n");
165         return;
166     }
167 
168     mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
169 
170     for (size_t i = 0; i < mFonts.size(); ++i) {
171         std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxes();
172         mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
173     }
174 }
175 
hasGlyph(uint32_t codepoint,uint32_t variationSelector) const176 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
177     if (variationSelector == 0) {
178         return mCoverage.get(codepoint);
179     }
180 
181     if (mCmapFmt14Coverage.empty()) {
182         return false;
183     }
184 
185     const uint16_t vsIndex = getVsIndex(variationSelector);
186 
187     if (vsIndex >= mCmapFmt14Coverage.size()) {
188         // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
189         // be at the maximum end of the range.
190         return false;
191     }
192 
193     const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
194     if (bitset.get() == nullptr) {
195         return false;
196     }
197 
198     return bitset->get(codepoint);
199 }
200 
createFamilyWithVariation(const std::vector<FontVariation> & variations) const201 std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
202         const std::vector<FontVariation>& variations) const {
203     if (variations.empty() || mSupportedAxes.empty()) {
204         return nullptr;
205     }
206 
207     bool hasSupportedAxis = false;
208     for (const FontVariation& variation : variations) {
209         if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
210             hasSupportedAxis = true;
211             break;
212         }
213     }
214     if (!hasSupportedAxis) {
215         // None of variation axes are suppored by this family.
216         return nullptr;
217     }
218 
219     std::vector<Font> fonts;
220     for (const Font& font : mFonts) {
221         bool supportedVariations = false;
222         std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxes();
223         if (!supportedAxes.empty()) {
224             for (const FontVariation& variation : variations) {
225                 if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
226                     supportedVariations = true;
227                     break;
228                 }
229             }
230         }
231         std::shared_ptr<MinikinFont> minikinFont;
232         if (supportedVariations) {
233             minikinFont = font.typeface()->createFontWithVariation(variations);
234         }
235         if (minikinFont == nullptr) {
236             minikinFont = font.typeface();
237         }
238         fonts.push_back(Font::Builder(minikinFont).setStyle(font.style()).build());
239     }
240 
241     return std::shared_ptr<FontFamily>(
242             new FontFamily(mLocaleListId, mVariant, std::move(fonts), mIsCustomFallback));
243 }
244 
245 }  // namespace minikin
246