Home | History | Annotate | Download | only in minikin
      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 
     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
     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
     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 
     97 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 
    107 FontFamily::FontFamily(std::vector<Font>&& fonts)
    108         : FontFamily(Variant::DEFAULT, std::move(fonts)) {}
    109 
    110 FontFamily::FontFamily(Variant variant, std::vector<Font>&& fonts)
    111         : FontFamily(LocaleListCache::kEmptyListId, variant, std::move(fonts)) {}
    112 
    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
    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 
    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 
    143 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 
    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 
    173 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 
    198 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