Line data Source code
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 : #include "mozilla/SizePrintfMacros.h"
8 :
9 : #include "gfxFcPlatformFontList.h"
10 : #include "gfxFont.h"
11 : #include "gfxFontConstants.h"
12 : #include "gfxFontFamilyList.h"
13 : #include "gfxFT2Utils.h"
14 : #include "gfxPlatform.h"
15 : #include "mozilla/ArrayUtils.h"
16 : #include "mozilla/Preferences.h"
17 : #include "mozilla/Sprintf.h"
18 : #include "mozilla/TimeStamp.h"
19 : #include "nsGkAtoms.h"
20 : #include "nsUnicodeProperties.h"
21 : #include "nsUnicodeRange.h"
22 : #include "nsDirectoryServiceUtils.h"
23 : #include "nsDirectoryServiceDefs.h"
24 : #include "nsAppDirectoryServiceDefs.h"
25 : #include "nsCharSeparatedTokenizer.h"
26 :
27 : #include "mozilla/gfx/HelpersCairo.h"
28 :
29 : #include <fontconfig/fcfreetype.h>
30 :
31 : #ifdef MOZ_WIDGET_GTK
32 : #include <gdk/gdk.h>
33 : #include "gfxPlatformGtk.h"
34 : #endif
35 :
36 : #ifdef MOZ_X11
37 : #include "mozilla/X11Util.h"
38 : #endif
39 :
40 : using namespace mozilla;
41 : using namespace mozilla::gfx;
42 : using namespace mozilla::unicode;
43 :
44 : #ifndef FC_POSTSCRIPT_NAME
45 : #define FC_POSTSCRIPT_NAME "postscriptname" /* String */
46 : #endif
47 :
48 : #define PRINTING_FC_PROPERTY "gfx.printing"
49 :
50 : #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
51 : LogLevel::Debug, args)
52 : #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
53 : gfxPlatform::GetLog(eGfxLog_fontlist), \
54 : LogLevel::Debug)
55 : #define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
56 : gfxPlatform::GetLog(eGfxLog_cmapdata), \
57 : LogLevel::Debug)
58 :
59 : static const FcChar8*
60 38 : ToFcChar8Ptr(const char* aStr)
61 : {
62 38 : return reinterpret_cast<const FcChar8*>(aStr);
63 : }
64 :
65 : static const char*
66 12528 : ToCharPtr(const FcChar8 *aStr)
67 : {
68 12528 : return reinterpret_cast<const char*>(aStr);
69 : }
70 :
71 : FT_Library gfxFcPlatformFontList::sCairoFTLibrary = nullptr;
72 :
73 : static cairo_user_data_key_t sFcFontlistUserFontDataKey;
74 :
75 : // canonical name ==> first en name or first name if no en name
76 : // This is the required logic for fullname lookups as per CSS3 Fonts spec.
77 : static uint32_t
78 3668 : FindCanonicalNameIndex(FcPattern* aFont, const char* aLangField)
79 : {
80 3668 : uint32_t n = 0, en = 0;
81 : FcChar8* lang;
82 5310 : while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
83 : // look for 'en' or variants, en-US, en-JP etc.
84 3994 : uint32_t len = strlen(ToCharPtr(lang));
85 3994 : bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
86 3994 : if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
87 3173 : en = n;
88 3173 : break;
89 : }
90 821 : n++;
91 : }
92 3668 : return en;
93 : }
94 :
95 : static void
96 1805 : GetFaceNames(FcPattern* aFont, const nsAString& aFamilyName,
97 : nsAString& aPostscriptName, nsAString& aFullname)
98 : {
99 : // get the Postscript name
100 : FcChar8* psname;
101 1805 : if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) {
102 1805 : AppendUTF8toUTF16(ToCharPtr(psname), aPostscriptName);
103 : }
104 :
105 : // get the canonical fullname (i.e. en name or first name)
106 1805 : uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
107 : FcChar8* fullname;
108 1805 : if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
109 1640 : AppendUTF8toUTF16(ToCharPtr(fullname), aFullname);
110 : }
111 :
112 : // if have fullname, done
113 1805 : if (!aFullname.IsEmpty()) {
114 1640 : return;
115 : }
116 :
117 : // otherwise, set the fullname to family + style name [en] and use that
118 165 : aFullname.Append(aFamilyName);
119 :
120 : // figure out the en style name
121 165 : en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
122 330 : nsAutoString style;
123 165 : FcChar8* stylename = nullptr;
124 165 : FcPatternGetString(aFont, FC_STYLE, en, &stylename);
125 165 : if (stylename) {
126 165 : AppendUTF8toUTF16(ToCharPtr(stylename), style);
127 : }
128 :
129 165 : if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
130 135 : aFullname.Append(' ');
131 135 : aFullname.Append(style);
132 : }
133 : }
134 :
135 : static uint16_t
136 107 : MapFcWeight(int aFcWeight)
137 : {
138 107 : if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
139 0 : return 100;
140 : }
141 107 : if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
142 3 : return 200;
143 : }
144 104 : if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
145 2 : return 300;
146 : }
147 102 : if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
148 : // This includes FC_WEIGHT_BOOK
149 50 : return 400;
150 : }
151 52 : if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
152 2 : return 500;
153 : }
154 50 : if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
155 0 : return 600;
156 : }
157 50 : if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
158 50 : return 700;
159 : }
160 0 : if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
161 0 : return 800;
162 : }
163 0 : if (aFcWeight <= FC_WEIGHT_BLACK) {
164 0 : return 900;
165 : }
166 :
167 : // including FC_WEIGHT_EXTRABLACK
168 0 : return 901;
169 : }
170 :
171 : static int16_t
172 107 : MapFcWidth(int aFcWidth)
173 : {
174 107 : if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
175 0 : return NS_FONT_STRETCH_ULTRA_CONDENSED;
176 : }
177 107 : if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
178 0 : return NS_FONT_STRETCH_EXTRA_CONDENSED;
179 : }
180 107 : if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
181 0 : return NS_FONT_STRETCH_CONDENSED;
182 : }
183 107 : if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
184 20 : return NS_FONT_STRETCH_SEMI_CONDENSED;
185 : }
186 87 : if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
187 87 : return NS_FONT_STRETCH_NORMAL;
188 : }
189 0 : if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
190 0 : return NS_FONT_STRETCH_SEMI_EXPANDED;
191 : }
192 0 : if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
193 0 : return NS_FONT_STRETCH_EXPANDED;
194 : }
195 0 : if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
196 0 : return NS_FONT_STRETCH_EXTRA_EXPANDED;
197 : }
198 0 : return NS_FONT_STRETCH_ULTRA_EXPANDED;
199 : }
200 :
201 107 : gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
202 : FcPattern* aFontPattern,
203 107 : bool aIgnoreFcCharmap)
204 : : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
205 : mFTFace(nullptr), mFTFaceInitialized(false),
206 : mIgnoreFcCharmap(aIgnoreFcCharmap),
207 107 : mAspect(0.0), mFontData(nullptr)
208 : {
209 : // italic
210 : int slant;
211 107 : if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
212 0 : slant = FC_SLANT_ROMAN;
213 : }
214 107 : if (slant == FC_SLANT_OBLIQUE) {
215 18 : mStyle = NS_FONT_STYLE_OBLIQUE;
216 89 : } else if (slant > 0) {
217 32 : mStyle = NS_FONT_STYLE_ITALIC;
218 : }
219 :
220 : // weight
221 : int weight;
222 107 : if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
223 0 : weight = FC_WEIGHT_REGULAR;
224 : }
225 107 : mWeight = MapFcWeight(weight);
226 :
227 : // width
228 : int width;
229 107 : if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
230 0 : width = FC_WIDTH_NORMAL;
231 : }
232 107 : mStretch = MapFcWidth(width);
233 107 : }
234 :
235 0 : gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
236 : uint16_t aWeight,
237 : int16_t aStretch,
238 : uint8_t aStyle,
239 : const uint8_t *aData,
240 0 : FT_Face aFace)
241 : : gfxFontEntry(aFaceName),
242 : mFTFace(aFace), mFTFaceInitialized(true),
243 : mIgnoreFcCharmap(true),
244 0 : mAspect(0.0), mFontData(aData)
245 : {
246 0 : mWeight = aWeight;
247 0 : mStyle = aStyle;
248 0 : mStretch = aStretch;
249 0 : mIsDataUserFont = true;
250 :
251 : // Use fontconfig to fill out the pattern from the FTFace.
252 : // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
253 : // least). The dummy file passed here is removed below.
254 : //
255 : // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
256 : // is passed as the "blanks" argument, which provides that unexpectedly
257 : // blank glyphs are elided. Here, however, we pass nullptr for
258 : // "blanks", effectively assuming that, if the font has a blank glyph,
259 : // then the author intends any associated character to be rendered
260 : // blank.
261 0 : mFontPattern = FcFreeTypeQueryFace(mFTFace, ToFcChar8Ptr(""), 0, nullptr);
262 : // given that we have a FT_Face, not really sure this is possible...
263 0 : if (!mFontPattern) {
264 0 : mFontPattern = FcPatternCreate();
265 : }
266 0 : FcPatternDel(mFontPattern, FC_FILE);
267 0 : FcPatternDel(mFontPattern, FC_INDEX);
268 :
269 : // Make a new pattern and store the face in it so that cairo uses
270 : // that when creating a cairo font face.
271 0 : FcPatternAddFTFace(mFontPattern, FC_FT_FACE, mFTFace);
272 :
273 0 : mUserFontData = new FTUserFontData(mFTFace, mFontData);
274 0 : }
275 :
276 0 : gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
277 : FcPattern* aFontPattern,
278 : uint16_t aWeight,
279 : int16_t aStretch,
280 0 : uint8_t aStyle)
281 : : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
282 : mFTFace(nullptr), mFTFaceInitialized(false),
283 0 : mAspect(0.0), mFontData(nullptr)
284 : {
285 0 : mWeight = aWeight;
286 0 : mStyle = aStyle;
287 0 : mStretch = aStretch;
288 0 : mIsLocalUserFont = true;
289 :
290 : // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
291 : // via src:local()...
292 : // If the local font happens to come from the application fontset,
293 : // we want to set it to true so that color/svg fonts will work even
294 : // if the default glyphs are blank; but if the local font is a non-
295 : // sfnt face (e.g. legacy type 1) then we need to set it to false
296 : // because our cmap-reading code will fail and we depend on FT+Fc to
297 : // determine the coverage.
298 : // We set the flag here, but may flip it the first time TestCharacterMap
299 : // is called, at which point we'll look to see whether a 'cmap' is
300 : // actually present in the font.
301 0 : mIgnoreFcCharmap = true;
302 0 : }
303 :
304 0 : gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
305 : {
306 0 : }
307 :
308 : static bool
309 15 : PatternHasLang(const FcPattern *aPattern, const FcChar8 *aLang)
310 : {
311 : FcLangSet *langset;
312 :
313 15 : if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
314 0 : return false;
315 : }
316 :
317 15 : if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
318 15 : return true;
319 : }
320 0 : return false;
321 : }
322 :
323 : bool
324 0 : gfxFontconfigFontEntry::SupportsLangGroup(nsIAtom *aLangGroup) const
325 : {
326 0 : if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
327 0 : return true;
328 : }
329 :
330 0 : nsAutoCString fcLang;
331 0 : gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
332 0 : pfl->GetSampleLangForGroup(aLangGroup, fcLang);
333 0 : if (fcLang.IsEmpty()) {
334 0 : return true;
335 : }
336 :
337 : // is lang included in the underlying pattern?
338 0 : return PatternHasLang(mFontPattern, ToFcChar8Ptr(fcLang.get()));
339 : }
340 :
341 : nsresult
342 0 : gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
343 : {
344 : // attempt this once, if errors occur leave a blank cmap
345 0 : if (mCharacterMap) {
346 0 : return NS_OK;
347 : }
348 :
349 0 : RefPtr<gfxCharacterMap> charmap;
350 : nsresult rv;
351 0 : bool symbolFont = false; // currently ignored
352 :
353 0 : if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
354 : mUVSOffset,
355 0 : symbolFont))) {
356 0 : rv = NS_OK;
357 : } else {
358 0 : uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
359 0 : charmap = new gfxCharacterMap();
360 0 : AutoTable cmapTable(this, kCMAP);
361 :
362 0 : if (cmapTable) {
363 0 : bool unicodeFont = false; // currently ignored
364 : uint32_t cmapLen;
365 : const uint8_t* cmapData =
366 0 : reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
367 0 : &cmapLen));
368 0 : rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
369 0 : *charmap, mUVSOffset,
370 0 : unicodeFont, symbolFont);
371 : } else {
372 0 : rv = NS_ERROR_NOT_AVAILABLE;
373 : }
374 : }
375 :
376 0 : mHasCmapTable = NS_SUCCEEDED(rv);
377 0 : if (mHasCmapTable) {
378 0 : gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
379 0 : mCharacterMap = pfl->FindCharMap(charmap);
380 : } else {
381 : // if error occurred, initialize to null cmap
382 0 : mCharacterMap = new gfxCharacterMap();
383 : }
384 :
385 0 : LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %" PRIuSIZE " hash: %8.8x%s\n",
386 : NS_ConvertUTF16toUTF8(mName).get(),
387 : charmap->SizeOfIncludingThis(moz_malloc_size_of),
388 : charmap->mHash, mCharacterMap == charmap ? " new" : ""));
389 0 : if (LOG_CMAPDATA_ENABLED()) {
390 : char prefix[256];
391 0 : SprintfLiteral(prefix, "(cmapdata) name: %.220s",
392 0 : NS_ConvertUTF16toUTF8(mName).get());
393 0 : charmap->Dump(prefix, eGfxLog_cmapdata);
394 : }
395 :
396 0 : return rv;
397 : }
398 :
399 : static bool
400 860 : HasChar(FcPattern *aFont, FcChar32 aCh)
401 : {
402 860 : FcCharSet *charset = nullptr;
403 860 : FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
404 860 : return charset && FcCharSetHasChar(charset, aCh);
405 : }
406 :
407 : bool
408 860 : gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh)
409 : {
410 : // For user fonts, or for fonts bundled with the app (which might include
411 : // color/svg glyphs where the default glyphs may be blank, and thus confuse
412 : // fontconfig/freetype's char map checking), we instead check the cmap
413 : // directly for character coverage.
414 860 : if (mIgnoreFcCharmap) {
415 : // If it does not actually have a cmap, switch our strategy to use
416 : // fontconfig's charmap after all (except for data fonts, which must
417 : // always have a cmap to have passed OTS validation).
418 0 : if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c','m','a','p'))) {
419 0 : mIgnoreFcCharmap = false;
420 : // ...and continue with HasChar() below.
421 : } else {
422 0 : return gfxFontEntry::TestCharacterMap(aCh);
423 : }
424 : }
425 : // otherwise (for system fonts), use the charmap in the pattern
426 860 : return HasChar(mFontPattern, aCh);
427 : }
428 :
429 : hb_blob_t*
430 22 : gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
431 : {
432 : // for data fonts, read directly from the font data
433 22 : if (mFontData) {
434 0 : return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
435 : }
436 :
437 22 : return gfxFontEntry::GetFontTable(aTableTag);
438 : }
439 :
440 : void
441 2 : gfxFontconfigFontEntry::MaybeReleaseFTFace()
442 : {
443 : // don't release if either HB or Gr face still exists
444 2 : if (mHBFace || mGrFace) {
445 0 : return;
446 : }
447 : // only close out FT_Face for system fonts, not for data fonts
448 2 : if (!mIsDataUserFont) {
449 2 : if (mFTFace) {
450 2 : Factory::ReleaseFTFace(mFTFace);
451 2 : mFTFace = nullptr;
452 : }
453 2 : mFTFaceInitialized = false;
454 : }
455 : }
456 :
457 : void
458 2 : gfxFontconfigFontEntry::ForgetHBFace()
459 : {
460 2 : gfxFontEntry::ForgetHBFace();
461 2 : MaybeReleaseFTFace();
462 2 : }
463 :
464 : void
465 0 : gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace)
466 : {
467 0 : gfxFontEntry::ReleaseGrFace(aFace);
468 0 : MaybeReleaseFTFace();
469 0 : }
470 :
471 : double
472 0 : gfxFontconfigFontEntry::GetAspect()
473 : {
474 0 : if (mAspect != 0.0) {
475 0 : return mAspect;
476 : }
477 :
478 : // try to compute aspect from OS/2 metrics if available
479 0 : AutoTable os2Table(this, TRUETYPE_TAG('O','S','/','2'));
480 0 : if (os2Table) {
481 0 : uint16_t upem = UnitsPerEm();
482 0 : if (upem != kInvalidUPEM) {
483 : uint32_t len;
484 : auto os2 = reinterpret_cast<const OS2Table*>
485 0 : (hb_blob_get_data(os2Table, &len));
486 0 : if (uint16_t(os2->version) >= 2) {
487 0 : if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
488 0 : int16_t(os2->sxHeight) > 0.1 * upem) {
489 0 : mAspect = double(int16_t(os2->sxHeight)) / upem;
490 0 : return mAspect;
491 : }
492 : }
493 : }
494 : }
495 :
496 : // default to aspect = 0.5 if the code below fails
497 0 : mAspect = 0.5;
498 :
499 : // create a font to calculate x-height / em-height
500 0 : gfxFontStyle s;
501 0 : s.size = 100.0; // pick large size to avoid possible hinting artifacts
502 0 : RefPtr<gfxFont> font = FindOrMakeFont(&s, false);
503 0 : if (font) {
504 : const gfxFont::Metrics& metrics =
505 0 : font->GetMetrics(gfxFont::eHorizontal);
506 :
507 : // The factor of 0.1 ensures that xHeight is sane so fonts don't
508 : // become huge. Strictly ">" ensures that xHeight and emHeight are
509 : // not both zero.
510 0 : if (metrics.xHeight > 0.1 * metrics.emHeight) {
511 0 : mAspect = metrics.xHeight / metrics.emHeight;
512 : }
513 : }
514 :
515 0 : return mAspect;
516 : }
517 :
518 : static void
519 8 : PrepareFontOptions(FcPattern* aPattern,
520 : cairo_font_options_t* aFontOptions)
521 : {
522 8 : NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions");
523 :
524 : // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
525 :
526 : FcBool printing;
527 8 : if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
528 : FcResultMatch) {
529 8 : printing = FcFalse;
530 : }
531 :
532 : // Font options are set explicitly here to improve cairo's caching
533 : // behavior and to record the relevant parts of the pattern for
534 : // SetupCairoFont (so that the pattern can be released).
535 : //
536 : // Most font_options have already been set as defaults on the FcPattern
537 : // with cairo_ft_font_options_substitute(), then user and system
538 : // fontconfig configurations were applied. The resulting font_options
539 : // have been recorded on the face during
540 : // cairo_ft_font_face_create_for_pattern().
541 : //
542 : // None of the settings here cause this scaled_font to behave any
543 : // differently from how it would behave if it were created from the same
544 : // face with default font_options.
545 : //
546 : // We set options explicitly so that the same scaled_font will be found in
547 : // the cairo_scaled_font_map when cairo loads glyphs from a context with
548 : // the same font_face, font_matrix, ctm, and surface font_options.
549 : //
550 : // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
551 : // font_options on the cairo_ft_font_face, and doesn't consider default
552 : // option values to not match any explicit values.
553 : //
554 : // Even after cairo_set_scaled_font is used to set font_options for the
555 : // cairo context, when cairo looks for a scaled_font for the context, it
556 : // will look for a font with some option values from the target surface if
557 : // any values are left default on the context font_options. If this
558 : // scaled_font is created with default font_options, cairo will not find
559 : // it.
560 : //
561 : // The one option not recorded in the pattern is hint_metrics, which will
562 : // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
563 : // We should be considering the font_options of the surface on which this
564 : // font will be used, but currently we don't have different gfxFonts for
565 : // different surface font_options, so we'll create a font suitable for the
566 : // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
567 8 : if (printing) {
568 0 : cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF);
569 : } else {
570 8 : cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON);
571 : }
572 :
573 : // The remaining options have been recorded on the pattern and the face.
574 : // _cairo_ft_options_merge has some logic to decide which options from the
575 : // scaled_font or from the cairo_ft_font_face take priority in the way the
576 : // font behaves.
577 : //
578 : // In the majority of cases, _cairo_ft_options_merge uses the options from
579 : // the cairo_ft_font_face, so sometimes it is not so important which
580 : // values are set here so long as they are not defaults, but we'll set
581 : // them to the exact values that we expect from the font, to be consistent
582 : // and to protect against changes in cairo.
583 : //
584 : // In some cases, _cairo_ft_options_merge uses some options from the
585 : // scaled_font's font_options rather than options on the
586 : // cairo_ft_font_face (from fontconfig).
587 : // https://bugs.freedesktop.org/show_bug.cgi?id=11838
588 : //
589 : // Surface font options were set on the pattern in
590 : // cairo_ft_font_options_substitute. If fontconfig has changed the
591 : // hint_style then that is what the user (or distribution) wants, so we
592 : // use the setting from the FcPattern.
593 : //
594 : // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
595 8 : FcBool hinting = FcFalse;
596 8 : if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
597 0 : hinting = FcTrue;
598 : }
599 :
600 : cairo_hint_style_t hint_style;
601 8 : if (printing || !hinting) {
602 0 : hint_style = CAIRO_HINT_STYLE_NONE;
603 : } else {
604 : int fc_hintstyle;
605 8 : if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
606 : 0, &fc_hintstyle) != FcResultMatch) {
607 0 : fc_hintstyle = FC_HINT_FULL;
608 : }
609 8 : switch (fc_hintstyle) {
610 : case FC_HINT_NONE:
611 0 : hint_style = CAIRO_HINT_STYLE_NONE;
612 0 : break;
613 : case FC_HINT_SLIGHT:
614 8 : hint_style = CAIRO_HINT_STYLE_SLIGHT;
615 8 : break;
616 : case FC_HINT_MEDIUM:
617 : default: // This fallback mirrors _get_pattern_ft_options in cairo.
618 0 : hint_style = CAIRO_HINT_STYLE_MEDIUM;
619 0 : break;
620 : case FC_HINT_FULL:
621 0 : hint_style = CAIRO_HINT_STYLE_FULL;
622 0 : break;
623 : }
624 : }
625 8 : cairo_font_options_set_hint_style(aFontOptions, hint_style);
626 :
627 : int rgba;
628 8 : if (FcPatternGetInteger(aPattern,
629 : FC_RGBA, 0, &rgba) != FcResultMatch) {
630 0 : rgba = FC_RGBA_UNKNOWN;
631 : }
632 8 : cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
633 8 : switch (rgba) {
634 : case FC_RGBA_UNKNOWN:
635 : case FC_RGBA_NONE:
636 : default:
637 : // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
638 : // is disabled through cairo_antialias_t.
639 0 : rgba = FC_RGBA_NONE;
640 : // subpixel_order won't be used by the font as we won't use
641 : // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
642 : // caching reasons described above. Fall through:
643 : MOZ_FALLTHROUGH;
644 : case FC_RGBA_RGB:
645 8 : subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
646 8 : break;
647 : case FC_RGBA_BGR:
648 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
649 0 : break;
650 : case FC_RGBA_VRGB:
651 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
652 0 : break;
653 : case FC_RGBA_VBGR:
654 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
655 0 : break;
656 : }
657 8 : cairo_font_options_set_subpixel_order(aFontOptions, subpixel_order);
658 :
659 : FcBool fc_antialias;
660 8 : if (FcPatternGetBool(aPattern,
661 : FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
662 0 : fc_antialias = FcTrue;
663 : }
664 : cairo_antialias_t antialias;
665 8 : if (!fc_antialias) {
666 0 : antialias = CAIRO_ANTIALIAS_NONE;
667 8 : } else if (rgba == FC_RGBA_NONE) {
668 0 : antialias = CAIRO_ANTIALIAS_GRAY;
669 : } else {
670 8 : antialias = CAIRO_ANTIALIAS_SUBPIXEL;
671 : }
672 8 : cairo_font_options_set_antialias(aFontOptions, antialias);
673 8 : }
674 :
675 : static void
676 0 : ReleaseFTUserFontData(void* aData)
677 : {
678 0 : static_cast<FTUserFontData*>(aData)->Release();
679 0 : }
680 :
681 : cairo_scaled_font_t*
682 8 : gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
683 : gfxFloat aAdjustedSize,
684 : const gfxFontStyle *aStyle,
685 : bool aNeedsBold)
686 : {
687 8 : if (aNeedsBold) {
688 0 : FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
689 : }
690 :
691 : // synthetic oblique by skewing via the font matrix
692 16 : bool needsOblique = IsUpright() &&
693 8 : aStyle->style != NS_FONT_STYLE_NORMAL &&
694 8 : aStyle->allowSyntheticStyle;
695 :
696 8 : if (needsOblique) {
697 : // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
698 0 : FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
699 0 : FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
700 : }
701 :
702 : cairo_font_face_t *face =
703 8 : cairo_ft_font_face_create_for_pattern(aRenderPattern);
704 :
705 8 : if (mFontData) {
706 : // for data fonts, add the face/data pointer to the cairo font face
707 : // so that it gets deleted whenever cairo decides
708 0 : NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
709 0 : NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
710 0 : if (cairo_font_face_set_user_data(face,
711 : &sFcFontlistUserFontDataKey,
712 : mUserFontData,
713 0 : ReleaseFTUserFontData) != CAIRO_STATUS_SUCCESS) {
714 0 : NS_WARNING("Failed binding FTUserFontData to Cairo font face");
715 0 : cairo_font_face_destroy(face);
716 0 : return nullptr;
717 : }
718 0 : mUserFontData.get()->AddRef();
719 : }
720 :
721 8 : cairo_scaled_font_t *scaledFont = nullptr;
722 :
723 : cairo_matrix_t sizeMatrix;
724 : cairo_matrix_t identityMatrix;
725 :
726 8 : cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
727 8 : cairo_matrix_init_identity(&identityMatrix);
728 :
729 8 : if (needsOblique) {
730 0 : const double kSkewFactor = OBLIQUE_SKEW_FACTOR;
731 :
732 : cairo_matrix_t style;
733 : cairo_matrix_init(&style,
734 : 1, //xx
735 : 0, //yx
736 : -1 * kSkewFactor, //xy
737 : 1, //yy
738 : 0, //x0
739 0 : 0); //y0
740 0 : cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
741 : }
742 :
743 8 : cairo_font_options_t *fontOptions = cairo_font_options_create();
744 8 : PrepareFontOptions(aRenderPattern, fontOptions);
745 :
746 : scaledFont = cairo_scaled_font_create(face, &sizeMatrix,
747 8 : &identityMatrix, fontOptions);
748 8 : cairo_font_options_destroy(fontOptions);
749 :
750 8 : NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
751 : "Failed to make scaled font");
752 :
753 8 : cairo_font_face_destroy(face);
754 :
755 8 : return scaledFont;
756 : }
757 :
758 : #ifdef MOZ_WIDGET_GTK
759 : // defintion included below
760 : static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
761 : #endif
762 :
763 : #ifdef MOZ_X11
764 : static bool
765 0 : GetXftInt(Display* aDisplay, const char* aName, int* aResult)
766 : {
767 0 : if (!aDisplay) {
768 0 : return false;
769 : }
770 0 : char* value = XGetDefault(aDisplay, "Xft", aName);
771 0 : if (!value) {
772 0 : return false;
773 : }
774 0 : if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
775 0 : return true;
776 : }
777 : char* end;
778 0 : *aResult = strtol(value, &end, 0);
779 0 : if (end != value) {
780 0 : return true;
781 : }
782 0 : return false;
783 : }
784 : #endif
785 :
786 : static void
787 8 : PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
788 : {
789 8 : FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
790 :
791 : // This gets cairo_font_options_t for the Screen. We should have
792 : // different font options for printing (no hinting) but we are not told
793 : // what we are measuring for.
794 : //
795 : // If cairo adds support for lcd_filter, gdk will not provide the default
796 : // setting for that option. We could get the default setting by creating
797 : // an xlib surface once, recording its font_options, and then merging the
798 : // gdk options.
799 : //
800 : // Using an xlib surface would also be an option to get Screen font
801 : // options for non-GTK X11 toolkits, but less efficient than using GDK to
802 : // pick up dynamic changes.
803 8 : if(aIsPrinterFont) {
804 0 : cairo_font_options_t *options = cairo_font_options_create();
805 0 : cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
806 0 : cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
807 0 : cairo_ft_font_options_substitute(options, aPattern);
808 0 : cairo_font_options_destroy(options);
809 0 : FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
810 8 : } else if (!gfxPlatform::IsHeadless()) {
811 : #ifdef MOZ_WIDGET_GTK
812 8 : ApplyGdkScreenFontOptions(aPattern);
813 :
814 : #ifdef MOZ_X11
815 : FcValue value;
816 : int lcdfilter;
817 8 : if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
818 0 : GdkDisplay* dpy = gdk_display_get_default();
819 0 : if (GDK_IS_X11_DISPLAY(dpy) &&
820 0 : GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
821 0 : FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter);
822 : }
823 : }
824 : #endif // MOZ_X11
825 : #endif // MOZ_WIDGET_GTK
826 : }
827 :
828 8 : FcDefaultSubstitute(aPattern);
829 8 : }
830 :
831 : void
832 8 : gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
833 8 : if (aIndex > 0) {
834 : WeakPtr<UnscaledFont> front =
835 12 : Move(mUnscaledFonts[aIndex]);
836 18 : for (size_t i = aIndex; i > 0; i--) {
837 12 : mUnscaledFonts[i] = Move(mUnscaledFonts[i-1]);
838 : }
839 6 : mUnscaledFonts[0] = Move(front);
840 : }
841 8 : }
842 :
843 : already_AddRefed<UnscaledFontFontconfig>
844 8 : gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) {
845 26 : for (size_t i = 0; i < kNumEntries; i++) {
846 : UnscaledFontFontconfig* entry =
847 20 : static_cast<UnscaledFontFontconfig*>(mUnscaledFonts[i].get());
848 22 : if (entry &&
849 22 : !strcmp(entry->GetFile(), aFile) &&
850 2 : entry->GetIndex() == aIndex) {
851 2 : MoveToFront(i);
852 2 : return do_AddRef(entry);
853 : }
854 : }
855 6 : return nullptr;
856 : }
857 :
858 : static inline gfxFloat
859 8 : SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
860 : {
861 8 : return aStyle.sizeAdjust >= 0.0 ?
862 0 : aStyle.GetAdjustedSize(aEntry->GetAspect()) :
863 8 : aStyle.size;
864 : }
865 :
866 : static double
867 8 : ChooseFontSize(gfxFontconfigFontEntry* aEntry,
868 : const gfxFontStyle& aStyle)
869 : {
870 8 : double requestedSize = SizeForStyle(aEntry, aStyle);
871 8 : double bestDist = -1.0;
872 8 : double bestSize = requestedSize;
873 : double size;
874 8 : int v = 0;
875 8 : while (FcPatternGetDouble(aEntry->GetPattern(),
876 : FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
877 0 : ++v;
878 0 : double dist = fabs(size - requestedSize);
879 0 : if (bestDist < 0.0 || dist < bestDist) {
880 0 : bestDist = dist;
881 0 : bestSize = size;
882 : }
883 : }
884 : // If the font has bitmaps but wants to be scaled, then let it scale.
885 8 : if (bestSize >= 0.0) {
886 : FcBool scalable;
887 16 : if (FcPatternGetBool(aEntry->GetPattern(),
888 16 : FC_SCALABLE, 0, &scalable) == FcResultMatch &&
889 8 : scalable) {
890 8 : return requestedSize;
891 : }
892 : }
893 0 : return bestSize;
894 : }
895 :
896 : gfxFont*
897 8 : gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
898 : bool aNeedsBold)
899 : {
900 16 : nsAutoRef<FcPattern> pattern(FcPatternCreate());
901 8 : if (!pattern) {
902 0 : NS_WARNING("Failed to create Fontconfig pattern for font instance");
903 0 : return nullptr;
904 : }
905 :
906 8 : double size = ChooseFontSize(this, *aFontStyle);
907 8 : FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
908 :
909 8 : PreparePattern(pattern, aFontStyle->printerFont);
910 : nsAutoRef<FcPattern> renderPattern
911 16 : (FcFontRenderPrepare(nullptr, pattern, mFontPattern));
912 8 : if (!renderPattern) {
913 0 : NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
914 0 : return nullptr;
915 : }
916 :
917 : cairo_scaled_font_t* scaledFont =
918 8 : CreateScaledFont(renderPattern, size, aFontStyle, aNeedsBold);
919 :
920 8 : const FcChar8* file = ToFcChar8Ptr("");
921 8 : int index = 0;
922 8 : if (!mFontData) {
923 16 : if (FcPatternGetString(renderPattern, FC_FILE, 0,
924 24 : const_cast<FcChar8**>(&file)) != FcResultMatch ||
925 8 : FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) {
926 0 : NS_WARNING("No file in Fontconfig pattern for font instance");
927 0 : return nullptr;
928 : }
929 : }
930 :
931 : RefPtr<UnscaledFontFontconfig> unscaledFont =
932 16 : mUnscaledFontCache.Lookup(ToCharPtr(file), index);
933 8 : if (!unscaledFont) {
934 : unscaledFont =
935 6 : mFontData ?
936 0 : new UnscaledFontFontconfig(mFTFace) :
937 12 : new UnscaledFontFontconfig(ToCharPtr(file), index);
938 6 : mUnscaledFontCache.Add(unscaledFont);
939 : }
940 :
941 : gfxFont* newFont =
942 : new gfxFontconfigFont(unscaledFont, scaledFont,
943 : renderPattern, size,
944 16 : this, aFontStyle, aNeedsBold);
945 8 : cairo_scaled_font_destroy(scaledFont);
946 :
947 8 : return newFont;
948 : }
949 :
950 : nsresult
951 21 : gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
952 : nsTArray<uint8_t>& aBuffer)
953 : {
954 21 : NS_ASSERTION(!mIsDataUserFont,
955 : "data fonts should be reading tables directly from memory");
956 :
957 21 : if (!mFTFaceInitialized) {
958 4 : mFTFaceInitialized = true;
959 : FcChar8 *filename;
960 4 : if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
961 0 : return NS_ERROR_FAILURE;
962 : }
963 : int index;
964 4 : if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
965 0 : index = 0; // default to 0 if not found in pattern
966 : }
967 4 : mFTFace = Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
968 4 : if (!mFTFace) {
969 0 : return NS_ERROR_FAILURE;
970 : }
971 : }
972 :
973 21 : if (!mFTFace) {
974 0 : return NS_ERROR_NOT_AVAILABLE;
975 : }
976 :
977 21 : FT_ULong length = 0;
978 21 : if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
979 7 : return NS_ERROR_NOT_AVAILABLE;
980 : }
981 14 : if (!aBuffer.SetLength(length, fallible)) {
982 0 : return NS_ERROR_OUT_OF_MEMORY;
983 : }
984 14 : if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
985 0 : aBuffer.Clear();
986 0 : return NS_ERROR_FAILURE;
987 : }
988 :
989 14 : return NS_OK;
990 : }
991 :
992 : void
993 16 : gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
994 : {
995 16 : if (mHasStyles) {
996 0 : return;
997 : }
998 :
999 : // add font entries for each of the faces
1000 16 : uint32_t numFonts = mFontPatterns.Length();
1001 16 : NS_ASSERTION(numFonts, "font family containing no faces!!");
1002 16 : uint32_t numRegularFaces = 0;
1003 123 : for (uint32_t i = 0; i < numFonts; i++) {
1004 107 : FcPattern* face = mFontPatterns[i];
1005 :
1006 : // figure out the psname/fullname and choose which to use as the facename
1007 214 : nsAutoString psname, fullname;
1008 107 : GetFaceNames(face, mName, psname, fullname);
1009 107 : const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
1010 :
1011 : gfxFontconfigFontEntry *fontEntry =
1012 107 : new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
1013 107 : AddFontEntry(fontEntry);
1014 :
1015 271 : if (fontEntry->IsUpright() &&
1016 133 : fontEntry->Weight() == NS_FONT_WEIGHT_NORMAL &&
1017 26 : fontEntry->Stretch() == NS_FONT_STRETCH_NORMAL) {
1018 21 : numRegularFaces++;
1019 : }
1020 :
1021 107 : if (LOG_FONTLIST_ENABLED()) {
1022 0 : LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
1023 : " with style: %s weight: %d stretch: %d"
1024 : " psname: %s fullname: %s",
1025 : NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
1026 : NS_ConvertUTF16toUTF8(Name()).get(),
1027 : (fontEntry->IsItalic()) ?
1028 : "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
1029 : fontEntry->Weight(), fontEntry->Stretch(),
1030 : NS_ConvertUTF16toUTF8(psname).get(),
1031 : NS_ConvertUTF16toUTF8(fullname).get()));
1032 : }
1033 : }
1034 :
1035 : // somewhat arbitrary, but define a family with two or more regular
1036 : // faces as a family for which intra-family fallback should be used
1037 16 : if (numRegularFaces > 1) {
1038 5 : mCheckForFallbackFaces = true;
1039 : }
1040 16 : mFaceNamesInitialized = true;
1041 16 : mFontPatterns.Clear();
1042 16 : SetHasStyles(true);
1043 : }
1044 :
1045 : void
1046 1698 : gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
1047 : {
1048 1698 : NS_ASSERTION(!mHasStyles,
1049 : "font patterns must not be added to already enumerated families");
1050 :
1051 : FcBool outline;
1052 3396 : if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch ||
1053 1698 : !outline) {
1054 0 : mHasNonScalableFaces = true;
1055 :
1056 : FcBool scalable;
1057 0 : if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch &&
1058 0 : scalable) {
1059 0 : mForceScalable = true;
1060 : }
1061 : }
1062 :
1063 3396 : nsCountedRef<FcPattern> pattern(aFontPattern);
1064 1698 : mFontPatterns.AppendElement(pattern);
1065 1698 : }
1066 :
1067 : static const double kRejectDistance = 10000.0;
1068 :
1069 : // Calculate a distance score representing the size disparity between the
1070 : // requested style's size and the font entry's size.
1071 : static double
1072 0 : SizeDistance(gfxFontconfigFontEntry* aEntry,
1073 : const gfxFontStyle& aStyle,
1074 : bool aForceScalable)
1075 : {
1076 0 : double requestedSize = SizeForStyle(aEntry, aStyle);
1077 0 : double bestDist = -1.0;
1078 : double size;
1079 0 : int v = 0;
1080 0 : while (FcPatternGetDouble(aEntry->GetPattern(),
1081 : FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
1082 0 : ++v;
1083 0 : double dist = fabs(size - requestedSize);
1084 0 : if (bestDist < 0.0 || dist < bestDist) {
1085 0 : bestDist = dist;
1086 : }
1087 : }
1088 0 : if (bestDist < 0.0) {
1089 : // No size means scalable
1090 0 : return -1.0;
1091 0 : } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
1092 : // fontconfig prefers a matching family or lang to pixelsize of bitmap
1093 : // fonts. CSS suggests a tolerance of 20% on pixelsize.
1094 0 : return bestDist;
1095 : } else {
1096 : // Reject any non-scalable fonts that are not within tolerance.
1097 0 : return kRejectDistance;
1098 : }
1099 : }
1100 :
1101 : void
1102 19 : gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
1103 : nsTArray<gfxFontEntry*>& aFontEntryList,
1104 : bool& aNeedsSyntheticBold)
1105 : {
1106 19 : gfxFontFamily::FindAllFontsForStyle(aFontStyle,
1107 : aFontEntryList,
1108 19 : aNeedsSyntheticBold);
1109 :
1110 19 : if (!mHasNonScalableFaces) {
1111 19 : return;
1112 : }
1113 :
1114 : // Iterate over the the available fonts while compacting any groups
1115 : // of unscalable fonts with matching styles into a single entry
1116 : // corresponding to the closest available size. If the closest
1117 : // available size is rejected for being outside tolernace, then the
1118 : // entire group will be skipped.
1119 0 : size_t skipped = 0;
1120 0 : gfxFontconfigFontEntry* bestEntry = nullptr;
1121 0 : double bestDist = -1.0;
1122 0 : for (size_t i = 0; i < aFontEntryList.Length(); i++) {
1123 : gfxFontconfigFontEntry* entry =
1124 0 : static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
1125 0 : double dist = SizeDistance(entry, aFontStyle, mForceScalable);
1126 : // If the entry is scalable or has a style that does not match
1127 : // the group of unscalable fonts, then start a new group.
1128 0 : if (dist < 0.0 ||
1129 0 : !bestEntry ||
1130 0 : bestEntry->Stretch() != entry->Stretch() ||
1131 0 : bestEntry->Weight() != entry->Weight() ||
1132 0 : bestEntry->mStyle != entry->mStyle) {
1133 : // If the best entry in this group is still outside the tolerance,
1134 : // then skip the entire group.
1135 0 : if (bestDist >= kRejectDistance) {
1136 0 : skipped++;
1137 : }
1138 : // Remove any compacted entries from the previous group.
1139 0 : if (skipped) {
1140 0 : i -= skipped;
1141 0 : aFontEntryList.RemoveElementsAt(i, skipped);
1142 0 : skipped = 0;
1143 : }
1144 : // Mark the start of the new group.
1145 0 : bestEntry = entry;
1146 0 : bestDist = dist;
1147 : } else {
1148 : // If this entry more closely matches the requested size than the
1149 : // current best in the group, then take this entry instead.
1150 0 : if (dist < bestDist) {
1151 0 : aFontEntryList[i-1-skipped] = entry;
1152 0 : bestEntry = entry;
1153 0 : bestDist = dist;
1154 : }
1155 0 : skipped++;
1156 : }
1157 : }
1158 : // If the best entry in this group is still outside the tolerance,
1159 : // then skip the entire group.
1160 0 : if (bestDist >= kRejectDistance) {
1161 0 : skipped++;
1162 : }
1163 : // Remove any compacted entries from the current group.
1164 0 : if (skipped) {
1165 0 : aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
1166 : }
1167 : }
1168 :
1169 : /* virtual */
1170 0 : gfxFontconfigFontFamily::~gfxFontconfigFontFamily()
1171 : {
1172 : // Should not be dropped by stylo
1173 0 : MOZ_ASSERT(NS_IsMainThread());
1174 0 : }
1175 :
1176 8 : gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
1177 : cairo_scaled_font_t *aScaledFont,
1178 : FcPattern *aPattern,
1179 : gfxFloat aAdjustedSize,
1180 : gfxFontEntry *aFontEntry,
1181 : const gfxFontStyle *aFontStyle,
1182 8 : bool aNeedsBold) :
1183 8 : gfxFontconfigFontBase(aUnscaledFont, aScaledFont, aPattern, aFontEntry, aFontStyle)
1184 : {
1185 8 : mAdjustedSize = aAdjustedSize;
1186 8 : }
1187 :
1188 0 : gfxFontconfigFont::~gfxFontconfigFont()
1189 : {
1190 0 : }
1191 :
1192 3 : gfxFcPlatformFontList::gfxFcPlatformFontList()
1193 : : mLocalNames(64)
1194 : , mGenericMappings(32)
1195 : , mFcSubstituteCache(64)
1196 : , mLastConfig(nullptr)
1197 3 : , mAlwaysUseFontconfigGenerics(true)
1198 : {
1199 : // if the rescan interval is set, start the timer
1200 3 : int rescanInterval = FcConfigGetRescanInterval(nullptr);
1201 3 : if (rescanInterval) {
1202 3 : mLastConfig = FcConfigGetCurrent();
1203 3 : mCheckFontUpdatesTimer = do_CreateInstance("@mozilla.org/timer;1");
1204 3 : if (mCheckFontUpdatesTimer) {
1205 6 : mCheckFontUpdatesTimer->InitWithNamedFuncCallback(
1206 : CheckFontUpdates,
1207 : this,
1208 3 : (rescanInterval + 1) * 1000,
1209 : nsITimer::TYPE_REPEATING_SLACK,
1210 6 : "gfxFcPlatformFontList::gfxFcPlatformFontList");
1211 : } else {
1212 0 : NS_WARNING("Failure to create font updates timer");
1213 : }
1214 : }
1215 :
1216 : #ifdef MOZ_BUNDLED_FONTS
1217 3 : mBundledFontsInitialized = false;
1218 : #endif
1219 3 : }
1220 :
1221 0 : gfxFcPlatformFontList::~gfxFcPlatformFontList()
1222 : {
1223 0 : if (mCheckFontUpdatesTimer) {
1224 0 : mCheckFontUpdatesTimer->Cancel();
1225 0 : mCheckFontUpdatesTimer = nullptr;
1226 : }
1227 0 : }
1228 :
1229 : void
1230 6 : gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts)
1231 : {
1232 : // This iterates over the fonts in a font set and adds in gfxFontFamily
1233 : // objects for each family. The patterns for individual fonts are not
1234 : // copied here. When a family is actually used, the fonts in the family
1235 : // are enumerated and the patterns copied. Note that we're explicitly
1236 : // excluding non-scalable fonts such as X11 bitmap fonts, which
1237 : // Chrome Skia/Webkit code does also.
1238 :
1239 6 : if (!aFontSet) {
1240 0 : NS_WARNING("AddFontSetFamilies called with a null font set.");
1241 0 : return;
1242 : }
1243 :
1244 6 : FcChar8* lastFamilyName = (FcChar8*)"";
1245 12 : RefPtr<gfxFontconfigFontFamily> fontFamily;
1246 12 : nsAutoString familyName;
1247 1704 : for (int f = 0; f < aFontSet->nfont; f++) {
1248 1698 : FcPattern* font = aFontSet->fonts[f];
1249 :
1250 : // get canonical name
1251 1698 : uint32_t cIndex = FindCanonicalNameIndex(font, FC_FAMILYLANG);
1252 1698 : FcChar8* canonical = nullptr;
1253 1698 : FcPatternGetString(font, FC_FAMILY, cIndex, &canonical);
1254 1698 : if (!canonical) {
1255 0 : continue;
1256 : }
1257 :
1258 : // same as the last one? no need to add a new family, skip
1259 1698 : if (FcStrCmp(canonical, lastFamilyName) != 0) {
1260 684 : lastFamilyName = canonical;
1261 :
1262 : // add new family if one doesn't already exist
1263 684 : familyName.Truncate();
1264 684 : AppendUTF8toUTF16(ToCharPtr(canonical), familyName);
1265 1368 : nsAutoString keyName(familyName);
1266 684 : ToLowerCase(keyName);
1267 :
1268 : fontFamily = static_cast<gfxFontconfigFontFamily*>
1269 684 : (mFontFamilies.GetWeak(keyName));
1270 684 : if (!fontFamily) {
1271 558 : fontFamily = new gfxFontconfigFontFamily(familyName);
1272 558 : mFontFamilies.Put(keyName, fontFamily);
1273 : }
1274 : // Record if the family contains fonts from the app font set
1275 : // (in which case we won't rely on fontconfig's charmap, due to
1276 : // bug 1276594).
1277 684 : if (aAppFonts) {
1278 3 : fontFamily->SetFamilyContainsAppFonts(true);
1279 : }
1280 :
1281 : // Add pointers to other localized family names. Most fonts
1282 : // only have a single name, so the first call to GetString
1283 : // will usually not match
1284 : FcChar8* otherName;
1285 684 : int n = (cIndex == 0 ? 1 : 0);
1286 1008 : while (FcPatternGetString(font, FC_FAMILY, n, &otherName) == FcResultMatch) {
1287 324 : NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
1288 162 : AddOtherFamilyName(fontFamily, otherFamilyName);
1289 162 : n++;
1290 162 : if (n == int(cIndex)) {
1291 0 : n++; // skip over canonical name
1292 : }
1293 : }
1294 : }
1295 :
1296 1698 : NS_ASSERTION(fontFamily, "font must belong to a font family");
1297 1698 : fontFamily->AddFontPattern(font);
1298 :
1299 : // map the psname, fullname ==> font family for local font lookups
1300 3396 : nsAutoString psname, fullname;
1301 1698 : GetFaceNames(font, familyName, psname, fullname);
1302 1698 : if (!psname.IsEmpty()) {
1303 1698 : ToLowerCase(psname);
1304 1698 : mLocalNames.Put(psname, font);
1305 : }
1306 1698 : if (!fullname.IsEmpty()) {
1307 1698 : ToLowerCase(fullname);
1308 1698 : mLocalNames.Put(fullname, font);
1309 : }
1310 : }
1311 : }
1312 :
1313 : nsresult
1314 3 : gfxFcPlatformFontList::InitFontListForPlatform()
1315 : {
1316 3 : mLastConfig = FcConfigGetCurrent();
1317 :
1318 3 : mLocalNames.Clear();
1319 3 : mFcSubstituteCache.Clear();
1320 :
1321 : // iterate over available fonts
1322 3 : FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1323 3 : AddFontSetFamilies(systemFonts, /* aAppFonts = */ false);
1324 3 : mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1325 :
1326 : #ifdef MOZ_BUNDLED_FONTS
1327 3 : ActivateBundledFonts();
1328 3 : FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1329 3 : AddFontSetFamilies(appFonts, /* aAppFonts = */ true);
1330 : #endif
1331 :
1332 3 : mOtherFamilyNamesInitialized = true;
1333 :
1334 3 : return NS_OK;
1335 : }
1336 :
1337 : // For displaying the fontlist in UI, use explicit call to FcFontList. Using
1338 : // FcFontList results in the list containing the localized names as dictated
1339 : // by system defaults.
1340 : static void
1341 0 : GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsIAtom *aLangGroup)
1342 : {
1343 0 : aListOfFonts.Clear();
1344 :
1345 0 : nsAutoRef<FcPattern> pat(FcPatternCreate());
1346 0 : if (!pat) {
1347 0 : return;
1348 : }
1349 :
1350 0 : nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
1351 0 : if (!os) {
1352 0 : return;
1353 : }
1354 :
1355 : // add the lang to the pattern
1356 0 : nsAutoCString fcLang;
1357 0 : gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1358 0 : pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1359 0 : if (!fcLang.IsEmpty()) {
1360 0 : FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
1361 : }
1362 :
1363 0 : nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os));
1364 0 : if (!fs) {
1365 0 : return;
1366 : }
1367 :
1368 0 : for (int i = 0; i < fs->nfont; i++) {
1369 : char *family;
1370 :
1371 0 : if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
1372 : (FcChar8 **) &family) != FcResultMatch)
1373 : {
1374 0 : continue;
1375 : }
1376 :
1377 : // Remove duplicates...
1378 0 : nsAutoString strFamily;
1379 0 : AppendUTF8toUTF16(family, strFamily);
1380 0 : if (aListOfFonts.Contains(strFamily)) {
1381 0 : continue;
1382 : }
1383 :
1384 0 : aListOfFonts.AppendElement(strFamily);
1385 : }
1386 :
1387 0 : aListOfFonts.Sort();
1388 : }
1389 :
1390 : void
1391 0 : gfxFcPlatformFontList::GetFontList(nsIAtom *aLangGroup,
1392 : const nsACString& aGenericFamily,
1393 : nsTArray<nsString>& aListOfFonts)
1394 : {
1395 : // Get the list of font family names using fontconfig
1396 0 : GetSystemFontList(aListOfFonts, aLangGroup);
1397 :
1398 : // Under Linux, the generics "serif", "sans-serif" and "monospace"
1399 : // are included in the pref fontlist. These map to whatever fontconfig
1400 : // decides they should be for a given language, rather than one of the
1401 : // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
1402 0 : bool serif = false, sansSerif = false, monospace = false;
1403 0 : if (aGenericFamily.IsEmpty())
1404 0 : serif = sansSerif = monospace = true;
1405 0 : else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
1406 0 : serif = true;
1407 0 : else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
1408 0 : sansSerif = true;
1409 0 : else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
1410 0 : monospace = true;
1411 0 : else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
1412 0 : aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
1413 0 : serif = sansSerif = true;
1414 : else
1415 0 : NS_NOTREACHED("unexpected CSS generic font family");
1416 :
1417 : // The first in the list becomes the default in
1418 : // FontBuilder.readFontSelection() if the preference-selected font is not
1419 : // available, so put system configured defaults first.
1420 0 : if (monospace)
1421 0 : aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
1422 0 : if (sansSerif)
1423 0 : aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
1424 0 : if (serif)
1425 0 : aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
1426 0 : }
1427 :
1428 : gfxFontFamily*
1429 3 : gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
1430 : {
1431 : // Get the default font by using a fake name to retrieve the first
1432 : // scalable font that fontconfig suggests for the given language.
1433 : PrefFontList* prefFonts =
1434 3 : FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
1435 3 : NS_ASSERTION(prefFonts, "null list of generic fonts");
1436 3 : if (prefFonts && !prefFonts->IsEmpty()) {
1437 3 : return (*prefFonts)[0];
1438 : }
1439 0 : return nullptr;
1440 : }
1441 :
1442 : gfxFontEntry*
1443 0 : gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
1444 : uint16_t aWeight,
1445 : int16_t aStretch,
1446 : uint8_t aStyle)
1447 : {
1448 0 : nsAutoString keyName(aFontName);
1449 0 : ToLowerCase(keyName);
1450 :
1451 : // if name is not in the global list, done
1452 0 : FcPattern* fontPattern = mLocalNames.Get(keyName);
1453 0 : if (!fontPattern) {
1454 0 : return nullptr;
1455 : }
1456 :
1457 : return new gfxFontconfigFontEntry(aFontName, fontPattern,
1458 0 : aWeight, aStretch, aStyle);
1459 : }
1460 :
1461 : gfxFontEntry*
1462 0 : gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
1463 : uint16_t aWeight,
1464 : int16_t aStretch,
1465 : uint8_t aStyle,
1466 : const uint8_t* aFontData,
1467 : uint32_t aLength)
1468 : {
1469 0 : FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
1470 0 : if (!face) {
1471 0 : NS_Free((void*)aFontData);
1472 0 : return nullptr;
1473 : }
1474 0 : if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
1475 0 : Factory::ReleaseFTFace(face);
1476 0 : NS_Free((void*)aFontData);
1477 0 : return nullptr;
1478 : }
1479 :
1480 : return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
1481 0 : aStyle, aFontData, face);
1482 : }
1483 :
1484 : bool
1485 1 : gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
1486 : nsTArray<gfxFontFamily*>* aOutput,
1487 : gfxFontStyle* aStyle,
1488 : gfxFloat aDevToCssSize)
1489 : {
1490 2 : nsAutoString familyName(aFamily);
1491 1 : ToLowerCase(familyName);
1492 1 : nsIAtom* language = (aStyle ? aStyle->language.get() : nullptr);
1493 :
1494 : // deprecated generic names are explicitly converted to standard generics
1495 1 : bool isDeprecatedGeneric = false;
1496 2 : if (familyName.EqualsLiteral("sans") ||
1497 1 : familyName.EqualsLiteral("sans serif")) {
1498 0 : familyName.AssignLiteral("sans-serif");
1499 0 : isDeprecatedGeneric = true;
1500 1 : } else if (familyName.EqualsLiteral("mono")) {
1501 0 : familyName.AssignLiteral("monospace");
1502 0 : isDeprecatedGeneric = true;
1503 : }
1504 :
1505 : // fontconfig generics? use fontconfig to determine the family for lang
1506 4 : if (isDeprecatedGeneric ||
1507 4 : mozilla::FontFamilyName::Convert(familyName).IsGeneric()) {
1508 0 : PrefFontList* prefFonts = FindGenericFamilies(familyName, language);
1509 0 : if (prefFonts && !prefFonts->IsEmpty()) {
1510 0 : aOutput->AppendElements(*prefFonts);
1511 0 : return true;
1512 : }
1513 0 : return false;
1514 : }
1515 :
1516 : // fontconfig allows conditional substitutions in such a way that it's
1517 : // difficult to distinguish an explicit substitution from other suggested
1518 : // choices. To sniff out explicit substitutions, compare the substitutions
1519 : // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
1520 : // substitutions
1521 : //
1522 : // Example:
1523 : //
1524 : // serif ==> DejaVu Serif, ...
1525 : // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
1526 : //
1527 : // In this case fontconfig is including Tex Gyre Heros and
1528 : // Nimbus Sans L as alternatives for Helvetica.
1529 :
1530 : // Because the FcConfigSubstitute call is quite expensive, we cache the
1531 : // actual font families found via this process. So check the cache first:
1532 2 : NS_ConvertUTF16toUTF8 familyToFind(familyName);
1533 2 : AutoTArray<gfxFontFamily*,10> cachedFamilies;
1534 1 : if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
1535 0 : if (cachedFamilies.IsEmpty()) {
1536 0 : return false;
1537 : }
1538 0 : aOutput->AppendElements(cachedFamilies);
1539 0 : return true;
1540 : }
1541 :
1542 : // It wasn't in the cache, so we need to ask fontconfig...
1543 1 : const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
1544 1 : FcChar8* sentinelFirstFamily = nullptr;
1545 2 : nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
1546 1 : FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
1547 1 : FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
1548 1 : FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
1549 :
1550 : // substitutions for font, -moz-sentinel pattern
1551 2 : nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
1552 1 : FcPatternAddString(fontWithSentinel, FC_FAMILY,
1553 1 : ToFcChar8Ptr(familyToFind.get()));
1554 1 : FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
1555 1 : FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
1556 :
1557 : // Add all font family matches until reaching the sentinel.
1558 1 : FcChar8* substName = nullptr;
1559 4 : for (int i = 0;
1560 2 : FcPatternGetString(fontWithSentinel, FC_FAMILY,
1561 2 : i, &substName) == FcResultMatch;
1562 : i++)
1563 : {
1564 3 : NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
1565 4 : if (sentinelFirstFamily &&
1566 4 : FcStrCmp(substName, sentinelFirstFamily) == 0) {
1567 1 : break;
1568 : }
1569 1 : gfxPlatformFontList::FindAndAddFamilies(subst, &cachedFamilies);
1570 : }
1571 :
1572 : // Cache the resulting list, so we don't have to do this again.
1573 1 : mFcSubstituteCache.Put(familyToFind, cachedFamilies);
1574 :
1575 1 : if (cachedFamilies.IsEmpty()) {
1576 0 : return false;
1577 : }
1578 1 : aOutput->AppendElements(cachedFamilies);
1579 1 : return true;
1580 : }
1581 :
1582 : bool
1583 0 : gfxFcPlatformFontList::GetStandardFamilyName(const nsAString& aFontName,
1584 : nsAString& aFamilyName)
1585 : {
1586 0 : aFamilyName.Truncate();
1587 :
1588 : // The fontconfig list of fonts includes generic family names in the
1589 : // font list. For these, just use the generic name.
1590 0 : if (aFontName.EqualsLiteral("serif") ||
1591 0 : aFontName.EqualsLiteral("sans-serif") ||
1592 0 : aFontName.EqualsLiteral("monospace")) {
1593 0 : aFamilyName.Assign(aFontName);
1594 0 : return true;
1595 : }
1596 :
1597 0 : nsAutoRef<FcPattern> pat(FcPatternCreate());
1598 0 : if (!pat) {
1599 0 : return true;
1600 : }
1601 :
1602 0 : nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
1603 0 : if (!os) {
1604 0 : return true;
1605 : }
1606 :
1607 : // add the family name to the pattern
1608 0 : NS_ConvertUTF16toUTF8 familyName(aFontName);
1609 0 : FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(familyName.get()));
1610 :
1611 0 : nsAutoRef<FcFontSet> givenFS(FcFontList(nullptr, pat, os));
1612 0 : if (!givenFS) {
1613 0 : return true;
1614 : }
1615 :
1616 : // See if there is a font face with first family equal to the given family
1617 : // (needs to be in sync with names coming from GetFontList())
1618 0 : nsTArray<nsCString> candidates;
1619 0 : for (int i = 0; i < givenFS->nfont; i++) {
1620 : char* firstFamily;
1621 :
1622 0 : if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
1623 : (FcChar8 **) &firstFamily) != FcResultMatch)
1624 : {
1625 0 : continue;
1626 : }
1627 :
1628 0 : nsDependentCString first(firstFamily);
1629 0 : if (!candidates.Contains(first)) {
1630 0 : candidates.AppendElement(first);
1631 :
1632 0 : if (familyName.Equals(first)) {
1633 0 : aFamilyName.Assign(aFontName);
1634 0 : return true;
1635 : }
1636 : }
1637 : }
1638 :
1639 : // Because fontconfig conflates different family name types, need to
1640 : // double check that the candidate name is not simply a different
1641 : // name type. For example, if a font with nameID=16 "Minion Pro" and
1642 : // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
1643 : // family="Minion Pro" will return a set of patterns some of which
1644 : // will have a first family of "Minion Pro Caption". Ignore these
1645 : // patterns and use the first candidate that maps to a font set with
1646 : // the same number of faces and an identical set of patterns.
1647 0 : for (uint32_t j = 0; j < candidates.Length(); ++j) {
1648 0 : FcPatternDel(pat, FC_FAMILY);
1649 0 : FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
1650 :
1651 0 : nsAutoRef<FcFontSet> candidateFS(FcFontList(nullptr, pat, os));
1652 0 : if (!candidateFS) {
1653 0 : return true;
1654 : }
1655 :
1656 0 : if (candidateFS->nfont != givenFS->nfont) {
1657 0 : continue;
1658 : }
1659 :
1660 0 : bool equal = true;
1661 0 : for (int i = 0; i < givenFS->nfont; ++i) {
1662 0 : if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
1663 0 : equal = false;
1664 0 : break;
1665 : }
1666 : }
1667 0 : if (equal) {
1668 0 : AppendUTF8toUTF16(candidates[j], aFamilyName);
1669 0 : return true;
1670 : }
1671 : }
1672 :
1673 : // didn't find localized name, leave family name blank
1674 0 : return true;
1675 : }
1676 :
1677 : void
1678 5 : gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
1679 : nsIAtom* aLanguage,
1680 : nsTArray<gfxFontFamily*>& aFamilyList)
1681 : {
1682 5 : bool usePrefFontList = false;
1683 :
1684 : // treat -moz-fixed as monospace
1685 5 : if (aGenericType == eFamily_moz_fixed) {
1686 0 : aGenericType = eFamily_monospace;
1687 : }
1688 :
1689 5 : const char* generic = GetGenericName(aGenericType);
1690 5 : NS_ASSERTION(generic, "weird generic font type");
1691 5 : if (!generic) {
1692 0 : return;
1693 : }
1694 :
1695 : // By default, most font prefs on Linux map to "use fontconfig"
1696 : // keywords. So only need to explicitly lookup font pref if
1697 : // non-default settings exist
1698 10 : NS_ConvertASCIItoUTF16 genericToLookup(generic);
1699 5 : if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
1700 0 : aLanguage == nsGkAtoms::x_math) {
1701 5 : nsIAtom* langGroup = GetLangGroup(aLanguage);
1702 : nsAdoptingString fontlistValue =
1703 10 : Preferences::GetString(NamePref(generic, langGroup).get());
1704 5 : if (fontlistValue.IsEmpty()) {
1705 : // The font name list may have two or more family names as comma
1706 : // separated list. In such case, not matching with generic font
1707 : // name is fine because if the list prefers specific font, we
1708 : // should try to use the pref with complicated path.
1709 : fontlistValue =
1710 5 : Preferences::GetString(NameListPref(generic, langGroup).get());
1711 : }
1712 5 : if (fontlistValue) {
1713 10 : if (!fontlistValue.EqualsLiteral("serif") &&
1714 5 : !fontlistValue.EqualsLiteral("sans-serif") &&
1715 0 : !fontlistValue.EqualsLiteral("monospace")) {
1716 0 : usePrefFontList = true;
1717 : } else {
1718 : // serif, sans-serif or monospace was specified
1719 5 : genericToLookup.Assign(fontlistValue);
1720 : }
1721 : }
1722 : }
1723 :
1724 : // when pref fonts exist, use standard pref font lookup
1725 5 : if (usePrefFontList) {
1726 0 : return gfxPlatformFontList::AddGenericFonts(aGenericType,
1727 : aLanguage,
1728 0 : aFamilyList);
1729 : }
1730 :
1731 5 : PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
1732 5 : NS_ASSERTION(prefFonts, "null generic font list");
1733 5 : aFamilyList.AppendElements(*prefFonts);
1734 : }
1735 :
1736 : void
1737 3 : gfxFcPlatformFontList::ClearLangGroupPrefFonts()
1738 : {
1739 3 : ClearGenericMappings();
1740 3 : gfxPlatformFontList::ClearLangGroupPrefFonts();
1741 3 : mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1742 3 : }
1743 :
1744 : /* static */ FT_Library
1745 3 : gfxFcPlatformFontList::GetFTLibrary()
1746 : {
1747 3 : if (!sCairoFTLibrary) {
1748 : // Use cairo's FT_Library so that cairo takes care of shutdown of the
1749 : // FT_Library after it has destroyed its font_faces, and FT_Done_Face
1750 : // has been called on each FT_Face, at least until this bug is fixed:
1751 : // https://bugs.freedesktop.org/show_bug.cgi?id=18857
1752 : //
1753 : // Cairo keeps it's own FT_Library object for creating FT_Face
1754 : // instances, so use that. There's no simple API for accessing this
1755 : // so use the hacky method below of making a font and extracting
1756 : // the library pointer from that.
1757 :
1758 : bool needsBold;
1759 6 : gfxFontStyle style;
1760 3 : gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
1761 3 : gfxFontFamily* family = pfl->GetDefaultFont(&style);
1762 3 : NS_ASSERTION(family, "couldn't find a default font family");
1763 3 : gfxFontEntry* fe = family->FindFontForStyle(style, needsBold);
1764 3 : if (!fe) {
1765 0 : return nullptr;
1766 : }
1767 6 : RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
1768 3 : if (!font) {
1769 0 : return nullptr;
1770 : }
1771 :
1772 3 : gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
1773 6 : gfxFT2LockedFace face(ft2Font);
1774 3 : if (!face.get()) {
1775 0 : return nullptr;
1776 : }
1777 :
1778 3 : sCairoFTLibrary = face.get()->glyph->library;
1779 : }
1780 :
1781 3 : return sCairoFTLibrary;
1782 : }
1783 :
1784 : gfxPlatformFontList::PrefFontList*
1785 8 : gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
1786 : nsIAtom* aLanguage)
1787 : {
1788 : // set up name
1789 16 : NS_ConvertUTF16toUTF8 generic(aGeneric);
1790 :
1791 16 : nsAutoCString fcLang;
1792 8 : GetSampleLangForGroup(aLanguage, fcLang);
1793 8 : ToLowerCase(fcLang);
1794 :
1795 16 : nsAutoCString genericLang(generic);
1796 8 : if (fcLang.Length() > 0) {
1797 8 : genericLang.Append('-');
1798 : }
1799 8 : genericLang.Append(fcLang);
1800 :
1801 : // try to get the family from the cache
1802 8 : PrefFontList* prefFonts = mGenericMappings.Get(genericLang);
1803 8 : if (prefFonts) {
1804 3 : return prefFonts;
1805 : }
1806 :
1807 : // if not found, ask fontconfig to pick the appropriate font
1808 10 : nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
1809 5 : FcPatternAddString(genericPattern, FC_FAMILY,
1810 5 : ToFcChar8Ptr(generic.get()));
1811 :
1812 : // -- prefer scalable fonts
1813 5 : FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
1814 :
1815 : // -- add the lang to the pattern
1816 5 : if (!fcLang.IsEmpty()) {
1817 5 : FcPatternAddString(genericPattern, FC_LANG,
1818 5 : ToFcChar8Ptr(fcLang.get()));
1819 : }
1820 :
1821 : // -- perform substitutions
1822 5 : FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
1823 5 : FcDefaultSubstitute(genericPattern);
1824 :
1825 : // -- sort to get the closest matches
1826 : FcResult result;
1827 : nsAutoRef<FcFontSet> faces(FcFontSort(nullptr, genericPattern, FcFalse,
1828 10 : nullptr, &result));
1829 :
1830 5 : if (!faces) {
1831 0 : return nullptr;
1832 : }
1833 :
1834 : // -- select the fonts to be used for the generic
1835 5 : prefFonts = new PrefFontList; // can be empty but in practice won't happen
1836 5 : uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
1837 5 : bool foundFontWithLang = false;
1838 64 : for (int i = 0; i < faces->nfont; i++) {
1839 64 : FcPattern* font = faces->fonts[i];
1840 64 : FcChar8* mappedGeneric = nullptr;
1841 :
1842 64 : FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
1843 64 : if (mappedGeneric) {
1844 123 : NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
1845 123 : AutoTArray<gfxFontFamily*,1> genericFamilies;
1846 64 : if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
1847 : &genericFamilies)) {
1848 64 : MOZ_ASSERT(genericFamilies.Length() == 1,
1849 : "expected a single family");
1850 64 : if (!prefFonts->Contains(genericFamilies[0])) {
1851 15 : prefFonts->AppendElement(genericFamilies[0]);
1852 : bool foundLang =
1853 30 : !fcLang.IsEmpty() &&
1854 30 : PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
1855 15 : foundFontWithLang = foundFontWithLang || foundLang;
1856 : // check to see if the list is full
1857 15 : if (prefFonts->Length() >= limit) {
1858 5 : break;
1859 : }
1860 : }
1861 : }
1862 : }
1863 : }
1864 :
1865 : // if no font in the list matches the lang, trim all but the first one
1866 5 : if (!prefFonts->IsEmpty() && !foundFontWithLang) {
1867 0 : prefFonts->TruncateLength(1);
1868 : }
1869 :
1870 5 : mGenericMappings.Put(genericLang, prefFonts);
1871 5 : return prefFonts;
1872 : }
1873 :
1874 : bool
1875 6 : gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics()
1876 : {
1877 : static const char kFontNamePrefix[] = "font.name.";
1878 :
1879 6 : bool prefFontsUseOnlyGenerics = true;
1880 : uint32_t count;
1881 : char** names;
1882 6 : nsresult rv = Preferences::GetRootBranch()->
1883 6 : GetChildList(kFontNamePrefix, &count, &names);
1884 6 : if (NS_SUCCEEDED(rv) && count) {
1885 42 : for (size_t i = 0; i < count; i++) {
1886 : // Check whether all font.name prefs map to generic keywords
1887 : // and that the pref name and keyword match.
1888 : // Ex: font.name.serif.ar ==> "serif" (ok)
1889 : // Ex: font.name.serif.ar ==> "monospace" (return false)
1890 : // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
1891 : // Ex: font.name.serif.ar ==> "" and
1892 : // font.name-list.serif.ar ==> "serif" (ok)
1893 : // Ex: font.name.serif.ar ==> "" and
1894 : // font.name-list.serif.ar ==> "Something, serif"
1895 : // (return false)
1896 :
1897 42 : nsDependentCString prefName(names[i] +
1898 78 : ArrayLength(kFontNamePrefix) - 1);
1899 42 : nsCCharSeparatedTokenizer tokenizer(prefName, '.');
1900 78 : const nsDependentCSubstring& generic = tokenizer.nextToken();
1901 78 : const nsDependentCSubstring& langGroup = tokenizer.nextToken();
1902 78 : nsAdoptingCString fontPrefValue = Preferences::GetCString(names[i]);
1903 42 : if (fontPrefValue.IsEmpty()) {
1904 : // The font name list may have two or more family names as comma
1905 : // separated list. In such case, not matching with generic font
1906 : // name is fine because if the list prefers specific font, this
1907 : // should return false.
1908 : fontPrefValue =
1909 84 : Preferences::GetCString(NameListPref(generic,
1910 42 : langGroup).get());
1911 : }
1912 :
1913 78 : if (!langGroup.EqualsLiteral("x-math") &&
1914 36 : !generic.Equals(fontPrefValue)) {
1915 6 : prefFontsUseOnlyGenerics = false;
1916 6 : break;
1917 : }
1918 : }
1919 6 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
1920 : }
1921 6 : return prefFontsUseOnlyGenerics;
1922 : }
1923 :
1924 : /* static */ void
1925 0 : gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
1926 : {
1927 : // check for font updates
1928 0 : FcInitBringUptoDate();
1929 :
1930 : // update fontlist if current config changed
1931 0 : gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis);
1932 0 : FcConfig* current = FcConfigGetCurrent();
1933 0 : if (current != pfl->GetLastConfig()) {
1934 0 : pfl->UpdateFontList();
1935 0 : pfl->ForceGlobalReflow();
1936 : }
1937 0 : }
1938 :
1939 : #ifdef MOZ_BUNDLED_FONTS
1940 : void
1941 3 : gfxFcPlatformFontList::ActivateBundledFonts()
1942 : {
1943 3 : if (!mBundledFontsInitialized) {
1944 3 : mBundledFontsInitialized = true;
1945 6 : nsCOMPtr<nsIFile> localDir;
1946 3 : nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1947 3 : if (NS_FAILED(rv)) {
1948 0 : return;
1949 : }
1950 3 : if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1951 0 : return;
1952 : }
1953 : bool isDir;
1954 3 : if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1955 0 : return;
1956 : }
1957 3 : if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
1958 0 : return;
1959 : }
1960 : }
1961 3 : if (!mBundledFontsPath.IsEmpty()) {
1962 3 : FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
1963 : }
1964 : }
1965 : #endif
1966 :
1967 : #ifdef MOZ_WIDGET_GTK
1968 : /***************************************************************************
1969 : *
1970 : * This function must be last in the file because it uses the system cairo
1971 : * library. Above this point the cairo library used is the tree cairo if
1972 : * MOZ_TREE_CAIRO.
1973 : */
1974 :
1975 : #if MOZ_TREE_CAIRO
1976 : // Tree cairo symbols have different names. Disable their activation through
1977 : // preprocessor macros.
1978 : #undef cairo_ft_font_options_substitute
1979 :
1980 : // The system cairo functions are not declared because the include paths cause
1981 : // the gdk headers to pick up the tree cairo.h.
1982 : extern "C" {
1983 : NS_VISIBILITY_DEFAULT void
1984 : cairo_ft_font_options_substitute (const cairo_font_options_t *options,
1985 : FcPattern *pattern);
1986 : }
1987 : #endif
1988 :
1989 : static void
1990 8 : ApplyGdkScreenFontOptions(FcPattern *aPattern)
1991 : {
1992 : const cairo_font_options_t *options =
1993 8 : gdk_screen_get_font_options(gdk_screen_get_default());
1994 :
1995 8 : cairo_ft_font_options_substitute(options, aPattern);
1996 8 : }
1997 :
1998 : #endif // MOZ_WIDGET_GTK
|