FontFamily.cpp revision 71ec97055357b6ccb13a2697a56254bb19f43ae9
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
30using std::vector;
31
32namespace android {
33
34// Parse bcp-47 language identifier into internal structure
35FontLanguage::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]) | (uint8_t(buf[1]) << 8);
44    } else if (i == 3) {
45        bits = uint8_t(buf[0]) | (uint8_t(buf[1]) << 8) | (uint8_t(buf[2]) << 16);
46    } else {
47        mBits = kUnsupportedLanguage;
48        // We don't understand anything other than two-letter or three-letter
49        // language codes, so we skip parsing the rest of the string.
50        return;
51    }
52    size_t next;
53    for (i++; i < size; i = next + 1) {
54        for (next = i; next < size; next++) {
55            uint16_t c = buf[next];
56            if (c == '-' || c == '_') break;
57        }
58        if (next - i == 4 && buf[i] == 'H' && buf[i+1] == 'a' && buf[i+2] == 'n') {
59            if (buf[i+3] == 's') {
60                bits |= kHansFlag;
61            } else if (buf[i+3] == 't') {
62                bits |= kHantFlag;
63            }
64        }
65        // TODO: this might be a good place to infer script from country (zh_TW -> Hant),
66        // but perhaps it's up to the client to do that, before passing a string.
67    }
68    mBits = bits;
69}
70
71std::string FontLanguage::getString() const {
72    if (mBits == kUnsupportedLanguage) {
73        return "und";
74    }
75    char buf[16];
76    size_t i = 0;
77    if (mBits & kBaseLangMask) {
78        buf[i++] = mBits & 0xFFu;
79        buf[i++] = (mBits >> 8) & 0xFFu;
80        char third_letter = (mBits >> 16) & 0xFFu;
81        if (third_letter != 0) buf[i++] = third_letter;
82    }
83    if (mBits & kScriptMask) {
84        if (!i) {
85            // This should not happen, but as it apparently has, we fill the language code part
86            // with "und".
87            buf[i++] = 'u';
88            buf[i++] = 'n';
89            buf[i++] = 'd';
90        }
91        buf[i++] = '-';
92        buf[i++] = 'H';
93        buf[i++] = 'a';
94        buf[i++] = 'n';
95        buf[i++] = (mBits & kHansFlag) ? 's' : 't';
96    }
97    return std::string(buf, i);
98}
99
100int FontLanguage::match(const FontLanguage other) const {
101    if (mBits == kUnsupportedLanguage || other.mBits == kUnsupportedLanguage)
102        return 0;
103
104    int result = 0;
105    if ((mBits & kBaseLangMask) == (other.mBits & kBaseLangMask)) {
106        result++;
107        if ((mBits & kScriptMask) != 0 && (mBits & kScriptMask) == (other.mBits & kScriptMask)) {
108            result++;
109        }
110    }
111    return result;
112}
113
114FontFamily::~FontFamily() {
115    for (size_t i = 0; i < mFonts.size(); i++) {
116        mFonts[i].typeface->UnrefLocked();
117    }
118}
119
120bool FontFamily::addFont(MinikinFont* typeface) {
121    AutoMutex _l(gMinikinLock);
122    const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
123    size_t os2Size = 0;
124    bool ok = typeface->GetTable(os2Tag, NULL, &os2Size);
125    if (!ok) return false;
126    UniquePtr<uint8_t[]> os2Data(new uint8_t[os2Size]);
127    ok = typeface->GetTable(os2Tag, os2Data.get(), &os2Size);
128    if (!ok) return false;
129    int weight;
130    bool italic;
131    if (analyzeStyle(os2Data.get(), os2Size, &weight, &italic)) {
132        //ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
133        FontStyle style(weight, italic);
134        addFontLocked(typeface, style);
135        return true;
136    } else {
137        ALOGD("failed to analyze style");
138    }
139    return false;
140}
141
142void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
143    AutoMutex _l(gMinikinLock);
144    addFontLocked(typeface, style);
145}
146
147void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) {    typeface->RefLocked();
148    mFonts.push_back(Font(typeface, style));
149    mCoverageValid = false;
150}
151
152// Compute a matching metric between two styles - 0 is an exact match
153static int computeMatch(FontStyle style1, FontStyle style2) {
154    if (style1 == style2) return 0;
155    int score = abs(style1.getWeight() - style2.getWeight());
156    if (style1.getItalic() != style2.getItalic()) {
157        score += 2;
158    }
159    return score;
160}
161
162static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
163    // If desired weight is semibold or darker, and 2 or more grades
164    // higher than actual (for example, medium 500 -> bold 700), then
165    // select fake bold.
166    int wantedWeight = wanted.getWeight();
167    bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
168    bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
169    return FontFakery(isFakeBold, isFakeItalic);
170}
171
172FakedFont FontFamily::getClosestMatch(FontStyle style) const {
173    const Font* bestFont = NULL;
174    int bestMatch = 0;
175    for (size_t i = 0; i < mFonts.size(); i++) {
176        const Font& font = mFonts[i];
177        int match = computeMatch(font.style, style);
178        if (i == 0 || match < bestMatch) {
179            bestFont = &font;
180            bestMatch = match;
181        }
182    }
183    FakedFont result;
184    if (bestFont == NULL) {
185        result.font = NULL;
186    } else {
187        result.font = bestFont->typeface;
188        result.fakery = computeFakery(style, bestFont->style);
189    }
190    return result;
191}
192
193size_t FontFamily::getNumFonts() const {
194    return mFonts.size();
195}
196
197MinikinFont* FontFamily::getFont(size_t index) const {
198    return mFonts[index].typeface;
199}
200
201FontStyle FontFamily::getStyle(size_t index) const {
202    return mFonts[index].style;
203}
204
205const SparseBitSet* FontFamily::getCoverage() {
206    if (!mCoverageValid) {
207        const FontStyle defaultStyle;
208        MinikinFont* typeface = getClosestMatch(defaultStyle).font;
209        const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
210        size_t cmapSize = 0;
211        if (!typeface->GetTable(cmapTag, NULL, &cmapSize)) {
212            ALOGE("Could not get cmap table size!\n");
213            // Note: This means we will retry on the next call to getCoverage, as we can't store
214            //       the failure. This is fine, as we assume this doesn't really happen in practice.
215            return nullptr;
216        }
217        UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]);
218        if (!typeface->GetTable(cmapTag, cmapData.get(), &cmapSize)) {
219            ALOGE("Unexpected failure to read cmap table!\n");
220            return nullptr;
221        }
222        CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize);  // TODO: Error check?
223#ifdef VERBOSE_DEBUG
224        ALOGD("font coverage length=%d, first ch=%x\n", mCoverage->length(),
225                mCoverage->nextSetBit(0));
226#endif
227        mCoverageValid = true;
228    }
229    return &mCoverage;
230}
231
232}  // namespace android
233