1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkFontMgr.h"
9#include "SkFontStyle.h"
10#include "SkFontConfigInterface.h"
11#include "SkFontConfigTypeface.h"
12#include "SkMath.h"
13#include "SkString.h"
14#include "SkTDArray.h"
15#include "SkThread.h"
16
17// for now we pull these in directly. eventually we will solely rely on the
18// SkFontConfigInterface instance.
19#include <fontconfig/fontconfig.h>
20#include <unistd.h>
21
22namespace {
23
24// Fontconfig is not threadsafe before 2.10.91.  Before that, we lock with a global mutex.
25// See skia:1497 for background.
26SK_DECLARE_STATIC_MUTEX(gFCMutex);
27static bool gFCSafeToUse;
28
29struct FCLocker {
30    FCLocker() {
31        if (FcGetVersion() < 21091) {  // We assume FcGetVersion() has always been thread safe.
32            gFCMutex.acquire();
33            fUnlock = true;
34        } else {
35            fUnlock = false;
36        }
37        gFCSafeToUse = true;
38    }
39
40    ~FCLocker() {
41        if (fUnlock) {
42            gFCSafeToUse = false;
43            gFCMutex.release();
44        }
45    }
46
47private:
48    bool fUnlock;
49};
50
51} // namespace
52
53
54// Defined in SkFontHost_FreeType.cpp
55bool find_name_and_attributes(SkStream* stream, SkString* name,
56                              SkTypeface::Style* style, bool* isFixedWidth);
57
58// borrow this global from SkFontHost_fontconfig. eventually that file should
59// go away, and be replaced with this one.
60extern SkFontConfigInterface* SkFontHost_fontconfig_ref_global();
61static SkFontConfigInterface* RefFCI() {
62    return SkFontHost_fontconfig_ref_global();
63}
64
65// look for the last substring after a '/' and return that, or return null.
66static const char* find_just_name(const char* str) {
67    const char* last = strrchr(str, '/');
68    return last ? last + 1 : NULL;
69}
70
71static bool is_lower(char c) {
72    return c >= 'a' && c <= 'z';
73}
74
75static int get_int(FcPattern* pattern, const char field[]) {
76    SkASSERT(gFCSafeToUse);
77    int value;
78    if (FcPatternGetInteger(pattern, field, 0, &value) != FcResultMatch) {
79        value = SK_MinS32;
80    }
81    return value;
82}
83
84static const char* get_name(FcPattern* pattern, const char field[]) {
85    SkASSERT(gFCSafeToUse);
86    const char* name;
87    if (FcPatternGetString(pattern, field, 0, (FcChar8**)&name) != FcResultMatch) {
88        name = "";
89    }
90    return name;
91}
92
93static bool valid_pattern(FcPattern* pattern) {
94    SkASSERT(gFCSafeToUse);
95    FcBool is_scalable;
96    if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) {
97        return false;
98    }
99
100    // fontconfig can also return fonts which are unreadable
101    const char* c_filename = get_name(pattern, FC_FILE);
102    if (0 == *c_filename) {
103        return false;
104    }
105    if (access(c_filename, R_OK) != 0) {
106        return false;
107    }
108    return true;
109}
110
111static bool match_name(FcPattern* pattern, const char family_name[]) {
112    return !strcasecmp(family_name, get_name(pattern, FC_FAMILY));
113}
114
115static FcPattern** MatchFont(FcFontSet* font_set,
116                             const char post_config_family[],
117                             int* count) {
118  // Older versions of fontconfig have a bug where they cannot select
119  // only scalable fonts so we have to manually filter the results.
120
121    FcPattern** iter = font_set->fonts;
122    FcPattern** stop = iter + font_set->nfont;
123    // find the first good match
124    for (; iter < stop; ++iter) {
125        if (valid_pattern(*iter)) {
126            break;
127        }
128    }
129
130    if (iter == stop || !match_name(*iter, post_config_family)) {
131        return NULL;
132    }
133
134    FcPattern** firstIter = iter++;
135    for (; iter < stop; ++iter) {
136        if (!valid_pattern(*iter) || !match_name(*iter, post_config_family)) {
137            break;
138        }
139    }
140
141    *count = iter - firstIter;
142    return firstIter;
143}
144
145class SkFontStyleSet_FC : public SkFontStyleSet {
146public:
147    SkFontStyleSet_FC(FcPattern** matches, int count);
148    virtual ~SkFontStyleSet_FC();
149
150    virtual int count() SK_OVERRIDE { return fRecCount; }
151    virtual void getStyle(int index, SkFontStyle*, SkString* style) SK_OVERRIDE;
152    virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
153    virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
154
155private:
156    struct Rec {
157        SkString    fStyleName;
158        SkString    fFileName;
159        SkFontStyle fStyle;
160    };
161    Rec* fRecs;
162    int  fRecCount;
163};
164
165static int map_range(int value,
166                     int old_min, int old_max, int new_min, int new_max) {
167    SkASSERT(old_min < old_max);
168    SkASSERT(new_min < new_max);
169    return new_min + SkMulDiv(value - old_min,
170                              new_max - new_min, old_max - old_min);
171}
172
173static SkFontStyle make_fontconfig_style(FcPattern* match) {
174    int weight = get_int(match, FC_WEIGHT);
175    int width = get_int(match, FC_WIDTH);
176    int slant = get_int(match, FC_SLANT);
177//    SkDebugf("old weight %d new weight %d\n", weight, map_range(weight, 0, 80, 0, 400));
178
179    // fontconfig weight seems to be 0..200 or so, so we remap it here
180    weight = map_range(weight, 0, 80, 0, 400);
181    width = map_range(width, 0, 200, 0, 9);
182    return SkFontStyle(weight, width, slant > 0 ? SkFontStyle::kItalic_Slant
183                                                : SkFontStyle::kUpright_Slant);
184}
185
186SkFontStyleSet_FC::SkFontStyleSet_FC(FcPattern** matches, int count) {
187    fRecCount = count;
188    fRecs = SkNEW_ARRAY(Rec, count);
189    for (int i = 0; i < count; ++i) {
190        fRecs[i].fStyleName.set(get_name(matches[i], FC_STYLE));
191        fRecs[i].fFileName.set(get_name(matches[i], FC_FILE));
192        fRecs[i].fStyle = make_fontconfig_style(matches[i]);
193    }
194}
195
196SkFontStyleSet_FC::~SkFontStyleSet_FC() {
197    SkDELETE_ARRAY(fRecs);
198}
199
200void SkFontStyleSet_FC::getStyle(int index, SkFontStyle* style,
201                                 SkString* styleName) {
202    SkASSERT((unsigned)index < (unsigned)fRecCount);
203    if (style) {
204        *style = fRecs[index].fStyle;
205    }
206    if (styleName) {
207        *styleName = fRecs[index].fStyleName;
208    }
209}
210
211SkTypeface* SkFontStyleSet_FC::createTypeface(int index) {
212    return NULL;
213}
214
215SkTypeface* SkFontStyleSet_FC::matchStyle(const SkFontStyle& pattern) {
216    return NULL;
217}
218
219class SkFontMgr_fontconfig : public SkFontMgr {
220    SkAutoTUnref<SkFontConfigInterface> fFCI;
221    SkDataTable* fFamilyNames;
222
223
224public:
225    SkFontMgr_fontconfig(SkFontConfigInterface* fci)
226        : fFCI(fci)
227        , fFamilyNames(fFCI->getFamilyNames()) {}
228
229    virtual ~SkFontMgr_fontconfig() {
230        SkSafeUnref(fFamilyNames);
231    }
232
233protected:
234    virtual int onCountFamilies() const SK_OVERRIDE {
235        return fFamilyNames->count();
236    }
237
238    virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
239        familyName->set(fFamilyNames->atStr(index));
240    }
241
242    virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
243        return this->onMatchFamily(fFamilyNames->atStr(index));
244    }
245
246    virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
247        FCLocker lock;
248
249        FcPattern* pattern = FcPatternCreate();
250
251        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
252#if 0
253        FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
254#endif
255        FcConfigSubstitute(NULL, pattern, FcMatchPattern);
256        FcDefaultSubstitute(pattern);
257
258        const char* post_config_family = get_name(pattern, FC_FAMILY);
259
260        FcResult result;
261        FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
262        if (!font_set) {
263            FcPatternDestroy(pattern);
264            return NULL;
265        }
266
267        int count;
268        FcPattern** match = MatchFont(font_set, post_config_family, &count);
269        if (!match) {
270            FcPatternDestroy(pattern);
271            FcFontSetDestroy(font_set);
272            return NULL;
273        }
274
275        FcPatternDestroy(pattern);
276
277        SkTDArray<FcPattern*> trimmedMatches;
278        for (int i = 0; i < count; ++i) {
279            const char* justName = find_just_name(get_name(match[i], FC_FILE));
280            if (!is_lower(*justName)) {
281                *trimmedMatches.append() = match[i];
282            }
283        }
284
285        SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
286                                             (trimmedMatches.begin(),
287                                              trimmedMatches.count()));
288        return sset;
289    }
290
291    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
292                                           const SkFontStyle&) const SK_OVERRIDE { return NULL; }
293    virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
294                                         const SkFontStyle&) const SK_OVERRIDE { return NULL; }
295
296    virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const SK_OVERRIDE { return NULL; }
297
298    virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
299        const size_t length = stream->getLength();
300        if (!length) {
301            return NULL;
302        }
303        if (length >= 1024 * 1024 * 1024) {
304            return NULL;  // don't accept too large fonts (>= 1GB) for safety.
305        }
306
307        // TODO should the caller give us the style or should we get it from freetype?
308        SkTypeface::Style style = SkTypeface::kNormal;
309        bool isFixedWidth = false;
310        if (!find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
311            return NULL;
312        }
313
314        SkTypeface* face = FontConfigTypeface::Create(style, isFixedWidth, stream);
315        return face;
316    }
317
318    virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
319        SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
320        return stream.get() ? this->createFromStream(stream, ttcIndex) : NULL;
321    }
322
323    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
324                                               unsigned styleBits) const SK_OVERRIDE {
325        FCLocker lock;
326        return FontConfigTypeface::LegacyCreateTypeface(NULL, familyName,
327                                                  (SkTypeface::Style)styleBits);
328    }
329};
330
331SkFontMgr* SkFontMgr::Factory() {
332    SkFontConfigInterface* fci = RefFCI();
333    return fci ? SkNEW_ARGS(SkFontMgr_fontconfig, (fci)) : NULL;
334}
335