OLD | NEW |
| (Empty) |
1 /* libs/graphics/ports/SkFontHost_fontconfig.cpp | |
2 ** | |
3 ** Copyright 2008, Google Inc. | |
4 ** | |
5 ** Licensed under the Apache License, Version 2.0 (the "License"); | |
6 ** you may not use this file except in compliance with the License. | |
7 ** You may obtain a copy of the License at | |
8 ** | |
9 ** http://www.apache.org/licenses/LICENSE-2.0 | |
10 ** | |
11 ** Unless required by applicable law or agreed to in writing, software | |
12 ** distributed under the License is distributed on an "AS IS" BASIS, | |
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 ** See the License for the specific language governing permissions and | |
15 ** limitations under the License. | |
16 */ | |
17 | |
18 // ----------------------------------------------------------------------------- | |
19 // This file provides implementations of the font resolution members of | |
20 // SkFontHost by using the fontconfig[1] library. Fontconfig is usually found | |
21 // on Linux systems and handles configuration, parsing and caching issues | |
22 // involved with enumerating and matching fonts. | |
23 // | |
24 // [1] http://fontconfig.org | |
25 // ----------------------------------------------------------------------------- | |
26 | |
27 #include <map> | |
28 #include <string> | |
29 | |
30 #include <fontconfig/fontconfig.h> | |
31 | |
32 #include "SkDescriptor.h" | |
33 #include "SkFontHost.h" | |
34 #include "SkMMapStream.h" | |
35 #include "SkPaint.h" | |
36 #include "SkStream.h" | |
37 #include "SkString.h" | |
38 #include "SkThread.h" | |
39 #include "SkTSearch.h" | |
40 | |
41 // This is an extern from SkFontHost_FreeType | |
42 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); | |
43 | |
44 // ----------------------------------------------------------------------------- | |
45 // The rest of Skia requires that fonts be identified by a unique unsigned id | |
46 // and that we be able to load them given the id. What we actually get from | |
47 // fontconfig is the filename of the font so we keep a locked map from | |
48 // filenames to fileid numbers and back. | |
49 // | |
50 // Note that there's also a unique id in the SkTypeface. This is unique over | |
51 // both filename and style. Thus we encode that id as (fileid << 8) | style. | |
52 // Although truetype fonts can support multiple faces in a single file, at the | |
53 // moment Skia doesn't. | |
54 // ----------------------------------------------------------------------------- | |
55 static SkMutex global_fc_map_lock; | |
56 static std::map<std::string, unsigned> global_fc_map; | |
57 static std::map<unsigned, std::string> global_fc_map_inverted; | |
58 static std::map<uint32_t, SkTypeface *> global_fc_typefaces; | |
59 static unsigned global_fc_map_next_id = 0; | |
60 | |
61 // This is the maximum size of the font cache. | |
62 static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB | |
63 | |
64 static unsigned UniqueIdToFileId(unsigned uniqueid) | |
65 { | |
66 return uniqueid >> 8; | |
67 } | |
68 | |
69 static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) | |
70 { | |
71 return static_cast<SkTypeface::Style>(uniqueid & 0xff); | |
72 } | |
73 | |
74 static unsigned FileIdAndStyleToUniqueId(unsigned fileid, | |
75 SkTypeface::Style style) | |
76 { | |
77 SkASSERT(style & 0xff == style); | |
78 return (fileid << 8) | static_cast<int>(style); | |
79 } | |
80 | |
81 class FontConfigTypeface : public SkTypeface { | |
82 public: | |
83 FontConfigTypeface(Style style, uint32_t id) | |
84 : SkTypeface(style, id) | |
85 { } | |
86 }; | |
87 | |
88 // ----------------------------------------------------------------------------- | |
89 // Find a matching font where @type (one of FC_*) is equal to @value. For a | |
90 // list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27. | |
91 // The variable arguments are a list of triples, just like the first three | |
92 // arguments, and must be NULL terminated. | |
93 // | |
94 // For example, FontMatchString(FC_FILE, FcTypeString, | |
95 // "/usr/share/fonts/myfont.ttf", NULL); | |
96 // ----------------------------------------------------------------------------- | |
97 static FcPattern* FontMatch(bool is_fallback, | |
98 const char* type, FcType vtype, const void* value, | |
99 ...) | |
100 { | |
101 va_list ap; | |
102 va_start(ap, value); | |
103 | |
104 FcPattern* pattern = FcPatternCreate(); | |
105 bool family_requested = false; | |
106 | |
107 for (;;) { | |
108 FcValue fcvalue; | |
109 fcvalue.type = vtype; | |
110 switch (vtype) { | |
111 case FcTypeString: | |
112 fcvalue.u.s = (FcChar8*) value; | |
113 break; | |
114 case FcTypeInteger: | |
115 fcvalue.u.i = (int) value; | |
116 break; | |
117 default: | |
118 SkASSERT(!"FontMatch unhandled type"); | |
119 } | |
120 FcPatternAdd(pattern, type, fcvalue, 0); | |
121 | |
122 if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0) | |
123 family_requested = true; | |
124 | |
125 type = va_arg(ap, const char *); | |
126 if (!type) | |
127 break; | |
128 // FcType is promoted to int when passed through ... | |
129 vtype = static_cast<FcType>(va_arg(ap, int)); | |
130 value = va_arg(ap, const void *); | |
131 }; | |
132 va_end(ap); | |
133 | |
134 FcConfigSubstitute(0, pattern, FcMatchPattern); | |
135 FcDefaultSubstitute(pattern); | |
136 | |
137 // Font matching: | |
138 // CSS often specifies a fallback list of families: | |
139 // font-family: a, b, c, serif; | |
140 // However, fontconfig will always do its best to find *a* font when asked | |
141 // for something so we need a way to tell if the match which it has found is | |
142 // "good enough" for us. Otherwise, we can return NULL which gets piped up | |
143 // and lets WebKit know to try the next CSS family name. However, fontconfig | |
144 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we | |
145 // wish to support that. | |
146 // | |
147 // Thus, if a specific family is requested we set @family_requested. Then we | |
148 // record two strings: the family name after config processing and the | |
149 // family name after resolving. If the two are equal, it's a good match. | |
150 // | |
151 // So consider the case where a user has mapped Arial to Helvetica in their | |
152 // config. | |
153 // requested family: "Arial" | |
154 // post_config_family: "Helvetica" | |
155 // post_match_family: "Helvetica" | |
156 // -> good match | |
157 // | |
158 // and for a missing font: | |
159 // requested family: "Monaco" | |
160 // post_config_family: "Monaco" | |
161 // post_match_family: "Times New Roman" | |
162 // -> BAD match | |
163 FcChar8* post_config_family; | |
164 FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); | |
165 | |
166 FcResult result; | |
167 FcPattern* match = FcFontMatch(0, pattern, &result); | |
168 if (!match) { | |
169 FcPatternDestroy(pattern); | |
170 return NULL; | |
171 } | |
172 | |
173 FcChar8* post_match_family; | |
174 FcPatternGetString(match, FC_FAMILY, 0, &post_match_family); | |
175 const bool family_names_match = | |
176 !family_requested ? | |
177 true : | |
178 strcasecmp((char *) post_config_family, (char *) post_match_family) == 0
; | |
179 | |
180 FcPatternDestroy(pattern); | |
181 | |
182 if (!family_names_match && !is_fallback) { | |
183 FcPatternDestroy(match); | |
184 return NULL; | |
185 } | |
186 | |
187 return match; | |
188 } | |
189 | |
190 // ----------------------------------------------------------------------------- | |
191 // Check to see if the filename has already been assigned a fileid and, if so, | |
192 // use it. Otherwise, assign one. Return the resulting fileid. | |
193 // ----------------------------------------------------------------------------- | |
194 static unsigned FileIdFromFilename(const char* filename) | |
195 { | |
196 SkAutoMutexAcquire ac(global_fc_map_lock); | |
197 | |
198 std::map<std::string, unsigned>::const_iterator i = | |
199 global_fc_map.find(filename); | |
200 if (i == global_fc_map.end()) { | |
201 const unsigned fileid = global_fc_map_next_id++; | |
202 global_fc_map[filename] = fileid; | |
203 global_fc_map_inverted[fileid] = filename; | |
204 return fileid; | |
205 } else { | |
206 return i->second; | |
207 } | |
208 } | |
209 | |
210 SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, | |
211 const char familyName[], | |
212 SkTypeface::Style style) | |
213 { | |
214 const char* resolved_family_name = NULL; | |
215 FcPattern* face_match = NULL; | |
216 | |
217 if (familyFace) { | |
218 // Here we use the inverted global id map to find the filename from the | |
219 // SkTypeface object. Given the filename we can ask fontconfig for the | |
220 // familyname of the font. | |
221 SkAutoMutexAcquire ac(global_fc_map_lock); | |
222 | |
223 const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID()); | |
224 std::map<unsigned, std::string>::const_iterator i = | |
225 global_fc_map_inverted.find(fileid); | |
226 if (i == global_fc_map_inverted.end()) | |
227 return NULL; | |
228 | |
229 FcInit(); | |
230 face_match = FontMatch(false, FC_FILE, FcTypeString, i->second.c_str(), | |
231 NULL); | |
232 | |
233 if (!face_match) | |
234 return NULL; | |
235 FcChar8* family; | |
236 if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) { | |
237 FcPatternDestroy(face_match); | |
238 return NULL; | |
239 } | |
240 // At this point, @family is pointing into the @face_match object so we | |
241 // cannot release it yet. | |
242 | |
243 resolved_family_name = reinterpret_cast<char*>(family); | |
244 } else if (familyName) { | |
245 resolved_family_name = familyName; | |
246 } else { | |
247 return NULL; | |
248 } | |
249 | |
250 { | |
251 SkAutoMutexAcquire ac(global_fc_map_lock); | |
252 FcInit(); | |
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(false, | |
263 FC_FAMILY, FcTypeString, resolved_family_name, | |
264 FC_WEIGHT, FcTypeInteger, bold, | |
265 FC_SLANT, FcTypeInteger, italic, | |
266 NULL); | |
267 if (face_match) | |
268 FcPatternDestroy(face_match); | |
269 | |
270 if (!match) | |
271 return NULL; | |
272 | |
273 FcChar8* filename; | |
274 if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { | |
275 FcPatternDestroy(match); | |
276 return NULL; | |
277 } | |
278 // Now @filename is pointing into @match | |
279 | |
280 const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename)
); | |
281 const unsigned id = FileIdAndStyleToUniqueId(fileid, style); | |
282 SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); | |
283 FcPatternDestroy(match); | |
284 | |
285 { | |
286 SkAutoMutexAcquire ac(global_fc_map_lock); | |
287 global_fc_typefaces[id] = typeface; | |
288 } | |
289 | |
290 return typeface; | |
291 } | |
292 | |
293 SkTypeface* SkFontHost::ResolveTypeface(uint32_t id) | |
294 { | |
295 SkAutoMutexAcquire ac(global_fc_map_lock); | |
296 const std::map<uint32_t, SkTypeface *>::iterator | |
297 i = global_fc_typefaces.find(id); | |
298 | |
299 if (i == global_fc_typefaces.end()) | |
300 return NULL; | |
301 return i->second; | |
302 } | |
303 | |
304 SkStream* SkFontHost::OpenStream(uint32_t id) | |
305 { | |
306 SkAutoMutexAcquire ac(global_fc_map_lock); | |
307 const unsigned fileid = UniqueIdToFileId(id); | |
308 | |
309 std::map<unsigned, std::string>::const_iterator i = | |
310 global_fc_map_inverted.find(fileid); | |
311 if (i == global_fc_map_inverted.end()) | |
312 return NULL; | |
313 | |
314 return SkNEW_ARGS(SkFILEStream, (i->second.c_str())); | |
315 } | |
316 | |
317 void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream) | |
318 { | |
319 } | |
320 | |
321 SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) | |
322 { | |
323 SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); | |
324 return NULL; | |
325 } | |
326 | |
327 SkTypeface* SkFontHost::Deserialize(SkStream* stream) { | |
328 SkASSERT(!"SkFontHost::Deserialize unimplemented"); | |
329 return NULL; | |
330 } | |
331 | |
332 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { | |
333 SkASSERT(!"SkFontHost::Serialize unimplemented"); | |
334 } | |
335 | |
336 SkScalerContext* SkFontHost::CreateFallbackScalerContext | |
337 (const SkScalerContext::Rec& rec) { | |
338 FcPattern* match = FontMatch(true, FC_FAMILY, FcTypeString, "serif", | |
339 NULL); | |
340 | |
341 // This will fail when we have no fonts on the system. | |
342 SkASSERT(match); | |
343 | |
344 FcChar8* filename; | |
345 if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { | |
346 FcPatternDestroy(match); | |
347 return NULL; | |
348 } | |
349 // Now @filename is pointing into @match | |
350 | |
351 const unsigned id = FileIdFromFilename(reinterpret_cast<char*>(filename)); | |
352 FcPatternDestroy(match); | |
353 | |
354 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); | |
355 SkDescriptor* desc = ad.getDesc(); | |
356 | |
357 desc->init(); | |
358 SkScalerContext::Rec* newRec = | |
359 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, | |
360 sizeof(rec), &rec); | |
361 newRec->fFontID = id; | |
362 desc->computeChecksum(); | |
363 | |
364 return SkFontHost::CreateScalerContext(desc); | |
365 } | |
366 | |
367 /////////////////////////////////////////////////////////////////////////////// | |
368 | |
369 size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) | |
370 { | |
371 if (sizeAllocatedSoFar > kFontCacheMemoryBudget) | |
372 return sizeAllocatedSoFar - kFontCacheMemoryBudget; | |
373 else | |
374 return 0; // nothing to do | |
375 } | |
OLD | NEW |