/src/mozilla-central/gfx/thebes/gfxFcPlatformFontList.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "mozilla/Logging.h" |
7 | | |
8 | | #include "gfxFcPlatformFontList.h" |
9 | | #include "gfxFont.h" |
10 | | #include "gfxFontConstants.h" |
11 | | #include "gfxFontFamilyList.h" |
12 | | #include "gfxFT2Utils.h" |
13 | | #include "gfxPlatform.h" |
14 | | #include "mozilla/ArrayUtils.h" |
15 | | #include "mozilla/dom/ContentChild.h" |
16 | | #include "mozilla/dom/ContentParent.h" |
17 | | #include "mozilla/Preferences.h" |
18 | | #include "mozilla/Sprintf.h" |
19 | | #include "mozilla/TimeStamp.h" |
20 | | #include "nsGkAtoms.h" |
21 | | #include "nsUnicodeProperties.h" |
22 | | #include "nsUnicodeRange.h" |
23 | | #include "nsDirectoryServiceUtils.h" |
24 | | #include "nsDirectoryServiceDefs.h" |
25 | | #include "nsAppDirectoryServiceDefs.h" |
26 | | #include "nsCharSeparatedTokenizer.h" |
27 | | #include "nsXULAppAPI.h" |
28 | | |
29 | | #include "mozilla/gfx/HelpersCairo.h" |
30 | | |
31 | | #include <fontconfig/fcfreetype.h> |
32 | | #include <dlfcn.h> |
33 | | #include <unistd.h> |
34 | | |
35 | | #ifdef MOZ_WIDGET_GTK |
36 | | #include <gdk/gdk.h> |
37 | | #include "gfxPlatformGtk.h" |
38 | | #endif |
39 | | |
40 | | #ifdef MOZ_X11 |
41 | | #include "mozilla/X11Util.h" |
42 | | #endif |
43 | | |
44 | | #if defined(MOZ_CONTENT_SANDBOX) && defined (XP_LINUX) |
45 | | #include "mozilla/SandboxBrokerPolicyFactory.h" |
46 | | #include "mozilla/SandboxSettings.h" |
47 | | #endif |
48 | | |
49 | | #include FT_MULTIPLE_MASTERS_H |
50 | | |
51 | | using namespace mozilla; |
52 | | using namespace mozilla::gfx; |
53 | | using namespace mozilla::unicode; |
54 | | |
55 | | using mozilla::dom::SystemFontListEntry; |
56 | | using mozilla::dom::FontPatternListEntry; |
57 | | |
58 | | #ifndef FC_POSTSCRIPT_NAME |
59 | | #define FC_POSTSCRIPT_NAME "postscriptname" /* String */ |
60 | | #endif |
61 | | #ifndef FC_VARIABLE |
62 | 0 | #define FC_VARIABLE "variable" /* Bool */ |
63 | | #endif |
64 | | |
65 | 0 | #define PRINTING_FC_PROPERTY "gfx.printing" |
66 | | |
67 | 0 | #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ |
68 | 0 | LogLevel::Debug, args) |
69 | 0 | #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \ |
70 | 0 | gfxPlatform::GetLog(eGfxLog_fontlist), \ |
71 | 0 | LogLevel::Debug) |
72 | 0 | #define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \ |
73 | 0 | gfxPlatform::GetLog(eGfxLog_cmapdata), \ |
74 | 0 | LogLevel::Debug) |
75 | | |
76 | | template <> |
77 | | class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet> |
78 | | { |
79 | | public: |
80 | 0 | static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); } |
81 | | }; |
82 | | |
83 | | template <> |
84 | | class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet> |
85 | | { |
86 | | public: |
87 | 0 | static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); } |
88 | | }; |
89 | | |
90 | | static const FcChar8* |
91 | | ToFcChar8Ptr(const char* aStr) |
92 | 0 | { |
93 | 0 | return reinterpret_cast<const FcChar8*>(aStr); |
94 | 0 | } |
95 | | |
96 | | static const char* |
97 | | ToCharPtr(const FcChar8 *aStr) |
98 | 0 | { |
99 | 0 | return reinterpret_cast<const char*>(aStr); |
100 | 0 | } |
101 | | |
102 | | FT_Library gfxFcPlatformFontList::sCairoFTLibrary = nullptr; |
103 | | |
104 | | static cairo_user_data_key_t sFcFontlistUserFontDataKey; |
105 | | |
106 | | // canonical name ==> first en name or first name if no en name |
107 | | // This is the required logic for fullname lookups as per CSS3 Fonts spec. |
108 | | static uint32_t |
109 | | FindCanonicalNameIndex(FcPattern* aFont, const char* aLangField) |
110 | 0 | { |
111 | 0 | uint32_t n = 0, en = 0; |
112 | 0 | FcChar8* lang; |
113 | 0 | while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) { |
114 | 0 | // look for 'en' or variants, en-US, en-JP etc. |
115 | 0 | uint32_t len = strlen(ToCharPtr(lang)); |
116 | 0 | bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0); |
117 | 0 | if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) { |
118 | 0 | en = n; |
119 | 0 | break; |
120 | 0 | } |
121 | 0 | n++; |
122 | 0 | } |
123 | 0 | return en; |
124 | 0 | } |
125 | | |
126 | | static void |
127 | | GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName, |
128 | | nsACString& aPostscriptName, nsACString& aFullname) |
129 | 0 | { |
130 | 0 | // get the Postscript name |
131 | 0 | FcChar8* psname; |
132 | 0 | if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) { |
133 | 0 | aPostscriptName = ToCharPtr(psname); |
134 | 0 | } |
135 | 0 |
|
136 | 0 | // get the canonical fullname (i.e. en name or first name) |
137 | 0 | uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG); |
138 | 0 | FcChar8* fullname; |
139 | 0 | if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) { |
140 | 0 | aFullname = ToCharPtr(fullname); |
141 | 0 | } |
142 | 0 |
|
143 | 0 | // if have fullname, done |
144 | 0 | if (!aFullname.IsEmpty()) { |
145 | 0 | return; |
146 | 0 | } |
147 | 0 | |
148 | 0 | // otherwise, set the fullname to family + style name [en] and use that |
149 | 0 | aFullname = aFamilyName; |
150 | 0 |
|
151 | 0 | // figure out the en style name |
152 | 0 | en = FindCanonicalNameIndex(aFont, FC_STYLELANG); |
153 | 0 | nsAutoCString style; |
154 | 0 | FcChar8* stylename = nullptr; |
155 | 0 | FcPatternGetString(aFont, FC_STYLE, en, &stylename); |
156 | 0 | if (stylename) { |
157 | 0 | style = ToCharPtr(stylename); |
158 | 0 | } |
159 | 0 |
|
160 | 0 | if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) { |
161 | 0 | aFullname.Append(' '); |
162 | 0 | aFullname.Append(style); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | static FontWeight |
167 | | MapFcWeight(int aFcWeight) |
168 | 0 | { |
169 | 0 | if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) { |
170 | 0 | return FontWeight(100); |
171 | 0 | } |
172 | 0 | if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) { |
173 | 0 | return FontWeight(200); |
174 | 0 | } |
175 | 0 | if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) { |
176 | 0 | return FontWeight(300); |
177 | 0 | } |
178 | 0 | if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) { |
179 | 0 | // This includes FC_WEIGHT_BOOK |
180 | 0 | return FontWeight(400); |
181 | 0 | } |
182 | 0 | if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) { |
183 | 0 | return FontWeight(500); |
184 | 0 | } |
185 | 0 | if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) { |
186 | 0 | return FontWeight(600); |
187 | 0 | } |
188 | 0 | if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) { |
189 | 0 | return FontWeight(700); |
190 | 0 | } |
191 | 0 | if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) { |
192 | 0 | return FontWeight(800); |
193 | 0 | } |
194 | 0 | if (aFcWeight <= FC_WEIGHT_BLACK) { |
195 | 0 | return FontWeight(900); |
196 | 0 | } |
197 | 0 | |
198 | 0 | // including FC_WEIGHT_EXTRABLACK |
199 | 0 | return FontWeight(901); |
200 | 0 | } |
201 | | |
202 | | // TODO(emilio, jfkthame): I think this can now be more fine-grained. |
203 | | static FontStretch |
204 | | MapFcWidth(int aFcWidth) |
205 | 0 | { |
206 | 0 | if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) { |
207 | 0 | return FontStretch::UltraCondensed(); |
208 | 0 | } |
209 | 0 | if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) { |
210 | 0 | return FontStretch::ExtraCondensed(); |
211 | 0 | } |
212 | 0 | if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) { |
213 | 0 | return FontStretch::Condensed(); |
214 | 0 | } |
215 | 0 | if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) { |
216 | 0 | return FontStretch::SemiCondensed(); |
217 | 0 | } |
218 | 0 | if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) { |
219 | 0 | return FontStretch::Normal(); |
220 | 0 | } |
221 | 0 | if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) { |
222 | 0 | return FontStretch::SemiExpanded(); |
223 | 0 | } |
224 | 0 | if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) { |
225 | 0 | return FontStretch::Expanded(); |
226 | 0 | } |
227 | 0 | if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) { |
228 | 0 | return FontStretch::ExtraExpanded(); |
229 | 0 | } |
230 | 0 | return FontStretch::UltraExpanded(); |
231 | 0 | } |
232 | | |
233 | | gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName, |
234 | | FcPattern* aFontPattern, |
235 | | bool aIgnoreFcCharmap) |
236 | | : gfxFontEntry(aFaceName), mFontPattern(aFontPattern), |
237 | | mFTFace(nullptr), mFTFaceInitialized(false), |
238 | | mIgnoreFcCharmap(aIgnoreFcCharmap), |
239 | | mHasVariationsInitialized(false), |
240 | | mAspect(0.0), mFontData(nullptr), mLength(0) |
241 | 0 | { |
242 | 0 | // italic |
243 | 0 | int slant; |
244 | 0 | if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) { |
245 | 0 | slant = FC_SLANT_ROMAN; |
246 | 0 | } |
247 | 0 | if (slant == FC_SLANT_OBLIQUE) { |
248 | 0 | mStyleRange = SlantStyleRange(FontSlantStyle::Oblique()); |
249 | 0 | } else if (slant > 0) { |
250 | 0 | mStyleRange = SlantStyleRange(FontSlantStyle::Italic()); |
251 | 0 | } |
252 | 0 |
|
253 | 0 | // weight |
254 | 0 | int weight; |
255 | 0 | if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) { |
256 | 0 | weight = FC_WEIGHT_REGULAR; |
257 | 0 | } |
258 | 0 | mWeightRange = WeightRange(MapFcWeight(weight)); |
259 | 0 |
|
260 | 0 | // width |
261 | 0 | int width; |
262 | 0 | if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) { |
263 | 0 | width = FC_WIDTH_NORMAL; |
264 | 0 | } |
265 | 0 | mStretchRange = StretchRange(MapFcWidth(width)); |
266 | 0 | } |
267 | | |
268 | | gfxFontEntry* |
269 | | gfxFontconfigFontEntry::Clone() const |
270 | 0 | { |
271 | 0 | MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!"); |
272 | 0 | return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap); |
273 | 0 | } |
274 | | |
275 | | static FcPattern* |
276 | | CreatePatternForFace(FT_Face aFace) |
277 | 0 | { |
278 | 0 | // Use fontconfig to fill out the pattern from the FTFace. |
279 | 0 | // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at |
280 | 0 | // least). The dummy file passed here is removed below. |
281 | 0 | // |
282 | 0 | // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr) |
283 | 0 | // is passed as the "blanks" argument, which provides that unexpectedly |
284 | 0 | // blank glyphs are elided. Here, however, we pass nullptr for |
285 | 0 | // "blanks", effectively assuming that, if the font has a blank glyph, |
286 | 0 | // then the author intends any associated character to be rendered |
287 | 0 | // blank. |
288 | 0 | FcPattern* pattern = |
289 | 0 | FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr); |
290 | 0 | // given that we have a FT_Face, not really sure this is possible... |
291 | 0 | if (!pattern) { |
292 | 0 | pattern = FcPatternCreate(); |
293 | 0 | } |
294 | 0 | FcPatternDel(pattern, FC_FILE); |
295 | 0 | FcPatternDel(pattern, FC_INDEX); |
296 | 0 |
|
297 | 0 | // Make a new pattern and store the face in it so that cairo uses |
298 | 0 | // that when creating a cairo font face. |
299 | 0 | FcPatternAddFTFace(pattern, FC_FT_FACE, aFace); |
300 | 0 |
|
301 | 0 | return pattern; |
302 | 0 | } |
303 | | |
304 | | static FT_Face |
305 | | CreateFaceForPattern(FcPattern* aPattern) |
306 | 0 | { |
307 | 0 | FcChar8 *filename; |
308 | 0 | if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) { |
309 | 0 | return nullptr; |
310 | 0 | } |
311 | 0 | int index; |
312 | 0 | if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) { |
313 | 0 | index = 0; // default to 0 if not found in pattern |
314 | 0 | } |
315 | 0 | return Factory::NewFTFace(nullptr, ToCharPtr(filename), index); |
316 | 0 | } |
317 | | |
318 | | gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName, |
319 | | WeightRange aWeight, |
320 | | StretchRange aStretch, |
321 | | SlantStyleRange aStyle, |
322 | | const uint8_t *aData, |
323 | | uint32_t aLength, |
324 | | FT_Face aFace) |
325 | | : gfxFontEntry(aFaceName), |
326 | | mFTFace(aFace), mFTFaceInitialized(true), |
327 | | mIgnoreFcCharmap(true), |
328 | | mHasVariationsInitialized(false), |
329 | | mAspect(0.0), mFontData(aData), mLength(aLength) |
330 | 0 | { |
331 | 0 | mWeightRange = aWeight; |
332 | 0 | mStyleRange = aStyle; |
333 | 0 | mStretchRange = aStretch; |
334 | 0 | mIsDataUserFont = true; |
335 | 0 |
|
336 | 0 | mFontPattern = CreatePatternForFace(mFTFace); |
337 | 0 |
|
338 | 0 | mUserFontData = new FTUserFontData(mFTFace, mFontData); |
339 | 0 | } |
340 | | |
341 | | gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName, |
342 | | FcPattern* aFontPattern, |
343 | | WeightRange aWeight, |
344 | | StretchRange aStretch, |
345 | | SlantStyleRange aStyle) |
346 | | : gfxFontEntry(aFaceName), mFontPattern(aFontPattern), |
347 | | mFTFace(nullptr), mFTFaceInitialized(false), |
348 | | mHasVariationsInitialized(false), |
349 | | mAspect(0.0), mFontData(nullptr), mLength(0) |
350 | 0 | { |
351 | 0 | mWeightRange = aWeight; |
352 | 0 | mStyleRange = aStyle; |
353 | 0 | mStretchRange = aStretch; |
354 | 0 | mIsLocalUserFont = true; |
355 | 0 |
|
356 | 0 | // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded |
357 | 0 | // via src:local()... |
358 | 0 | // If the local font happens to come from the application fontset, |
359 | 0 | // we want to set it to true so that color/svg fonts will work even |
360 | 0 | // if the default glyphs are blank; but if the local font is a non- |
361 | 0 | // sfnt face (e.g. legacy type 1) then we need to set it to false |
362 | 0 | // because our cmap-reading code will fail and we depend on FT+Fc to |
363 | 0 | // determine the coverage. |
364 | 0 | // We set the flag here, but may flip it the first time TestCharacterMap |
365 | 0 | // is called, at which point we'll look to see whether a 'cmap' is |
366 | 0 | // actually present in the font. |
367 | 0 | mIgnoreFcCharmap = true; |
368 | 0 | } |
369 | | |
370 | | typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**); |
371 | | typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*); |
372 | | static GetVarFunc sGetVar; |
373 | | static DoneVarFunc sDoneVar; |
374 | | static bool sInitializedVarFuncs = false; |
375 | | |
376 | | static void |
377 | | InitializeVarFuncs() |
378 | 0 | { |
379 | 0 | if (sInitializedVarFuncs) { |
380 | 0 | return; |
381 | 0 | } |
382 | 0 | sInitializedVarFuncs = true; |
383 | | #if MOZ_TREE_FREETYPE |
384 | | sGetVar = &FT_Get_MM_Var; |
385 | | sDoneVar = &FT_Done_MM_Var; |
386 | | #else |
387 | | sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var"); |
388 | 0 | sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var"); |
389 | 0 | #endif |
390 | 0 | } |
391 | | |
392 | | gfxFontconfigFontEntry::~gfxFontconfigFontEntry() |
393 | 0 | { |
394 | 0 | if (mMMVar) { |
395 | 0 | // Prior to freetype 2.9, there was no specific function to free the |
396 | 0 | // FT_MM_Var record, and the docs just said to use free(). |
397 | 0 | // InitializeVarFuncs must have been called in order for mMMVar to be |
398 | 0 | // non-null here, so we don't need to do it again. |
399 | 0 | if (sDoneVar) { |
400 | 0 | MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?"); |
401 | 0 | (*sDoneVar)(mFTFace->glyph->library, mMMVar); |
402 | 0 | } else { |
403 | 0 | free(mMMVar); |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | nsresult |
409 | | gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData) |
410 | 0 | { |
411 | 0 | // attempt this once, if errors occur leave a blank cmap |
412 | 0 | if (mCharacterMap) { |
413 | 0 | return NS_OK; |
414 | 0 | } |
415 | 0 | |
416 | 0 | RefPtr<gfxCharacterMap> charmap; |
417 | 0 | nsresult rv; |
418 | 0 |
|
419 | 0 | if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, |
420 | 0 | mUVSOffset))) { |
421 | 0 | rv = NS_OK; |
422 | 0 | } else { |
423 | 0 | uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); |
424 | 0 | charmap = new gfxCharacterMap(); |
425 | 0 | AutoTable cmapTable(this, kCMAP); |
426 | 0 |
|
427 | 0 | if (cmapTable) { |
428 | 0 | uint32_t cmapLen; |
429 | 0 | const uint8_t* cmapData = |
430 | 0 | reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, |
431 | 0 | &cmapLen)); |
432 | 0 | rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, |
433 | 0 | *charmap, mUVSOffset); |
434 | 0 | } else { |
435 | 0 | rv = NS_ERROR_NOT_AVAILABLE; |
436 | 0 | } |
437 | 0 | } |
438 | 0 |
|
439 | 0 | mHasCmapTable = NS_SUCCEEDED(rv); |
440 | 0 | if (mHasCmapTable) { |
441 | 0 | gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); |
442 | 0 | mCharacterMap = pfl->FindCharMap(charmap); |
443 | 0 | } else { |
444 | 0 | // if error occurred, initialize to null cmap |
445 | 0 | mCharacterMap = new gfxCharacterMap(); |
446 | 0 | } |
447 | 0 |
|
448 | 0 | LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", |
449 | 0 | mName.get(), |
450 | 0 | charmap->SizeOfIncludingThis(moz_malloc_size_of), |
451 | 0 | charmap->mHash, mCharacterMap == charmap ? " new" : "")); |
452 | 0 | if (LOG_CMAPDATA_ENABLED()) { |
453 | 0 | char prefix[256]; |
454 | 0 | SprintfLiteral(prefix, "(cmapdata) name: %.220s", |
455 | 0 | mName.get()); |
456 | 0 | charmap->Dump(prefix, eGfxLog_cmapdata); |
457 | 0 | } |
458 | 0 |
|
459 | 0 | return rv; |
460 | 0 | } |
461 | | |
462 | | static bool |
463 | | HasChar(FcPattern *aFont, FcChar32 aCh) |
464 | 0 | { |
465 | 0 | FcCharSet *charset = nullptr; |
466 | 0 | FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset); |
467 | 0 | return charset && FcCharSetHasChar(charset, aCh); |
468 | 0 | } |
469 | | |
470 | | bool |
471 | | gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) |
472 | 0 | { |
473 | 0 | // For user fonts, or for fonts bundled with the app (which might include |
474 | 0 | // color/svg glyphs where the default glyphs may be blank, and thus confuse |
475 | 0 | // fontconfig/freetype's char map checking), we instead check the cmap |
476 | 0 | // directly for character coverage. |
477 | 0 | if (mIgnoreFcCharmap) { |
478 | 0 | // If it does not actually have a cmap, switch our strategy to use |
479 | 0 | // fontconfig's charmap after all (except for data fonts, which must |
480 | 0 | // always have a cmap to have passed OTS validation). |
481 | 0 | if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c','m','a','p'))) { |
482 | 0 | mIgnoreFcCharmap = false; |
483 | 0 | // ...and continue with HasChar() below. |
484 | 0 | } else { |
485 | 0 | return gfxFontEntry::TestCharacterMap(aCh); |
486 | 0 | } |
487 | 0 | } |
488 | 0 | // otherwise (for system fonts), use the charmap in the pattern |
489 | 0 | return HasChar(mFontPattern, aCh); |
490 | 0 | } |
491 | | |
492 | | hb_blob_t* |
493 | | gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) |
494 | 0 | { |
495 | 0 | // for data fonts, read directly from the font data |
496 | 0 | if (mFontData) { |
497 | 0 | return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag); |
498 | 0 | } |
499 | 0 | |
500 | 0 | return gfxFontEntry::GetFontTable(aTableTag); |
501 | 0 | } |
502 | | |
503 | | void |
504 | | gfxFontconfigFontEntry::MaybeReleaseFTFace() |
505 | 0 | { |
506 | 0 | // don't release if either HB or Gr face still exists |
507 | 0 | if (mHBFace || mGrFace) { |
508 | 0 | return; |
509 | 0 | } |
510 | 0 | // only close out FT_Face for system fonts, not for data fonts |
511 | 0 | if (!mIsDataUserFont) { |
512 | 0 | if (mFTFace) { |
513 | 0 | if (mMMVar) { |
514 | 0 | if (sDoneVar) { |
515 | 0 | (*sDoneVar)(mFTFace->glyph->library, mMMVar); |
516 | 0 | } else { |
517 | 0 | free(mMMVar); |
518 | 0 | } |
519 | 0 | mMMVar = nullptr; |
520 | 0 | } |
521 | 0 | Factory::ReleaseFTFace(mFTFace); |
522 | 0 | mFTFace = nullptr; |
523 | 0 | } |
524 | 0 | mFTFaceInitialized = false; |
525 | 0 | } |
526 | 0 | } |
527 | | |
528 | | void |
529 | | gfxFontconfigFontEntry::ForgetHBFace() |
530 | 0 | { |
531 | 0 | gfxFontEntry::ForgetHBFace(); |
532 | 0 | MaybeReleaseFTFace(); |
533 | 0 | } |
534 | | |
535 | | void |
536 | | gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace) |
537 | 0 | { |
538 | 0 | gfxFontEntry::ReleaseGrFace(aFace); |
539 | 0 | MaybeReleaseFTFace(); |
540 | 0 | } |
541 | | |
542 | | double |
543 | | gfxFontconfigFontEntry::GetAspect() |
544 | 0 | { |
545 | 0 | if (mAspect != 0.0) { |
546 | 0 | return mAspect; |
547 | 0 | } |
548 | 0 | |
549 | 0 | // try to compute aspect from OS/2 metrics if available |
550 | 0 | AutoTable os2Table(this, TRUETYPE_TAG('O','S','/','2')); |
551 | 0 | if (os2Table) { |
552 | 0 | uint16_t upem = UnitsPerEm(); |
553 | 0 | if (upem != kInvalidUPEM) { |
554 | 0 | uint32_t len; |
555 | 0 | auto os2 = reinterpret_cast<const OS2Table*> |
556 | 0 | (hb_blob_get_data(os2Table, &len)); |
557 | 0 | if (uint16_t(os2->version) >= 2) { |
558 | 0 | if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) && |
559 | 0 | int16_t(os2->sxHeight) > 0.1 * upem) { |
560 | 0 | mAspect = double(int16_t(os2->sxHeight)) / upem; |
561 | 0 | return mAspect; |
562 | 0 | } |
563 | 0 | } |
564 | 0 | } |
565 | 0 | } |
566 | 0 | |
567 | 0 | // default to aspect = 0.5 if the code below fails |
568 | 0 | mAspect = 0.5; |
569 | 0 |
|
570 | 0 | // create a font to calculate x-height / em-height |
571 | 0 | gfxFontStyle s; |
572 | 0 | s.size = 100.0; // pick large size to avoid possible hinting artifacts |
573 | 0 | RefPtr<gfxFont> font = FindOrMakeFont(&s); |
574 | 0 | if (font) { |
575 | 0 | const gfxFont::Metrics& metrics = |
576 | 0 | font->GetMetrics(gfxFont::eHorizontal); |
577 | 0 |
|
578 | 0 | // The factor of 0.1 ensures that xHeight is sane so fonts don't |
579 | 0 | // become huge. Strictly ">" ensures that xHeight and emHeight are |
580 | 0 | // not both zero. |
581 | 0 | if (metrics.xHeight > 0.1 * metrics.emHeight) { |
582 | 0 | mAspect = metrics.xHeight / metrics.emHeight; |
583 | 0 | } |
584 | 0 | } |
585 | 0 |
|
586 | 0 | return mAspect; |
587 | 0 | } |
588 | | |
589 | | static void |
590 | | PrepareFontOptions(FcPattern* aPattern, |
591 | | cairo_font_options_t* aFontOptions) |
592 | 0 | { |
593 | 0 | NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions"); |
594 | 0 |
|
595 | 0 | // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed |
596 | 0 |
|
597 | 0 | FcBool printing; |
598 | 0 | if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != |
599 | 0 | FcResultMatch) { |
600 | 0 | printing = FcFalse; |
601 | 0 | } |
602 | 0 |
|
603 | 0 | // Font options are set explicitly here to improve cairo's caching |
604 | 0 | // behavior and to record the relevant parts of the pattern for |
605 | 0 | // SetupCairoFont (so that the pattern can be released). |
606 | 0 | // |
607 | 0 | // Most font_options have already been set as defaults on the FcPattern |
608 | 0 | // with cairo_ft_font_options_substitute(), then user and system |
609 | 0 | // fontconfig configurations were applied. The resulting font_options |
610 | 0 | // have been recorded on the face during |
611 | 0 | // cairo_ft_font_face_create_for_pattern(). |
612 | 0 | // |
613 | 0 | // None of the settings here cause this scaled_font to behave any |
614 | 0 | // differently from how it would behave if it were created from the same |
615 | 0 | // face with default font_options. |
616 | 0 | // |
617 | 0 | // We set options explicitly so that the same scaled_font will be found in |
618 | 0 | // the cairo_scaled_font_map when cairo loads glyphs from a context with |
619 | 0 | // the same font_face, font_matrix, ctm, and surface font_options. |
620 | 0 | // |
621 | 0 | // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the |
622 | 0 | // font_options on the cairo_ft_font_face, and doesn't consider default |
623 | 0 | // option values to not match any explicit values. |
624 | 0 | // |
625 | 0 | // Even after cairo_set_scaled_font is used to set font_options for the |
626 | 0 | // cairo context, when cairo looks for a scaled_font for the context, it |
627 | 0 | // will look for a font with some option values from the target surface if |
628 | 0 | // any values are left default on the context font_options. If this |
629 | 0 | // scaled_font is created with default font_options, cairo will not find |
630 | 0 | // it. |
631 | 0 | // |
632 | 0 | // The one option not recorded in the pattern is hint_metrics, which will |
633 | 0 | // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON. |
634 | 0 | // We should be considering the font_options of the surface on which this |
635 | 0 | // font will be used, but currently we don't have different gfxFonts for |
636 | 0 | // different surface font_options, so we'll create a font suitable for the |
637 | 0 | // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON. |
638 | 0 | if (printing) { |
639 | 0 | cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF); |
640 | 0 | } else { |
641 | 0 | cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON); |
642 | 0 | } |
643 | 0 |
|
644 | 0 | // The remaining options have been recorded on the pattern and the face. |
645 | 0 | // _cairo_ft_options_merge has some logic to decide which options from the |
646 | 0 | // scaled_font or from the cairo_ft_font_face take priority in the way the |
647 | 0 | // font behaves. |
648 | 0 | // |
649 | 0 | // In the majority of cases, _cairo_ft_options_merge uses the options from |
650 | 0 | // the cairo_ft_font_face, so sometimes it is not so important which |
651 | 0 | // values are set here so long as they are not defaults, but we'll set |
652 | 0 | // them to the exact values that we expect from the font, to be consistent |
653 | 0 | // and to protect against changes in cairo. |
654 | 0 | // |
655 | 0 | // In some cases, _cairo_ft_options_merge uses some options from the |
656 | 0 | // scaled_font's font_options rather than options on the |
657 | 0 | // cairo_ft_font_face (from fontconfig). |
658 | 0 | // https://bugs.freedesktop.org/show_bug.cgi?id=11838 |
659 | 0 | // |
660 | 0 | // Surface font options were set on the pattern in |
661 | 0 | // cairo_ft_font_options_substitute. If fontconfig has changed the |
662 | 0 | // hint_style then that is what the user (or distribution) wants, so we |
663 | 0 | // use the setting from the FcPattern. |
664 | 0 | // |
665 | 0 | // Fallback values here mirror treatment of defaults in cairo-ft-font.c. |
666 | 0 | FcBool hinting = FcFalse; |
667 | 0 | if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) { |
668 | 0 | hinting = FcTrue; |
669 | 0 | } |
670 | 0 |
|
671 | 0 | cairo_hint_style_t hint_style; |
672 | 0 | if (printing || !hinting) { |
673 | 0 | hint_style = CAIRO_HINT_STYLE_NONE; |
674 | 0 | } else { |
675 | 0 | int fc_hintstyle; |
676 | 0 | if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, |
677 | 0 | 0, &fc_hintstyle) != FcResultMatch) { |
678 | 0 | fc_hintstyle = FC_HINT_FULL; |
679 | 0 | } |
680 | 0 | switch (fc_hintstyle) { |
681 | 0 | case FC_HINT_NONE: |
682 | 0 | hint_style = CAIRO_HINT_STYLE_NONE; |
683 | 0 | break; |
684 | 0 | case FC_HINT_SLIGHT: |
685 | 0 | hint_style = CAIRO_HINT_STYLE_SLIGHT; |
686 | 0 | break; |
687 | 0 | case FC_HINT_MEDIUM: |
688 | 0 | default: // This fallback mirrors _get_pattern_ft_options in cairo. |
689 | 0 | hint_style = CAIRO_HINT_STYLE_MEDIUM; |
690 | 0 | break; |
691 | 0 | case FC_HINT_FULL: |
692 | 0 | hint_style = CAIRO_HINT_STYLE_FULL; |
693 | 0 | break; |
694 | 0 | } |
695 | 0 | } |
696 | 0 | cairo_font_options_set_hint_style(aFontOptions, hint_style); |
697 | 0 |
|
698 | 0 | int rgba; |
699 | 0 | if (FcPatternGetInteger(aPattern, |
700 | 0 | FC_RGBA, 0, &rgba) != FcResultMatch) { |
701 | 0 | rgba = FC_RGBA_UNKNOWN; |
702 | 0 | } |
703 | 0 | cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; |
704 | 0 | switch (rgba) { |
705 | 0 | case FC_RGBA_UNKNOWN: |
706 | 0 | case FC_RGBA_NONE: |
707 | 0 | default: |
708 | 0 | // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing |
709 | 0 | // is disabled through cairo_antialias_t. |
710 | 0 | rgba = FC_RGBA_NONE; |
711 | 0 | // subpixel_order won't be used by the font as we won't use |
712 | 0 | // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for |
713 | 0 | // caching reasons described above. Fall through: |
714 | 0 | MOZ_FALLTHROUGH; |
715 | 0 | case FC_RGBA_RGB: |
716 | 0 | subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; |
717 | 0 | break; |
718 | 0 | case FC_RGBA_BGR: |
719 | 0 | subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; |
720 | 0 | break; |
721 | 0 | case FC_RGBA_VRGB: |
722 | 0 | subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; |
723 | 0 | break; |
724 | 0 | case FC_RGBA_VBGR: |
725 | 0 | subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; |
726 | 0 | break; |
727 | 0 | } |
728 | 0 | cairo_font_options_set_subpixel_order(aFontOptions, subpixel_order); |
729 | 0 |
|
730 | 0 | FcBool fc_antialias; |
731 | 0 | if (FcPatternGetBool(aPattern, |
732 | 0 | FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) { |
733 | 0 | fc_antialias = FcTrue; |
734 | 0 | } |
735 | 0 | cairo_antialias_t antialias; |
736 | 0 | if (!fc_antialias) { |
737 | 0 | antialias = CAIRO_ANTIALIAS_NONE; |
738 | 0 | } else if (rgba == FC_RGBA_NONE) { |
739 | 0 | antialias = CAIRO_ANTIALIAS_GRAY; |
740 | 0 | } else { |
741 | 0 | antialias = CAIRO_ANTIALIAS_SUBPIXEL; |
742 | 0 | } |
743 | 0 | cairo_font_options_set_antialias(aFontOptions, antialias); |
744 | 0 | } |
745 | | |
746 | | static void |
747 | | ReleaseFTUserFontData(void* aData) |
748 | 0 | { |
749 | 0 | static_cast<FTUserFontData*>(aData)->Release(); |
750 | 0 | } |
751 | | |
752 | | static cairo_user_data_key_t sFcFontlistFTFaceKey; |
753 | | |
754 | | static void |
755 | | ReleaseFTFace(void* aData) |
756 | 0 | { |
757 | 0 | Factory::ReleaseFTFace(static_cast<FT_Face>(aData)); |
758 | 0 | } |
759 | | |
760 | | cairo_scaled_font_t* |
761 | | gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern, |
762 | | gfxFloat aAdjustedSize, |
763 | | const gfxFontStyle *aStyle, |
764 | | FT_Face aFTFace) |
765 | 0 | { |
766 | 0 | if (aStyle->NeedsSyntheticBold(this)) { |
767 | 0 | FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue); |
768 | 0 | } |
769 | 0 |
|
770 | 0 | // will synthetic oblique be applied using a transform? |
771 | 0 | bool needsOblique = IsUpright() && |
772 | 0 | aStyle->style != FontSlantStyle::Normal() && |
773 | 0 | aStyle->allowSyntheticStyle; |
774 | 0 |
|
775 | 0 | if (needsOblique) { |
776 | 0 | // disable embedded bitmaps (mimics behavior in 90-synthetic.conf) |
777 | 0 | FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP); |
778 | 0 | FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse); |
779 | 0 | } |
780 | 0 |
|
781 | 0 | AutoTArray<FT_Fixed,8> coords; |
782 | 0 | if (HasVariations()) { |
783 | 0 | FT_Face ftFace = GetFTFace(); |
784 | 0 | if (ftFace) { |
785 | 0 | AutoTArray<gfxFontVariation,8> settings; |
786 | 0 | GetVariationsForStyle(settings, *aStyle); |
787 | 0 | gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, &coords); |
788 | 0 | } |
789 | 0 | } |
790 | 0 |
|
791 | 0 | cairo_font_face_t *face = |
792 | 0 | cairo_ft_font_face_create_for_pattern(aRenderPattern, |
793 | 0 | coords.Elements(), |
794 | 0 | coords.Length()); |
795 | 0 |
|
796 | 0 | if (aFTFace) { |
797 | 0 | if (cairo_font_face_set_user_data(face, |
798 | 0 | &sFcFontlistFTFaceKey, |
799 | 0 | aFTFace, |
800 | 0 | ReleaseFTFace) != CAIRO_STATUS_SUCCESS) { |
801 | 0 | NS_WARNING("Failed binding FT_Face to Cairo font face"); |
802 | 0 | cairo_font_face_destroy(face); |
803 | 0 | Factory::ReleaseFTFace(aFTFace); |
804 | 0 | return nullptr; |
805 | 0 | } |
806 | 0 | } |
807 | 0 |
|
808 | 0 | if (mFontData) { |
809 | 0 | // for data fonts, add the face/data pointer to the cairo font face |
810 | 0 | // so that it ges deleted whenever cairo decides |
811 | 0 | NS_ASSERTION(mFTFace, "FT_Face is null when setting user data"); |
812 | 0 | NS_ASSERTION(mUserFontData, "user font data is null when setting user data"); |
813 | 0 | mUserFontData.get()->AddRef(); |
814 | 0 | if (cairo_font_face_set_user_data(face, |
815 | 0 | &sFcFontlistUserFontDataKey, |
816 | 0 | mUserFontData, |
817 | 0 | ReleaseFTUserFontData) != CAIRO_STATUS_SUCCESS) { |
818 | 0 | NS_WARNING("Failed binding FTUserFontData to Cairo font face"); |
819 | 0 | mUserFontData.get()->Release(); |
820 | 0 | cairo_font_face_destroy(face); |
821 | 0 | return nullptr; |
822 | 0 | } |
823 | 0 | } |
824 | 0 |
|
825 | 0 | cairo_scaled_font_t *scaledFont = nullptr; |
826 | 0 |
|
827 | 0 | cairo_matrix_t sizeMatrix; |
828 | 0 | cairo_matrix_t identityMatrix; |
829 | 0 |
|
830 | 0 | cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize); |
831 | 0 | cairo_matrix_init_identity(&identityMatrix); |
832 | 0 |
|
833 | 0 | cairo_font_options_t *fontOptions = cairo_font_options_create(); |
834 | 0 | PrepareFontOptions(aRenderPattern, fontOptions); |
835 | 0 |
|
836 | 0 | scaledFont = cairo_scaled_font_create(face, &sizeMatrix, |
837 | 0 | &identityMatrix, fontOptions); |
838 | 0 | cairo_font_options_destroy(fontOptions); |
839 | 0 |
|
840 | 0 | NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS, |
841 | 0 | "Failed to make scaled font"); |
842 | 0 |
|
843 | 0 | cairo_font_face_destroy(face); |
844 | 0 |
|
845 | 0 | return scaledFont; |
846 | 0 | } |
847 | | |
848 | | #ifdef MOZ_WIDGET_GTK |
849 | | // defintion included below |
850 | | static void ApplyGdkScreenFontOptions(FcPattern *aPattern); |
851 | | #endif |
852 | | |
853 | | #ifdef MOZ_X11 |
854 | | static bool |
855 | | GetXftInt(Display* aDisplay, const char* aName, int* aResult) |
856 | 0 | { |
857 | 0 | if (!aDisplay) { |
858 | 0 | return false; |
859 | 0 | } |
860 | 0 | char* value = XGetDefault(aDisplay, "Xft", aName); |
861 | 0 | if (!value) { |
862 | 0 | return false; |
863 | 0 | } |
864 | 0 | if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) { |
865 | 0 | return true; |
866 | 0 | } |
867 | 0 | char* end; |
868 | 0 | *aResult = strtol(value, &end, 0); |
869 | 0 | if (end != value) { |
870 | 0 | return true; |
871 | 0 | } |
872 | 0 | return false; |
873 | 0 | } |
874 | | #endif |
875 | | |
876 | | static void |
877 | | PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) |
878 | 0 | { |
879 | 0 | FcConfigSubstitute(nullptr, aPattern, FcMatchPattern); |
880 | 0 |
|
881 | 0 | // This gets cairo_font_options_t for the Screen. We should have |
882 | 0 | // different font options for printing (no hinting) but we are not told |
883 | 0 | // what we are measuring for. |
884 | 0 | // |
885 | 0 | // If cairo adds support for lcd_filter, gdk will not provide the default |
886 | 0 | // setting for that option. We could get the default setting by creating |
887 | 0 | // an xlib surface once, recording its font_options, and then merging the |
888 | 0 | // gdk options. |
889 | 0 | // |
890 | 0 | // Using an xlib surface would also be an option to get Screen font |
891 | 0 | // options for non-GTK X11 toolkits, but less efficient than using GDK to |
892 | 0 | // pick up dynamic changes. |
893 | 0 | if(aIsPrinterFont) { |
894 | 0 | cairo_font_options_t *options = cairo_font_options_create(); |
895 | 0 | cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); |
896 | 0 | cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); |
897 | 0 | cairo_ft_font_options_substitute(options, aPattern); |
898 | 0 | cairo_font_options_destroy(options); |
899 | 0 | FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue); |
900 | 0 | } else if (!gfxPlatform::IsHeadless()) { |
901 | 0 | #ifdef MOZ_WIDGET_GTK |
902 | 0 | ApplyGdkScreenFontOptions(aPattern); |
903 | 0 |
|
904 | 0 | #ifdef MOZ_X11 |
905 | 0 | FcValue value; |
906 | 0 | int lcdfilter; |
907 | 0 | if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) { |
908 | 0 | GdkDisplay* dpy = gdk_display_get_default(); |
909 | 0 | if (GDK_IS_X11_DISPLAY(dpy) && |
910 | 0 | GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) { |
911 | 0 | FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter); |
912 | 0 | } |
913 | 0 | } |
914 | 0 | #endif // MOZ_X11 |
915 | 0 | #endif // MOZ_WIDGET_GTK |
916 | 0 | } |
917 | 0 |
|
918 | 0 | FcDefaultSubstitute(aPattern); |
919 | 0 | } |
920 | | |
921 | | void |
922 | 0 | gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) { |
923 | 0 | if (aIndex > 0) { |
924 | 0 | ThreadSafeWeakPtr<UnscaledFontFontconfig> front = |
925 | 0 | std::move(mUnscaledFonts[aIndex]); |
926 | 0 | for (size_t i = aIndex; i > 0; i--) { |
927 | 0 | mUnscaledFonts[i] = std::move(mUnscaledFonts[i-1]); |
928 | 0 | } |
929 | 0 | mUnscaledFonts[0] = std::move(front); |
930 | 0 | } |
931 | 0 | } |
932 | | |
933 | | already_AddRefed<UnscaledFontFontconfig> |
934 | 0 | gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) { |
935 | 0 | for (size_t i = 0; i < kNumEntries; i++) { |
936 | 0 | RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]); |
937 | 0 | if (entry && |
938 | 0 | !strcmp(entry->GetFile(), aFile) && |
939 | 0 | entry->GetIndex() == aIndex) { |
940 | 0 | MoveToFront(i); |
941 | 0 | return entry.forget(); |
942 | 0 | } |
943 | 0 | } |
944 | 0 | return nullptr; |
945 | 0 | } |
946 | | |
947 | | static inline gfxFloat |
948 | | SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle) |
949 | 0 | { |
950 | 0 | return aStyle.sizeAdjust >= 0.0 ? |
951 | 0 | aStyle.GetAdjustedSize(aEntry->GetAspect()) : |
952 | 0 | aStyle.size; |
953 | 0 | } |
954 | | |
955 | | static double |
956 | | ChooseFontSize(gfxFontconfigFontEntry* aEntry, |
957 | | const gfxFontStyle& aStyle) |
958 | 0 | { |
959 | 0 | double requestedSize = SizeForStyle(aEntry, aStyle); |
960 | 0 | double bestDist = -1.0; |
961 | 0 | double bestSize = requestedSize; |
962 | 0 | double size; |
963 | 0 | int v = 0; |
964 | 0 | while (FcPatternGetDouble(aEntry->GetPattern(), |
965 | 0 | FC_PIXEL_SIZE, v, &size) == FcResultMatch) { |
966 | 0 | ++v; |
967 | 0 | double dist = fabs(size - requestedSize); |
968 | 0 | if (bestDist < 0.0 || dist < bestDist) { |
969 | 0 | bestDist = dist; |
970 | 0 | bestSize = size; |
971 | 0 | } |
972 | 0 | } |
973 | 0 | // If the font has bitmaps but wants to be scaled, then let it scale. |
974 | 0 | if (bestSize >= 0.0) { |
975 | 0 | FcBool scalable; |
976 | 0 | if (FcPatternGetBool(aEntry->GetPattern(), |
977 | 0 | FC_SCALABLE, 0, &scalable) == FcResultMatch && |
978 | 0 | scalable) { |
979 | 0 | return requestedSize; |
980 | 0 | } |
981 | 0 | } |
982 | 0 | return bestSize; |
983 | 0 | } |
984 | | |
985 | | gfxFont* |
986 | | gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle) |
987 | 0 | { |
988 | 0 | nsAutoRef<FcPattern> pattern(FcPatternCreate()); |
989 | 0 | if (!pattern) { |
990 | 0 | NS_WARNING("Failed to create Fontconfig pattern for font instance"); |
991 | 0 | return nullptr; |
992 | 0 | } |
993 | 0 |
|
994 | 0 | double size = ChooseFontSize(this, *aFontStyle); |
995 | 0 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size); |
996 | 0 |
|
997 | 0 | FT_Face face = mFTFace; |
998 | 0 | FcPattern* fontPattern = mFontPattern; |
999 | 0 | if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { |
1000 | 0 | // For variation fonts, we create a new FT_Face and FcPattern here |
1001 | 0 | // so that variation coordinates from the style can be applied |
1002 | 0 | // without affecting other font instances created from the same |
1003 | 0 | // entry (font resource). |
1004 | 0 | if (mFontData) { |
1005 | 0 | // For user fonts: create a new FT_Face from the font data, and then |
1006 | 0 | // make a pattern from that. |
1007 | 0 | face = Factory::NewFTFaceFromData(nullptr, mFontData, mLength, 0); |
1008 | 0 | fontPattern = CreatePatternForFace(face); |
1009 | 0 | } else { |
1010 | 0 | // For system fonts: create a new FT_Face and store it in a copy of |
1011 | 0 | // the original mFontPattern. |
1012 | 0 | fontPattern = FcPatternDuplicate(mFontPattern); |
1013 | 0 | face = CreateFaceForPattern(fontPattern); |
1014 | 0 | if (face) { |
1015 | 0 | FcPatternAddFTFace(fontPattern, FC_FT_FACE, face); |
1016 | 0 | } else { |
1017 | 0 | // I don't think CreateFaceForPattern above should ever fail, |
1018 | 0 | // but just in case let's fall back here. |
1019 | 0 | face = mFTFace; |
1020 | 0 | } |
1021 | 0 | } |
1022 | 0 | } |
1023 | 0 |
|
1024 | 0 | PreparePattern(pattern, aFontStyle->printerFont); |
1025 | 0 | nsAutoRef<FcPattern> renderPattern |
1026 | 0 | (FcFontRenderPrepare(nullptr, pattern, fontPattern)); |
1027 | 0 | if (fontPattern != mFontPattern) { |
1028 | 0 | // Discard temporary pattern used for variation support |
1029 | 0 | FcPatternDestroy(fontPattern); |
1030 | 0 | } |
1031 | 0 | if (!renderPattern) { |
1032 | 0 | NS_WARNING("Failed to prepare Fontconfig pattern for font instance"); |
1033 | 0 | if (face != mFTFace) { |
1034 | 0 | Factory::ReleaseFTFace(face); |
1035 | 0 | } |
1036 | 0 | return nullptr; |
1037 | 0 | } |
1038 | 0 |
|
1039 | 0 | cairo_scaled_font_t* scaledFont = |
1040 | 0 | CreateScaledFont(renderPattern, size, aFontStyle, face != mFTFace ? face : nullptr); |
1041 | 0 |
|
1042 | 0 | const FcChar8* file = ToFcChar8Ptr(""); |
1043 | 0 | int index = 0; |
1044 | 0 | if (!mFontData) { |
1045 | 0 | if (FcPatternGetString(renderPattern, FC_FILE, 0, |
1046 | 0 | const_cast<FcChar8**>(&file)) != FcResultMatch || |
1047 | 0 | FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) { |
1048 | 0 | NS_WARNING("No file in Fontconfig pattern for font instance"); |
1049 | 0 | return nullptr; |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 |
|
1053 | 0 | RefPtr<UnscaledFontFontconfig> unscaledFont = |
1054 | 0 | mUnscaledFontCache.Lookup(ToCharPtr(file), index); |
1055 | 0 | if (!unscaledFont) { |
1056 | 0 | unscaledFont = |
1057 | 0 | mFontData ? |
1058 | 0 | new UnscaledFontFontconfig(face) : |
1059 | 0 | new UnscaledFontFontconfig(ToCharPtr(file), index); |
1060 | 0 | mUnscaledFontCache.Add(unscaledFont); |
1061 | 0 | } |
1062 | 0 |
|
1063 | 0 | gfxFont* newFont = |
1064 | 0 | new gfxFontconfigFont(unscaledFont, scaledFont, |
1065 | 0 | renderPattern, size, |
1066 | 0 | this, aFontStyle); |
1067 | 0 | cairo_scaled_font_destroy(scaledFont); |
1068 | 0 |
|
1069 | 0 | return newFont; |
1070 | 0 | } |
1071 | | |
1072 | | FT_Face |
1073 | | gfxFontconfigFontEntry::GetFTFace() |
1074 | 0 | { |
1075 | 0 | if (!mFTFaceInitialized) { |
1076 | 0 | mFTFaceInitialized = true; |
1077 | 0 | mFTFace = CreateFaceForPattern(mFontPattern); |
1078 | 0 | } |
1079 | 0 | return mFTFace; |
1080 | 0 | } |
1081 | | |
1082 | | bool |
1083 | | gfxFontconfigFontEntry::HasVariations() |
1084 | 0 | { |
1085 | 0 | if (mHasVariationsInitialized) { |
1086 | 0 | return mHasVariations; |
1087 | 0 | } |
1088 | 0 | mHasVariationsInitialized = true; |
1089 | 0 | mHasVariations = false; |
1090 | 0 |
|
1091 | 0 | if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) { |
1092 | 0 | return mHasVariations; |
1093 | 0 | } |
1094 | 0 | |
1095 | 0 | // For installed fonts, query the fontconfig pattern rather than paying |
1096 | 0 | // the cost of loading a FT_Face that we otherwise might never need. |
1097 | 0 | if (!IsUserFont() || IsLocalUserFont()) { |
1098 | 0 | FcBool variable; |
1099 | 0 | if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, |
1100 | 0 | &variable) == FcResultMatch) && variable) { |
1101 | 0 | mHasVariations = true; |
1102 | 0 | } |
1103 | 0 | } else { |
1104 | 0 | FT_Face face = GetFTFace(); |
1105 | 0 | if (face) { |
1106 | 0 | mHasVariations = face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS; |
1107 | 0 | } |
1108 | 0 | } |
1109 | 0 |
|
1110 | 0 | return mHasVariations; |
1111 | 0 | } |
1112 | | |
1113 | | FT_MM_Var* |
1114 | | gfxFontconfigFontEntry::GetMMVar() |
1115 | 0 | { |
1116 | 0 | if (mMMVarInitialized) { |
1117 | 0 | return mMMVar; |
1118 | 0 | } |
1119 | 0 | mMMVarInitialized = true; |
1120 | 0 | InitializeVarFuncs(); |
1121 | 0 | if (!sGetVar) { |
1122 | 0 | return nullptr; |
1123 | 0 | } |
1124 | 0 | FT_Face face = GetFTFace(); |
1125 | 0 | if (!face) { |
1126 | 0 | return nullptr; |
1127 | 0 | } |
1128 | 0 | if (FT_Err_Ok != (*sGetVar)(face, &mMMVar)) { |
1129 | 0 | mMMVar = nullptr; |
1130 | 0 | } |
1131 | 0 | return mMMVar; |
1132 | 0 | } |
1133 | | |
1134 | | void |
1135 | | gfxFontconfigFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) |
1136 | 0 | { |
1137 | 0 | if (!HasVariations()) { |
1138 | 0 | return; |
1139 | 0 | } |
1140 | 0 | gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes); |
1141 | 0 | } |
1142 | | |
1143 | | void |
1144 | | gfxFontconfigFontEntry::GetVariationInstances( |
1145 | | nsTArray<gfxFontVariationInstance>& aInstances) |
1146 | 0 | { |
1147 | 0 | if (!HasVariations()) { |
1148 | 0 | return; |
1149 | 0 | } |
1150 | 0 | gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances); |
1151 | 0 | } |
1152 | | |
1153 | | nsresult |
1154 | | gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag, |
1155 | | nsTArray<uint8_t>& aBuffer) |
1156 | 0 | { |
1157 | 0 | NS_ASSERTION(!mIsDataUserFont, |
1158 | 0 | "data fonts should be reading tables directly from memory"); |
1159 | 0 |
|
1160 | 0 | FT_Face face = GetFTFace(); |
1161 | 0 | if (!face) { |
1162 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1163 | 0 | } |
1164 | 0 | |
1165 | 0 | FT_ULong length = 0; |
1166 | 0 | if (FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &length) != 0) { |
1167 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1168 | 0 | } |
1169 | 0 | if (!aBuffer.SetLength(length, fallible)) { |
1170 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1171 | 0 | } |
1172 | 0 | if (FT_Load_Sfnt_Table(face, aTableTag, 0, aBuffer.Elements(), &length) != 0) { |
1173 | 0 | aBuffer.Clear(); |
1174 | 0 | return NS_ERROR_FAILURE; |
1175 | 0 | } |
1176 | 0 | |
1177 | 0 | return NS_OK; |
1178 | 0 | } |
1179 | | |
1180 | | void |
1181 | | gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) |
1182 | 0 | { |
1183 | 0 | if (mHasStyles) { |
1184 | 0 | return; |
1185 | 0 | } |
1186 | 0 | |
1187 | 0 | // add font entries for each of the faces |
1188 | 0 | uint32_t numFonts = mFontPatterns.Length(); |
1189 | 0 | NS_ASSERTION(numFonts, "font family containing no faces!!"); |
1190 | 0 | uint32_t numRegularFaces = 0; |
1191 | 0 | for (uint32_t i = 0; i < numFonts; i++) { |
1192 | 0 | FcPattern* face = mFontPatterns[i]; |
1193 | 0 |
|
1194 | 0 | // figure out the psname/fullname and choose which to use as the facename |
1195 | 0 | nsAutoCString psname, fullname; |
1196 | 0 | GetFaceNames(face, mName, psname, fullname); |
1197 | 0 | const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname; |
1198 | 0 |
|
1199 | 0 | gfxFontconfigFontEntry *fontEntry = |
1200 | 0 | new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts); |
1201 | 0 |
|
1202 | 0 | if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) { |
1203 | 0 | fontEntry->SetupVariationRanges(); |
1204 | 0 | } |
1205 | 0 |
|
1206 | 0 | AddFontEntry(fontEntry); |
1207 | 0 |
|
1208 | 0 | if (fontEntry->IsNormalStyle()) { |
1209 | 0 | numRegularFaces++; |
1210 | 0 | } |
1211 | 0 |
|
1212 | 0 | if (LOG_FONTLIST_ENABLED()) { |
1213 | 0 | nsAutoCString weightString; |
1214 | 0 | fontEntry->Weight().ToString(weightString); |
1215 | 0 | nsAutoCString stretchString; |
1216 | 0 | fontEntry->Stretch().ToString(stretchString); |
1217 | 0 | nsAutoCString styleString; |
1218 | 0 | fontEntry->SlantStyle().ToString(styleString); |
1219 | 0 | LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" |
1220 | 0 | " with style: %s weight: %s stretch: %s" |
1221 | 0 | " psname: %s fullname: %s", |
1222 | 0 | fontEntry->Name().get(), |
1223 | 0 | Name().get(), |
1224 | 0 | styleString.get(), |
1225 | 0 | weightString.get(), |
1226 | 0 | stretchString.get(), |
1227 | 0 | psname.get(), |
1228 | 0 | fullname.get())); |
1229 | 0 | } |
1230 | 0 | } |
1231 | 0 |
|
1232 | 0 | // somewhat arbitrary, but define a family with two or more regular |
1233 | 0 | // faces as a family for which intra-family fallback should be used |
1234 | 0 | if (numRegularFaces > 1) { |
1235 | 0 | mCheckForFallbackFaces = true; |
1236 | 0 | } |
1237 | 0 | mFaceNamesInitialized = true; |
1238 | 0 | mFontPatterns.Clear(); |
1239 | 0 | SetHasStyles(true); |
1240 | 0 | } |
1241 | | |
1242 | | void |
1243 | | gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern) |
1244 | 0 | { |
1245 | 0 | NS_ASSERTION(!mHasStyles, |
1246 | 0 | "font patterns must not be added to already enumerated families"); |
1247 | 0 |
|
1248 | 0 | FcBool outline; |
1249 | 0 | if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch || |
1250 | 0 | !outline) { |
1251 | 0 | mHasNonScalableFaces = true; |
1252 | 0 |
|
1253 | 0 | FcBool scalable; |
1254 | 0 | if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch && |
1255 | 0 | scalable) { |
1256 | 0 | mForceScalable = true; |
1257 | 0 | } |
1258 | 0 | } |
1259 | 0 |
|
1260 | 0 | nsCountedRef<FcPattern> pattern(aFontPattern); |
1261 | 0 | mFontPatterns.AppendElement(pattern); |
1262 | 0 | } |
1263 | | |
1264 | | static const double kRejectDistance = 10000.0; |
1265 | | |
1266 | | // Calculate a distance score representing the size disparity between the |
1267 | | // requested style's size and the font entry's size. |
1268 | | static double |
1269 | | SizeDistance(gfxFontconfigFontEntry* aEntry, |
1270 | | const gfxFontStyle& aStyle, |
1271 | | bool aForceScalable) |
1272 | 0 | { |
1273 | 0 | double requestedSize = SizeForStyle(aEntry, aStyle); |
1274 | 0 | double bestDist = -1.0; |
1275 | 0 | double size; |
1276 | 0 | int v = 0; |
1277 | 0 | while (FcPatternGetDouble(aEntry->GetPattern(), |
1278 | 0 | FC_PIXEL_SIZE, v, &size) == FcResultMatch) { |
1279 | 0 | ++v; |
1280 | 0 | double dist = fabs(size - requestedSize); |
1281 | 0 | if (bestDist < 0.0 || dist < bestDist) { |
1282 | 0 | bestDist = dist; |
1283 | 0 | } |
1284 | 0 | } |
1285 | 0 | if (bestDist < 0.0) { |
1286 | 0 | // No size means scalable |
1287 | 0 | return -1.0; |
1288 | 0 | } else if (aForceScalable || 5.0 * bestDist < requestedSize) { |
1289 | 0 | // fontconfig prefers a matching family or lang to pixelsize of bitmap |
1290 | 0 | // fonts. CSS suggests a tolerance of 20% on pixelsize. |
1291 | 0 | return bestDist; |
1292 | 0 | } else { |
1293 | 0 | // Reject any non-scalable fonts that are not within tolerance. |
1294 | 0 | return kRejectDistance; |
1295 | 0 | } |
1296 | 0 | } |
1297 | | |
1298 | | void |
1299 | | gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle, |
1300 | | nsTArray<gfxFontEntry*>& aFontEntryList, |
1301 | | bool aIgnoreSizeTolerance) |
1302 | 0 | { |
1303 | 0 | gfxFontFamily::FindAllFontsForStyle(aFontStyle, |
1304 | 0 | aFontEntryList, |
1305 | 0 | aIgnoreSizeTolerance); |
1306 | 0 |
|
1307 | 0 | if (!mHasNonScalableFaces) { |
1308 | 0 | return; |
1309 | 0 | } |
1310 | 0 | |
1311 | 0 | // Iterate over the the available fonts while compacting any groups |
1312 | 0 | // of unscalable fonts with matching styles into a single entry |
1313 | 0 | // corresponding to the closest available size. If the closest |
1314 | 0 | // available size is rejected for being outside tolerance, then the |
1315 | 0 | // entire group will be skipped. |
1316 | 0 | size_t skipped = 0; |
1317 | 0 | gfxFontconfigFontEntry* bestEntry = nullptr; |
1318 | 0 | double bestDist = -1.0; |
1319 | 0 | for (size_t i = 0; i < aFontEntryList.Length(); i++) { |
1320 | 0 | gfxFontconfigFontEntry* entry = |
1321 | 0 | static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]); |
1322 | 0 | double dist = SizeDistance(entry, aFontStyle, |
1323 | 0 | mForceScalable || aIgnoreSizeTolerance); |
1324 | 0 | // If the entry is scalable or has a style that does not match |
1325 | 0 | // the group of unscalable fonts, then start a new group. |
1326 | 0 | if (dist < 0.0 || |
1327 | 0 | !bestEntry || |
1328 | 0 | bestEntry->Stretch() != entry->Stretch() || |
1329 | 0 | bestEntry->Weight() != entry->Weight() || |
1330 | 0 | bestEntry->SlantStyle() != entry->SlantStyle()) { |
1331 | 0 | // If the best entry in this group is still outside the tolerance, |
1332 | 0 | // then skip the entire group. |
1333 | 0 | if (bestDist >= kRejectDistance) { |
1334 | 0 | skipped++; |
1335 | 0 | } |
1336 | 0 | // Remove any compacted entries from the previous group. |
1337 | 0 | if (skipped) { |
1338 | 0 | i -= skipped; |
1339 | 0 | aFontEntryList.RemoveElementsAt(i, skipped); |
1340 | 0 | skipped = 0; |
1341 | 0 | } |
1342 | 0 | // Mark the start of the new group. |
1343 | 0 | bestEntry = entry; |
1344 | 0 | bestDist = dist; |
1345 | 0 | } else { |
1346 | 0 | // If this entry more closely matches the requested size than the |
1347 | 0 | // current best in the group, then take this entry instead. |
1348 | 0 | if (dist < bestDist) { |
1349 | 0 | aFontEntryList[i-1-skipped] = entry; |
1350 | 0 | bestEntry = entry; |
1351 | 0 | bestDist = dist; |
1352 | 0 | } |
1353 | 0 | skipped++; |
1354 | 0 | } |
1355 | 0 | } |
1356 | 0 | // If the best entry in this group is still outside the tolerance, |
1357 | 0 | // then skip the entire group. |
1358 | 0 | if (bestDist >= kRejectDistance) { |
1359 | 0 | skipped++; |
1360 | 0 | } |
1361 | 0 | // Remove any compacted entries from the current group. |
1362 | 0 | if (skipped) { |
1363 | 0 | aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped); |
1364 | 0 | } |
1365 | 0 | } |
1366 | | |
1367 | | static bool |
1368 | | PatternHasLang(const FcPattern *aPattern, const FcChar8 *aLang) |
1369 | 0 | { |
1370 | 0 | FcLangSet *langset; |
1371 | 0 |
|
1372 | 0 | if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) { |
1373 | 0 | return false; |
1374 | 0 | } |
1375 | 0 | |
1376 | 0 | if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) { |
1377 | 0 | return true; |
1378 | 0 | } |
1379 | 0 | return false; |
1380 | 0 | } |
1381 | | |
1382 | | bool |
1383 | | gfxFontconfigFontFamily::SupportsLangGroup(nsAtom *aLangGroup) const |
1384 | 0 | { |
1385 | 0 | if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) { |
1386 | 0 | return true; |
1387 | 0 | } |
1388 | 0 | |
1389 | 0 | nsAutoCString fcLang; |
1390 | 0 | gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList(); |
1391 | 0 | pfl->GetSampleLangForGroup(aLangGroup, fcLang); |
1392 | 0 | if (fcLang.IsEmpty()) { |
1393 | 0 | return true; |
1394 | 0 | } |
1395 | 0 | |
1396 | 0 | // Before FindStyleVariations has been called, mFontPatterns will contain |
1397 | 0 | // the font patterns. Afterward, it'll be empty, but mAvailableFonts |
1398 | 0 | // will contain the font entries, each of which holds a reference to its |
1399 | 0 | // pattern. We only check the first pattern in each list, because support |
1400 | 0 | // for langs is considered to be consistent across all faces in a family. |
1401 | 0 | FcPattern* fontPattern; |
1402 | 0 | if (mFontPatterns.Length()) { |
1403 | 0 | fontPattern = mFontPatterns[0]; |
1404 | 0 | } else if (mAvailableFonts.Length()) { |
1405 | 0 | fontPattern = static_cast<gfxFontconfigFontEntry*> |
1406 | 0 | (mAvailableFonts[0].get())->GetPattern(); |
1407 | 0 | } else { |
1408 | 0 | return true; |
1409 | 0 | } |
1410 | 0 | |
1411 | 0 | // is lang included in the underlying pattern? |
1412 | 0 | return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get())); |
1413 | 0 | } |
1414 | | |
1415 | | /* virtual */ |
1416 | | gfxFontconfigFontFamily::~gfxFontconfigFontFamily() |
1417 | 0 | { |
1418 | 0 | // Should not be dropped by stylo |
1419 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1420 | 0 | } |
1421 | | |
1422 | | template<typename Func> |
1423 | | void |
1424 | | gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) |
1425 | 0 | { |
1426 | 0 | if (HasStyles()) { |
1427 | 0 | for (auto& fe : mAvailableFonts) { |
1428 | 0 | if (!fe) { |
1429 | 0 | continue; |
1430 | 0 | } |
1431 | 0 | auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get()); |
1432 | 0 | aAddPatternFunc(fce->GetPattern(), mContainsAppFonts); |
1433 | 0 | } |
1434 | 0 | } else { |
1435 | 0 | for (auto& pat : mFontPatterns) { |
1436 | 0 | aAddPatternFunc(pat, mContainsAppFonts); |
1437 | 0 | } |
1438 | 0 | } |
1439 | 0 | } Unexecuted instantiation: gfxFcPlatformFontList.cpp:void gfxFontconfigFontFamily::AddFacesToFontList<gfxFcPlatformFontList::ReadSystemFontList(nsTArray<mozilla::dom::SystemFontListEntry>*)::$_0>(gfxFcPlatformFontList::ReadSystemFontList(nsTArray<mozilla::dom::SystemFontListEntry>*)::$_0) Unexecuted instantiation: gfxFcPlatformFontList.cpp:void gfxFontconfigFontFamily::AddFacesToFontList<gfxFcPlatformFontList::ReadSystemFontList(nsTArray<mozilla::dom::SystemFontListEntry>*)::$_1>(gfxFcPlatformFontList::ReadSystemFontList(nsTArray<mozilla::dom::SystemFontListEntry>*)::$_1) |
1440 | | |
1441 | | gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont, |
1442 | | cairo_scaled_font_t *aScaledFont, |
1443 | | FcPattern *aPattern, |
1444 | | gfxFloat aAdjustedSize, |
1445 | | gfxFontEntry *aFontEntry, |
1446 | | const gfxFontStyle *aFontStyle) |
1447 | | : gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle) |
1448 | | , mPattern(aPattern) |
1449 | 0 | { |
1450 | 0 | mAdjustedSize = aAdjustedSize; |
1451 | 0 | } |
1452 | | |
1453 | | gfxFontconfigFont::~gfxFontconfigFont() |
1454 | 0 | { |
1455 | 0 | } |
1456 | | |
1457 | | already_AddRefed<ScaledFont> |
1458 | | gfxFontconfigFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget) |
1459 | 0 | { |
1460 | 0 | if (!mAzureScaledFont) { |
1461 | 0 | NativeFont nativeFont; |
1462 | 0 | nativeFont.mType = NativeFontType::FONTCONFIG_PATTERN; |
1463 | 0 | nativeFont.mFont = GetPattern(); |
1464 | 0 |
|
1465 | 0 | mAzureScaledFont = |
1466 | 0 | Factory::CreateScaledFontForNativeFont(nativeFont, |
1467 | 0 | GetUnscaledFont(), |
1468 | 0 | GetAdjustedSize(), |
1469 | 0 | GetCairoScaledFont()); |
1470 | 0 | InitializeScaledFont(); |
1471 | 0 | } |
1472 | 0 |
|
1473 | 0 | RefPtr<ScaledFont> scaledFont(mAzureScaledFont); |
1474 | 0 | return scaledFont.forget(); |
1475 | 0 | } |
1476 | | |
1477 | | gfxFcPlatformFontList::gfxFcPlatformFontList() |
1478 | | : mLocalNames(64) |
1479 | | , mGenericMappings(32) |
1480 | | , mFcSubstituteCache(64) |
1481 | | , mLastConfig(nullptr) |
1482 | | , mAlwaysUseFontconfigGenerics(true) |
1483 | 0 | { |
1484 | 0 | if (XRE_IsParentProcess()) { |
1485 | 0 | // if the rescan interval is set, start the timer |
1486 | 0 | int rescanInterval = FcConfigGetRescanInterval(nullptr); |
1487 | 0 | if (rescanInterval) { |
1488 | 0 | mLastConfig = FcConfigGetCurrent(); |
1489 | 0 | NS_NewTimerWithFuncCallback(getter_AddRefs(mCheckFontUpdatesTimer), |
1490 | 0 | CheckFontUpdates, |
1491 | 0 | this, |
1492 | 0 | (rescanInterval + 1) * 1000, |
1493 | 0 | nsITimer::TYPE_REPEATING_SLACK, |
1494 | 0 | "gfxFcPlatformFontList::gfxFcPlatformFontList"); |
1495 | 0 | if (!mCheckFontUpdatesTimer) { |
1496 | 0 | NS_WARNING("Failure to create font updates timer"); |
1497 | 0 | } |
1498 | 0 | } |
1499 | 0 | } |
1500 | 0 |
|
1501 | 0 | #ifdef MOZ_BUNDLED_FONTS |
1502 | 0 | mBundledFontsInitialized = false; |
1503 | 0 | #endif |
1504 | 0 | } |
1505 | | |
1506 | | gfxFcPlatformFontList::~gfxFcPlatformFontList() |
1507 | 0 | { |
1508 | 0 | if (mCheckFontUpdatesTimer) { |
1509 | 0 | mCheckFontUpdatesTimer->Cancel(); |
1510 | 0 | mCheckFontUpdatesTimer = nullptr; |
1511 | 0 | } |
1512 | 0 | } |
1513 | | |
1514 | | void |
1515 | | gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet, |
1516 | | const SandboxPolicy* aPolicy, |
1517 | | bool aAppFonts) |
1518 | 0 | { |
1519 | 0 | // This iterates over the fonts in a font set and adds in gfxFontFamily |
1520 | 0 | // objects for each family. Individual gfxFontEntry objects for each face |
1521 | 0 | // are not created here; the patterns are just stored in the family. When |
1522 | 0 | // a family is actually used, it will be populated with gfxFontEntry |
1523 | 0 | // records and the patterns moved to those. |
1524 | 0 |
|
1525 | 0 | if (!aFontSet) { |
1526 | 0 | NS_WARNING("AddFontSetFamilies called with a null font set."); |
1527 | 0 | return; |
1528 | 0 | } |
1529 | 0 |
|
1530 | 0 | FcChar8* lastFamilyName = (FcChar8*)""; |
1531 | 0 | RefPtr<gfxFontconfigFontFamily> fontFamily; |
1532 | 0 | nsAutoCString familyName; |
1533 | 0 | for (int f = 0; f < aFontSet->nfont; f++) { |
1534 | 0 | FcPattern* pattern = aFontSet->fonts[f]; |
1535 | 0 |
|
1536 | 0 | // Skip any fonts that aren't readable for us (e.g. due to restrictive |
1537 | 0 | // file ownership/permissions). |
1538 | 0 | FcChar8* path; |
1539 | 0 | if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) { |
1540 | 0 | continue; |
1541 | 0 | } |
1542 | 0 | if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) { |
1543 | 0 | continue; |
1544 | 0 | } |
1545 | 0 | |
1546 | 0 | #if defined(MOZ_CONTENT_SANDBOX) && defined (XP_LINUX) |
1547 | 0 | // Skip any fonts that will be blocked by the content-process sandbox |
1548 | 0 | // policy. |
1549 | 0 | if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) & |
1550 | 0 | SandboxBroker::Perms::MAY_READ)) { |
1551 | 0 | continue; |
1552 | 0 | } |
1553 | 0 | #endif |
1554 | 0 | |
1555 | 0 | AddPatternToFontList(pattern, lastFamilyName, |
1556 | 0 | familyName, fontFamily, aAppFonts); |
1557 | 0 | } |
1558 | 0 | } |
1559 | | |
1560 | | void |
1561 | | gfxFcPlatformFontList::AddPatternToFontList(FcPattern* aFont, |
1562 | | FcChar8*& aLastFamilyName, |
1563 | | nsACString& aFamilyName, |
1564 | | RefPtr<gfxFontconfigFontFamily>& aFontFamily, |
1565 | | bool aAppFonts) |
1566 | 0 | { |
1567 | 0 | // get canonical name |
1568 | 0 | uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG); |
1569 | 0 | FcChar8* canonical = nullptr; |
1570 | 0 | FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical); |
1571 | 0 | if (!canonical) { |
1572 | 0 | return; |
1573 | 0 | } |
1574 | 0 | |
1575 | 0 | // same as the last one? no need to add a new family, skip |
1576 | 0 | if (FcStrCmp(canonical, aLastFamilyName) != 0) { |
1577 | 0 | aLastFamilyName = canonical; |
1578 | 0 |
|
1579 | 0 | // add new family if one doesn't already exist |
1580 | 0 | aFamilyName.Truncate(); |
1581 | 0 | aFamilyName = ToCharPtr(canonical); |
1582 | 0 | nsAutoCString keyName(aFamilyName); |
1583 | 0 | ToLowerCase(keyName); |
1584 | 0 |
|
1585 | 0 | aFontFamily = static_cast<gfxFontconfigFontFamily*> |
1586 | 0 | (mFontFamilies.GetWeak(keyName)); |
1587 | 0 | if (!aFontFamily) { |
1588 | 0 | aFontFamily = new gfxFontconfigFontFamily(aFamilyName); |
1589 | 0 | mFontFamilies.Put(keyName, aFontFamily); |
1590 | 0 | } |
1591 | 0 | // Record if the family contains fonts from the app font set |
1592 | 0 | // (in which case we won't rely on fontconfig's charmap, due to |
1593 | 0 | // bug 1276594). |
1594 | 0 | if (aAppFonts) { |
1595 | 0 | aFontFamily->SetFamilyContainsAppFonts(true); |
1596 | 0 | } |
1597 | 0 |
|
1598 | 0 | // Add pointers to other localized family names. Most fonts |
1599 | 0 | // only have a single name, so the first call to GetString |
1600 | 0 | // will usually not match |
1601 | 0 | FcChar8* otherName; |
1602 | 0 | int n = (cIndex == 0 ? 1 : 0); |
1603 | 0 | while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == |
1604 | 0 | FcResultMatch) { |
1605 | 0 | nsAutoCString otherFamilyName(ToCharPtr(otherName)); |
1606 | 0 | AddOtherFamilyName(aFontFamily, otherFamilyName); |
1607 | 0 | n++; |
1608 | 0 | if (n == int(cIndex)) { |
1609 | 0 | n++; // skip over canonical name |
1610 | 0 | } |
1611 | 0 | } |
1612 | 0 | } |
1613 | 0 |
|
1614 | 0 | MOZ_ASSERT(aFontFamily, "font must belong to a font family"); |
1615 | 0 | aFontFamily->AddFontPattern(aFont); |
1616 | 0 |
|
1617 | 0 | // map the psname, fullname ==> font family for local font lookups |
1618 | 0 | nsAutoCString psname, fullname; |
1619 | 0 | GetFaceNames(aFont, aFamilyName, psname, fullname); |
1620 | 0 | if (!psname.IsEmpty()) { |
1621 | 0 | ToLowerCase(psname); |
1622 | 0 | mLocalNames.Put(psname, aFont); |
1623 | 0 | } |
1624 | 0 | if (!fullname.IsEmpty()) { |
1625 | 0 | ToLowerCase(fullname); |
1626 | 0 | mLocalNames.Put(fullname, aFont); |
1627 | 0 | } |
1628 | 0 | } |
1629 | | |
1630 | | nsresult |
1631 | | gfxFcPlatformFontList::InitFontListForPlatform() |
1632 | 0 | { |
1633 | 0 | #ifdef MOZ_BUNDLED_FONTS |
1634 | 0 | ActivateBundledFonts(); |
1635 | 0 | #endif |
1636 | 0 |
|
1637 | 0 | mLocalNames.Clear(); |
1638 | 0 | mFcSubstituteCache.Clear(); |
1639 | 0 |
|
1640 | 0 | mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics(); |
1641 | 0 | mOtherFamilyNamesInitialized = true; |
1642 | 0 |
|
1643 | 0 | if (XRE_IsContentProcess()) { |
1644 | 0 | // Content process: use the font list passed from the chrome process, |
1645 | 0 | // because we can't rely on fontconfig in the presence of sandboxing; |
1646 | 0 | // it may report fonts that we can't actually access. |
1647 | 0 |
|
1648 | 0 | FcChar8* lastFamilyName = (FcChar8*)""; |
1649 | 0 | RefPtr<gfxFontconfigFontFamily> fontFamily; |
1650 | 0 | nsAutoCString familyName; |
1651 | 0 |
|
1652 | 0 | // Get font list that was passed during XPCOM startup |
1653 | 0 | // or in an UpdateFontList message. |
1654 | 0 | auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList(); |
1655 | 0 |
|
1656 | 0 | // For fontconfig versions between 2.10.94 and 2.11.1 inclusive, |
1657 | 0 | // we need to escape any leading space in the charset element, |
1658 | 0 | // otherwise FcNameParse will fail. :( |
1659 | 0 | // |
1660 | 0 | // The bug was introduced on 2013-05-24 by |
1661 | 0 | // https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7 |
1662 | 0 | // "Bug 64906 - FcNameParse() should ignore leading whitespace in parameters" |
1663 | 0 | // because ignoring a leading space in the encoded value of charset |
1664 | 0 | // causes erroneous decoding of the whole element. |
1665 | 0 | // This first shipped in version 2.10.94, and was eventually fixed as |
1666 | 0 | // a side-effect of switching to the "human-readable" representation of |
1667 | 0 | // charsets on 2014-07-03 in |
1668 | 0 | // https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730 |
1669 | 0 | // "Change charset parse/unparse format to be human readable" |
1670 | 0 | // (with a followup fix next day) which means a leading space is no |
1671 | 0 | // longer significant. This fix landed after 2.11.1 had been shipped, |
1672 | 0 | // so the first version tag without the bug is 2.11.91. |
1673 | 0 | int fcVersion = FcGetVersion(); |
1674 | 0 | bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101; |
1675 | 0 |
|
1676 | 0 | for (SystemFontListEntry& fle : fontList) { |
1677 | 0 | MOZ_ASSERT(fle.type() == |
1678 | 0 | SystemFontListEntry::Type::TFontPatternListEntry); |
1679 | 0 | FontPatternListEntry& fpe(fle); |
1680 | 0 | nsCString& patternStr = fpe.pattern(); |
1681 | 0 | if (fcCharsetParseBug) { |
1682 | 0 | int32_t index = patternStr.Find(":charset= "); |
1683 | 0 | if (index != kNotFound) { |
1684 | 0 | // insert backslash after the =, before the space |
1685 | 0 | patternStr.Insert('\\', index + 9); |
1686 | 0 | } |
1687 | 0 | } |
1688 | 0 | FcPattern* pattern = |
1689 | 0 | FcNameParse((const FcChar8*)patternStr.get()); |
1690 | 0 | AddPatternToFontList(pattern, lastFamilyName, familyName, |
1691 | 0 | fontFamily, fpe.appFontFamily()); |
1692 | 0 | FcPatternDestroy(pattern); |
1693 | 0 | } |
1694 | 0 |
|
1695 | 0 | LOG_FONTLIST(("got font list from chrome process: " |
1696 | 0 | "%u faces in %u families", |
1697 | 0 | (unsigned)fontList.Length(), mFontFamilies.Count())); |
1698 | 0 |
|
1699 | 0 | fontList.Clear(); |
1700 | 0 |
|
1701 | 0 | return NS_OK; |
1702 | 0 | } |
1703 | 0 |
|
1704 | 0 | mLastConfig = FcConfigGetCurrent(); |
1705 | 0 |
|
1706 | 0 | UniquePtr<SandboxPolicy> policy; |
1707 | 0 |
|
1708 | 0 | #if defined(MOZ_CONTENT_SANDBOX) && defined (XP_LINUX) |
1709 | 0 | // If read sandboxing is enabled, create a temporary SandboxPolicy to |
1710 | 0 | // check font paths; use a fake PID to avoid picking up any PID-specific |
1711 | 0 | // rules by accident. |
1712 | 0 | SandboxBrokerPolicyFactory policyFactory; |
1713 | 0 | if (GetEffectiveContentSandboxLevel() > 2 && |
1714 | 0 | !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) { |
1715 | 0 | policy = policyFactory.GetContentPolicy(-1, false); |
1716 | 0 | } |
1717 | 0 | #endif |
1718 | 0 |
|
1719 | 0 | // iterate over available fonts |
1720 | 0 | FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem); |
1721 | 0 | AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false); |
1722 | 0 |
|
1723 | 0 | #ifdef MOZ_BUNDLED_FONTS |
1724 | 0 | FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication); |
1725 | 0 | AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true); |
1726 | 0 | #endif |
1727 | 0 |
|
1728 | 0 | return NS_OK; |
1729 | 0 | } |
1730 | | |
1731 | | void |
1732 | | gfxFcPlatformFontList::ReadSystemFontList( |
1733 | | InfallibleTArray<SystemFontListEntry>* retValue) |
1734 | 0 | { |
1735 | 0 | // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse |
1736 | 0 | // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using |
1737 | 0 | // an older version, we manually append it to the unparsed pattern. |
1738 | 0 | if (FcGetVersion() < 20900) { |
1739 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
1740 | 0 | auto family = |
1741 | 0 | static_cast<gfxFontconfigFontFamily*>(iter.Data().get()); |
1742 | 0 | family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) { |
1743 | 0 | char* s = (char*)FcNameUnparse(aPat); |
1744 | 0 | nsAutoCString patternStr(s); |
1745 | 0 | free(s); |
1746 | 0 | if (FcResultMatch == |
1747 | 0 | FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&s)) { |
1748 | 0 | patternStr.Append(":file="); |
1749 | 0 | patternStr.Append(s); |
1750 | 0 | } |
1751 | 0 | retValue->AppendElement(FontPatternListEntry(patternStr, |
1752 | 0 | aAppFonts)); |
1753 | 0 | }); |
1754 | 0 | } |
1755 | 0 | } else { |
1756 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
1757 | 0 | auto family = |
1758 | 0 | static_cast<gfxFontconfigFontFamily*>(iter.Data().get()); |
1759 | 0 | family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) { |
1760 | 0 | char* s = (char*)FcNameUnparse(aPat); |
1761 | 0 | nsDependentCString patternStr(s); |
1762 | 0 | retValue->AppendElement(FontPatternListEntry(patternStr, |
1763 | 0 | aAppFonts)); |
1764 | 0 | free(s); |
1765 | 0 | }); |
1766 | 0 | } |
1767 | 0 | } |
1768 | 0 | } |
1769 | | |
1770 | | // For displaying the fontlist in UI, use explicit call to FcFontList. Using |
1771 | | // FcFontList results in the list containing the localized names as dictated |
1772 | | // by system defaults. |
1773 | | static void |
1774 | | GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsAtom *aLangGroup) |
1775 | 0 | { |
1776 | 0 | aListOfFonts.Clear(); |
1777 | 0 |
|
1778 | 0 | nsAutoRef<FcPattern> pat(FcPatternCreate()); |
1779 | 0 | if (!pat) { |
1780 | 0 | return; |
1781 | 0 | } |
1782 | 0 | |
1783 | 0 | nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr)); |
1784 | 0 | if (!os) { |
1785 | 0 | return; |
1786 | 0 | } |
1787 | 0 | |
1788 | 0 | // add the lang to the pattern |
1789 | 0 | nsAutoCString fcLang; |
1790 | 0 | gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList(); |
1791 | 0 | pfl->GetSampleLangForGroup(aLangGroup, fcLang, |
1792 | 0 | /*aForFontEnumerationThread*/ true); |
1793 | 0 | if (!fcLang.IsEmpty()) { |
1794 | 0 | FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get())); |
1795 | 0 | } |
1796 | 0 |
|
1797 | 0 | nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os)); |
1798 | 0 | if (!fs) { |
1799 | 0 | return; |
1800 | 0 | } |
1801 | 0 | |
1802 | 0 | for (int i = 0; i < fs->nfont; i++) { |
1803 | 0 | char *family; |
1804 | 0 |
|
1805 | 0 | if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, |
1806 | 0 | (FcChar8 **) &family) != FcResultMatch) |
1807 | 0 | { |
1808 | 0 | continue; |
1809 | 0 | } |
1810 | 0 | |
1811 | 0 | // Remove duplicates... |
1812 | 0 | nsAutoString strFamily; |
1813 | 0 | AppendUTF8toUTF16(MakeStringSpan(family), strFamily); |
1814 | 0 | if (aListOfFonts.Contains(strFamily)) { |
1815 | 0 | continue; |
1816 | 0 | } |
1817 | 0 | |
1818 | 0 | aListOfFonts.AppendElement(strFamily); |
1819 | 0 | } |
1820 | 0 |
|
1821 | 0 | aListOfFonts.Sort(); |
1822 | 0 | } |
1823 | | |
1824 | | void |
1825 | | gfxFcPlatformFontList::GetFontList(nsAtom *aLangGroup, |
1826 | | const nsACString& aGenericFamily, |
1827 | | nsTArray<nsString>& aListOfFonts) |
1828 | 0 | { |
1829 | 0 | // Get the list of font family names using fontconfig |
1830 | 0 | GetSystemFontList(aListOfFonts, aLangGroup); |
1831 | 0 |
|
1832 | 0 | // Under Linux, the generics "serif", "sans-serif" and "monospace" |
1833 | 0 | // are included in the pref fontlist. These map to whatever fontconfig |
1834 | 0 | // decides they should be for a given language, rather than one of the |
1835 | 0 | // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*) |
1836 | 0 | bool serif = false, sansSerif = false, monospace = false; |
1837 | 0 | if (aGenericFamily.IsEmpty()) |
1838 | 0 | serif = sansSerif = monospace = true; |
1839 | 0 | else if (aGenericFamily.LowerCaseEqualsLiteral("serif")) |
1840 | 0 | serif = true; |
1841 | 0 | else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif")) |
1842 | 0 | sansSerif = true; |
1843 | 0 | else if (aGenericFamily.LowerCaseEqualsLiteral("monospace")) |
1844 | 0 | monospace = true; |
1845 | 0 | else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") || |
1846 | 0 | aGenericFamily.LowerCaseEqualsLiteral("fantasy")) |
1847 | 0 | serif = sansSerif = true; |
1848 | 0 | else |
1849 | 0 | MOZ_ASSERT_UNREACHABLE("unexpected CSS generic font family"); |
1850 | 0 |
|
1851 | 0 | // The first in the list becomes the default in |
1852 | 0 | // FontBuilder.readFontSelection() if the preference-selected font is not |
1853 | 0 | // available, so put system configured defaults first. |
1854 | 0 | if (monospace) |
1855 | 0 | aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace")); |
1856 | 0 | if (sansSerif) |
1857 | 0 | aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif")); |
1858 | 0 | if (serif) |
1859 | 0 | aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif")); |
1860 | 0 | } |
1861 | | |
1862 | | gfxFontFamily* |
1863 | | gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle) |
1864 | 0 | { |
1865 | 0 | // Get the default font by using a fake name to retrieve the first |
1866 | 0 | // scalable font that fontconfig suggests for the given language. |
1867 | 0 | PrefFontList* prefFonts = |
1868 | 0 | FindGenericFamilies(NS_LITERAL_CSTRING("-moz-default"), aStyle->language); |
1869 | 0 | NS_ASSERTION(prefFonts, "null list of generic fonts"); |
1870 | 0 | if (prefFonts && !prefFonts->IsEmpty()) { |
1871 | 0 | return (*prefFonts)[0]; |
1872 | 0 | } |
1873 | 0 | return nullptr; |
1874 | 0 | } |
1875 | | |
1876 | | gfxFontEntry* |
1877 | | gfxFcPlatformFontList::LookupLocalFont(const nsACString& aFontName, |
1878 | | WeightRange aWeightForEntry, |
1879 | | StretchRange aStretchForEntry, |
1880 | | SlantStyleRange aStyleForEntry) |
1881 | 0 | { |
1882 | 0 | nsAutoCString keyName(aFontName); |
1883 | 0 | ToLowerCase(keyName); |
1884 | 0 |
|
1885 | 0 | // if name is not in the global list, done |
1886 | 0 | FcPattern* fontPattern = mLocalNames.Get(keyName); |
1887 | 0 | if (!fontPattern) { |
1888 | 0 | return nullptr; |
1889 | 0 | } |
1890 | 0 | |
1891 | 0 | return new gfxFontconfigFontEntry(aFontName, |
1892 | 0 | fontPattern, |
1893 | 0 | aWeightForEntry, |
1894 | 0 | aStretchForEntry, |
1895 | 0 | aStyleForEntry); |
1896 | 0 | } |
1897 | | |
1898 | | gfxFontEntry* |
1899 | | gfxFcPlatformFontList::MakePlatformFont(const nsACString& aFontName, |
1900 | | WeightRange aWeightForEntry, |
1901 | | StretchRange aStretchForEntry, |
1902 | | SlantStyleRange aStyleForEntry, |
1903 | | const uint8_t* aFontData, |
1904 | | uint32_t aLength) |
1905 | 0 | { |
1906 | 0 | FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0); |
1907 | 0 | if (!face) { |
1908 | 0 | free((void*)aFontData); |
1909 | 0 | return nullptr; |
1910 | 0 | } |
1911 | 0 | if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { |
1912 | 0 | Factory::ReleaseFTFace(face); |
1913 | 0 | free((void*)aFontData); |
1914 | 0 | return nullptr; |
1915 | 0 | } |
1916 | 0 | |
1917 | 0 | return new gfxFontconfigFontEntry(aFontName, |
1918 | 0 | aWeightForEntry, |
1919 | 0 | aStretchForEntry, |
1920 | 0 | aStyleForEntry, |
1921 | 0 | aFontData, aLength, face); |
1922 | 0 | } |
1923 | | |
1924 | | bool |
1925 | | gfxFcPlatformFontList::FindAndAddFamilies(const nsACString& aFamily, |
1926 | | nsTArray<FamilyAndGeneric>* aOutput, |
1927 | | FindFamiliesFlags aFlags, |
1928 | | gfxFontStyle* aStyle, |
1929 | | gfxFloat aDevToCssSize) |
1930 | 0 | { |
1931 | 0 | nsAutoCString familyName(aFamily); |
1932 | 0 | ToLowerCase(familyName); |
1933 | 0 | nsAtom* language = (aStyle ? aStyle->language.get() : nullptr); |
1934 | 0 |
|
1935 | 0 | // deprecated generic names are explicitly converted to standard generics |
1936 | 0 | bool isDeprecatedGeneric = false; |
1937 | 0 | if (familyName.EqualsLiteral("sans") || |
1938 | 0 | familyName.EqualsLiteral("sans serif")) { |
1939 | 0 | familyName.AssignLiteral("sans-serif"); |
1940 | 0 | isDeprecatedGeneric = true; |
1941 | 0 | } else if (familyName.EqualsLiteral("mono")) { |
1942 | 0 | familyName.AssignLiteral("monospace"); |
1943 | 0 | isDeprecatedGeneric = true; |
1944 | 0 | } |
1945 | 0 |
|
1946 | 0 | // fontconfig generics? use fontconfig to determine the family for lang |
1947 | 0 | if (isDeprecatedGeneric || |
1948 | 0 | mozilla::FontFamilyName::Convert(familyName).IsGeneric()) { |
1949 | 0 | PrefFontList* prefFonts = FindGenericFamilies(familyName, language); |
1950 | 0 | if (prefFonts && !prefFonts->IsEmpty()) { |
1951 | 0 | aOutput->AppendElements(*prefFonts); |
1952 | 0 | return true; |
1953 | 0 | } |
1954 | 0 | return false; |
1955 | 0 | } |
1956 | 0 | |
1957 | 0 | // fontconfig allows conditional substitutions in such a way that it's |
1958 | 0 | // difficult to distinguish an explicit substitution from other suggested |
1959 | 0 | // choices. To sniff out explicit substitutions, compare the substitutions |
1960 | 0 | // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the |
1961 | 0 | // substitutions |
1962 | 0 | // |
1963 | 0 | // Example: |
1964 | 0 | // |
1965 | 0 | // serif ==> DejaVu Serif, ... |
1966 | 0 | // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif |
1967 | 0 | // |
1968 | 0 | // In this case fontconfig is including Tex Gyre Heros and |
1969 | 0 | // Nimbus Sans L as alternatives for Helvetica. |
1970 | 0 | |
1971 | 0 | // Because the FcConfigSubstitute call is quite expensive, we cache the |
1972 | 0 | // actual font families found via this process. So check the cache first: |
1973 | 0 | AutoTArray<FamilyAndGeneric,10> cachedFamilies; |
1974 | 0 | if (mFcSubstituteCache.Get(familyName, &cachedFamilies)) { |
1975 | 0 | if (cachedFamilies.IsEmpty()) { |
1976 | 0 | return false; |
1977 | 0 | } |
1978 | 0 | aOutput->AppendElements(cachedFamilies); |
1979 | 0 | return true; |
1980 | 0 | } |
1981 | 0 | |
1982 | 0 | // It wasn't in the cache, so we need to ask fontconfig... |
1983 | 0 | const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel"); |
1984 | 0 | FcChar8* sentinelFirstFamily = nullptr; |
1985 | 0 | nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate()); |
1986 | 0 | FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName); |
1987 | 0 | FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern); |
1988 | 0 | FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily); |
1989 | 0 |
|
1990 | 0 | // substitutions for font, -moz-sentinel pattern |
1991 | 0 | nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate()); |
1992 | 0 | FcPatternAddString(fontWithSentinel, FC_FAMILY, |
1993 | 0 | ToFcChar8Ptr(familyName.get())); |
1994 | 0 | FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName); |
1995 | 0 | FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern); |
1996 | 0 |
|
1997 | 0 | // Add all font family matches until reaching the sentinel. |
1998 | 0 | FcChar8* substName = nullptr; |
1999 | 0 | for (int i = 0; |
2000 | 0 | FcPatternGetString(fontWithSentinel, FC_FAMILY, |
2001 | 0 | i, &substName) == FcResultMatch; |
2002 | 0 | i++) |
2003 | 0 | { |
2004 | 0 | if (sentinelFirstFamily && |
2005 | 0 | FcStrCmp(substName, sentinelFirstFamily) == 0) { |
2006 | 0 | break; |
2007 | 0 | } |
2008 | 0 | gfxPlatformFontList::FindAndAddFamilies( |
2009 | 0 | nsDependentCString(ToCharPtr(substName)), |
2010 | 0 | &cachedFamilies, |
2011 | 0 | aFlags); |
2012 | 0 | } |
2013 | 0 |
|
2014 | 0 | // Cache the resulting list, so we don't have to do this again. |
2015 | 0 | mFcSubstituteCache.Put(familyName, cachedFamilies); |
2016 | 0 |
|
2017 | 0 | if (cachedFamilies.IsEmpty()) { |
2018 | 0 | return false; |
2019 | 0 | } |
2020 | 0 | aOutput->AppendElements(cachedFamilies); |
2021 | 0 | return true; |
2022 | 0 | } |
2023 | | |
2024 | | bool |
2025 | | gfxFcPlatformFontList::GetStandardFamilyName(const nsCString& aFontName, |
2026 | | nsACString& aFamilyName) |
2027 | 0 | { |
2028 | 0 | aFamilyName.Truncate(); |
2029 | 0 |
|
2030 | 0 | // The fontconfig list of fonts includes generic family names in the |
2031 | 0 | // font list. For these, just use the generic name. |
2032 | 0 | if (aFontName.EqualsLiteral("serif") || |
2033 | 0 | aFontName.EqualsLiteral("sans-serif") || |
2034 | 0 | aFontName.EqualsLiteral("monospace")) { |
2035 | 0 | aFamilyName.Assign(aFontName); |
2036 | 0 | return true; |
2037 | 0 | } |
2038 | 0 | |
2039 | 0 | nsAutoRef<FcPattern> pat(FcPatternCreate()); |
2040 | 0 | if (!pat) { |
2041 | 0 | return true; |
2042 | 0 | } |
2043 | 0 | |
2044 | 0 | nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr)); |
2045 | 0 | if (!os) { |
2046 | 0 | return true; |
2047 | 0 | } |
2048 | 0 | |
2049 | 0 | // add the family name to the pattern |
2050 | 0 | FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(aFontName.get())); |
2051 | 0 |
|
2052 | 0 | nsAutoRef<FcFontSet> givenFS(FcFontList(nullptr, pat, os)); |
2053 | 0 | if (!givenFS) { |
2054 | 0 | return true; |
2055 | 0 | } |
2056 | 0 | |
2057 | 0 | // See if there is a font face with first family equal to the given family |
2058 | 0 | // (needs to be in sync with names coming from GetFontList()) |
2059 | 0 | nsTArray<nsCString> candidates; |
2060 | 0 | for (int i = 0; i < givenFS->nfont; i++) { |
2061 | 0 | char* firstFamily; |
2062 | 0 |
|
2063 | 0 | if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0, |
2064 | 0 | (FcChar8 **) &firstFamily) != FcResultMatch) |
2065 | 0 | { |
2066 | 0 | continue; |
2067 | 0 | } |
2068 | 0 | |
2069 | 0 | nsDependentCString first(firstFamily); |
2070 | 0 | if (!candidates.Contains(first)) { |
2071 | 0 | candidates.AppendElement(first); |
2072 | 0 |
|
2073 | 0 | if (aFontName.Equals(first)) { |
2074 | 0 | aFamilyName.Assign(aFontName); |
2075 | 0 | return true; |
2076 | 0 | } |
2077 | 0 | } |
2078 | 0 | } |
2079 | 0 |
|
2080 | 0 | // Because fontconfig conflates different family name types, need to |
2081 | 0 | // double check that the candidate name is not simply a different |
2082 | 0 | // name type. For example, if a font with nameID=16 "Minion Pro" and |
2083 | 0 | // nameID=21 "Minion Pro Caption" exists, calling FcFontList with |
2084 | 0 | // family="Minion Pro" will return a set of patterns some of which |
2085 | 0 | // will have a first family of "Minion Pro Caption". Ignore these |
2086 | 0 | // patterns and use the first candidate that maps to a font set with |
2087 | 0 | // the same number of faces and an identical set of patterns. |
2088 | 0 | for (uint32_t j = 0; j < candidates.Length(); ++j) { |
2089 | 0 | FcPatternDel(pat, FC_FAMILY); |
2090 | 0 | FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get()); |
2091 | 0 |
|
2092 | 0 | nsAutoRef<FcFontSet> candidateFS(FcFontList(nullptr, pat, os)); |
2093 | 0 | if (!candidateFS) { |
2094 | 0 | return true; |
2095 | 0 | } |
2096 | 0 | |
2097 | 0 | if (candidateFS->nfont != givenFS->nfont) { |
2098 | 0 | continue; |
2099 | 0 | } |
2100 | 0 | |
2101 | 0 | bool equal = true; |
2102 | 0 | for (int i = 0; i < givenFS->nfont; ++i) { |
2103 | 0 | if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) { |
2104 | 0 | equal = false; |
2105 | 0 | break; |
2106 | 0 | } |
2107 | 0 | } |
2108 | 0 | if (equal) { |
2109 | 0 | aFamilyName = candidates[j]; |
2110 | 0 | return true; |
2111 | 0 | } |
2112 | 0 | } |
2113 | 0 |
|
2114 | 0 | // didn't find localized name, leave family name blank |
2115 | 0 | return true; |
2116 | 0 | } |
2117 | | |
2118 | | void |
2119 | | gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType, |
2120 | | nsAtom* aLanguage, |
2121 | | nsTArray<FamilyAndGeneric>& aFamilyList) |
2122 | 0 | { |
2123 | 0 | bool usePrefFontList = false; |
2124 | 0 |
|
2125 | 0 | // treat -moz-fixed as monospace |
2126 | 0 | if (aGenericType == eFamily_moz_fixed) { |
2127 | 0 | aGenericType = eFamily_monospace; |
2128 | 0 | } |
2129 | 0 |
|
2130 | 0 | const char* generic = GetGenericName(aGenericType); |
2131 | 0 | NS_ASSERTION(generic, "weird generic font type"); |
2132 | 0 | if (!generic) { |
2133 | 0 | return; |
2134 | 0 | } |
2135 | 0 | |
2136 | 0 | // By default, most font prefs on Linux map to "use fontconfig" |
2137 | 0 | // keywords. So only need to explicitly lookup font pref if |
2138 | 0 | // non-default settings exist |
2139 | 0 | nsAutoCString genericToLookup(generic); |
2140 | 0 | if ((!mAlwaysUseFontconfigGenerics && aLanguage) || |
2141 | 0 | aLanguage == nsGkAtoms::x_math) { |
2142 | 0 | nsAtom* langGroup = GetLangGroup(aLanguage); |
2143 | 0 | nsAutoString fontlistValue; |
2144 | 0 | Preferences::GetString(NamePref(generic, langGroup).get(), |
2145 | 0 | fontlistValue); |
2146 | 0 | nsresult rv; |
2147 | 0 | if (fontlistValue.IsEmpty()) { |
2148 | 0 | // The font name list may have two or more family names as comma |
2149 | 0 | // separated list. In such case, not matching with generic font |
2150 | 0 | // name is fine because if the list prefers specific font, we |
2151 | 0 | // should try to use the pref with complicated path. |
2152 | 0 | rv = Preferences::GetString(NameListPref(generic, langGroup).get(), |
2153 | 0 | fontlistValue); |
2154 | 0 | } else { |
2155 | 0 | rv = NS_OK; |
2156 | 0 | } |
2157 | 0 | if (NS_SUCCEEDED(rv)) { |
2158 | 0 | if (!fontlistValue.EqualsLiteral("serif") && |
2159 | 0 | !fontlistValue.EqualsLiteral("sans-serif") && |
2160 | 0 | !fontlistValue.EqualsLiteral("monospace")) { |
2161 | 0 | usePrefFontList = true; |
2162 | 0 | } else { |
2163 | 0 | // serif, sans-serif or monospace was specified |
2164 | 0 | genericToLookup.Truncate(); |
2165 | 0 | AppendUTF16toUTF8(fontlistValue, genericToLookup); |
2166 | 0 | } |
2167 | 0 | } |
2168 | 0 | } |
2169 | 0 |
|
2170 | 0 | // when pref fonts exist, use standard pref font lookup |
2171 | 0 | if (usePrefFontList) { |
2172 | 0 | return gfxPlatformFontList::AddGenericFonts(aGenericType, |
2173 | 0 | aLanguage, |
2174 | 0 | aFamilyList); |
2175 | 0 | } |
2176 | 0 | |
2177 | 0 | PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage); |
2178 | 0 | NS_ASSERTION(prefFonts, "null generic font list"); |
2179 | 0 | aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length()); |
2180 | 0 | for (auto& f : *prefFonts) { |
2181 | 0 | aFamilyList.AppendElement(FamilyAndGeneric(f.get(), aGenericType)); |
2182 | 0 | } |
2183 | 0 | } |
2184 | | |
2185 | | void |
2186 | | gfxFcPlatformFontList::ClearLangGroupPrefFonts() |
2187 | 0 | { |
2188 | 0 | ClearGenericMappings(); |
2189 | 0 | gfxPlatformFontList::ClearLangGroupPrefFonts(); |
2190 | 0 | mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics(); |
2191 | 0 | } |
2192 | | |
2193 | | /* static */ FT_Library |
2194 | | gfxFcPlatformFontList::GetFTLibrary() |
2195 | 0 | { |
2196 | 0 | if (!sCairoFTLibrary) { |
2197 | 0 | // Use cairo's FT_Library so that cairo takes care of shutdown of the |
2198 | 0 | // FT_Library after it has destroyed its font_faces, and FT_Done_Face |
2199 | 0 | // has been called on each FT_Face, at least until this bug is fixed: |
2200 | 0 | // https://bugs.freedesktop.org/show_bug.cgi?id=18857 |
2201 | 0 | // |
2202 | 0 | // Cairo keeps it's own FT_Library object for creating FT_Face |
2203 | 0 | // instances, so use that. There's no simple API for accessing this |
2204 | 0 | // so use the hacky method below of making a font and extracting |
2205 | 0 | // the library pointer from that. |
2206 | 0 |
|
2207 | 0 | FcPattern* pat = |
2208 | 0 | FcPatternBuild(0, FC_FAMILY, FcTypeString, "serif", (char*)0); |
2209 | 0 | cairo_font_face_t* face = |
2210 | 0 | cairo_ft_font_face_create_for_pattern(pat, nullptr, 0); |
2211 | 0 | FcPatternDestroy(pat); |
2212 | 0 |
|
2213 | 0 | cairo_matrix_t identity; |
2214 | 0 | cairo_matrix_init_identity(&identity); |
2215 | 0 | cairo_font_options_t* options = cairo_font_options_create(); |
2216 | 0 | cairo_scaled_font_t* sf = |
2217 | 0 | cairo_scaled_font_create(face, &identity, &identity, options); |
2218 | 0 | cairo_font_options_destroy(options); |
2219 | 0 | cairo_font_face_destroy(face); |
2220 | 0 |
|
2221 | 0 | FT_Face ft = cairo_ft_scaled_font_lock_face(sf); |
2222 | 0 |
|
2223 | 0 | sCairoFTLibrary = ft->glyph->library; |
2224 | 0 |
|
2225 | 0 | cairo_ft_scaled_font_unlock_face(sf); |
2226 | 0 | cairo_scaled_font_destroy(sf); |
2227 | 0 | } |
2228 | 0 |
|
2229 | 0 | return sCairoFTLibrary; |
2230 | 0 | } |
2231 | | |
2232 | | gfxPlatformFontList::PrefFontList* |
2233 | | gfxFcPlatformFontList::FindGenericFamilies(const nsCString& aGeneric, |
2234 | | nsAtom* aLanguage) |
2235 | 0 | { |
2236 | 0 | // set up name |
2237 | 0 | nsAutoCString fcLang; |
2238 | 0 | GetSampleLangForGroup(aLanguage, fcLang); |
2239 | 0 | ToLowerCase(fcLang); |
2240 | 0 |
|
2241 | 0 | nsAutoCString genericLang(aGeneric); |
2242 | 0 | if (fcLang.Length() > 0) { |
2243 | 0 | genericLang.Append('-'); |
2244 | 0 | } |
2245 | 0 | genericLang.Append(fcLang); |
2246 | 0 |
|
2247 | 0 | // try to get the family from the cache |
2248 | 0 | PrefFontList* prefFonts = mGenericMappings.Get(genericLang); |
2249 | 0 | if (prefFonts) { |
2250 | 0 | return prefFonts; |
2251 | 0 | } |
2252 | 0 | |
2253 | 0 | // if not found, ask fontconfig to pick the appropriate font |
2254 | 0 | nsAutoRef<FcPattern> genericPattern(FcPatternCreate()); |
2255 | 0 | FcPatternAddString(genericPattern, FC_FAMILY, |
2256 | 0 | ToFcChar8Ptr(aGeneric.get())); |
2257 | 0 |
|
2258 | 0 | // -- prefer scalable fonts |
2259 | 0 | FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue); |
2260 | 0 |
|
2261 | 0 | // -- add the lang to the pattern |
2262 | 0 | if (!fcLang.IsEmpty()) { |
2263 | 0 | FcPatternAddString(genericPattern, FC_LANG, |
2264 | 0 | ToFcChar8Ptr(fcLang.get())); |
2265 | 0 | } |
2266 | 0 |
|
2267 | 0 | // -- perform substitutions |
2268 | 0 | FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern); |
2269 | 0 | FcDefaultSubstitute(genericPattern); |
2270 | 0 |
|
2271 | 0 | // -- sort to get the closest matches |
2272 | 0 | FcResult result; |
2273 | 0 | nsAutoRef<FcFontSet> faces(FcFontSort(nullptr, genericPattern, FcFalse, |
2274 | 0 | nullptr, &result)); |
2275 | 0 |
|
2276 | 0 | if (!faces) { |
2277 | 0 | return nullptr; |
2278 | 0 | } |
2279 | 0 | |
2280 | 0 | // -- select the fonts to be used for the generic |
2281 | 0 | prefFonts = new PrefFontList; // can be empty but in practice won't happen |
2282 | 0 | uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions(); |
2283 | 0 | bool foundFontWithLang = false; |
2284 | 0 | for (int i = 0; i < faces->nfont; i++) { |
2285 | 0 | FcPattern* font = faces->fonts[i]; |
2286 | 0 | FcChar8* mappedGeneric = nullptr; |
2287 | 0 |
|
2288 | 0 | FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric); |
2289 | 0 | if (mappedGeneric) { |
2290 | 0 | nsAutoCString mappedGenericName(ToCharPtr(mappedGeneric)); |
2291 | 0 | AutoTArray<FamilyAndGeneric,1> genericFamilies; |
2292 | 0 | if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName, |
2293 | 0 | &genericFamilies, |
2294 | 0 | FindFamiliesFlags(0))) { |
2295 | 0 | MOZ_ASSERT(genericFamilies.Length() == 1, |
2296 | 0 | "expected a single family"); |
2297 | 0 | if (!prefFonts->Contains(genericFamilies[0].mFamily)) { |
2298 | 0 | prefFonts->AppendElement(genericFamilies[0].mFamily); |
2299 | 0 | bool foundLang = |
2300 | 0 | !fcLang.IsEmpty() && |
2301 | 0 | PatternHasLang(font, ToFcChar8Ptr(fcLang.get())); |
2302 | 0 | foundFontWithLang = foundFontWithLang || foundLang; |
2303 | 0 | // check to see if the list is full |
2304 | 0 | if (prefFonts->Length() >= limit) { |
2305 | 0 | break; |
2306 | 0 | } |
2307 | 0 | } |
2308 | 0 | } |
2309 | 0 | } |
2310 | 0 | } |
2311 | 0 |
|
2312 | 0 | // if no font in the list matches the lang, trim all but the first one |
2313 | 0 | if (!prefFonts->IsEmpty() && !foundFontWithLang) { |
2314 | 0 | prefFonts->TruncateLength(1); |
2315 | 0 | } |
2316 | 0 |
|
2317 | 0 | mGenericMappings.Put(genericLang, prefFonts); |
2318 | 0 | return prefFonts; |
2319 | 0 | } |
2320 | | |
2321 | | bool |
2322 | | gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics() |
2323 | 0 | { |
2324 | 0 | static const char kFontNamePrefix[] = "font.name."; |
2325 | 0 |
|
2326 | 0 | bool prefFontsUseOnlyGenerics = true; |
2327 | 0 | uint32_t count; |
2328 | 0 | char** names; |
2329 | 0 | nsresult rv = Preferences::GetRootBranch()-> |
2330 | 0 | GetChildList(kFontNamePrefix, &count, &names); |
2331 | 0 | if (NS_SUCCEEDED(rv) && count) { |
2332 | 0 | for (size_t i = 0; i < count; i++) { |
2333 | 0 | // Check whether all font.name prefs map to generic keywords |
2334 | 0 | // and that the pref name and keyword match. |
2335 | 0 | // Ex: font.name.serif.ar ==> "serif" (ok) |
2336 | 0 | // Ex: font.name.serif.ar ==> "monospace" (return false) |
2337 | 0 | // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false) |
2338 | 0 | // Ex: font.name.serif.ar ==> "" and |
2339 | 0 | // font.name-list.serif.ar ==> "serif" (ok) |
2340 | 0 | // Ex: font.name.serif.ar ==> "" and |
2341 | 0 | // font.name-list.serif.ar ==> "Something, serif" |
2342 | 0 | // (return false) |
2343 | 0 |
|
2344 | 0 | nsDependentCString prefName(names[i] + |
2345 | 0 | ArrayLength(kFontNamePrefix) - 1); |
2346 | 0 | nsCCharSeparatedTokenizer tokenizer(prefName, '.'); |
2347 | 0 | const nsDependentCSubstring& generic = tokenizer.nextToken(); |
2348 | 0 | const nsDependentCSubstring& langGroup = tokenizer.nextToken(); |
2349 | 0 | nsAutoCString fontPrefValue; |
2350 | 0 | Preferences::GetCString(names[i], fontPrefValue); |
2351 | 0 | if (fontPrefValue.IsEmpty()) { |
2352 | 0 | // The font name list may have two or more family names as comma |
2353 | 0 | // separated list. In such case, not matching with generic font |
2354 | 0 | // name is fine because if the list prefers specific font, this |
2355 | 0 | // should return false. |
2356 | 0 | Preferences::GetCString(NameListPref(generic, langGroup).get(), |
2357 | 0 | fontPrefValue); |
2358 | 0 | } |
2359 | 0 |
|
2360 | 0 | if (!langGroup.EqualsLiteral("x-math") && |
2361 | 0 | !generic.Equals(fontPrefValue)) { |
2362 | 0 | prefFontsUseOnlyGenerics = false; |
2363 | 0 | break; |
2364 | 0 | } |
2365 | 0 | } |
2366 | 0 | NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names); |
2367 | 0 | } |
2368 | 0 | return prefFontsUseOnlyGenerics; |
2369 | 0 | } |
2370 | | |
2371 | | /* static */ void |
2372 | | gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis) |
2373 | 0 | { |
2374 | 0 | // A content process is not supposed to check this directly; |
2375 | 0 | // it will be notified by the parent when the font list changes. |
2376 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
2377 | 0 |
|
2378 | 0 | // check for font updates |
2379 | 0 | FcInitBringUptoDate(); |
2380 | 0 |
|
2381 | 0 | // update fontlist if current config changed |
2382 | 0 | gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis); |
2383 | 0 | FcConfig* current = FcConfigGetCurrent(); |
2384 | 0 | if (current != pfl->GetLastConfig()) { |
2385 | 0 | pfl->UpdateFontList(); |
2386 | 0 | pfl->ForceGlobalReflow(); |
2387 | 0 |
|
2388 | 0 | mozilla::dom::ContentParent::NotifyUpdatedFonts(); |
2389 | 0 | } |
2390 | 0 | } |
2391 | | |
2392 | | gfxFontFamily* |
2393 | | gfxFcPlatformFontList::CreateFontFamily(const nsACString& aName) const |
2394 | 0 | { |
2395 | 0 | return new gfxFontconfigFontFamily(aName); |
2396 | 0 | } |
2397 | | |
2398 | | // mapping of moz lang groups ==> default lang |
2399 | | struct MozLangGroupData { |
2400 | | nsAtom* const& mozLangGroup; |
2401 | | const char *defaultLang; |
2402 | | }; |
2403 | | |
2404 | | const MozLangGroupData MozLangGroups[] = { |
2405 | | { nsGkAtoms::x_western, "en" }, |
2406 | | { nsGkAtoms::x_cyrillic, "ru" }, |
2407 | | { nsGkAtoms::x_devanagari, "hi" }, |
2408 | | { nsGkAtoms::x_tamil, "ta" }, |
2409 | | { nsGkAtoms::x_armn, "hy" }, |
2410 | | { nsGkAtoms::x_beng, "bn" }, |
2411 | | { nsGkAtoms::x_cans, "iu" }, |
2412 | | { nsGkAtoms::x_ethi, "am" }, |
2413 | | { nsGkAtoms::x_geor, "ka" }, |
2414 | | { nsGkAtoms::x_gujr, "gu" }, |
2415 | | { nsGkAtoms::x_guru, "pa" }, |
2416 | | { nsGkAtoms::x_khmr, "km" }, |
2417 | | { nsGkAtoms::x_knda, "kn" }, |
2418 | | { nsGkAtoms::x_mlym, "ml" }, |
2419 | | { nsGkAtoms::x_orya, "or" }, |
2420 | | { nsGkAtoms::x_sinh, "si" }, |
2421 | | { nsGkAtoms::x_tamil, "ta" }, |
2422 | | { nsGkAtoms::x_telu, "te" }, |
2423 | | { nsGkAtoms::x_tibt, "bo" }, |
2424 | | { nsGkAtoms::Unicode, 0 } |
2425 | | }; |
2426 | | |
2427 | | bool |
2428 | | gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang, |
2429 | | nsAtom* aLangGroup, |
2430 | | nsACString& aFcLang, |
2431 | | bool aForFontEnumerationThread) |
2432 | 0 | { |
2433 | 0 | // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'. |
2434 | 0 | // aOSLang is in the form "language[_territory][.codeset][@modifier]". |
2435 | 0 | // fontconfig takes languages in the form "language-territory". |
2436 | 0 | // nsLanguageAtomService takes languages in the form language-subtag, |
2437 | 0 | // where subtag may be a territory. fontconfig and nsLanguageAtomService |
2438 | 0 | // handle case-conversion for us. |
2439 | 0 | const char *pos, *end; |
2440 | 0 | aOSLang.BeginReading(pos); |
2441 | 0 | aOSLang.EndReading(end); |
2442 | 0 | aFcLang.Truncate(); |
2443 | 0 | while (pos < end) { |
2444 | 0 | switch (*pos) { |
2445 | 0 | case '.': |
2446 | 0 | case '@': |
2447 | 0 | end = pos; |
2448 | 0 | break; |
2449 | 0 | case '_': |
2450 | 0 | aFcLang.Append('-'); |
2451 | 0 | break; |
2452 | 0 | default: |
2453 | 0 | aFcLang.Append(*pos); |
2454 | 0 | } |
2455 | 0 | ++pos; |
2456 | 0 | } |
2457 | 0 |
|
2458 | 0 | if (!aForFontEnumerationThread) { |
2459 | 0 | nsAtom *atom = mLangService->LookupLanguage(aFcLang); |
2460 | 0 | return atom == aLangGroup; |
2461 | 0 | } |
2462 | 0 | |
2463 | 0 | // If we were called by the font enumeration thread, we can't use |
2464 | 0 | // mLangService->LookupLanguage because it is not thread-safe. |
2465 | 0 | // Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group |
2466 | 0 | // mapping cache hashtable. |
2467 | 0 | nsAutoCString lowered(aFcLang); |
2468 | 0 | ToLowerCase(lowered); |
2469 | 0 | RefPtr<nsAtom> lang = NS_Atomize(lowered); |
2470 | 0 | RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang); |
2471 | 0 | return group.get() == aLangGroup; |
2472 | 0 | } |
2473 | | |
2474 | | void |
2475 | | gfxFcPlatformFontList::GetSampleLangForGroup(nsAtom* aLanguage, |
2476 | | nsACString& aLangStr, |
2477 | | bool aForFontEnumerationThread) |
2478 | 0 | { |
2479 | 0 | aLangStr.Truncate(); |
2480 | 0 | if (!aLanguage) { |
2481 | 0 | return; |
2482 | 0 | } |
2483 | 0 | |
2484 | 0 | // set up lang string |
2485 | 0 | const MozLangGroupData *mozLangGroup = nullptr; |
2486 | 0 |
|
2487 | 0 | // -- look it up in the list of moz lang groups |
2488 | 0 | for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) { |
2489 | 0 | if (aLanguage == MozLangGroups[i].mozLangGroup) { |
2490 | 0 | mozLangGroup = &MozLangGroups[i]; |
2491 | 0 | break; |
2492 | 0 | } |
2493 | 0 | } |
2494 | 0 |
|
2495 | 0 | // -- not a mozilla lang group? Just return the BCP47 string |
2496 | 0 | // representation of the lang group |
2497 | 0 | if (!mozLangGroup) { |
2498 | 0 | // Not a special mozilla language group. |
2499 | 0 | // Use aLanguage as a language code. |
2500 | 0 | aLanguage->ToUTF8String(aLangStr); |
2501 | 0 | return; |
2502 | 0 | } |
2503 | 0 | |
2504 | 0 | // -- check the environment for the user's preferred language that |
2505 | 0 | // corresponds to this mozilla lang group. |
2506 | 0 | const char *languages = getenv("LANGUAGE"); |
2507 | 0 | if (languages) { |
2508 | 0 | const char separator = ':'; |
2509 | 0 |
|
2510 | 0 | for (const char *pos = languages; true; ++pos) { |
2511 | 0 | if (*pos == '\0' || *pos == separator) { |
2512 | 0 | if (languages < pos && |
2513 | 0 | TryLangForGroup(Substring(languages, pos), |
2514 | 0 | aLanguage, aLangStr, |
2515 | 0 | aForFontEnumerationThread)) { |
2516 | 0 | return; |
2517 | 0 | } |
2518 | 0 | |
2519 | 0 | if (*pos == '\0') { |
2520 | 0 | break; |
2521 | 0 | } |
2522 | 0 | |
2523 | 0 | languages = pos + 1; |
2524 | 0 | } |
2525 | 0 | } |
2526 | 0 | } |
2527 | 0 | const char *ctype = setlocale(LC_CTYPE, nullptr); |
2528 | 0 | if (ctype && |
2529 | 0 | TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr, |
2530 | 0 | aForFontEnumerationThread)) { |
2531 | 0 | return; |
2532 | 0 | } |
2533 | 0 | |
2534 | 0 | if (mozLangGroup->defaultLang) { |
2535 | 0 | aLangStr.Assign(mozLangGroup->defaultLang); |
2536 | 0 | } else { |
2537 | 0 | aLangStr.Truncate(); |
2538 | 0 | } |
2539 | 0 | } |
2540 | | |
2541 | | #ifdef MOZ_BUNDLED_FONTS |
2542 | | void |
2543 | | gfxFcPlatformFontList::ActivateBundledFonts() |
2544 | 0 | { |
2545 | 0 | if (!mBundledFontsInitialized) { |
2546 | 0 | mBundledFontsInitialized = true; |
2547 | 0 | nsCOMPtr<nsIFile> localDir; |
2548 | 0 | nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)); |
2549 | 0 | if (NS_FAILED(rv)) { |
2550 | 0 | return; |
2551 | 0 | } |
2552 | 0 | if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) { |
2553 | 0 | return; |
2554 | 0 | } |
2555 | 0 | bool isDir; |
2556 | 0 | if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { |
2557 | 0 | return; |
2558 | 0 | } |
2559 | 0 | if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) { |
2560 | 0 | return; |
2561 | 0 | } |
2562 | 0 | } |
2563 | 0 | if (!mBundledFontsPath.IsEmpty()) { |
2564 | 0 | FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get())); |
2565 | 0 | } |
2566 | 0 | } |
2567 | | #endif |
2568 | | |
2569 | | #ifdef MOZ_WIDGET_GTK |
2570 | | /*************************************************************************** |
2571 | | * |
2572 | | * This function must be last in the file because it uses the system cairo |
2573 | | * library. Above this point the cairo library used is the tree cairo if |
2574 | | * MOZ_TREE_CAIRO. |
2575 | | */ |
2576 | | |
2577 | | #if MOZ_TREE_CAIRO |
2578 | | // Tree cairo symbols have different names. Disable their activation through |
2579 | | // preprocessor macros. |
2580 | | #undef cairo_ft_font_options_substitute |
2581 | | |
2582 | | // The system cairo functions are not declared because the include paths cause |
2583 | | // the gdk headers to pick up the tree cairo.h. |
2584 | | extern "C" { |
2585 | | NS_VISIBILITY_DEFAULT void |
2586 | | cairo_ft_font_options_substitute (const cairo_font_options_t *options, |
2587 | | FcPattern *pattern); |
2588 | | } |
2589 | | #endif |
2590 | | |
2591 | | static void |
2592 | | ApplyGdkScreenFontOptions(FcPattern *aPattern) |
2593 | 0 | { |
2594 | 0 | const cairo_font_options_t *options = |
2595 | 0 | gdk_screen_get_font_options(gdk_screen_get_default()); |
2596 | 0 |
|
2597 | 0 | cairo_ft_font_options_substitute(options, aPattern); |
2598 | 0 | } |
2599 | | |
2600 | | #endif // MOZ_WIDGET_GTK |