• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2008 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 // -----------------------------------------------------------------------------
11 // This file provides implementations of the font resolution members of
12 // SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
13 // on Linux systems and handles configuration, parsing and caching issues
14 // involved with enumerating and matching fonts.
15 //
16 // [1] http://fontconfig.org
17 // -----------------------------------------------------------------------------
18 
19 #include <map>
20 #include <string>
21 
22 #include <fontconfig/fontconfig.h>
23 
24 #include "SkFontHost.h"
25 #include "SkStream.h"
26 
27 // This is an extern from SkFontHost_FreeType
28 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
29 
30 // -----------------------------------------------------------------------------
31 // The rest of Skia requires that fonts be identified by a unique unsigned id
32 // and that we be able to load them given the id. What we actually get from
33 // fontconfig is the filename of the font so we keep a locked map from
34 // filenames to fileid numbers and back.
35 //
36 // Note that there's also a unique id in the SkTypeface. This is unique over
37 // both filename and style. Thus we encode that id as (fileid << 8) | style.
38 // Although truetype fonts can support multiple faces in a single file, at the
39 // moment Skia doesn't.
40 // -----------------------------------------------------------------------------
41 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
42 static std::map<std::string, unsigned> global_fc_map;
43 static std::map<unsigned, std::string> global_fc_map_inverted;
44 static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
45 static unsigned global_fc_map_next_id = 0;
46 
UniqueIdToFileId(unsigned uniqueid)47 static unsigned UniqueIdToFileId(unsigned uniqueid)
48 {
49     return uniqueid >> 8;
50 }
51 
UniqueIdToStyle(unsigned uniqueid)52 static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
53 {
54     return static_cast<SkTypeface::Style>(uniqueid & 0xff);
55 }
56 
FileIdAndStyleToUniqueId(unsigned fileid,SkTypeface::Style style)57 static unsigned FileIdAndStyleToUniqueId(unsigned fileid,
58                                          SkTypeface::Style style)
59 {
60     SkASSERT((style & 0xff) == style);
61     return (fileid << 8) | static_cast<int>(style);
62 }
63 
64 // -----------------------------------------------------------------------------
65 // Normally we only return exactly the font asked for. In last-resort cases,
66 // the request is for one of the basic font names "Sans", "Serif" or
67 // "Monospace". This function tells you whether a given request is for such a
68 // fallback.
69 // -----------------------------------------------------------------------------
IsFallbackFontAllowed(const char * request)70 static bool IsFallbackFontAllowed(const char* request)
71 {
72     return strcmp(request, "Sans") == 0 ||
73            strcmp(request, "Serif") == 0 ||
74            strcmp(request, "Monospace") == 0;
75 }
76 
77 class FontConfigTypeface : public SkTypeface {
78 public:
FontConfigTypeface(Style style,uint32_t id)79     FontConfigTypeface(Style style, uint32_t id)
80         : SkTypeface(style, id)
81     { }
82 };
83 
84 // -----------------------------------------------------------------------------
85 // Find a matching font where @type (one of FC_*) is equal to @value. For a
86 // list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
87 // The variable arguments are a list of triples, just like the first three
88 // arguments, and must be NULL terminated.
89 //
90 // For example,
91 //   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf",
92 //                   NULL);
93 // -----------------------------------------------------------------------------
FontMatch(const char * type,FcType vtype,const void * value,...)94 static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
95                             ...)
96 {
97     va_list ap;
98     va_start(ap, value);
99 
100     FcPattern* pattern = FcPatternCreate();
101     const char* family_requested = NULL;
102 
103     for (;;) {
104         FcValue fcvalue;
105         fcvalue.type = vtype;
106         switch (vtype) {
107             case FcTypeString:
108                 fcvalue.u.s = (FcChar8*) value;
109                 break;
110             case FcTypeInteger:
111                 fcvalue.u.i = (int)(intptr_t)value;
112                 break;
113             default:
114                 SkDEBUGFAIL("FontMatch unhandled type");
115         }
116         FcPatternAdd(pattern, type, fcvalue, 0);
117 
118         if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
119             family_requested = (const char*) value;
120 
121         type = va_arg(ap, const char *);
122         if (!type)
123             break;
124         // FcType is promoted to int when passed through ...
125         vtype = static_cast<FcType>(va_arg(ap, int));
126         value = va_arg(ap, const void *);
127     };
128     va_end(ap);
129 
130     FcConfigSubstitute(0, pattern, FcMatchPattern);
131     FcDefaultSubstitute(pattern);
132 
133     // Font matching:
134     // CSS often specifies a fallback list of families:
135     //    font-family: a, b, c, serif;
136     // However, fontconfig will always do its best to find *a* font when asked
137     // for something so we need a way to tell if the match which it has found is
138     // "good enough" for us. Otherwise, we can return NULL which gets piped up
139     // and lets WebKit know to try the next CSS family name. However, fontconfig
140     // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
141     // wish to support that.
142     //
143     // Thus, if a specific family is requested we set @family_requested. Then we
144     // record two strings: the family name after config processing and the
145     // family name after resolving. If the two are equal, it's a good match.
146     //
147     // So consider the case where a user has mapped Arial to Helvetica in their
148     // config.
149     //    requested family: "Arial"
150     //    post_config_family: "Helvetica"
151     //    post_match_family: "Helvetica"
152     //      -> good match
153     //
154     // and for a missing font:
155     //    requested family: "Monaco"
156     //    post_config_family: "Monaco"
157     //    post_match_family: "Times New Roman"
158     //      -> BAD match
159     //
160     // However, we special-case fallback fonts; see IsFallbackFontAllowed().
161     FcChar8* post_config_family;
162     FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
163 
164     FcResult result;
165     FcPattern* match = FcFontMatch(0, pattern, &result);
166     if (!match) {
167         FcPatternDestroy(pattern);
168         return NULL;
169     }
170 
171     FcChar8* post_match_family;
172     FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
173     const bool family_names_match =
174         !family_requested ?
175         true :
176         strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;
177 
178     FcPatternDestroy(pattern);
179 
180     if (!family_names_match && !IsFallbackFontAllowed(family_requested)) {
181         FcPatternDestroy(match);
182         return NULL;
183     }
184 
185     return match;
186 }
187 
188 // -----------------------------------------------------------------------------
189 // Check to see if the filename has already been assigned a fileid and, if so,
190 // use it. Otherwise, assign one. Return the resulting fileid.
191 // -----------------------------------------------------------------------------
FileIdFromFilename(const char * filename)192 static unsigned FileIdFromFilename(const char* filename)
193 {
194     SkAutoMutexAcquire ac(global_fc_map_lock);
195 
196     std::map<std::string, unsigned>::const_iterator i =
197         global_fc_map.find(filename);
198     if (i == global_fc_map.end()) {
199         const unsigned fileid = global_fc_map_next_id++;
200         global_fc_map[filename] = fileid;
201         global_fc_map_inverted[fileid] = filename;
202         return fileid;
203     } else {
204         return i->second;
205     }
206 }
207 
208 // static
CreateTypeface(const SkTypeface * familyFace,const char familyName[],const void * data,size_t bytelength,SkTypeface::Style style)209 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
210                                        const char familyName[],
211                                        const void* data, size_t bytelength,
212                                        SkTypeface::Style style)
213 {
214     const char* resolved_family_name = NULL;
215     FcPattern* face_match = NULL;
216 
217     {
218         SkAutoMutexAcquire ac(global_fc_map_lock);
219         FcInit();
220     }
221 
222     if (familyFace) {
223         // Here we use the inverted global id map to find the filename from the
224         // SkTypeface object. Given the filename we can ask fontconfig for the
225         // familyname of the font.
226         SkAutoMutexAcquire ac(global_fc_map_lock);
227 
228         const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID());
229         std::map<unsigned, std::string>::const_iterator i =
230             global_fc_map_inverted.find(fileid);
231         if (i == global_fc_map_inverted.end())
232             return NULL;
233 
234         FcInit();
235         face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(),
236                                NULL);
237 
238         if (!face_match)
239             return NULL;
240         FcChar8* family;
241         if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
242             FcPatternDestroy(face_match);
243             return NULL;
244         }
245         // At this point, @family is pointing into the @face_match object so we
246         // cannot release it yet.
247 
248         resolved_family_name = reinterpret_cast<char*>(family);
249     } else if (familyName) {
250         resolved_family_name = familyName;
251     } else {
252         return NULL;
253     }
254 
255     // At this point, we have a resolved_family_name from somewhere
256     SkASSERT(resolved_family_name);
257 
258     const int bold = style & SkTypeface::kBold ?
259                      FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
260     const int italic = style & SkTypeface::kItalic ?
261                        FC_SLANT_ITALIC : FC_SLANT_ROMAN;
262     FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
263                                  FC_WEIGHT, FcTypeInteger, bold,
264                                  FC_SLANT, FcTypeInteger, italic,
265                                  NULL);
266     if (face_match)
267         FcPatternDestroy(face_match);
268 
269     if (!match)
270         return NULL;
271 
272     FcChar8* filename;
273     if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
274         FcPatternDestroy(match);
275         return NULL;
276     }
277     // Now @filename is pointing into @match
278 
279     const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
280     const unsigned id = FileIdAndStyleToUniqueId(fileid, style);
281     SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
282     FcPatternDestroy(match);
283 
284     {
285         SkAutoMutexAcquire ac(global_fc_map_lock);
286         global_fc_typefaces[id] = typeface;
287     }
288 
289     return typeface;
290 }
291 
292 // static
CreateTypefaceFromStream(SkStream * stream)293 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
294 {
295     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
296     return NULL;
297 }
298 
299 // static
CreateTypefaceFromFile(const char path[])300 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
301 {
302     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
303     return NULL;
304 }
305 
306 // static
OpenStream(uint32_t id)307 SkStream* SkFontHost::OpenStream(uint32_t id)
308 {
309     SkAutoMutexAcquire ac(global_fc_map_lock);
310     const unsigned fileid = UniqueIdToFileId(id);
311 
312     std::map<unsigned, std::string>::const_iterator i =
313         global_fc_map_inverted.find(fileid);
314     if (i == global_fc_map_inverted.end())
315         return NULL;
316 
317     return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
318 }
319 
GetFileName(SkFontID fontID,char path[],size_t length,int32_t * index)320 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
321                                int32_t* index) {
322     SkAutoMutexAcquire ac(global_fc_map_lock);
323     const unsigned fileid = UniqueIdToFileId(fontID);
324 
325     std::map<unsigned, std::string>::const_iterator i =
326     global_fc_map_inverted.find(fileid);
327     if (i == global_fc_map_inverted.end()) {
328         return 0;
329     }
330 
331     const std::string& str = i->second;
332     if (path) {
333         memcpy(path, str.c_str(), SkMin32(str.size(), length));
334     }
335     if (index) {    // TODO: check if we're in a TTC
336         *index = 0;
337     }
338     return str.size();
339 }
340 
Serialize(const SkTypeface *,SkWStream *)341 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
342     SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
343 }
344 
Deserialize(SkStream * stream)345 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
346     SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
347     return NULL;
348 }
349 
NextLogicalFont(SkFontID currFontID,SkFontID origFontID)350 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
351     // We don't handle font fallback, WebKit does.
352     return 0;
353 }
354 
355