Coverage Report

Created: 2018-09-25 14:53

/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