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 
     23 #include "MinikinInternal.h"
     24 #include <minikin/MinikinFont.h>
     25 #include <minikin/AnalyzeStyle.h>
     26 #include <minikin/CmapCoverage.h>
     27 #include <minikin/FontFamily.h>
     28 #include <UniquePtr.h>
     29 
     30 using std::vector;
     31 
     32 namespace android {
     33 
     34 // Parse bcp-47 language identifier into internal structure
     35 FontLanguage::FontLanguage(const char* buf, size_t size) {
     36     uint32_t bits = 0;
     37     size_t i;
     38     for (i = 0; i < size; i++) {
     39         uint16_t c = buf[i];
     40         if (c == '-' || c == '_') break;
     41     }
     42     if (i == 2) {
     43         bits = (uint8_t(buf[0]) << 8) | uint8_t(buf[1]);
     44     }
     45     size_t next;
     46     for (i++; i < size; i = next + 1) {
     47         for (next = i; next < size; next++) {
     48             uint16_t c = buf[next];
     49             if (c == '-' || c == '_') break;
     50         }
     51         if (next - i == 4 && buf[i] == 'H' && buf[i+1] == 'a' && buf[i+2] == 'n') {
     52             if (buf[i+3] == 's') {
     53                 bits |= kHansFlag;
     54             } else if (buf[i+3] == 't') {
     55                 bits |= kHantFlag;
     56             }
     57         }
     58         // TODO: this might be a good place to infer script from country (zh_TW -> Hant),
     59         // but perhaps it's up to the client to do that, before passing a string.
     60     }
     61     mBits = bits;
     62 }
     63 
     64 std::string FontLanguage::getString() const {
     65   char buf[16];
     66   size_t i = 0;
     67   if (mBits & kBaseLangMask) {
     68     buf[i++] = (mBits >> 8) & 0xFFu;
     69     buf[i++] = mBits & 0xFFu;
     70   }
     71   if (mBits & kScriptMask) {
     72     if (!i)
     73       buf[i++] = 'x';
     74     buf[i++] = '-';
     75     buf[i++] = 'H';
     76     buf[i++] = 'a';
     77     buf[i++] = 'n';
     78     if (mBits & kHansFlag)
     79       buf[i++] = 's';
     80     else
     81       buf[i++] = 't';
     82   }
     83   return std::string(buf, i);
     84 }
     85 
     86 int FontLanguage::match(const FontLanguage other) const {
     87     int result = 0;
     88     if ((mBits & kBaseLangMask) == (other.mBits & kBaseLangMask)) {
     89         result++;
     90         if ((mBits & kScriptMask) != 0 && (mBits & kScriptMask) == (other.mBits & kScriptMask)) {
     91             result++;
     92         }
     93     }
     94     return result;
     95 }
     96 
     97 FontFamily::~FontFamily() {
     98     for (size_t i = 0; i < mFonts.size(); i++) {
     99         mFonts[i].typeface->UnrefLocked();
    100     }
    101 }
    102 
    103 bool FontFamily::addFont(MinikinFont* typeface) {
    104     AutoMutex _l(gMinikinLock);
    105     const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
    106     size_t os2Size = 0;
    107     bool ok = typeface->GetTable(os2Tag, NULL, &os2Size);
    108     if (!ok) return false;
    109     UniquePtr<uint8_t[]> os2Data(new uint8_t[os2Size]);
    110     ok = typeface->GetTable(os2Tag, os2Data.get(), &os2Size);
    111     if (!ok) return false;
    112     int weight;
    113     bool italic;
    114     if (analyzeStyle(os2Data.get(), os2Size, &weight, &italic)) {
    115         //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
    116         FontStyle style(weight, italic);
    117         addFontLocked(typeface, style);
    118         return true;
    119     } else {
    120         ALOGD("failed to analyze style");
    121     }
    122     return false;
    123 }
    124 
    125 void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
    126     AutoMutex _l(gMinikinLock);
    127     addFontLocked(typeface, style);
    128 }
    129 
    130 void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) {    typeface->RefLocked();
    131     mFonts.push_back(Font(typeface, style));
    132     mCoverageValid = false;
    133 }
    134 
    135 // Compute a matching metric between two styles - 0 is an exact match
    136 static int computeMatch(FontStyle style1, FontStyle style2) {
    137     if (style1 == style2) return 0;
    138     int score = abs(style1.getWeight() - style2.getWeight());
    139     if (style1.getItalic() != style2.getItalic()) {
    140         score += 2;
    141     }
    142     return score;
    143 }
    144 
    145 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
    146     // If desired weight is semibold or darker, and 2 or more grades
    147     // higher than actual (for example, medium 500 -> bold 700), then
    148     // select fake bold.
    149     int wantedWeight = wanted.getWeight();
    150     bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
    151     bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
    152     return FontFakery(isFakeBold, isFakeItalic);
    153 }
    154 
    155 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
    156     const Font* bestFont = NULL;
    157     int bestMatch = 0;
    158     for (size_t i = 0; i < mFonts.size(); i++) {
    159         const Font& font = mFonts[i];
    160         int match = computeMatch(font.style, style);
    161         if (i == 0 || match < bestMatch) {
    162             bestFont = &font;
    163             bestMatch = match;
    164         }
    165     }
    166     FakedFont result;
    167     if (bestFont == NULL) {
    168         result.font = NULL;
    169     } else {
    170         result.font = bestFont->typeface;
    171         result.fakery = computeFakery(style, bestFont->style);
    172     }
    173     return result;
    174 }
    175 
    176 size_t FontFamily::getNumFonts() const {
    177     return mFonts.size();
    178 }
    179 
    180 MinikinFont* FontFamily::getFont(size_t index) const {
    181     return mFonts[index].typeface;
    182 }
    183 
    184 FontStyle FontFamily::getStyle(size_t index) const {
    185     return mFonts[index].style;
    186 }
    187 
    188 const SparseBitSet* FontFamily::getCoverage() {
    189     if (!mCoverageValid) {
    190         const FontStyle defaultStyle;
    191         MinikinFont* typeface = getClosestMatch(defaultStyle).font;
    192         const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
    193         size_t cmapSize = 0;
    194         bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize);
    195         UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]);
    196         ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize);
    197         CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize);
    198 #ifdef VERBOSE_DEBUG
    199         ALOGD("font coverage length=%d, first ch=%x\n", mCoverage->length(),
    200                 mCoverage->nextSetBit(0));
    201 #endif
    202         mCoverageValid = true;
    203     }
    204     return &mCoverage;
    205 }
    206 
    207 }  // namespace android
    208