Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/ports/SkFontMgr_fontconfig.cpp

Issue 396143004: Add a working SkFontMgr_fontconfig. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Fix ttc index and descriptor. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2014 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 "SkDataTable.h"
9 #include "SkFontDescriptor.h"
10 #include "SkFontHost_FreeType_common.h"
11 #include "SkFontMgr.h"
12 #include "SkFontStyle.h"
13 #include "SkMath.h"
14 #include "SkString.h"
15 #include "SkStream.h"
16 #include "SkTDArray.h"
17 #include "SkThread.h"
18 #include "SkTypefaceCache.h"
19 #include "SkOSFile.h"
20
21 #include <fontconfig/fontconfig.h>
22
23 // FC_POSTSCRIPT_NAME was added with b561ff20 which ended up in 2.10.92
24 // Ubuntu 12.04 is on 2.8.0, 13.10 is on 2.10.93
25 // Debian 7 is on 2.9.0, 8 is on 2.11
26 // OpenSUSE 12.2 is on 2.9.0, 12.3 is on 2.10.2, 13.1 2.11.0
27 // Fedora 19 is on 2.10.93
28 #ifndef FC_POSTSCRIPT_NAME
29 # define FC_POSTSCRIPT_NAME "postscriptname"
30 #endif
31
32 #ifdef SK_DEBUG
33 # include "SkTLS.h"
34 #endif
35
36 /** Since FontConfig is poorly documented, this gives a high level overview:
37 *
38 * FcConfig is a handle to a FontConfig configuration instance. Each 'configura tion' is independent
39 * from any others which may exist. There exists a default global configuration which is created
40 * and destroyed by FcInit and FcFini, but this default should not normally be used.
41 * Instead, one should use FcConfigCreate and FcInit* to have a named local sta te.
42 *
43 * FcPatterns are {objectName -> [element]} (maps from object names to a list o f elements).
44 * Each element is some internal data plus an FcValue which is a variant (a uni on with a type tag).
45 * Lists of elements are not typed, except by convention. Any collection of FcV alues must be
46 * assumed to be heterogeneous by the code, but the code need not do anything p articularly
47 * interesting if the values go against convention.
48 *
49 * Somewhat like DirectWrite, FontConfig supports synthetics through FC_EMBOLDE N and FC_MATRIX.
50 * Like all synthetic information, such information must be passed with the fon t data.
51 */
52
53 namespace {
54
55 // Fontconfig is not threadsafe before 2.10.91. Before that, we lock with a glob al mutex.
56 // See http://skbug.com/1497 for background.
57 SK_DECLARE_STATIC_MUTEX(gFCMutex);
58
59 #ifdef SK_DEBUG
mtklein 2014/07/22 15:48:44 What's the idea here? So that we can test the loc
bungeman-skia 2014/07/22 22:09:57 Yes, I can't test the old stuff on my new machine,
60 void *CreateThreadFcLocked() { return SkNEW(bool); }
mtklein 2014/07/22 15:48:44 * with void?
bungeman-skia 2014/07/22 22:09:58 I tried that, but you aren't allowed to muck with
61 void DeleteThreadFcLocked(void* v) { SkDELETE(reinterpret_cast<bool*>(v)); }
mtklein 2014/07/22 15:48:43 I think you can get away with a simple static_cast
bungeman-skia 2014/07/22 22:09:58 A prvalue of type pointer to void (possibly cv-qua
62 # define THREAD_FC_LOCKED \
63 reinterpret_cast<bool*>(SkTLS::Get(CreateThreadFcLocked, DeleteThreadFcL ocked))
64 #endif
65
66 struct FCLocker {
67 // Assume FcGetVersion() has always been thread safe.
68
69 FCLocker() {
70 if (FcGetVersion() < 21091) {
71 gFCMutex.acquire();
72 fUnlock = true;
73 } else {
74 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED);
75 SkASSERT(0 == *threadLocked);
mtklein 2014/07/22 15:48:43 0 -> false?
bungeman-skia 2014/07/22 22:09:58 Done.
76 SkDEBUGCODE(*threadLocked = true);
77 }
78 }
79
80 ~FCLocker() {
81 if (FcGetVersion() < 21091) {
82 if (fUnlock) {
mtklein 2014/07/22 15:48:44 Do FcGetVersion() < 21091 and fUnlock ever evaluat
bungeman-skia 2014/07/22 22:09:58 Hmmm... yes, that does fall out of changing everyt
83 gFCMutex.release();
84 }
85 } else {
86 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED);
87 SkASSERT(1 == *threadLocked);
mtklein 2014/07/22 15:48:44 1 -> true?
bungeman-skia 2014/07/22 22:09:59 Done.
88 SkDEBUGCODE(*threadLocked = false);
89 }
90 }
91
92 static void AssertHeld() { SkDEBUGCODE(
93 if (FcGetVersion() < 21091) {
94 gFCMutex.assertHeld();
95 } else {
96 SkASSERT(true == *THREAD_FC_LOCKED);
97 }
98 ) }
99
100 private:
101 bool fUnlock;
102 };
103
104 } // namespace
105
106 template<typename T, void (*P)(T*)> void FcTDestroy(T* t) {
107 FCLocker::AssertHeld();
108 P(t);
109 }
110 typedef SkAutoTCallVProc<FcConfig, FcTDestroy<FcConfig, FcConfigDestroy> > SkAut oFcConfig;
mtklein 2014/07/22 15:48:44 OCD these guys into columns?
bungeman-skia 2014/07/22 22:09:58 Your OCD is making fun of my OCD. When this sort o
111 typedef SkAutoTCallVProc<FcFontSet, FcTDestroy<FcFontSet, FcFontSetDestroy> > Sk AutoFcFontSet;
112 typedef SkAutoTCallVProc<FcLangSet, FcTDestroy<FcLangSet, FcLangSetDestroy> > Sk AutoFcLangSet;
113 typedef SkAutoTCallVProc<FcObjectSet, FcTDestroy<FcObjectSet, FcObjectSetDestroy > > SkAutoFcObjectSet;
114 typedef SkAutoTCallVProc<FcPattern, FcTDestroy<FcPattern, FcPatternDestroy> > Sk AutoFcPattern;
115
116 static int get_int(FcPattern* pattern, const char object[], int missing) {
117 int value;
118 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
mtklein 2014/07/22 15:48:44 It makes me so sad these don't take a const FcPatt
bungeman-skia 2014/07/22 22:09:58 Yeah, COM does the same thing, where 'const' is ne
119 return missing;
120 }
121 return value;
122 }
123
124 static const char* get_string(FcPattern* pattern, const char object[], const cha r* missing = "") {
125 FcChar8* value;
126 if (FcPatternGetString(pattern, object, 0, &value) != FcResultMatch) {
127 return missing;
128 }
129 return (const char*)value;
130 }
131
132 enum is_weak_return {
mtklein 2014/07/22 15:48:43 Funky type name and constant values for Skia?
bungeman-skia 2014/07/22 22:09:57 This is a cpp file!!! Eh, why not.
133 isWeak, isStrong, noId
134 };
135 /** Ideally there would exist a call like
136 * FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak);
137 *
138 * However, there is no such call and as of Fc 2.11.0 even FcPatternEquals igno res the weak bit.
139 * Currently, the only reliable way of finding the weak bit is by its effect on matching.
140 * The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME o bject values.
141 * A element with the weak bit is scored after FC_LANG, without the weak bit is scored before.
142 * Note that the weak bit is stored on the element, not on the value it holds.
143 */
144 static is_weak_return is_weak(FcPattern* pattern, const char object[], int id) {
145 FCLocker::AssertHeld();
146
147 FcResult result;
148
149 // Create a copy of the pattern with only the value 'pattern'['object'['id'] ] in it.
150 // Internally, FontConfig pattern objects are linked lists, so faster to rem ove from head.
151 SkAutoFcObjectSet requestedObjectOnly(FcObjectSetBuild(object, NULL));
152 SkAutoFcPattern minimal(FcPatternFilter(pattern, requestedObjectOnly));
153 FcBool hasId = true;
154 for (int i = 0; hasId && i < id; ++i) {
155 hasId = FcPatternRemove(minimal, object, 0);
156 }
157 if (!hasId) {
158 return noId;
159 }
160 FcValue value;
161 result = FcPatternGet(minimal, object, 0, &value);
162 if (result != FcResultMatch) {
163 return noId;
164 }
165 while (hasId) {
166 hasId = FcPatternRemove(minimal, object, 1);
167 }
168
169 // Create a font set with two patterns.
170 // 1. the same 'object' as minimal and a lang object with only 'nomatchlang' .
171 // 2. a different 'object' from minimal and a lang object with only 'matchla ng'.
172 SkAutoFcFontSet fontSet(FcFontSetCreate());
173
174 SkAutoFcLangSet strongLangSet(FcLangSetCreate());
175 FcLangSetAdd(strongLangSet, (const FcChar8*)"nomatchlang");
176 SkAutoFcPattern strong(FcPatternDuplicate(minimal));
177 FcPatternAddLangSet(strong, FC_LANG, strongLangSet);
178
179 SkAutoFcLangSet weakLangSet(FcLangSetCreate());
180 FcLangSetAdd(weakLangSet, (const FcChar8*)"matchlang");
181 SkAutoFcPattern weak(FcPatternCreate());
182 FcPatternAddString(weak, object, (const FcChar8*)"nomatchstring");
183 FcPatternAddLangSet(weak, FC_LANG, weakLangSet);
184
185 FcFontSetAdd(fontSet, strong.detach());
186 FcFontSetAdd(fontSet, weak.detach());
187
188 // Add 'matchlang' to the copy of the pattern.
189 FcPatternAddLangSet(minimal, FC_LANG, weakLangSet);
190
191 // Run a match against the copy of the pattern.
192 // If the 'id' was weak, then we should match the pattern with 'matchlang'.
193 // If the 'id' was strong, then we should match the pattern with 'nomatchlan g'.
194
195 // Note that this config is only used for FcFontRenderPrepare, which we don' t even want.
196 // However, there appears to be no way to match/sort without it.
197 SkAutoFcConfig config(FcConfigCreate());
198 SkAutoFcPattern match(FcFontSetMatch(config, &fontSet, 1, minimal, &result)) ;
199
200 FcLangSet* matchLangSet;
201 FcPatternGetLangSet(match, FC_LANG, 0, &matchLangSet);
202 return FcLangEqual == FcLangSetHasLang(matchLangSet, (const FcChar8*)"matchl ang")
203 ? isWeak : isStrong;
204 }
205
206 /** Removes weak elements from either FC_FAMILY or FC_POSTSCRIPT_NAME objects in the property.
207 * This can be quite expensive, and should not be used more than once per font lookup.
208 * This removes all of the weak elements after the last strong element.
209 */
210 static void remove_weak(FcPattern* pattern, const char object[]) {
211 FCLocker::AssertHeld();
212
213 SkAutoFcObjectSet requestedObjectOnly(FcObjectSetBuild(object, NULL));
214 SkAutoFcPattern minimal(FcPatternFilter(pattern, requestedObjectOnly));
215
216 int lastStrongId = -1;
217 int numIds;
218 is_weak_return result;
219 for (int id = 0; ; ++id) {
220 result = is_weak(minimal, object, 0);
221 if (noId == result) {
222 numIds = id;
223 break;
224 }
225 if (isStrong == result) {
226 lastStrongId = id;
227 }
228 SkAssertResult(FcPatternRemove(minimal, object, 0));
229 }
230
231 // If they were all weak, then leave the pattern alone.
232 if (lastStrongId < 0) {
233 return;
234 }
235
236 // Remove everything after the last strong.
237 for (int id = lastStrongId + 1; id < numIds; ++id) {
238 SkAssertResult(FcPatternRemove(pattern, object, lastStrongId + 1));
239 }
240 }
241
242 static int map_range(SkFixed value,
243 SkFixed old_min, SkFixed old_max,
244 SkFixed new_min, SkFixed new_max)
245 {
246 SkASSERT(old_min < old_max);
247 SkASSERT(new_min <= new_max);
248 return new_min + SkMulDiv(value - old_min, new_max - new_min,
249 old_max - old_min);
250 }
251
252 static int ave(SkFixed a, SkFixed b) {
253 return SkFixedAve(a, b);
254 }
255
256 struct MapRanges {
257 SkFixed old_val;
258 SkFixed new_val;
259 };
260
261 static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int range sCount) {
262 // -Inf to [0]
263 if (val < ranges[0].old_val)
mtklein 2014/07/22 15:48:43 {} around all these ifs?
bungeman-skia 2014/07/22 22:09:57 Done.
264 return ranges[0].new_val;
265
266 // Linear from [i] to ave([i], [i+1]), then from ave([i], [i+1]) to [i+1]
267 for (int i = 0; i < rangesCount - 1; ++i) {
mtklein 2014/07/22 15:48:43 Is it ever worth doing some o(N) search here?
bungeman-skia 2014/07/22 22:09:59 Probably not.
268 if (val < ave(ranges[i].old_val, ranges[i+1].old_val))
269 return map_range(val, ranges[i].old_val, ave(ranges[i].old_val, rang es[i+1].old_val),
270 ranges[i].new_val, ave(ranges[i].new_val, rang es[i+1].new_val));
271 if (val < ranges[i+1].old_val)
272 return map_range(val, ave(ranges[i].old_val, ranges[i+1].old_val), r anges[i+1].old_val,
273 ave(ranges[i].new_val, ranges[i+1].new_val), r anges[i+1].new_val);
274 }
275
276 // From [n] to +Inf
277 // if (fcweight < Inf)
278 return ranges[rangesCount-1].new_val;
279 }
280
281 static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
282 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesC ount));
283 }
284
285 template<int n> struct SkTFixed {
286 SK_COMPILE_ASSERT(-32768 <= n && n <= 32767, SkTFixed_n_not_in_range);
287 static const SkFixed value = static_cast<SkFixed>(n << 16);
288 };
289
290 static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
291 typedef SkFontStyle SkFS;
292
293 static const MapRanges weightRanges[] = {
294 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value } ,
mtklein 2014/07/22 15:48:43 Line all these guys up too?
bungeman-skia 2014/07/22 22:09:58 Generally, I'm against monospace ascii art, but si
295 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weig ht>::value },
296 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
297 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::va lue },
298 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::val ue },
299 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>: :value },
300 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value } ,
301 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight >::value },
302 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
303 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
304 };
305 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
306 weightRanges, SK_ARRAY_COUNT(weightRanges));
307
308 static const MapRanges widthRanges[] = {
309 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondens ed_Width>::value },
310 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondens ed_Width>::value },
311 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>: :value },
312 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed _Width>::value },
313 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
314 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_W idth>::value },
315 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::v alue },
316 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded _Width>::value },
317 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltaExpanded_ Width>::value },
318 };
319 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
320 widthRanges, SK_ARRAY_COUNT(widthRanges));
321
322 SkFS::Slant slant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN) > 0
323 ? SkFS::kItalic_Slant
324 : SkFS::kUpright_Slant;
325
326 return SkFontStyle(weight, width, slant);
327 }
328
329 static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
330 FCLocker::AssertHeld();
331
332 typedef SkFontStyle SkFS;
333
334 static const MapRanges weightRanges[] = {
335 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value } ,
336 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIG HT>::value },
337 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
338 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::va lue },
339 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::val ue },
340 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>: :value },
341 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value } ,
342 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD >::value },
343 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
344 { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
345 };
346 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightR anges));
347
348 static const MapRanges widthRanges[] = {
349 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRAC ONDENSED>::value },
350 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRAC ONDENSED>::value },
351 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>: :value },
352 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICON DENSED>::value },
353 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
354 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPA NDED>::value },
355 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::v alue },
356 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEX PANDED>::value },
357 { SkTFixed<SkFS::kUltaExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXP ANDED>::value },
358 };
359 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRange s));
360
361 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
362 FcPatternAddInteger(pattern, FC_WIDTH, width);
363 FcPatternAddInteger(pattern, FC_SLANT, style.isItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
364 }
365
366 static SkTypeface::Style sktypefacestyle_from_fcpattern(FcPattern* pattern) {
367 int fcweight = get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR);
368 int fcslant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN);
369 return (SkTypeface::Style)((fcweight >= FC_WEIGHT_BOLD ? SkTypeface::kBold : 0) |
370 (fcslant > FC_SLANT_ROMAN ? SkTypeface::kItalic : 0));
371 }
372
373 class SkTypeface_stream : public SkTypeface_FreeType {
374 public:
375 /** @param stream does not ownership of the reference, does take ownership o f the stream. */
mtklein 2014/07/22 15:48:43 Awkward? Taking ownership of the stream sounds li
bungeman-skia 2014/07/22 22:09:58 Haha, 'will take a ref' sounds like it takes owner
376 SkTypeface_stream(SkTypeface::Style style, bool fixedWidth, int ttcIndex, Sk StreamAsset* stream)
377 : INHERITED(style, SkTypefaceCache::NewFontID(), fixedWidth)
378 , fStream(SkRef(stream))
379 , fIndex(ttcIndex)
380 { };
381
382 virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) co nst SK_OVERRIDE {
383 desc->setStyle(this->style());
384 *serialize = true;
385 }
386
387 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
388 *ttcIndex = fIndex;
389 return fStream->duplicate();
390 }
391
392 private:
393 SkAutoTUnref<SkStreamAsset> fStream;
394 int fIndex;
395
396 typedef SkTypeface_FreeType INHERITED;
397 };
398
399 class SkTypeface_fontconfig : public SkTypeface_FreeType {
400 public:
401 /** @param pattern takes ownership of the reference. */
402 static SkTypeface_fontconfig* Create(FcPattern* pattern) {
403 return SkNEW_ARGS(SkTypeface_fontconfig, (pattern));
404 }
405 mutable SkAutoFcPattern fPattern;
mtklein 2014/07/22 15:48:44 public, mutable, and sometimes we need to hold a g
bungeman-skia 2014/07/22 22:09:58 Yes, I could put it in a global linked list. I'd m
406
407 virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) co nst SK_OVERRIDE {
408 FCLocker lock;
409 desc->setFamilyName(get_string(fPattern, FC_FAMILY));
410 desc->setFontFileName(get_string(fPattern, FC_FILE));
411 desc->setFullName(get_string(fPattern, FC_FULLNAME));
412 desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME));
413 desc->setStyle(this->style());
414 *serialize = false;
415 }
416
417 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
418 FCLocker lock;
419 *ttcIndex = get_int(fPattern, FC_INDEX, 0);
420 return SkStream::NewFromFile(get_string(fPattern, FC_FILE));
421 }
422
423 virtual ~SkTypeface_fontconfig() {
424 // Hold the lock while unrefing the pattern.
425 FCLocker lock;
426 fPattern.reset();
427 }
428
429 private:
430 /** @param pattern takes ownership of the reference. */
431 SkTypeface_fontconfig(FcPattern* pattern)
432 : INHERITED(sktypefacestyle_from_fcpattern(pattern),
433 SkTypefaceCache::NewFontID(),
434 FC_PROPORTIONAL != get_int(pattern, FC_SPACING, FC_PROPORTIO NAL))
435 , fPattern(pattern)
436 { };
437
438 typedef SkTypeface_FreeType INHERITED;
439 };
440
441 class SkFontMgr_fontconfig : public SkFontMgr {
442 mutable SkAutoFcConfig fFC;
mtklein 2014/07/22 15:48:43 Ditto?
bungeman-skia 2014/07/22 22:09:58 What? This one's already private, what do you have
443 SkAutoTUnref<SkDataTable> fFamilyNames;
444
445 class StyleSet : public SkFontStyleSet {
446 public:
447 /** @param parent does not take ownership of the reference.
448 * @param fontSet takes ownership of the reference.
449 */
450 StyleSet(const SkFontMgr_fontconfig* parent, FcFontSet* fontSet)
451 : fFontMgr(SkRef(parent)), fFontSet(fontSet)
452 { }
453
454 virtual ~StyleSet() {
455 // Hold the lock while unrefing the font set.
456 FCLocker lock;
457 fFontSet.reset();
458 }
459
460 virtual int count() SK_OVERRIDE { return fFontSet->nfont; }
461
462 virtual void getStyle(int index, SkFontStyle* style, SkString* styleName ) SK_OVERRIDE {
463 if (index < 0 || fFontSet->nfont <= index) {
464 return;
465 }
466
467 FCLocker lock;
468 if (style) {
469 *style = skfontstyle_from_fcpattern(fFontSet->fonts[index]);
470 }
471 if (styleName) {
472 *styleName = get_string(fFontSet->fonts[index], FC_STYLE);
473 }
474 }
475
476 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
477 FCLocker lock;
478
479 FcPattern* match = fFontSet->fonts[index];
480 return fFontMgr->createTypefaceFromFcPattern(match);
481 }
482
483 virtual SkTypeface* matchStyle(const SkFontStyle& style) SK_OVERRIDE {
484 FCLocker lock;
485
486 SkAutoFcPattern pattern(FcPatternCreate());
487 if (NULL == pattern) {
488 return NULL;
489 }
490
491 fcpattern_from_skfontstyle(style, pattern);
492 FcConfigSubstitute(fFontMgr->fFC, pattern, FcMatchPattern);
493 FcDefaultSubstitute(pattern);
494
495 FcResult result;
496 SkAutoFcPattern match(FcFontSetMatch(fFontMgr->fFC, &fFontSet, 1, pa ttern, &result));
497 if (NULL == match) {
498 return NULL;
499 }
500
501 return fFontMgr->createTypefaceFromFcPattern(match);
502 }
503
504 private:
505 SkAutoTUnref<const SkFontMgr_fontconfig> fFontMgr;
506 SkAutoFcFontSet fFontSet;
507 };
508
509 static bool find_name(const SkTDArray<const char*>& list, const char* str) {
mtklein 2014/07/22 15:48:43 FindName, etc?
bungeman-skia 2014/07/22 22:09:58 Why do we name private static methods of classes (
510 int count = list.count();
511 for (int i = 0; i < count; ++i) {
512 if (!strcmp(list[i], str)) {
513 return true;
514 }
515 }
516 return false;
517 }
518
519 SkDataTable* get_family_names() {
mtklein 2014/07/22 15:48:43 getFamilyNames?
bungeman-skia 2014/07/22 22:09:59 Acknowledged.
520 FCLocker lock;
521
522 SkTDArray<const char*> names;
523 SkTDArray<size_t> sizes;
524
525 static const FcSetName fcNameSet[] = { FcSetSystem, FcSetApplication };
526 for (int setIndex = 0; setIndex < (int)SK_ARRAY_COUNT(fcNameSet); ++setI ndex) {
527 // Return value of FcConfigGetFonts must not be destroyed.
528 FcFontSet* allFonts(FcConfigGetFonts(fFC, fcNameSet[setIndex]));
529 if (NULL == allFonts) {
530 continue;
531 }
532
533 for (int fontIndex = 0; fontIndex < allFonts->nfont; ++fontIndex) {
534 FcPattern* current = allFonts->fonts[fontIndex];
535 for (int id = 0; ; ++id) {
536 FcChar8* fcFamilyName;
537 FcResult result = FcPatternGetString(current, FC_FAMILY, id, &fcFamilyName);
538 if (FcResultNoId == result) {
539 break;
540 }
541 if (FcResultMatch != result) {
542 continue;
543 }
544 const char* familyName = reinterpret_cast<const char*>(fcFam ilyName);
mtklein 2014/07/22 15:48:44 also probably can static_cast?
bungeman-skia 2014/07/22 22:09:58 Nope.
545 if (familyName && !find_name(names, familyName)) {
546 *names.append() = familyName;
547 *sizes.append() = strlen(familyName) + 1;
548 }
549 }
550 }
551 }
552
553 return SkDataTable::NewCopyArrays((void *const *const)names.begin(),
mtklein 2014/07/22 15:48:43 woah. Is that, a const pointer to a const pointer
bungeman-skia 2014/07/22 22:09:57 Done.
554 sizes.begin(), names.count());
555 }
556
557 static bool find_by_fc_pattern(SkTypeface* cached, SkTypeface::Style, void* ctx) {
558 SkTypeface_fontconfig* cshFace = reinterpret_cast<SkTypeface_fontconfig* >(cached);
mtklein 2014/07/22 15:48:44 Also should both safely static_cast, no?
bungeman-skia 2014/07/22 22:09:57 Done.
559 FcPattern* ctxPattern = reinterpret_cast<FcPattern*>(ctx);
560 return FcTrue == FcPatternEqual(cshFace->fPattern, ctxPattern);
561 }
562
563 mutable SkMutex fTFCacheMutex;
mtklein 2014/07/22 15:48:43 Can you break some of the bodies of these methods
bungeman-skia 2014/07/22 22:09:57 It did end up with ~350 lines in this block. Maybe
564 mutable SkTypefaceCache fTFCache;
565 /** Creates a typeface using a typeface cache.
566 * @param pattern a complete pattern from FcFontRenderPrepare.
567 */
568 SkTypeface* createTypefaceFromFcPattern(FcPattern* pattern) const {
569 FCLocker::AssertHeld();
570 SkAutoMutexAcquire ama(fTFCacheMutex);
571 SkTypeface* face = fTFCache.findByProcAndRef(find_by_fc_pattern, pattern );
572 if (NULL == face) {
573 FcPatternReference(pattern);
574 face = SkTypeface_fontconfig::Create(pattern);
575 if (face) {
576 fTFCache.add(face, SkTypeface::kNormal, true);
577 }
578 }
579 return face;
580 }
581
582 public:
583 SkFontMgr_fontconfig()
584 : fFC(FcInitLoadConfigAndFonts())
585 , fFamilyNames(get_family_names()) { }
586
587 /** Takes control of the reference to 'config'. */
588 explicit SkFontMgr_fontconfig(FcConfig* config)
589 : fFC(config)
590 , fFamilyNames(get_family_names()) { }
591
592 virtual ~SkFontMgr_fontconfig() {
593 // Hold the lock while unrefing the config.
594 FCLocker lock;
595 fFC.reset();
596 }
597
598 protected:
599 virtual int onCountFamilies() const SK_OVERRIDE {
mtklein 2014/07/22 15:48:44 Does anything else derive from this? If not, migh
bungeman-skia 2014/07/22 22:09:58 We could, I suppose. Seems odd since others don't
600 return fFamilyNames->count();
601 }
602
603 virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERR IDE {
604 familyName->set(fFamilyNames->atStr(index));
605 }
606
607 virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
608 return this->onMatchFamily(fFamilyNames->atStr(index));
609 }
610
611 /** True if any string object value in the font is the same
612 * as a string object value in the pattern.
mtklein 2014/07/22 15:48:44 <3 alignment <3
bungeman-skia 2014/07/22 22:09:58 Heh, you're all over vertical alignment above, but
613 */
614 static bool any_matching(FcPattern* font, FcPattern* pattern, const char* ob ject) {
615 FcChar8* fontString;
616 FcChar8* patternString;
617 FcResult result;
618 // Set an arbitrary limit on the number of pattern object values to cons ider.
mtklein 2014/07/22 15:48:44 Why?
bungeman-skia 2014/07/22 22:09:59 To prevent N*M from getting too big. Eventually th
619 static const int maxId = 16;
620 for (int patternId = 0; patternId < maxId; ++patternId) {
621 result = FcPatternGetString(pattern, object, patternId, &patternStri ng);
622 if (FcResultNoId == result) {
623 break;
624 }
625 if (FcResultMatch != result) {
626 continue;
627 }
628 for (int fontId = 0; fontId < maxId; ++fontId) {
629 result = FcPatternGetString(font, object, fontId, &fontString);
630 if (FcResultNoId == result) {
631 break;
632 }
633 if (FcResultMatch != result) {
634 continue;
635 }
636 if (0 == FcStrCmpIgnoreCase(patternString, fontString)) {
637 return true;
638 }
639 }
640 }
641 return false;
642 }
643
644 static bool valid_pattern(FcPattern* pattern) {
645 // FontConfig can return fonts which are unreadable.
646 const char* filename = get_string(pattern, FC_FILE, NULL);
647 if (NULL == filename) {
648 return false;
649 }
650 return sk_exists(filename, kRead_SkFILE_Flag);
651 }
652
653 static bool font_matches(FcPattern* font, FcPattern* pattern) {
654 return valid_pattern(font) && any_matching(font, pattern, FC_FAMILY);
655 }
656
657 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVER RIDE {
658 FCLocker lock;
659
660 SkAutoFcPattern pattern(FcPatternCreate());
661 if (NULL == pattern) {
662 return NULL;
663 }
664
665 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
666 FcConfigSubstitute(fFC, pattern, FcMatchPattern);
667 FcDefaultSubstitute(pattern);
668
669 FcPattern* matchPattern;
670 SkAutoFcPattern strongPattern(NULL);
671 if (familyName) {
672 strongPattern.reset(FcPatternDuplicate(pattern));
673 remove_weak(strongPattern, FC_FAMILY);
674 matchPattern = strongPattern;
675 } else {
676 matchPattern = pattern;
677 }
678
679 SkAutoFcFontSet matches(FcFontSetCreate());
680 // TODO: Some families have 'duplicates' due to symbolic links.
681 // The patterns are exactly the same except for the FC_FILE.
682 // It should be possible to collapse these patterns by normalizing.
683 static const FcSetName fcNameSet[] = { FcSetSystem, FcSetApplication };
684 for (int setIndex = 0; setIndex < (int)SK_ARRAY_COUNT(fcNameSet); ++setI ndex) {
685 // Return value of FcConfigGetFonts must not be destroyed.
686 FcFontSet* allFonts(FcConfigGetFonts(fFC, fcNameSet[setIndex]));
687 if (NULL == allFonts) {
688 continue;
689 }
690
691 for (int fontIndex = 0; fontIndex < allFonts->nfont; ++fontIndex) {
692 if (font_matches(allFonts->fonts[fontIndex], matchPattern)) {
693 FcFontSetAdd(matches,
694 FcFontRenderPrepare(fFC, pattern, allFonts->fon ts[fontIndex]));
695 }
696 }
697 }
698
699 return SkNEW_ARGS(StyleSet, (this, matches.detach()));
700 }
701
702 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
703 const SkFontStyle& style) const SK_OV ERRIDE
704 {
705 FCLocker lock;
706
707 SkAutoFcPattern pattern(FcPatternCreate());
708 if (NULL == pattern) {
709 return NULL;
710 }
711
712 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
713 fcpattern_from_skfontstyle(style, pattern);
714 FcConfigSubstitute(fFC, pattern, FcMatchPattern);
715 FcDefaultSubstitute(pattern);
716
717 // We really want to match strong (prefered) and same (acceptable) only here.
718 // If a family name was specified, assume that any weak matches after th e last strong match
719 // are weak (default) and ignore them.
720 // The reason for is that after substitution the pattern for 'sans-serif ' looks like
721 // "wwwwwwwwwwwwwwswww" where there are many weak but preferred names, f ollowed by defaults.
722 // So it is possible to have weakly matching but preferred names.
723 // In aliases, bindings are weak by default, so this is easy and common.
724 // If no family name was specified, we'll probably only get weak matches , but that's ok.
725 FcPattern* matchPattern;
726 SkAutoFcPattern strongPattern(NULL);
727 if (familyName) {
728 strongPattern.reset(FcPatternDuplicate(pattern));
729 remove_weak(strongPattern, FC_FAMILY);
730 matchPattern = strongPattern;
731 } else {
732 matchPattern = pattern;
733 }
734
735 FcResult result;
736 SkAutoFcPattern match(FcFontMatch(fFC, pattern, &result));
737 if (NULL == match || !font_matches(match, matchPattern)) {
738 return NULL;
739 }
740
741 return createTypefaceFromFcPattern(match);
742 }
743
744 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface,
745 const SkFontStyle& style) const SK_OVER RIDE
746 {
747 //TODO: should the SkTypeface_fontconfig know its family?
748 const SkTypeface_fontconfig* fcTypeface =
749 reinterpret_cast<const SkTypeface_fontconfig*>(typeface);
mtklein 2014/07/22 15:48:44 static_cast?
bungeman-skia 2014/07/22 22:09:58 Done.
750 return this->matchFamilyStyle(get_string(fcTypeface->fPattern, FC_FAMILY ), style);
751 }
752
753 /** @param stream does not take ownership of the reference. */
754 virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
755 const size_t length = stream->getLength();
756 if (!(0 < length && length < (1u << 30))) {
mtklein 2014/07/22 15:48:45 Might invert this? if (length <= 0 || length >= (
bungeman-skia 2014/07/22 22:09:57 Changed from !(in_bounds) to out_of_bounds.
757 return NULL;
758 }
759
760 SkTypeface::Style style = SkTypeface::kNormal;
761 bool isFixedWidth = false;
762 if (!SkTypeface_FreeType::ScanFont(stream, ttcIndex, NULL, &style, &isFi xedWidth)) {
763 return NULL;
764 }
765
766 return SkNEW_ARGS(SkTypeface_stream, (style, isFixedWidth, ttcIndex,
767 reinterpret_cast<SkStreamAsset*>(s tream)));
mtklein 2014/07/22 15:48:44 static_cast?
bungeman-skia 2014/07/22 22:09:57 Done.
768 }
769
770 virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OV ERRIDE {
771 SkAutoTUnref<SkStreamAsset> stream(SkNEW_ARGS(SkMemoryStream, (data)));
772 return this->createFromStream(stream, ttcIndex);
773 }
774
775 virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
776 SkAutoTUnref<SkStreamAsset> stream(SkStream::NewFromFile(path));
777 return this->createFromStream(stream, ttcIndex);
778 }
779
780 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
781 unsigned styleBits) const SK_OVER RIDE {
782 bool bold = styleBits & SkTypeface::kBold;
783 bool italic = styleBits & SkTypeface::kItalic;
784 SkFontStyle style = SkFontStyle(bold ? SkFontStyle::kBold_Weight
785 : SkFontStyle::kNormal_Weight,
786 SkFontStyle::kNormal_Width,
787 italic ? SkFontStyle::kItalic_Slant
788 : SkFontStyle::kUpright_Slant);
789 SkAutoTUnref<SkTypeface> typeface(this->matchFamilyStyle(familyName, sty le));
790 if (NULL != typeface.get()) {
791 return typeface.detach();
792 }
793
794 return this->matchFamilyStyle(NULL, style);
795 }
796 };
797
798 SkFontMgr* SkFontMgr::Factory() {
799 return SkNEW_ARGS(SkFontMgr_fontconfig, ());
mtklein 2014/07/22 15:48:44 Also known as SkNEW(SkFontMgr_fontconfig) :P
bungeman-skia 2014/07/22 22:09:57 Ah yes, another victim of copy pasta. Done.
800 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698