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 #include <utils/JenkinsHash.h>
28
29 #include "minikin/CmapCoverage.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(Variant::DEFAULT, std::move(fonts)) {}
109
FontFamily(Variant variant,std::vector<Font> && fonts)110 FontFamily::FontFamily(Variant variant, std::vector<Font>&& fonts)
111 : FontFamily(LocaleListCache::kEmptyListId, variant, std::move(fonts)) {}
112
FontFamily(uint32_t localeListId,Variant variant,std::vector<Font> && fonts)113 FontFamily::FontFamily(uint32_t localeListId, Variant variant, std::vector<Font>&& fonts)
114 : mLocaleListId(localeListId),
115 mVariant(variant),
116 mFonts(std::move(fonts)),
117 mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
118 EmojiStyle::EMOJI) {
119 MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
120 computeCoverage();
121 }
122
123 // Compute a matching metric between two styles - 0 is an exact match
computeMatch(FontStyle style1,FontStyle style2)124 static int computeMatch(FontStyle style1, FontStyle style2) {
125 if (style1 == style2) return 0;
126 int score = abs(style1.weight() / 100 - style2.weight() / 100);
127 if (style1.slant() != style2.slant()) {
128 score += 2;
129 }
130 return score;
131 }
132
computeFakery(FontStyle wanted,FontStyle actual)133 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
134 // If desired weight is semibold or darker, and 2 or more grades
135 // higher than actual (for example, medium 500 -> bold 700), then
136 // select fake bold.
137 bool isFakeBold = wanted.weight() >= 600 && (wanted.weight() - actual.weight()) >= 200;
138 bool isFakeItalic = wanted.slant() == FontStyle::Slant::ITALIC &&
139 actual.slant() == FontStyle::Slant::UPRIGHT;
140 return FontFakery(isFakeBold, isFakeItalic);
141 }
142
getClosestMatch(FontStyle style) const143 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
144 const Font* bestFont = &mFonts[0];
145 int bestMatch = computeMatch(bestFont->style(), style);
146 for (size_t i = 1; i < mFonts.size(); i++) {
147 const Font& font = mFonts[i];
148 int match = computeMatch(font.style(), style);
149 if (i == 0 || match < bestMatch) {
150 bestFont = &font;
151 bestMatch = match;
152 }
153 }
154 return FakedFont{bestFont, computeFakery(style, bestFont->style())};
155 }
156
computeCoverage()157 void FontFamily::computeCoverage() {
158 const Font* font = getClosestMatch(FontStyle()).font;
159 HbBlob cmapTable(font->baseFont(), MinikinFont::MakeTag('c', 'm', 'a', 'p'));
160 if (cmapTable.get() == nullptr) {
161 ALOGE("Could not get cmap table size!\n");
162 return;
163 }
164
165 mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
166
167 for (size_t i = 0; i < mFonts.size(); ++i) {
168 std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxes();
169 mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
170 }
171 }
172
hasGlyph(uint32_t codepoint,uint32_t variationSelector) const173 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
174 if (variationSelector == 0) {
175 return mCoverage.get(codepoint);
176 }
177
178 if (mCmapFmt14Coverage.empty()) {
179 return false;
180 }
181
182 const uint16_t vsIndex = getVsIndex(variationSelector);
183
184 if (vsIndex >= mCmapFmt14Coverage.size()) {
185 // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
186 // be at the maximum end of the range.
187 return false;
188 }
189
190 const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
191 if (bitset.get() == nullptr) {
192 return false;
193 }
194
195 return bitset->get(codepoint);
196 }
197
createFamilyWithVariation(const std::vector<FontVariation> & variations) const198 std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
199 const std::vector<FontVariation>& variations) const {
200 if (variations.empty() || mSupportedAxes.empty()) {
201 return nullptr;
202 }
203
204 bool hasSupportedAxis = false;
205 for (const FontVariation& variation : variations) {
206 if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
207 hasSupportedAxis = true;
208 break;
209 }
210 }
211 if (!hasSupportedAxis) {
212 // None of variation axes are suppored by this family.
213 return nullptr;
214 }
215
216 std::vector<Font> fonts;
217 for (const Font& font : mFonts) {
218 bool supportedVariations = false;
219 std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxes();
220 if (!supportedAxes.empty()) {
221 for (const FontVariation& variation : variations) {
222 if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
223 supportedVariations = true;
224 break;
225 }
226 }
227 }
228 std::shared_ptr<MinikinFont> minikinFont;
229 if (supportedVariations) {
230 minikinFont = font.typeface()->createFontWithVariation(variations);
231 }
232 if (minikinFont == nullptr) {
233 minikinFont = font.typeface();
234 }
235 fonts.push_back(Font::Builder(minikinFont).setStyle(font.style()).build());
236 }
237
238 return std::shared_ptr<FontFamily>(new FontFamily(mLocaleListId, mVariant, std::move(fonts)));
239 }
240
241 } // namespace minikin
242