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