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]) << 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
64std::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
86int 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
97FontFamily::~FontFamily() {
98    for (size_t i = 0; i < mFonts.size(); i++) {
99        mFonts[i].typeface->UnrefLocked();
100    }
101}
102
103bool 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
125void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
126    AutoMutex _l(gMinikinLock);
127    addFontLocked(typeface, style);
128}
129
130void 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
136static 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
145static 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
155FakedFont 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
176size_t FontFamily::getNumFonts() const {
177    return mFonts.size();
178}
179
180MinikinFont* FontFamily::getFont(size_t index) const {
181    return mFonts[index].typeface;
182}
183
184FontStyle FontFamily::getStyle(size_t index) const {
185    return mFonts[index].style;
186}
187
188const 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