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 #define LOG_TAG "Minikin"
18 
19 #include "minikin/Font.h"
20 
21 #include <hb-ot.h>
22 #include <hb.h>
23 #include <log/log.h>
24 
25 #include <vector>
26 
27 #include "FontUtils.h"
28 #include "LocaleListCache.h"
29 #include "MinikinInternal.h"
30 #include "minikin/Constants.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 
build()59 std::shared_ptr<Font> Font::Builder::build() {
60     if (mIsWeightSet && mIsSlantSet) {
61         // No need to read OS/2 header of the font file.
62         return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
63                                               prepareFont(mTypeface), mLocaleListId));
64     }
65 
66     HbFontUniquePtr font = prepareFont(mTypeface);
67     FontStyle styleFromFont = analyzeStyle(font);
68     if (!mIsWeightSet) {
69         mWeight = styleFromFont.weight();
70     }
71     if (!mIsSlantSet) {
72         mSlant = styleFromFont.slant();
73     }
74     return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
75                                           std::move(font), mLocaleListId));
76 }
77 
Font(BufferReader * reader)78 Font::Font(BufferReader* reader)
79         : mExternalRefsHolder(nullptr),
80           mExternalRefsBuilder(nullptr),
81           mTypefaceMetadataReader(nullptr) {
82     mStyle = FontStyle(reader);
83     mLocaleListId = LocaleListCache::readFrom(reader);
84     const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
85     if (axesCount > 0) {
86         mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
87         std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
88         mSupportedAxesCount = axesCount;
89     } else {
90         mSupportedAxes = nullptr;
91         mSupportedAxesCount = 0;
92     }
93 
94     mTypefaceMetadataReader = *reader;
95     MinikinFontFactory::getInstance().skip(reader);
96 }
97 
Font(const std::shared_ptr<Font> & parent,const std::vector<FontVariation> & axes)98 Font::Font(const std::shared_ptr<Font>& parent, const std::vector<FontVariation>& axes)
99         : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
100     mStyle = parent->style();
101     mLocaleListId = parent->getLocaleListId();
102     mSupportedAxesCount = parent->mSupportedAxesCount;
103     if (mSupportedAxesCount != 0) {
104         uint16_t axesCount = parent->mSupportedAxesCount;
105         AxisTag* axesPtr = parent->mSupportedAxes.get();
106         mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
107         std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
108     }
109 
110     if (parent->typefaceMetadataReader().current() == nullptr) {
111         // The parent font is fully initialized. Just create new one.
112         std::shared_ptr<MinikinFont> typeface =
113                 parent->baseTypeface()->createFontWithVariation(axes);
114         HbFontUniquePtr hbFont = prepareFont(typeface);
115         mExternalRefsHolder.exchange(new ExternalRefs(std::move(typeface), std::move(hbFont)));
116     } else {
117         // If not fully initialized, set external ref builder for lazy creation.
118         mExternalRefsBuilder = [=]() {
119             std::shared_ptr<MinikinFont> typeface =
120                     parent->baseTypeface()->createFontWithVariation(axes);
121             HbFontUniquePtr hbFont = prepareFont(typeface);
122             return new ExternalRefs(std::move(typeface), std::move(hbFont));
123         };
124     }
125 }
126 
writeTo(BufferWriter * writer) const127 void Font::writeTo(BufferWriter* writer) const {
128     mStyle.writeTo(writer);
129     LocaleListCache::writeTo(writer, mLocaleListId);
130     writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
131     MinikinFontFactory::getInstance().write(writer, baseTypeface().get());
132 }
133 
Font(Font && o)134 Font::Font(Font&& o) noexcept
135         : mStyle(o.mStyle),
136           mLocaleListId(o.mLocaleListId),
137           mSupportedAxes(std::move(o.mSupportedAxes)),
138           mSupportedAxesCount(o.mSupportedAxesCount),
139           mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
140     mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
141 }
142 
operator =(Font && o)143 Font& Font::operator=(Font&& o) noexcept {
144     resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
145     mStyle = o.mStyle;
146     mLocaleListId = o.mLocaleListId;
147     mTypefaceMetadataReader = o.mTypefaceMetadataReader;
148     mSupportedAxesCount = o.mSupportedAxesCount;
149     mSupportedAxes = std::move(o.mSupportedAxes);
150     return *this;
151 }
152 
isAxisSupported(uint32_t tag) const153 bool Font::isAxisSupported(uint32_t tag) const {
154     if (mSupportedAxesCount == 0) {
155         return false;
156     }
157     return std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
158                               tag);
159 }
160 
~Font()161 Font::~Font() {
162     resetExternalRefs(nullptr);
163 }
164 
resetExternalRefs(ExternalRefs * refs)165 void Font::resetExternalRefs(ExternalRefs* refs) {
166     ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
167     if (oldRefs != nullptr) {
168         delete oldRefs;
169     }
170 }
171 
baseTypeface() const172 const std::shared_ptr<MinikinFont>& Font::baseTypeface() const {
173     return getExternalRefs()->mTypeface;
174 }
175 
baseFont() const176 const HbFontUniquePtr& Font::baseFont() const {
177     return getExternalRefs()->mBaseFont;
178 }
179 
getExternalRefs() const180 const Font::ExternalRefs* Font::getExternalRefs() const {
181     // Thread safety note: getExternalRefs() is thread-safe.
182     // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
183     // When multiple threads called getExternalRefs() at the same time and
184     // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
185     // but only one ExternalRefs will be set to mExternalRefsHolder and
186     // others will be deleted.
187     Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
188     if (externalRefs) return externalRefs;
189     // mExternalRefsHolder is null. Try creating an ExternalRefs.
190     Font::ExternalRefs* newExternalRefs;
191     if (mExternalRefsBuilder != nullptr) {
192         newExternalRefs = mExternalRefsBuilder();
193     } else {
194         std::shared_ptr<MinikinFont> typeface =
195                 MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
196         HbFontUniquePtr font = prepareFont(typeface);
197         newExternalRefs = new Font::ExternalRefs(std::move(typeface), std::move(font));
198     }
199     // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
200     Font::ExternalRefs* expected = nullptr;
201     if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
202         return newExternalRefs;
203     } else {
204         // Another thread has already created and set an ExternalRefs.
205         // Delete ours and use theirs instead.
206         delete newExternalRefs;
207         // compare_exchange_strong writes the stored value into 'expected'
208         // when comparison fails.
209         return expected;
210     }
211 }
212 
213 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)214 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
215     const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
216     size_t size = typeface->GetFontSize();
217     uint32_t ttcIndex = typeface->GetFontIndex();
218 
219     HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
220     HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
221     HbFontUniquePtr parent(hb_font_create(face.get()));
222     hb_ot_font_set_funcs(parent.get());
223 
224     uint32_t upem = hb_face_get_upem(face.get());
225     hb_font_set_scale(parent.get(), upem, upem);
226 
227     HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
228     std::vector<hb_variation_t> variations;
229     variations.reserve(typeface->GetAxes().size());
230     for (const FontVariation& variation : typeface->GetAxes()) {
231         variations.push_back({variation.axisTag, variation.value});
232     }
233     hb_font_set_variations(font.get(), variations.data(), variations.size());
234     return font;
235 }
236 
237 // static
analyzeStyle(const HbFontUniquePtr & font)238 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
239     HbBlob os2Table(font, MakeTag('O', 'S', '/', '2'));
240     if (!os2Table) {
241         return FontStyle();
242     }
243 
244     int weight;
245     bool italic;
246     if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
247         return FontStyle();
248     }
249     // TODO: Update weight/italic based on fvar value.
250     return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
251 }
252 
calculateSupportedAxes()253 void Font::calculateSupportedAxes() {
254     HbBlob fvarTable(baseFont(), MakeTag('f', 'v', 'a', 'r'));
255     if (!fvarTable) {
256         mSupportedAxesCount = 0;
257         mSupportedAxes = nullptr;
258         return;
259     }
260     std::unordered_set<AxisTag> supportedAxes;
261     analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
262     mSupportedAxesCount = supportedAxes.size();
263     mSupportedAxes = sortedArrayFromSet(supportedAxes);
264 }
265 
getAdjustedFont(int wght,int ital) const266 HbFontUniquePtr Font::getAdjustedFont(int wght, int ital) const {
267     return getExternalRefs()->getAdjustedFont(wght, ital);
268 }
269 
getAdjustedFont(int wght,int ital) const270 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(int wght, int ital) const {
271     if (wght == -1 && ital == -1) {
272         return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
273     }
274 
275     const uint16_t key = packKey(wght, ital);
276 
277     std::lock_guard<std::mutex> lock(mMutex);
278     auto it = mVarFontCache.find(key);
279     if (it != mVarFontCache.end()) {
280         return HbFontUniquePtr(hb_font_reference(it->second.get()));
281     }
282 
283     HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
284     std::vector<hb_variation_t> variations;
285     variations.reserve(mTypeface->GetAxes().size());
286     for (const FontVariation& variation : mTypeface->GetAxes()) {
287         if (wght != -1 && variation.axisTag == TAG_wght) {
288             continue;  // Add wght axis later
289         } else if (ital != -1 && variation.axisTag == TAG_ital) {
290             continue;  // Add ital axis later
291         } else {
292             variations.push_back({variation.axisTag, variation.value});
293         }
294     }
295     if (wght != -1) {
296         variations.push_back({TAG_wght, static_cast<float>(wght)});
297     }
298     if (ital != -1) {
299         variations.push_back({TAG_ital, static_cast<float>(ital)});
300     }
301     hb_font_set_variations(font.get(), variations.data(), variations.size());
302     mVarFontCache.emplace(key, HbFontUniquePtr(hb_font_reference(font.get())));
303     return font;
304 }
305 
getAdjustedTypeface(int wght,int ital) const306 const std::shared_ptr<MinikinFont>& Font::getAdjustedTypeface(int wght, int ital) const {
307     return getExternalRefs()->getAdjustedTypeface(wght, ital);
308 }
309 
getAdjustedTypeface(int wght,int ital) const310 const std::shared_ptr<MinikinFont>& Font::ExternalRefs::getAdjustedTypeface(int wght,
311                                                                             int ital) const {
312     if (wght == -1 && ital == -1) {
313         return mTypeface;
314     }
315 
316     const uint16_t key = packKey(wght, ital);
317 
318     std::lock_guard<std::mutex> lock(mMutex);
319 
320     std::map<uint16_t, std::shared_ptr<MinikinFont>>::iterator it = mVarTypefaceCache.find(key);
321     if (it != mVarTypefaceCache.end()) {
322         return it->second;
323     }
324 
325     std::vector<FontVariation> variations;
326     variations.reserve(mTypeface->GetAxes().size());
327     for (const FontVariation& variation : mTypeface->GetAxes()) {
328         if (wght != -1 && variation.axisTag == TAG_wght) {
329             continue;  // Add wght axis later
330         } else if (ital != -1 && variation.axisTag == TAG_ital) {
331             continue;  // Add ital axis later
332         } else {
333             variations.push_back({variation.axisTag, variation.value});
334         }
335     }
336     if (wght != -1) {
337         variations.push_back({TAG_wght, static_cast<float>(wght)});
338     }
339     if (ital != -1) {
340         variations.push_back({TAG_ital, static_cast<float>(ital)});
341     }
342     std::shared_ptr<MinikinFont> newTypeface = mTypeface->createFontWithVariation(variations);
343 
344     auto [result_iterator, _] =
345             mVarTypefaceCache.insert(std::make_pair(key, std::move(newTypeface)));
346 
347     return result_iterator->second;
348 }
349 
350 }  // namespace minikin
351