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
FontLanguage(const char * buf,size_t size)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 
getString() const64 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 
match(const FontLanguage other) const86 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 
~FontFamily()97 FontFamily::~FontFamily() {
98     for (size_t i = 0; i < mFonts.size(); i++) {
99         mFonts[i].typeface->UnrefLocked();
100     }
101 }
102 
addFont(MinikinFont * typeface)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 
addFont(MinikinFont * typeface,FontStyle style)125 void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
126     AutoMutex _l(gMinikinLock);
127     addFontLocked(typeface, style);
128 }
129 
addFontLocked(MinikinFont * typeface,FontStyle style)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
computeMatch(FontStyle style1,FontStyle style2)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 
computeFakery(FontStyle wanted,FontStyle actual)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 
getClosestMatch(FontStyle style) const155 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 
getNumFonts() const176 size_t FontFamily::getNumFonts() const {
177     return mFonts.size();
178 }
179 
getFont(size_t index) const180 MinikinFont* FontFamily::getFont(size_t index) const {
181     return mFonts[index].typeface;
182 }
183 
getStyle(size_t index) const184 FontStyle FontFamily::getStyle(size_t index) const {
185     return mFonts[index].style;
186 }
187 
getCoverage()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