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 <cutils/log.h>
     20 #include <stdlib.h>
     21 #include <stdint.h>
     22 #include <string.h>
     23 
     24 #include <hb.h>
     25 #include <hb-ot.h>
     26 
     27 #include <utils/JenkinsHash.h>
     28 
     29 #include "FontLanguage.h"
     30 #include "FontLanguageListCache.h"
     31 #include "HbFontCache.h"
     32 #include "MinikinInternal.h"
     33 #include <minikin/MinikinFont.h>
     34 #include <minikin/AnalyzeStyle.h>
     35 #include <minikin/CmapCoverage.h>
     36 #include <minikin/FontFamily.h>
     37 #include <UniquePtr.h>
     38 
     39 using std::vector;
     40 
     41 namespace android {
     42 
     43 FontStyle::FontStyle(int variant, int weight, bool italic)
     44         : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
     45 }
     46 
     47 FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic)
     48         : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
     49 }
     50 
     51 hash_t FontStyle::hash() const {
     52     uint32_t hash = JenkinsHashMix(0, bits);
     53     hash = JenkinsHashMix(hash, mLanguageListId);
     54     return JenkinsHashWhiten(hash);
     55 }
     56 
     57 // static
     58 uint32_t FontStyle::registerLanguageList(const std::string& languages) {
     59     AutoMutex _l(gMinikinLock);
     60     return FontLanguageListCache::getId(languages);
     61 }
     62 
     63 // static
     64 uint32_t FontStyle::pack(int variant, int weight, bool italic) {
     65     return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
     66 }
     67 
     68 FontFamily::FontFamily() : FontFamily(0 /* variant */) {
     69 }
     70 
     71 FontFamily::FontFamily(int variant) : FontFamily(FontLanguageListCache::kEmptyListId, variant) {
     72 }
     73 
     74 FontFamily::~FontFamily() {
     75     for (size_t i = 0; i < mFonts.size(); i++) {
     76         mFonts[i].typeface->UnrefLocked();
     77     }
     78 }
     79 
     80 bool FontFamily::addFont(MinikinFont* typeface) {
     81     AutoMutex _l(gMinikinLock);
     82     const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
     83     HbBlob os2Table(getFontTable(typeface, os2Tag));
     84     if (os2Table.get() == nullptr) return false;
     85     int weight;
     86     bool italic;
     87     if (analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
     88         //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
     89         FontStyle style(weight, italic);
     90         addFontLocked(typeface, style);
     91         return true;
     92     } else {
     93         ALOGD("failed to analyze style");
     94     }
     95     return false;
     96 }
     97 
     98 void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
     99     AutoMutex _l(gMinikinLock);
    100     addFontLocked(typeface, style);
    101 }
    102 
    103 void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) {
    104     typeface->RefLocked();
    105     mFonts.push_back(Font(typeface, style));
    106     mCoverageValid = false;
    107 }
    108 
    109 // Compute a matching metric between two styles - 0 is an exact match
    110 static int computeMatch(FontStyle style1, FontStyle style2) {
    111     if (style1 == style2) return 0;
    112     int score = abs(style1.getWeight() - style2.getWeight());
    113     if (style1.getItalic() != style2.getItalic()) {
    114         score += 2;
    115     }
    116     return score;
    117 }
    118 
    119 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
    120     // If desired weight is semibold or darker, and 2 or more grades
    121     // higher than actual (for example, medium 500 -> bold 700), then
    122     // select fake bold.
    123     int wantedWeight = wanted.getWeight();
    124     bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
    125     bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
    126     return FontFakery(isFakeBold, isFakeItalic);
    127 }
    128 
    129 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
    130     const Font* bestFont = NULL;
    131     int bestMatch = 0;
    132     for (size_t i = 0; i < mFonts.size(); i++) {
    133         const Font& font = mFonts[i];
    134         int match = computeMatch(font.style, style);
    135         if (i == 0 || match < bestMatch) {
    136             bestFont = &font;
    137             bestMatch = match;
    138         }
    139     }
    140     FakedFont result;
    141     if (bestFont == NULL) {
    142         result.font = NULL;
    143     } else {
    144         result.font = bestFont->typeface;
    145         result.fakery = computeFakery(style, bestFont->style);
    146     }
    147     return result;
    148 }
    149 
    150 size_t FontFamily::getNumFonts() const {
    151     return mFonts.size();
    152 }
    153 
    154 MinikinFont* FontFamily::getFont(size_t index) const {
    155     return mFonts[index].typeface;
    156 }
    157 
    158 FontStyle FontFamily::getStyle(size_t index) const {
    159     return mFonts[index].style;
    160 }
    161 
    162 bool FontFamily::isColorEmojiFamily() const {
    163     const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
    164     for (size_t i = 0; i < languageList.size(); ++i) {
    165         if (languageList[i].hasEmojiFlag()) {
    166             return true;
    167         }
    168     }
    169     return false;
    170 }
    171 
    172 const SparseBitSet* FontFamily::getCoverage() {
    173     if (!mCoverageValid) {
    174         const FontStyle defaultStyle;
    175         MinikinFont* typeface = getClosestMatch(defaultStyle).font;
    176         const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
    177         HbBlob cmapTable(getFontTable(typeface, cmapTag));
    178         if (cmapTable.get() == nullptr) {
    179             ALOGE("Could not get cmap table size!\n");
    180             // Note: This means we will retry on the next call to getCoverage, as we can't store
    181             //       the failure. This is fine, as we assume this doesn't really happen in practice.
    182             return nullptr;
    183         }
    184         // TODO: Error check?
    185         CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable);
    186 #ifdef VERBOSE_DEBUG
    187         ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(),
    188                 mCoverage.nextSetBit(0));
    189 #endif
    190         mCoverageValid = true;
    191     }
    192     return &mCoverage;
    193 }
    194 
    195 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) {
    196     assertMinikinLocked();
    197     if (variationSelector != 0 && !mHasVSTable) {
    198         // Early exit if the variation selector is specified but the font doesn't have a cmap format
    199         // 14 subtable.
    200         return false;
    201     }
    202 
    203     const FontStyle defaultStyle;
    204     MinikinFont* minikinFont = getClosestMatch(defaultStyle).font;
    205     hb_font_t* font = getHbFontLocked(minikinFont);
    206     uint32_t unusedGlyph;
    207     bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
    208     hb_font_destroy(font);
    209     return result;
    210 }
    211 
    212 bool FontFamily::hasVSTable() const {
    213     LOG_ALWAYS_FATAL_IF(!mCoverageValid, "Do not call this method before getCoverage() call");
    214     return mHasVSTable;
    215 }
    216 
    217 }  // namespace android
    218