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/HbUtils.h"
31 #include "minikin/MinikinFont.h"
32 #include "minikin/MinikinFontFactory.h"
33
34 namespace minikin {
35
build()36 std::shared_ptr<Font> Font::Builder::build() {
37 if (mIsWeightSet && mIsSlantSet) {
38 // No need to read OS/2 header of the font file.
39 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
40 prepareFont(mTypeface), mLocaleListId));
41 }
42
43 HbFontUniquePtr font = prepareFont(mTypeface);
44 FontStyle styleFromFont = analyzeStyle(font);
45 if (!mIsWeightSet) {
46 mWeight = styleFromFont.weight();
47 }
48 if (!mIsSlantSet) {
49 mSlant = styleFromFont.slant();
50 }
51 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
52 std::move(font), mLocaleListId));
53 }
54
Font(BufferReader * reader)55 Font::Font(BufferReader* reader) : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
56 mStyle = FontStyle(reader);
57 mLocaleListId = LocaleListCache::readFrom(reader);
58 mTypefaceMetadataReader = *reader;
59 MinikinFontFactory::getInstance().skip(reader);
60 }
61
writeTo(BufferWriter * writer) const62 void Font::writeTo(BufferWriter* writer) const {
63 mStyle.writeTo(writer);
64 LocaleListCache::writeTo(writer, mLocaleListId);
65 MinikinFontFactory::getInstance().write(writer, typeface().get());
66 }
67
Font(Font && o)68 Font::Font(Font&& o) noexcept
69 : mStyle(o.mStyle),
70 mLocaleListId(o.mLocaleListId),
71 mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
72 mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
73 }
74
operator =(Font && o)75 Font& Font::operator=(Font&& o) noexcept {
76 resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
77 mStyle = o.mStyle;
78 mLocaleListId = o.mLocaleListId;
79 mTypefaceMetadataReader = o.mTypefaceMetadataReader;
80 return *this;
81 }
82
~Font()83 Font::~Font() {
84 resetExternalRefs(nullptr);
85 }
86
resetExternalRefs(ExternalRefs * refs)87 void Font::resetExternalRefs(ExternalRefs* refs) {
88 ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
89 if (oldRefs != nullptr) {
90 delete oldRefs;
91 }
92 }
93
typeface() const94 const std::shared_ptr<MinikinFont>& Font::typeface() const {
95 return getExternalRefs()->mTypeface;
96 }
97
baseFont() const98 const HbFontUniquePtr& Font::baseFont() const {
99 return getExternalRefs()->mBaseFont;
100 }
101
getExternalRefs() const102 const Font::ExternalRefs* Font::getExternalRefs() const {
103 // Thread safety note: getExternalRefs() is thread-safe.
104 // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
105 // When multiple threads called getExternalRefs() at the same time and
106 // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
107 // but only one ExternalRefs will be set to mExternalRefsHolder and
108 // others will be deleted.
109 Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
110 if (externalRefs) return externalRefs;
111 // mExternalRefsHolder is null. Try creating an ExternalRefs.
112 std::shared_ptr<MinikinFont> typeface =
113 MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
114 HbFontUniquePtr font = prepareFont(typeface);
115 Font::ExternalRefs* newExternalRefs =
116 new Font::ExternalRefs(std::move(typeface), std::move(font));
117 // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
118 Font::ExternalRefs* expected = nullptr;
119 if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
120 return newExternalRefs;
121 } else {
122 // Another thread has already created and set an ExternalRefs.
123 // Delete ours and use theirs instead.
124 delete newExternalRefs;
125 // compare_exchange_strong writes the stored value into 'expected'
126 // when comparison fails.
127 return expected;
128 }
129 }
130
131 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)132 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
133 const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
134 size_t size = typeface->GetFontSize();
135 uint32_t ttcIndex = typeface->GetFontIndex();
136
137 HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
138 HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
139 HbFontUniquePtr parent(hb_font_create(face.get()));
140 hb_ot_font_set_funcs(parent.get());
141
142 uint32_t upem = hb_face_get_upem(face.get());
143 hb_font_set_scale(parent.get(), upem, upem);
144
145 HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
146 std::vector<hb_variation_t> variations;
147 variations.reserve(typeface->GetAxes().size());
148 for (const FontVariation& variation : typeface->GetAxes()) {
149 variations.push_back({variation.axisTag, variation.value});
150 }
151 hb_font_set_variations(font.get(), variations.data(), variations.size());
152 return font;
153 }
154
155 // static
analyzeStyle(const HbFontUniquePtr & font)156 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
157 HbBlob os2Table(font, MinikinFont::MakeTag('O', 'S', '/', '2'));
158 if (!os2Table) {
159 return FontStyle();
160 }
161
162 int weight;
163 bool italic;
164 if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
165 return FontStyle();
166 }
167 // TODO: Update weight/italic based on fvar value.
168 return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
169 }
170
getSupportedAxes() const171 std::unordered_set<AxisTag> Font::getSupportedAxes() const {
172 HbBlob fvarTable(baseFont(), MinikinFont::MakeTag('f', 'v', 'a', 'r'));
173 if (!fvarTable) {
174 return std::unordered_set<AxisTag>();
175 }
176 std::unordered_set<AxisTag> supportedAxes;
177 analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
178 return supportedAxes;
179 }
180
181 } // namespace minikin
182