1 /*
2 * Copyright (C) 2021 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 #include "minikin/Font.h"
18
19 #include <hb-ot.h>
20 #include <hb.h>
21 #include <log/log.h>
22
23 #include <vector>
24
25 #include "FeatureFlags.h"
26 #include "FontUtils.h"
27 #include "LocaleListCache.h"
28 #include "MinikinInternal.h"
29 #include "minikin/Constants.h"
30 #include "minikin/Hasher.h"
31 #include "minikin/HbUtils.h"
32 #include "minikin/MinikinFont.h"
33 #include "minikin/MinikinFontFactory.h"
34
35 namespace minikin {
36
37 namespace {
38
39 // |-------|-------|
40 // X : (1 bit) 1 if weight variation is available, otherwise 0.
41 // Y : (1 bit) 1 if italic variation is available, otherwise 0.
42 // I : (1 bit) 1 for italic, 0 for upright
43 // WWWWWWWWWW : (10 bits) unsigned 10 bits integer for weight value.
packKey(int wght,int ital)44 inline uint16_t packKey(int wght, int ital) {
45 uint16_t res = 0;
46 if (wght != -1) {
47 res |= 1u;
48 res |= static_cast<uint16_t>(wght) << 3;
49 }
50 if (ital != -1) {
51 res |= 1u << 1;
52 res |= (ital == 1) ? 1 << 2 : 0;
53 }
54 return res;
55 }
56
57 } // namespace
58
hash_type(const VariationSettings & vars)59 inline android::hash_t hash_type(const VariationSettings& vars) {
60 return Hasher().update(vars).hash();
61 }
62
build()63 std::shared_ptr<Font> Font::Builder::build() {
64 if (mIsWeightSet && mIsSlantSet) {
65 // No need to read OS/2 header of the font file.
66 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
67 prepareFont(mTypeface), mLocaleListId));
68 }
69
70 HbFontUniquePtr font = prepareFont(mTypeface);
71 FontStyle styleFromFont = analyzeStyle(font);
72 if (!mIsWeightSet) {
73 mWeight = styleFromFont.weight();
74 }
75 if (!mIsSlantSet) {
76 mSlant = styleFromFont.slant();
77 }
78 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
79 std::move(font), mLocaleListId));
80 }
81
Font(BufferReader * reader)82 Font::Font(BufferReader* reader)
83 : mExternalRefsHolder(nullptr),
84 mExternalRefsBuilder(nullptr),
85 mTypefaceMetadataReader(nullptr) {
86 mStyle = FontStyle(reader);
87 mLocaleListId = LocaleListCache::readFrom(reader);
88 const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
89 if (axesCount > 0) {
90 mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
91 std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
92 mSupportedAxesCount = axesCount;
93 } else {
94 mSupportedAxes = nullptr;
95 mSupportedAxesCount = 0;
96 }
97
98 mTypefaceMetadataReader = *reader;
99 MinikinFontFactory::getInstance().skip(reader);
100 }
101
Font(const std::shared_ptr<Font> & parent,const VariationSettings & axes)102 Font::Font(const std::shared_ptr<Font>& parent, const VariationSettings& axes)
103 : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
104 mStyle = parent->style();
105 mLocaleListId = parent->getLocaleListId();
106 mSupportedAxesCount = parent->mSupportedAxesCount;
107 if (mSupportedAxesCount != 0) {
108 uint16_t axesCount = parent->mSupportedAxesCount;
109 AxisTag* axesPtr = parent->mSupportedAxes.get();
110 mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
111 std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
112 }
113
114 if (parent->typefaceMetadataReader().current() == nullptr) {
115 // The parent font is fully initialized. Just create new one.
116 std::shared_ptr<MinikinFont> typeface =
117 parent->baseTypeface()->createFontWithVariation(axes);
118 HbFontUniquePtr hbFont = prepareFont(typeface);
119 mExternalRefsHolder.exchange(new ExternalRefs(std::move(typeface), std::move(hbFont)));
120 } else {
121 // If not fully initialized, set external ref builder for lazy creation.
122 mExternalRefsBuilder = [=]() {
123 std::shared_ptr<MinikinFont> typeface =
124 parent->baseTypeface()->createFontWithVariation(axes);
125 HbFontUniquePtr hbFont = prepareFont(typeface);
126 return new ExternalRefs(std::move(typeface), std::move(hbFont));
127 };
128 }
129 }
130
writeTo(BufferWriter * writer) const131 void Font::writeTo(BufferWriter* writer) const {
132 mStyle.writeTo(writer);
133 LocaleListCache::writeTo(writer, mLocaleListId);
134 writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
135 MinikinFontFactory::getInstance().write(writer, baseTypeface().get());
136 }
137
Font(Font && o)138 Font::Font(Font&& o) noexcept
139 : mStyle(o.mStyle),
140 mLocaleListId(o.mLocaleListId),
141 mSupportedAxes(std::move(o.mSupportedAxes)),
142 mSupportedAxesCount(o.mSupportedAxesCount),
143 mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
144 mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
145 }
146
operator =(Font && o)147 Font& Font::operator=(Font&& o) noexcept {
148 resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
149 mStyle = o.mStyle;
150 mLocaleListId = o.mLocaleListId;
151 mTypefaceMetadataReader = o.mTypefaceMetadataReader;
152 mSupportedAxesCount = o.mSupportedAxesCount;
153 mSupportedAxes = std::move(o.mSupportedAxes);
154 return *this;
155 }
156
isAxisSupported(uint32_t tag) const157 bool Font::isAxisSupported(uint32_t tag) const {
158 if (mSupportedAxesCount == 0) {
159 return false;
160 }
161 return std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
162 tag);
163 }
164
~Font()165 Font::~Font() {
166 resetExternalRefs(nullptr);
167
168 FVarTable* fvarTable = mFVarTableHolder.exchange(nullptr);
169 if (fvarTable != nullptr) {
170 delete fvarTable;
171 }
172 }
173
resetExternalRefs(ExternalRefs * refs)174 void Font::resetExternalRefs(ExternalRefs* refs) {
175 ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
176 if (oldRefs != nullptr) {
177 delete oldRefs;
178 }
179 }
180
baseTypeface() const181 const std::shared_ptr<MinikinFont>& Font::baseTypeface() const {
182 return getExternalRefs()->mTypeface;
183 }
184
baseFont() const185 const HbFontUniquePtr& Font::baseFont() const {
186 return getExternalRefs()->mBaseFont;
187 }
188
getExternalRefs() const189 const Font::ExternalRefs* Font::getExternalRefs() const {
190 // Thread safety note: getExternalRefs() is thread-safe.
191 // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
192 // When multiple threads called getExternalRefs() at the same time and
193 // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
194 // but only one ExternalRefs will be set to mExternalRefsHolder and
195 // others will be deleted.
196 Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
197 if (externalRefs) return externalRefs;
198 // mExternalRefsHolder is null. Try creating an ExternalRefs.
199 Font::ExternalRefs* newExternalRefs;
200 if (mExternalRefsBuilder != nullptr) {
201 newExternalRefs = mExternalRefsBuilder();
202 } else {
203 std::shared_ptr<MinikinFont> typeface =
204 MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
205 HbFontUniquePtr font = prepareFont(typeface);
206 newExternalRefs = new Font::ExternalRefs(std::move(typeface), std::move(font));
207 }
208 // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
209 Font::ExternalRefs* expected = nullptr;
210 if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
211 return newExternalRefs;
212 } else {
213 // Another thread has already created and set an ExternalRefs.
214 // Delete ours and use theirs instead.
215 delete newExternalRefs;
216 // compare_exchange_strong writes the stored value into 'expected'
217 // when comparison fails.
218 return expected;
219 }
220 }
221
getFVarTable() const222 const FVarTable& Font::getFVarTable() const {
223 FVarTable* fvarTable = mFVarTableHolder.load();
224 if (fvarTable) return *fvarTable;
225
226 FVarTable* newFvar = new FVarTable();
227 HbBlob fvarBlob(baseFont(), TAG_fvar);
228 if (fvarBlob) {
229 readFVarTable(fvarBlob.get(), fvarBlob.size(), newFvar);
230 }
231 FVarTable* expected = nullptr;
232 if (mFVarTableHolder.compare_exchange_strong(expected, newFvar)) {
233 return *newFvar;
234 } else {
235 delete newFvar;
236 return *expected;
237 }
238 }
239
240 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)241 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
242 const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
243 size_t size = typeface->GetFontSize();
244 uint32_t ttcIndex = typeface->GetFontIndex();
245
246 HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
247 HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
248 HbFontUniquePtr parent(hb_font_create(face.get()));
249 hb_ot_font_set_funcs(parent.get());
250
251 uint32_t upem = hb_face_get_upem(face.get());
252 hb_font_set_scale(parent.get(), upem, upem);
253
254 HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
255 std::vector<hb_variation_t> variations;
256 variations.reserve(typeface->GetAxes().size());
257 for (const FontVariation& variation : typeface->GetAxes()) {
258 variations.push_back({variation.axisTag, variation.value});
259 }
260 hb_font_set_variations(font.get(), variations.data(), variations.size());
261 return font;
262 }
263
264 // static
analyzeStyle(const HbFontUniquePtr & font)265 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
266 HbBlob os2Table(font, MakeTag('O', 'S', '/', '2'));
267 if (!os2Table) {
268 return FontStyle();
269 }
270
271 int weight;
272 bool italic;
273 if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
274 return FontStyle();
275 }
276 // TODO: Update weight/italic based on fvar value.
277 return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
278 }
279
calculateSupportedAxes()280 void Font::calculateSupportedAxes() {
281 HbBlob fvarTable(baseFont(), MakeTag('f', 'v', 'a', 'r'));
282 if (!fvarTable) {
283 mSupportedAxesCount = 0;
284 mSupportedAxes = nullptr;
285 return;
286 }
287 std::unordered_set<AxisTag> supportedAxes;
288 analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
289 mSupportedAxesCount = supportedAxes.size();
290 mSupportedAxes = sortedArrayFromSet(supportedAxes);
291 }
292
getAdjustedFont(int wght,int ital) const293 HbFontUniquePtr Font::getAdjustedFont(int wght, int ital) const {
294 return getExternalRefs()->getAdjustedFont(wght, ital);
295 }
296
getAdjustedFont(int wght,int ital) const297 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(int wght, int ital) const {
298 if (wght == -1 && ital == -1) {
299 return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
300 }
301
302 const uint16_t key = packKey(wght, ital);
303
304 std::lock_guard<std::mutex> lock(mMutex);
305 auto it = mVarFontCache.find(key);
306 if (it != mVarFontCache.end()) {
307 return HbFontUniquePtr(hb_font_reference(it->second.get()));
308 }
309
310 HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
311 std::vector<hb_variation_t> variations;
312 variations.reserve(mTypeface->GetAxes().size());
313 for (const FontVariation& variation : mTypeface->GetAxes()) {
314 if (wght != -1 && variation.axisTag == TAG_wght) {
315 continue; // Add wght axis later
316 } else if (ital != -1 && variation.axisTag == TAG_ital) {
317 continue; // Add ital axis later
318 } else {
319 variations.push_back({variation.axisTag, variation.value});
320 }
321 }
322 if (wght != -1) {
323 variations.push_back({TAG_wght, static_cast<float>(wght)});
324 }
325 if (ital != -1) {
326 variations.push_back({TAG_ital, static_cast<float>(ital)});
327 }
328 hb_font_set_variations(font.get(), variations.data(), variations.size());
329 mVarFontCache.emplace(key, HbFontUniquePtr(hb_font_reference(font.get())));
330 return font;
331 }
332
getAdjustedTypeface(int wght,int ital) const333 std::shared_ptr<MinikinFont> Font::getAdjustedTypeface(int wght, int ital) const {
334 return getExternalRefs()->getAdjustedTypeface(wght, ital);
335 }
336
getAdjustedTypeface(int wght,int ital) const337 const std::shared_ptr<MinikinFont>& Font::ExternalRefs::getAdjustedTypeface(int wght,
338 int ital) const {
339 if (wght == -1 && ital == -1) {
340 return mTypeface;
341 }
342
343 const uint16_t key = packKey(wght, ital);
344
345 std::lock_guard<std::mutex> lock(mMutex);
346
347 std::map<uint16_t, std::shared_ptr<MinikinFont>>::iterator it = mVarTypefaceCache.find(key);
348 if (it != mVarTypefaceCache.end()) {
349 return it->second;
350 }
351
352 std::vector<FontVariation> variations;
353 variations.reserve(mTypeface->GetAxes().size());
354 for (const FontVariation& variation : mTypeface->GetAxes()) {
355 if (wght != -1 && variation.axisTag == TAG_wght) {
356 continue; // Add wght axis later
357 } else if (ital != -1 && variation.axisTag == TAG_ital) {
358 continue; // Add ital axis later
359 } else {
360 variations.push_back({variation.axisTag, variation.value});
361 }
362 }
363 if (wght != -1) {
364 variations.push_back({TAG_wght, static_cast<float>(wght)});
365 }
366 if (ital != -1) {
367 variations.push_back({TAG_ital, static_cast<float>(ital)});
368 }
369 std::shared_ptr<MinikinFont> newTypeface = mTypeface->createFontWithVariation(variations);
370
371 auto [result_iterator, _] =
372 mVarTypefaceCache.insert(std::make_pair(key, std::move(newTypeface)));
373
374 return result_iterator->second;
375 }
376
getAdjustedFont(const VariationSettings & axes) const377 HbFontUniquePtr Font::getAdjustedFont(const VariationSettings& axes) const {
378 return getExternalRefs()->getAdjustedFont(axes, getFVarTable());
379 }
380
getAdjustedFont(const VariationSettings & axes,const FVarTable & table) const381 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(const VariationSettings& axes,
382 const FVarTable& table) const {
383 if (axes.empty()) {
384 return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
385 }
386
387 std::lock_guard<std::mutex> lock(mMutex);
388 HbFontUniquePtr* cached = mVarFontCache2.get(axes);
389 if (cached != nullptr) {
390 return HbFontUniquePtr(hb_font_reference(cached->get()));
391 }
392
393 HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
394 std::vector<hb_variation_t> variations;
395 variations.reserve(axes.size());
396 for (const FontVariation& variation : axes) {
397 auto it = table.find(variation.axisTag);
398 if (it == table.end() || it->second.defValue == variation.value) {
399 continue;
400 }
401 variations.push_back({variation.axisTag, variation.value});
402 }
403 hb_font_set_variations(font.get(), variations.data(), variations.size());
404 mVarFontCache2.put(axes, new HbFontUniquePtr(hb_font_reference(font.get())));
405 return font;
406 }
407
getAdjustedTypeface(const VariationSettings & axes) const408 std::shared_ptr<MinikinFont> Font::getAdjustedTypeface(const VariationSettings& axes) const {
409 return getExternalRefs()->getAdjustedTypeface(axes, getFVarTable());
410 }
411
getAdjustedTypeface(const VariationSettings & axes,const FVarTable & table) const412 std::shared_ptr<MinikinFont> Font::ExternalRefs::getAdjustedTypeface(const VariationSettings& axes,
413 const FVarTable& table) const {
414 if (axes.empty()) {
415 return mTypeface;
416 }
417
418 std::lock_guard<std::mutex> lock(mMutex);
419 const std::shared_ptr<MinikinFont>& cached = mVarTypefaceCache2.get(axes);
420 if (cached != nullptr) {
421 return cached;
422 }
423
424 std::vector<FontVariation> variations;
425 variations.reserve(axes.size());
426 for (const FontVariation& variation : axes) {
427 auto it = table.find(variation.axisTag);
428 if (it == table.end() || it->second.defValue == variation.value) {
429 continue;
430 }
431 variations.push_back({variation.axisTag, variation.value});
432 }
433 std::shared_ptr<MinikinFont> newTypeface =
434 mTypeface->createFontWithVariation(VariationSettings(variations, false));
435 mVarTypefaceCache2.put(axes, newTypeface);
436 return mVarTypefaceCache2.get(axes);
437 }
438
hbFont() const439 HbFontUniquePtr FakedFont::hbFont() const {
440 if (features::typeface_redesign_readonly()) {
441 return font->getAdjustedFont(fakery.variationSettings());
442 } else {
443 return font->getAdjustedFont(fakery.wghtAdjustment(), fakery.italAdjustment());
444 }
445 }
446
typeface() const447 std::shared_ptr<MinikinFont> FakedFont::typeface() const {
448 if (features::typeface_redesign_readonly()) {
449 return font->getAdjustedTypeface(fakery.variationSettings());
450 } else {
451 return font->getAdjustedTypeface(fakery.wghtAdjustment(), fakery.italAdjustment());
452 }
453 }
454
455 } // namespace minikin
456