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 <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include <log/log.h>
24#include <utils/JenkinsHash.h>
25
26#include <hb.h>
27#include <hb-ot.h>
28
29#include "FontLanguage.h"
30#include "FontLanguageListCache.h"
31#include "FontUtils.h"
32#include "HbFontCache.h"
33#include "MinikinInternal.h"
34#include <minikin/CmapCoverage.h>
35#include <minikin/MinikinFont.h>
36#include <minikin/FontFamily.h>
37#include <minikin/MinikinFont.h>
38
39using std::vector;
40
41namespace minikin {
42
43FontStyle::FontStyle(int variant, int weight, bool italic)
44        : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
45}
46
47FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic)
48        : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
49}
50
51android::hash_t FontStyle::hash() const {
52    uint32_t hash = android::JenkinsHashMix(0, bits);
53    hash = android::JenkinsHashMix(hash, mLanguageListId);
54    return android::JenkinsHashWhiten(hash);
55}
56
57// static
58uint32_t FontStyle::registerLanguageList(const std::string& languages) {
59    android::AutoMutex _l(gMinikinLock);
60    return FontLanguageListCache::getId(languages);
61}
62
63// static
64uint32_t FontStyle::pack(int variant, int weight, bool italic) {
65    return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
66}
67
68Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
69    : typeface(typeface), style(style) {
70}
71
72Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
73    : typeface(typeface), style(style) {
74}
75
76std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
77    const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
78    HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
79    if (fvarTable.size() == 0) {
80        return std::unordered_set<AxisTag>();
81    }
82
83    std::unordered_set<AxisTag> supportedAxes;
84    analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
85    return supportedAxes;
86}
87
88Font::Font(Font&& o) {
89    typeface = std::move(o.typeface);
90    style = o.style;
91    o.typeface = nullptr;
92}
93
94Font::Font(const Font& o) {
95    typeface = o.typeface;
96    style = o.style;
97}
98
99// static
100FontFamily::FontFamily(std::vector<Font>&& fonts) : FontFamily(0 /* variant */, std::move(fonts)) {
101}
102
103FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
104    : FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) {
105}
106
107FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
108    : mLangId(langId), mVariant(variant), mFonts(std::move(fonts)) {
109    computeCoverage();
110}
111
112bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
113        bool* italic) {
114    android::AutoMutex _l(gMinikinLock);
115    const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
116    HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
117    if (os2Table.get() == nullptr) return false;
118    return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight, italic);
119}
120
121// Compute a matching metric between two styles - 0 is an exact match
122static int computeMatch(FontStyle style1, FontStyle style2) {
123    if (style1 == style2) return 0;
124    int score = abs(style1.getWeight() - style2.getWeight());
125    if (style1.getItalic() != style2.getItalic()) {
126        score += 2;
127    }
128    return score;
129}
130
131static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
132    // If desired weight is semibold or darker, and 2 or more grades
133    // higher than actual (for example, medium 500 -> bold 700), then
134    // select fake bold.
135    int wantedWeight = wanted.getWeight();
136    bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
137    bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
138    return FontFakery(isFakeBold, isFakeItalic);
139}
140
141FakedFont FontFamily::getClosestMatch(FontStyle style) const {
142    const Font* bestFont = nullptr;
143    int bestMatch = 0;
144    for (size_t i = 0; i < mFonts.size(); i++) {
145        const Font& font = mFonts[i];
146        int match = computeMatch(font.style, style);
147        if (i == 0 || match < bestMatch) {
148            bestFont = &font;
149            bestMatch = match;
150        }
151    }
152    if (bestFont != nullptr) {
153        return FakedFont{ bestFont->typeface.get(), computeFakery(style, bestFont->style) };
154    }
155    return FakedFont{ nullptr, FontFakery() };
156}
157
158bool FontFamily::isColorEmojiFamily() const {
159    const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
160    for (size_t i = 0; i < languageList.size(); ++i) {
161        if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
162            return true;
163        }
164    }
165    return false;
166}
167
168void FontFamily::computeCoverage() {
169    android::AutoMutex _l(gMinikinLock);
170    const FontStyle defaultStyle;
171    const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
172    const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
173    HbBlob cmapTable(getFontTable(typeface, cmapTag));
174    if (cmapTable.get() == nullptr) {
175        ALOGE("Could not get cmap table size!\n");
176        return;
177    }
178    mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
179
180    for (size_t i = 0; i < mFonts.size(); ++i) {
181        std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxesLocked();
182        mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
183    }
184}
185
186bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
187    if (variationSelector == 0) {
188        return mCoverage.get(codepoint);
189    }
190
191    if (mCmapFmt14Coverage.empty()) {
192        return false;
193    }
194
195    const uint16_t vsIndex = getVsIndex(variationSelector);
196
197    if (vsIndex >= mCmapFmt14Coverage.size()) {
198        // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
199        // be at the maximum end of the range.
200        return false;
201    }
202
203    const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
204    if (bitset.get() == nullptr) {
205        return false;
206    }
207
208    return bitset->get(codepoint);
209}
210
211std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
212        const std::vector<FontVariation>& variations) const {
213    if (variations.empty() || mSupportedAxes.empty()) {
214        return nullptr;
215    }
216
217    bool hasSupportedAxis = false;
218    for (const FontVariation& variation : variations) {
219        if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
220            hasSupportedAxis = true;
221            break;
222        }
223    }
224    if (!hasSupportedAxis) {
225        // None of variation axes are suppored by this family.
226        return nullptr;
227    }
228
229    std::vector<Font> fonts;
230    for (const Font& font : mFonts) {
231        bool supportedVariations = false;
232        android::AutoMutex _l(gMinikinLock);
233        std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
234        if (!supportedAxes.empty()) {
235            for (const FontVariation& variation : variations) {
236                if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
237                    supportedVariations = true;
238                    break;
239                }
240            }
241        }
242        std::shared_ptr<MinikinFont> minikinFont;
243        if (supportedVariations) {
244            minikinFont = font.typeface->createFontWithVariation(variations);
245        }
246        if (minikinFont == nullptr) {
247            minikinFont = font.typeface;
248        }
249        fonts.push_back(Font(std::move(minikinFont), font.style));
250    }
251
252    return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
253}
254
255}  // namespace minikin
256