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 "prlink.h"
7 #include "gfxTypes.h"
8
9 #include "nsTArray.h"
10
11 #include "gfxContext.h"
12 #ifdef MOZ_WIDGET_GTK
13 #include "gfxPlatformGtk.h"
14 #endif
15 #include "gfxFontconfigFonts.h"
16 #include "gfxFT2FontBase.h"
17 #include "gfxFT2Utils.h"
18 #include "harfbuzz/hb.h"
19 #include "harfbuzz/hb-glib.h"
20 #include "harfbuzz/hb-ot.h"
21 #include "nsUnicodeProperties.h"
22 #include "nsUnicodeScriptCodes.h"
23 #include "gfxFontconfigUtils.h"
24 #include "gfxUserFontSet.h"
25 #include "gfxFontConstants.h"
26 #include "nsGkAtoms.h"
27 #include "nsILanguageAtomService.h"
28 #include "nsServiceManagerUtils.h"
29
30 #include <cairo.h>
31 #include <cairo-ft.h>
32 #include "mozilla/gfx/HelpersCairo.h"
33
34 #include <fontconfig/fcfreetype.h>
35 #include <pango/pango.h>
36
37 #include FT_TRUETYPE_TABLES_H
38
39 #ifdef MOZ_WIDGET_GTK
40 #include <gdk/gdk.h>
41 #endif
42
43 #include <math.h>
44
45 using namespace mozilla;
46 using namespace mozilla::unicode;
47
48 #define PRINTING_FC_PROPERTY "gfx.printing"
49
50 static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
51
52 static cairo_scaled_font_t *
53 CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
54
55 static FT_Library gFTLibrary;
56
57 // FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97
58 // and so fontconfig-2.3.0 (2005).
59 #ifndef FC_FAMILYLANG
60 #define FC_FAMILYLANG "familylang"
61 #endif
62 #ifndef FC_FULLNAME
63 #define FC_FULLNAME "fullname"
64 #endif
65
66 static PRFuncPtr
FindFunctionSymbol(const char * name)67 FindFunctionSymbol(const char *name)
68 {
69 PRLibrary *lib = nullptr;
70 PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(name, &lib);
71 if (lib) {
72 PR_UnloadLibrary(lib);
73 }
74
75 return result;
76 }
77
HasChar(FcPattern * aFont,FcChar32 wc)78 static bool HasChar(FcPattern *aFont, FcChar32 wc)
79 {
80 FcCharSet *charset = nullptr;
81 FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
82
83 return charset && FcCharSetHasChar(charset, wc);
84 }
85
86 /**
87 * gfxFcFontEntry:
88 *
89 * An abstract base class of for gfxFontEntry implementations used by
90 * gfxFcFont and gfxUserFontSet.
91 */
92
93 class gfxFcFontEntry : public gfxFontEntry {
94 public:
95 // For all FontEntrys attached to gfxFcFonts, there will be only one
96 // pattern in this array. This is always a font pattern, not a fully
97 // resolved pattern. gfxFcFont only uses this to construct a PangoFont.
98 //
99 // FontEntrys for src:local() fonts in gfxUserFontSet may return more than
100 // one pattern. (See comment in gfxUserFcFontEntry.)
GetPatterns()101 const nsTArray< nsCountedRef<FcPattern> >& GetPatterns()
102 {
103 return mPatterns;
104 }
105
LookupFontEntry(cairo_font_face_t * aFace)106 static gfxFcFontEntry *LookupFontEntry(cairo_font_face_t *aFace)
107 {
108 return static_cast<gfxFcFontEntry*>
109 (cairo_font_face_get_user_data(aFace, &sFontEntryKey));
110 }
111
112 // override the gfxFontEntry impl to read the name from fontconfig
113 // instead of trying to get the 'name' table, as we don't implement
114 // GetFontTable() here
115 virtual nsString RealFaceName();
116
117 // This is needed to make gfxFontEntry::HasCharacter(aCh) work.
TestCharacterMap(uint32_t aCh)118 virtual bool TestCharacterMap(uint32_t aCh)
119 {
120 for (uint32_t i = 0; i < mPatterns.Length(); ++i) {
121 if (HasChar(mPatterns[i], aCh)) {
122 return true;
123 }
124 }
125 return false;
126 }
127
128 protected:
gfxFcFontEntry(const nsAString & aName)129 explicit gfxFcFontEntry(const nsAString& aName)
130 : gfxFontEntry(aName)
131 {
132 }
133
134 // One pattern is the common case and some subclasses rely on successful
135 // addition of the first element to the array.
136 AutoTArray<nsCountedRef<FcPattern>,1> mPatterns;
137
138 static cairo_user_data_key_t sFontEntryKey;
139 };
140
141 cairo_user_data_key_t gfxFcFontEntry::sFontEntryKey;
142
143 nsString
RealFaceName()144 gfxFcFontEntry::RealFaceName()
145 {
146 FcChar8 *name;
147 if (!mPatterns.IsEmpty()) {
148 if (FcPatternGetString(mPatterns[0],
149 FC_FULLNAME, 0, &name) == FcResultMatch) {
150 return NS_ConvertUTF8toUTF16((const char*)name);
151 }
152 if (FcPatternGetString(mPatterns[0],
153 FC_FAMILY, 0, &name) == FcResultMatch) {
154 NS_ConvertUTF8toUTF16 result((const char*)name);
155 if (FcPatternGetString(mPatterns[0],
156 FC_STYLE, 0, &name) == FcResultMatch) {
157 result.Append(' ');
158 AppendUTF8toUTF16((const char*)name, result);
159 }
160 return result;
161 }
162 }
163 // fall back to gfxFontEntry implementation (only works for sfnt fonts)
164 return gfxFontEntry::RealFaceName();
165 }
166
167 /**
168 * gfxSystemFcFontEntry:
169 *
170 * An implementation of gfxFcFontEntry used by gfxFcFonts for system fonts,
171 * including those from regular family-name based font selection as well as
172 * those from src:local().
173 *
174 * All gfxFcFonts using the same cairo_font_face_t share the same FontEntry.
175 */
176
177 class gfxSystemFcFontEntry : public gfxFcFontEntry {
178 public:
179 // For memory efficiency, aFontPattern should be a font pattern,
180 // not a fully resolved pattern.
gfxSystemFcFontEntry(cairo_font_face_t * aFontFace,FcPattern * aFontPattern,const nsAString & aName)181 gfxSystemFcFontEntry(cairo_font_face_t *aFontFace,
182 FcPattern *aFontPattern,
183 const nsAString& aName)
184 : gfxFcFontEntry(aName), mFontFace(aFontFace),
185 mFTFace(nullptr), mFTFaceInitialized(false)
186 {
187 cairo_font_face_reference(mFontFace);
188 cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, this, nullptr);
189
190 // mPatterns is an AutoTArray with 1 space always available, so the
191 // AppendElement always succeeds.
192 // FIXME: Make this infallible after bug 968520 is done.
193 MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
194 mPatterns[0] = aFontPattern;
195
196 FcChar8 *name;
197 if (FcPatternGetString(aFontPattern,
198 FC_FAMILY, 0, &name) == FcResultMatch) {
199 mFamilyName = NS_ConvertUTF8toUTF16((const char*)name);
200 }
201 }
202
~gfxSystemFcFontEntry()203 ~gfxSystemFcFontEntry()
204 {
205 cairo_font_face_set_user_data(mFontFace,
206 &sFontEntryKey,
207 nullptr,
208 nullptr);
209 cairo_font_face_destroy(mFontFace);
210 }
211
212 virtual void ForgetHBFace() override;
213 virtual void ReleaseGrFace(gr_face* aFace) override;
214
215 protected:
216 virtual nsresult
217 CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer) override;
218
219 void MaybeReleaseFTFace();
220
221 private:
222 cairo_font_face_t *mFontFace;
223 FT_Face mFTFace;
224 bool mFTFaceInitialized;
225 };
226
227 nsresult
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)228 gfxSystemFcFontEntry::CopyFontTable(uint32_t aTableTag,
229 nsTArray<uint8_t>& aBuffer)
230 {
231 if (!mFTFaceInitialized) {
232 mFTFaceInitialized = true;
233 FcChar8 *filename;
234 if (FcPatternGetString(mPatterns[0], FC_FILE, 0, &filename) != FcResultMatch) {
235 return NS_ERROR_FAILURE;
236 }
237 int index;
238 if (FcPatternGetInteger(mPatterns[0], FC_INDEX, 0, &index) != FcResultMatch) {
239 index = 0; // default to 0 if not found in pattern
240 }
241 if (FT_New_Face(gfxPangoFontGroup::GetFTLibrary(),
242 (const char*)filename, index, &mFTFace) != 0) {
243 return NS_ERROR_FAILURE;
244 }
245 }
246
247 if (!mFTFace) {
248 return NS_ERROR_NOT_AVAILABLE;
249 }
250
251 FT_ULong length = 0;
252 if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
253 return NS_ERROR_NOT_AVAILABLE;
254 }
255 if (!aBuffer.SetLength(length, fallible)) {
256 return NS_ERROR_OUT_OF_MEMORY;
257 }
258 if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
259 aBuffer.Clear();
260 return NS_ERROR_FAILURE;
261 }
262
263 return NS_OK;
264 }
265
266 void
MaybeReleaseFTFace()267 gfxSystemFcFontEntry::MaybeReleaseFTFace()
268 {
269 // don't release if either HB or Gr face still exists
270 if (mHBFace || mGrFace) {
271 return;
272 }
273 if (mFTFace) {
274 FT_Done_Face(mFTFace);
275 mFTFace = nullptr;
276 }
277 mFTFaceInitialized = false;
278 }
279
280 void
ForgetHBFace()281 gfxSystemFcFontEntry::ForgetHBFace()
282 {
283 gfxFontEntry::ForgetHBFace();
284 MaybeReleaseFTFace();
285 }
286
287 void
ReleaseGrFace(gr_face * aFace)288 gfxSystemFcFontEntry::ReleaseGrFace(gr_face* aFace)
289 {
290 gfxFontEntry::ReleaseGrFace(aFace);
291 MaybeReleaseFTFace();
292 }
293
294 // A namespace for @font-face family names in FcPatterns so that fontconfig
295 // aliases do not pick up families from @font-face rules and so that
296 // fontconfig rules can distinguish between web fonts and platform fonts.
297 // http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html
298 #define FONT_FACE_FAMILY_PREFIX "@font-face:"
299
300 /**
301 * gfxUserFcFontEntry:
302 *
303 * An abstract class for objects in a gfxUserFontSet that can provide
304 * FcPattern* handles to fonts.
305 *
306 * Separate implementations of this class support local fonts from src:local()
307 * and web fonts from src:url().
308 */
309
310 // There is a one-to-one correspondence between gfxUserFcFontEntry objects and
311 // @font-face rules, but sometimes a one-to-many correspondence between font
312 // entries and font patterns.
313 //
314 // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802#font-descriptions
315 // provided a font-size descriptor to specify the sizes supported by the face,
316 // but the "Editor's Draft 27 June 2008"
317 // http://dev.w3.org/csswg/css3-fonts/#font-resources does not provide such a
318 // descriptor, and Mozilla does not recognize such a descriptor.
319 //
320 // Font face names used in src:local() also do not usually specify a size.
321 //
322 // PCF format fonts have each size in a different file, and each of these
323 // files is referenced by its own pattern, but really these are each
324 // different sizes of one face with one name.
325 //
326 // Multiple patterns in an entry also effectively deals with a set of
327 // PostScript Type 1 font files that all have the same face name but are in
328 // several files because of the limit on the number of glyphs in a Type 1 font
329 // file. (e.g. Computer Modern.)
330
331 class gfxUserFcFontEntry : public gfxFcFontEntry {
332 protected:
gfxUserFcFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle)333 explicit gfxUserFcFontEntry(const nsAString& aFontName,
334 uint16_t aWeight,
335 int16_t aStretch,
336 uint8_t aStyle)
337 : gfxFcFontEntry(aFontName)
338 {
339 mStyle = aStyle;
340 mWeight = aWeight;
341 mStretch = aStretch;
342 }
343
344 // Helper function to change a pattern so that it matches the CSS style
345 // descriptors and so gets properly sorted in font selection. This also
346 // avoids synthetic style effects being added by the renderer when the
347 // style of the font itself does not match the descriptor provided by the
348 // author.
349 void AdjustPatternToCSS(FcPattern *aPattern);
350 };
351
352 void
AdjustPatternToCSS(FcPattern * aPattern)353 gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern)
354 {
355 int fontWeight = -1;
356 FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &fontWeight);
357 int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight / 100);
358 if (cssWeight != fontWeight) {
359 FcPatternDel(aPattern, FC_WEIGHT);
360 FcPatternAddInteger(aPattern, FC_WEIGHT, cssWeight);
361 }
362
363 int fontSlant;
364 FcResult res = FcPatternGetInteger(aPattern, FC_SLANT, 0, &fontSlant);
365 // gfxFontEntry doesn't understand the difference between oblique
366 // and italic.
367 if (res != FcResultMatch ||
368 IsItalic() != (fontSlant != FC_SLANT_ROMAN)) {
369 FcPatternDel(aPattern, FC_SLANT);
370 FcPatternAddInteger(aPattern, FC_SLANT,
371 IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN);
372 }
373
374 int fontWidth = -1;
375 FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth);
376 int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch);
377 if (cssWidth != fontWidth) {
378 FcPatternDel(aPattern, FC_WIDTH);
379 FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth);
380 }
381
382 // Ensure that there is a fullname property (if there is a family
383 // property) so that fontconfig rules can identify the real name of the
384 // font, because the family property will be replaced.
385 FcChar8 *unused;
386 if (FcPatternGetString(aPattern,
387 FC_FULLNAME, 0, &unused) == FcResultNoMatch) {
388 nsAutoCString fullname;
389 if (gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(aPattern,
390 &fullname)) {
391 FcPatternAddString(aPattern, FC_FULLNAME,
392 gfxFontconfigUtils::ToFcChar8(fullname));
393 }
394 }
395
396 nsAutoCString family;
397 family.Append(FONT_FACE_FAMILY_PREFIX);
398 AppendUTF16toUTF8(Name(), family);
399
400 FcPatternDel(aPattern, FC_FAMILY);
401 FcPatternDel(aPattern, FC_FAMILYLANG);
402 FcPatternAddString(aPattern, FC_FAMILY,
403 gfxFontconfigUtils::ToFcChar8(family));
404 }
405
406 /**
407 * gfxLocalFcFontEntry:
408 *
409 * An implementation of gfxUserFcFontEntry for local fonts from src:local().
410 *
411 * This class is used only in gfxUserFontSet and for providing FcPattern*
412 * handles to system fonts for font selection. gfxFcFonts created from these
413 * patterns will use gfxSystemFcFontEntrys, which may be shared with
414 * gfxFcFonts from regular family-name based font selection.
415 */
416
417 class gfxLocalFcFontEntry : public gfxUserFcFontEntry {
418 public:
gfxLocalFcFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const nsTArray<nsCountedRef<FcPattern>> & aPatterns)419 gfxLocalFcFontEntry(const nsAString& aFontName,
420 uint16_t aWeight,
421 int16_t aStretch,
422 uint8_t aStyle,
423 const nsTArray< nsCountedRef<FcPattern> >& aPatterns)
424 : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle)
425 {
426 if (!mPatterns.SetCapacity(aPatterns.Length(), fallible))
427 return; // OOM
428
429 for (uint32_t i = 0; i < aPatterns.Length(); ++i) {
430 FcPattern *pattern = FcPatternDuplicate(aPatterns.ElementAt(i));
431 if (!pattern)
432 return; // OOM
433
434 AdjustPatternToCSS(pattern);
435
436 // FIXME: Make this infallible after bug 968520 is done.
437 MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
438 mPatterns[i].own(pattern);
439 }
440 mIsLocalUserFont = true;
441 }
442 };
443
444 /**
445 * gfxDownloadedFcFontEntry:
446 *
447 * An implementation of gfxFcFontEntry for web fonts from src:url().
448 *
449 * When a cairo_font_face_t is created for these fonts, the cairo_font_face_t
450 * keeps a reference to the FontEntry to keep the font data alive.
451 */
452
453 class gfxDownloadedFcFontEntry : public gfxUserFcFontEntry {
454 public:
455 // This takes ownership of the face and its underlying data
gfxDownloadedFcFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const uint8_t * aData,FT_Face aFace)456 gfxDownloadedFcFontEntry(const nsAString& aFontName,
457 uint16_t aWeight,
458 int16_t aStretch,
459 uint8_t aStyle,
460 const uint8_t *aData, FT_Face aFace)
461 : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle),
462 mFontData(aData), mFace(aFace)
463 {
464 NS_PRECONDITION(aFace != nullptr, "aFace is NULL!");
465 mIsDataUserFont = true;
466 InitPattern();
467 }
468
469 virtual ~gfxDownloadedFcFontEntry();
470
471 // Returns true on success
472 bool SetCairoFace(cairo_font_face_t *aFace);
473
474 virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
475
476 protected:
477 void InitPattern();
478
479 // mFontData holds the data used to instantiate the FT_Face;
480 // this has to persist until we are finished with the face,
481 // then be released with free().
482 const uint8_t* mFontData;
483
484 FT_Face mFace;
485 };
486
487 // A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
488 static const char *kFontEntryFcProp = "-moz-font-entry";
489
AddDownloadedFontEntry(FcPattern * aPattern,gfxDownloadedFcFontEntry * aFontEntry)490 static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
491 gfxDownloadedFcFontEntry *aFontEntry)
492 {
493 FcValue value;
494 value.type = FcTypeFTFace; // void* field of union
495 value.u.f = aFontEntry;
496
497 return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse);
498 }
499
DelDownloadedFontEntry(FcPattern * aPattern)500 static FcBool DelDownloadedFontEntry(FcPattern *aPattern)
501 {
502 return FcPatternDel(aPattern, kFontEntryFcProp);
503 }
504
GetDownloadedFontEntry(FcPattern * aPattern)505 static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern)
506 {
507 FcValue value;
508 if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch)
509 return nullptr;
510
511 if (value.type != FcTypeFTFace) {
512 NS_NOTREACHED("Wrong type for -moz-font-entry font property");
513 return nullptr;
514 }
515
516 return static_cast<gfxDownloadedFcFontEntry*>(value.u.f);
517 }
518
~gfxDownloadedFcFontEntry()519 gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry()
520 {
521 if (mPatterns.Length() != 0) {
522 // Remove back reference to this font entry and the face in case
523 // anyone holds a reference to the pattern.
524 NS_ASSERTION(mPatterns.Length() == 1,
525 "More than one pattern in gfxDownloadedFcFontEntry!");
526 DelDownloadedFontEntry(mPatterns[0]);
527 FcPatternDel(mPatterns[0], FC_FT_FACE);
528 }
529 FT_Done_Face(mFace);
530 free((void*)mFontData);
531 }
532
533 typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
534 const FcChar8 *file, int id,
535 FcBlanks *blanks);
536
537 void
InitPattern()538 gfxDownloadedFcFontEntry::InitPattern()
539 {
540 static QueryFaceFunction sQueryFacePtr =
541 reinterpret_cast<QueryFaceFunction>
542 (FindFunctionSymbol("FcFreeTypeQueryFace"));
543 FcPattern *pattern;
544
545 // FcFreeTypeQueryFace is the same function used to construct patterns for
546 // system fonts and so is the preferred function to use for this purpose.
547 // This will set up the langset property, which helps with sorting, and
548 // the foundry, fullname, and fontversion properties, which properly
549 // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is
550 // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has
551 // fontconfig-2.4.1.)
552 if (sQueryFacePtr) {
553 // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
554 // least). The dummy file passed here is removed below.
555 //
556 // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
557 // is passed as the "blanks" argument, which provides that unexpectedly
558 // blank glyphs are elided. Here, however, we pass nullptr for
559 // "blanks", effectively assuming that, if the font has a blank glyph,
560 // then the author intends any associated character to be rendered
561 // blank.
562 pattern =
563 (*sQueryFacePtr)(mFace,
564 gfxFontconfigUtils::ToFcChar8(""),
565 0,
566 nullptr);
567 if (!pattern)
568 // Either OOM, or fontconfig chose to skip this font because it
569 // has "no encoded characters", which I think means "BDF and PCF
570 // fonts which are not in Unicode (or the effectively equivalent
571 // ISO Latin-1) encoding".
572 return;
573
574 // These properties don't make sense for this face without a file.
575 FcPatternDel(pattern, FC_FILE);
576 FcPatternDel(pattern, FC_INDEX);
577
578 } else {
579 // Do the minimum necessary to construct a pattern for sorting.
580
581 // FC_CHARSET is vital to determine which characters are supported.
582 nsAutoRef<FcCharSet> charset(FcFreeTypeCharSet(mFace, nullptr));
583 // If there are no characters then assume we don't know how to read
584 // this font.
585 if (!charset || FcCharSetCount(charset) == 0)
586 return;
587
588 pattern = FcPatternCreate();
589 FcPatternAddCharSet(pattern, FC_CHARSET, charset);
590
591 // FC_PIXEL_SIZE can be important for font selection of fixed-size
592 // fonts.
593 if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) {
594 for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) {
595 #if HAVE_FT_BITMAP_SIZE_Y_PPEM
596 double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem);
597 #else
598 double size = mFace->available_sizes[i].height;
599 #endif
600 FcPatternAddDouble (pattern, FC_PIXEL_SIZE, size);
601 }
602
603 // Not sure whether this is important;
604 // imitating FcFreeTypeQueryFace:
605 FcPatternAddBool (pattern, FC_ANTIALIAS, FcFalse);
606 }
607
608 // Setting up the FC_LANGSET property is very difficult with the APIs
609 // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET
610 // property seems better than having a property with an empty LangSet.
611 // With no FC_LANGSET property, fontconfig sort functions will
612 // consider this face to have the same priority as (otherwise equal)
613 // faces that have support for the primary requested language, but
614 // will not consider any language to have been satisfied (and so will
615 // continue to look for a face with language support in fallback
616 // fonts).
617 }
618
619 AdjustPatternToCSS(pattern);
620
621 FcPatternAddFTFace(pattern, FC_FT_FACE, mFace);
622 AddDownloadedFontEntry(pattern, this);
623
624 // There is never more than one pattern
625 // FIXME: Make this infallible after bug 968520 is done.
626 MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
627 mPatterns[0].own(pattern);
628 }
629
ReleaseDownloadedFontEntry(void * data)630 static void ReleaseDownloadedFontEntry(void *data)
631 {
632 gfxDownloadedFcFontEntry *downloadedFontEntry =
633 static_cast<gfxDownloadedFcFontEntry*>(data);
634 NS_RELEASE(downloadedFontEntry);
635 }
636
SetCairoFace(cairo_font_face_t * aFace)637 bool gfxDownloadedFcFontEntry::SetCairoFace(cairo_font_face_t *aFace)
638 {
639 if (CAIRO_STATUS_SUCCESS !=
640 cairo_font_face_set_user_data(aFace, &sFontEntryKey, this,
641 ReleaseDownloadedFontEntry))
642 return false;
643
644 // Hold a reference to this font entry to keep the font face data.
645 NS_ADDREF(this);
646 return true;
647 }
648
649 hb_blob_t *
GetFontTable(uint32_t aTableTag)650 gfxDownloadedFcFontEntry::GetFontTable(uint32_t aTableTag)
651 {
652 // The entry already owns the (sanitized) sfnt data in mFontData,
653 // so we can just return a blob that "wraps" the appropriate chunk of it.
654 // The blob should not attempt to free its data, as the entire sfnt data
655 // will be freed when the font entry is deleted.
656 return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
657 }
658
659 /*
660 * gfxFcFont
661 *
662 * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
663 * cairo_scaled_font created from an FcPattern.
664 */
665
666 class gfxFcFont : public gfxFontconfigFontBase {
667 public:
668 virtual ~gfxFcFont();
669 static already_AddRefed<gfxFcFont>
670 GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
671 const gfxFontStyle *aFontStyle);
672
673 // return a cloned font resized and offset to simulate sub/superscript glyphs
674 virtual already_AddRefed<gfxFont>
675 GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) override;
676
677 protected:
678 virtual already_AddRefed<gfxFont> MakeScaledFont(gfxFontStyle *aFontStyle,
679 gfxFloat aFontScale);
680 virtual already_AddRefed<gfxFont> GetSmallCapsFont() override;
681
682 private:
683 gfxFcFont(cairo_scaled_font_t *aCairoFont,
684 FcPattern *aPattern,
685 gfxFcFontEntry *aFontEntry,
686 const gfxFontStyle *aFontStyle);
687
688 // key for locating a gfxFcFont corresponding to a cairo_scaled_font
689 static cairo_user_data_key_t sGfxFontKey;
690 };
691
692 /**
693 * gfxFcFontSet:
694 *
695 * Translation from a desired FcPattern to a sorted set of font references
696 * (fontconfig cache data) and (when needed) fonts.
697 */
698
699 class gfxFcFontSet final {
700 public:
701 NS_INLINE_DECL_REFCOUNTING(gfxFcFontSet)
702
gfxFcFontSet(FcPattern * aPattern,gfxUserFontSet * aUserFontSet)703 explicit gfxFcFontSet(FcPattern *aPattern,
704 gfxUserFontSet *aUserFontSet)
705 : mSortPattern(aPattern), mUserFontSet(aUserFontSet),
706 mFcFontsTrimmed(0),
707 mHaveFallbackFonts(false)
708 {
709 bool waitForUserFont;
710 mFcFontSet = SortPreferredFonts(waitForUserFont);
711 mWaitingForUserFont = waitForUserFont;
712 }
713
714 // A reference is held by the FontSet.
715 // The caller may add a ref to keep the font alive longer than the FontSet.
GetFontAt(uint32_t i,const gfxFontStyle * aFontStyle)716 gfxFcFont *GetFontAt(uint32_t i, const gfxFontStyle *aFontStyle)
717 {
718 if (i >= mFonts.Length() || !mFonts[i].mFont) {
719 // GetFontPatternAt sets up mFonts
720 FcPattern *fontPattern = GetFontPatternAt(i);
721 if (!fontPattern)
722 return nullptr;
723
724 mFonts[i].mFont =
725 gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern,
726 aFontStyle);
727 }
728 return mFonts[i].mFont;
729 }
730
731 FcPattern *GetFontPatternAt(uint32_t i);
732
WaitingForUserFont() const733 bool WaitingForUserFont() const {
734 return mWaitingForUserFont;
735 }
736
737 private:
738 // Private destructor, to discourage deletion outside of Release():
~gfxFcFontSet()739 ~gfxFcFontSet()
740 {
741 }
742
743 nsReturnRef<FcFontSet> SortPreferredFonts(bool& aWaitForUserFont);
744 nsReturnRef<FcFontSet> SortFallbackFonts();
745
746 struct FontEntry {
FontEntrygfxFcFontSet::FontEntry747 explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
748 nsCountedRef<FcPattern> mPattern;
749 RefPtr<gfxFcFont> mFont;
750 };
751
752 struct LangSupportEntry {
LangSupportEntrygfxFcFontSet::LangSupportEntry753 LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
754 mLang(aLang), mBestSupport(aSupport) {}
755 FcChar8 *mLang;
756 FcLangResult mBestSupport;
757 };
758
759 public:
760 // public for nsTArray
761 class LangComparator {
762 public:
Equals(const LangSupportEntry & a,const FcChar8 * b) const763 bool Equals(const LangSupportEntry& a, const FcChar8 *b) const
764 {
765 return FcStrCmpIgnoreCase(a.mLang, b) == 0;
766 }
767 };
768
769 private:
770 // The requested pattern
771 nsCountedRef<FcPattern> mSortPattern;
772 // Fonts from @font-face rules
773 RefPtr<gfxUserFontSet> mUserFontSet;
774 // A (trimmed) list of font patterns and fonts that is built up as
775 // required.
776 nsTArray<FontEntry> mFonts;
777 // Holds a list of font patterns that will be trimmed. This is first set
778 // to a list of preferred fonts. Then, if/when all the preferred fonts
779 // have been trimmed and added to mFonts, this is set to a list of
780 // fallback fonts.
781 nsAutoRef<FcFontSet> mFcFontSet;
782 // The set of characters supported by the fonts in mFonts.
783 nsAutoRef<FcCharSet> mCharSet;
784 // The index of the next font in mFcFontSet that has not yet been
785 // considered for mFonts.
786 int mFcFontsTrimmed;
787 // True iff fallback fonts are either stored in mFcFontSet or have been
788 // trimmed and added to mFonts (so that mFcFontSet is nullptr).
789 bool mHaveFallbackFonts;
790 // True iff there was a user font set with pending downloads,
791 // so the set may be updated when downloads complete
792 bool mWaitingForUserFont;
793 };
794
795 // Find the FcPattern for an @font-face font suitable for CSS family |aFamily|
796 // and style |aStyle| properties.
797 static const nsTArray< nsCountedRef<FcPattern> >*
FindFontPatterns(gfxUserFontSet * mUserFontSet,const nsACString & aFamily,uint8_t aStyle,uint16_t aWeight,int16_t aStretch,bool & aWaitForUserFont)798 FindFontPatterns(gfxUserFontSet *mUserFontSet,
799 const nsACString &aFamily, uint8_t aStyle,
800 uint16_t aWeight, int16_t aStretch,
801 bool& aWaitForUserFont)
802 {
803 // Convert to UTF16
804 NS_ConvertUTF8toUTF16 utf16Family(aFamily);
805
806 // needsBold is not used here. Instead synthetic bold is enabled through
807 // FcFontRenderPrepare when the weight in the requested pattern is
808 // compared against the weight in the font pattern.
809 bool needsBold;
810
811 gfxFontStyle style;
812 style.style = aStyle;
813 style.weight = aWeight;
814 style.stretch = aStretch;
815
816 gfxUserFcFontEntry *fontEntry = nullptr;
817 gfxFontFamily *family = mUserFontSet->LookupFamily(utf16Family);
818 if (family) {
819 gfxUserFontEntry* userFontEntry =
820 mUserFontSet->FindUserFontEntryAndLoad(family, style, needsBold,
821 aWaitForUserFont);
822 if (userFontEntry) {
823 fontEntry = static_cast<gfxUserFcFontEntry*>
824 (userFontEntry->GetPlatformFontEntry());
825 }
826
827 // Accept synthetic oblique for italic and oblique.
828 // xxx - this isn't really ideal behavior, for docs that only use a
829 // single italic face it will also pull down the normal face
830 // and probably never use it
831 if (!fontEntry && aStyle != NS_FONT_STYLE_NORMAL) {
832 style.style = NS_FONT_STYLE_NORMAL;
833 userFontEntry =
834 mUserFontSet->FindUserFontEntryAndLoad(family, style,
835 needsBold,
836 aWaitForUserFont);
837 if (userFontEntry) {
838 fontEntry = static_cast<gfxUserFcFontEntry*>
839 (userFontEntry->GetPlatformFontEntry());
840 }
841 }
842 }
843
844 if (!fontEntry) {
845 return nullptr;
846 }
847
848 return &fontEntry->GetPatterns();
849 }
850
851 typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
852 int id);
853
854 // FcPatternRemove is available in fontconfig-2.3.0 (2005)
855 static FcBool
moz_FcPatternRemove(FcPattern * p,const char * object,int id)856 moz_FcPatternRemove(FcPattern *p, const char *object, int id)
857 {
858 static FcPatternRemoveFunction sFcPatternRemovePtr =
859 reinterpret_cast<FcPatternRemoveFunction>
860 (FindFunctionSymbol("FcPatternRemove"));
861
862 if (!sFcPatternRemovePtr)
863 return FcFalse;
864
865 return (*sFcPatternRemovePtr)(p, object, id);
866 }
867
868 // fontconfig prefers a matching family or lang to pixelsize of bitmap
869 // fonts. CSS suggests a tolerance of 20% on pixelsize.
870 static bool
SizeIsAcceptable(FcPattern * aFont,double aRequestedSize)871 SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
872 {
873 double size;
874 int v = 0;
875 while (FcPatternGetDouble(aFont,
876 FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
877 ++v;
878 if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
879 return true;
880 }
881
882 // No size means scalable
883 return v == 0;
884 }
885
886 // Sorting only the preferred fonts first usually saves having to sort through
887 // every font on the system.
888 nsReturnRef<FcFontSet>
SortPreferredFonts(bool & aWaitForUserFont)889 gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
890 {
891 aWaitForUserFont = false;
892
893 gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
894 if (!utils)
895 return nsReturnRef<FcFontSet>();
896
897 // The list of families in mSortPattern has values with both weak and
898 // strong bindings. Values with strong bindings should be preferred.
899 // Values with weak bindings are default fonts that should be considered
900 // only when the font provides the best support for a requested language
901 // or after other fonts have satisfied all the requested languages.
902 //
903 // There are no direct fontconfig APIs to get the binding type. The
904 // binding only takes effect in the sort and match functions.
905
906 // |requiredLangs| is a list of requested languages that have not yet been
907 // satisfied. gfxFontconfigUtils only sets one FC_LANG property value,
908 // but FcConfigSubstitute may add more values (e.g. prepending "en" to
909 // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
910 // text.)
911 AutoTArray<LangSupportEntry,10> requiredLangs;
912 for (int v = 0; ; ++v) {
913 FcChar8 *lang;
914 FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
915 if (result != FcResultMatch) {
916 // No need to check FcPatternGetLangSet() because
917 // gfxFontconfigUtils sets only a string value for FC_LANG and
918 // FcConfigSubstitute cannot add LangSets.
919 NS_ASSERTION(result != FcResultTypeMismatch,
920 "Expected a string for FC_LANG");
921 break;
922 }
923
924 if (!requiredLangs.Contains(lang, LangComparator())) {
925 FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
926 if (bestLangSupport != FcLangDifferentLang) {
927 requiredLangs.
928 AppendElement(LangSupportEntry(lang, bestLangSupport));
929 }
930 }
931 }
932
933 nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
934 if (!fontSet)
935 return fontSet.out();
936
937 // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
938 // doesn't happen, Roman will be used.
939 int requestedSlant = FC_SLANT_ROMAN;
940 FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
941 double requestedSize = -1.0;
942 FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
943
944 nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies(32);
945 FcChar8 *family;
946 for (int v = 0;
947 FcPatternGetString(mSortPattern,
948 FC_FAMILY, v, &family) == FcResultMatch; ++v) {
949 const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nullptr;
950
951 // Is this an @font-face family?
952 bool isUserFont = false;
953 if (mUserFontSet) {
954 // Have some @font-face definitions
955
956 nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family));
957 NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX);
958
959 if (StringBeginsWith(cFamily, userPrefix)) {
960 isUserFont = true;
961
962 // Trim off the prefix
963 nsDependentCSubstring cssFamily(cFamily, userPrefix.Length());
964
965 uint8_t thebesStyle =
966 gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant);
967 uint16_t thebesWeight =
968 gfxFontconfigUtils::GetThebesWeight(mSortPattern);
969 int16_t thebesStretch =
970 gfxFontconfigUtils::GetThebesStretch(mSortPattern);
971
972 bool waitForUserFont;
973 familyFonts = FindFontPatterns(mUserFontSet, cssFamily,
974 thebesStyle,
975 thebesWeight, thebesStretch,
976 waitForUserFont);
977 if (waitForUserFont) {
978 aWaitForUserFont = true;
979 }
980 }
981 }
982
983 if (!isUserFont) {
984 familyFonts = &utils->GetFontsForFamily(family);
985 }
986
987 if (!familyFonts || familyFonts->Length() == 0) {
988 // There are no fonts matching this family, so there is no point
989 // in searching for this family in the FontSort.
990 //
991 // Perhaps the original pattern should be retained for
992 // FcFontRenderPrepare. However, the only a useful config
993 // substitution test against missing families that i can imagine
994 // would only be interested in the preferred family
995 // (qual="first"), so always keep the first family and use the
996 // same pattern for Sort and RenderPrepare.
997 if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
998 --v;
999 }
1000 continue;
1001 }
1002
1003 // Aliases seem to often end up occurring more than once, but
1004 // duplicate families can't be removed from the sort pattern without
1005 // knowing whether duplicates have the same binding.
1006 gfxFontconfigUtils::DepFcStrEntry *familyEntry =
1007 existingFamilies.PutEntry(family);
1008 if (familyEntry) {
1009 if (familyEntry->mKey) // old entry
1010 continue;
1011
1012 familyEntry->mKey = family; // initialize new entry
1013 }
1014
1015 for (uint32_t f = 0; f < familyFonts->Length(); ++f) {
1016 FcPattern *font = familyFonts->ElementAt(f);
1017
1018 // Fix up the family name of user-font patterns, as the same
1019 // font entry may be used (via the UserFontCache) for multiple
1020 // CSS family names
1021 if (isUserFont) {
1022 font = FcPatternDuplicate(font);
1023 FcPatternDel(font, FC_FAMILY);
1024 FcPatternAddString(font, FC_FAMILY, family);
1025 }
1026
1027 // User fonts are already filtered by slant (but not size) in
1028 // mUserFontSet->FindUserFontEntry().
1029 if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1030 continue;
1031
1032 for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
1033 const LangSupportEntry& langEntry = requiredLangs[r];
1034 FcLangResult support =
1035 gfxFontconfigUtils::GetLangSupport(font, langEntry.mLang);
1036 if (support <= langEntry.mBestSupport) { // lower is better
1037 requiredLangs.RemoveElementAt(r);
1038 --r;
1039 }
1040 }
1041
1042 // FcFontSetDestroy will remove a reference but FcFontSetAdd
1043 // does _not_ take a reference!
1044 if (FcFontSetAdd(fontSet, font)) {
1045 // We don't add a reference here for user fonts, because we're
1046 // using a local clone of the pattern (see above) in order to
1047 // override the family name
1048 if (!isUserFont) {
1049 FcPatternReference(font);
1050 }
1051 }
1052 }
1053 }
1054
1055 FcPattern *truncateMarker = nullptr;
1056 for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
1057 const nsTArray< nsCountedRef<FcPattern> >& langFonts =
1058 utils->GetFontsForLang(requiredLangs[r].mLang);
1059
1060 bool haveLangFont = false;
1061 for (uint32_t f = 0; f < langFonts.Length(); ++f) {
1062 FcPattern *font = langFonts[f];
1063 if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1064 continue;
1065
1066 haveLangFont = true;
1067 if (FcFontSetAdd(fontSet, font)) {
1068 FcPatternReference(font);
1069 }
1070 }
1071
1072 if (!haveLangFont && langFonts.Length() > 0) {
1073 // There is a font that supports this language but it didn't pass
1074 // the slant and size criteria. Weak default font families should
1075 // not be considered until the language has been satisfied.
1076 //
1077 // Insert a font that supports the language so that it will mark
1078 // the position of fonts from weak families in the sorted set and
1079 // they can be removed. The language and weak families will be
1080 // considered in the fallback fonts, which use fontconfig's
1081 // algorithm.
1082 //
1083 // Of the fonts that don't meet slant and size criteria, strong
1084 // default font families should be considered before (other) fonts
1085 // for this language, so this marker font will be removed (as well
1086 // as the fonts from weak families), and strong families will be
1087 // reconsidered in the fallback fonts.
1088 FcPattern *font = langFonts[0];
1089 if (FcFontSetAdd(fontSet, font)) {
1090 FcPatternReference(font);
1091 truncateMarker = font;
1092 }
1093 break;
1094 }
1095 }
1096
1097 FcFontSet *sets[1] = { fontSet };
1098 FcResult result;
1099 #ifdef SOLARIS
1100 // Get around a crash of FcFontSetSort when FcConfig is nullptr
1101 // Solaris's FcFontSetSort needs an FcConfig (bug 474758)
1102 fontSet.own(FcFontSetSort(FcConfigGetCurrent(), sets, 1, mSortPattern,
1103 FcFalse, nullptr, &result));
1104 #else
1105 fontSet.own(FcFontSetSort(nullptr, sets, 1, mSortPattern,
1106 FcFalse, nullptr, &result));
1107 #endif
1108
1109 if (truncateMarker != nullptr && fontSet) {
1110 nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
1111
1112 for (int f = 0; f < fontSet->nfont; ++f) {
1113 FcPattern *font = fontSet->fonts[f];
1114 if (font == truncateMarker)
1115 break;
1116
1117 if (FcFontSetAdd(truncatedSet, font)) {
1118 FcPatternReference(font);
1119 }
1120 }
1121
1122 fontSet.steal(truncatedSet);
1123 }
1124
1125 return fontSet.out();
1126 }
1127
1128 nsReturnRef<FcFontSet>
SortFallbackFonts()1129 gfxFcFontSet::SortFallbackFonts()
1130 {
1131 // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
1132 // but would take much longer due to comparing all the character sets.
1133 //
1134 // The references to fonts in this FcFontSet are almost free
1135 // as they are pointers into mmaped cache files.
1136 //
1137 // GetFontPatternAt() will trim lazily if and as needed, which will also
1138 // remove duplicates of preferred fonts.
1139 FcResult result;
1140 return nsReturnRef<FcFontSet>(FcFontSort(nullptr, mSortPattern,
1141 FcFalse, nullptr, &result));
1142 }
1143
1144 // GetFontAt relies on this setting up all patterns up to |i|.
1145 FcPattern *
GetFontPatternAt(uint32_t i)1146 gfxFcFontSet::GetFontPatternAt(uint32_t i)
1147 {
1148 while (i >= mFonts.Length()) {
1149 while (!mFcFontSet) {
1150 if (mHaveFallbackFonts)
1151 return nullptr;
1152
1153 mFcFontSet = SortFallbackFonts();
1154 mHaveFallbackFonts = true;
1155 mFcFontsTrimmed = 0;
1156 // Loop to test that mFcFontSet is non-nullptr.
1157 }
1158
1159 while (mFcFontsTrimmed < mFcFontSet->nfont) {
1160 FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
1161 ++mFcFontsTrimmed;
1162
1163 if (mFonts.Length() != 0) {
1164 // See if the next font provides support for any extra
1165 // characters. Most often the next font is not going to
1166 // support more characters so check for a SubSet first before
1167 // allocating a new CharSet with Union.
1168 FcCharSet *supportedChars = mCharSet;
1169 if (!supportedChars) {
1170 FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
1171 FC_CHARSET, 0, &supportedChars);
1172 }
1173
1174 if (supportedChars) {
1175 FcCharSet *newChars = nullptr;
1176 FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
1177 if (newChars) {
1178 if (FcCharSetIsSubset(newChars, supportedChars))
1179 continue;
1180
1181 mCharSet.own(FcCharSetUnion(supportedChars, newChars));
1182 } else if (!mCharSet) {
1183 mCharSet.own(FcCharSetCopy(supportedChars));
1184 }
1185 }
1186 }
1187
1188 mFonts.AppendElement(font);
1189 if (mFonts.Length() >= i)
1190 break;
1191 }
1192
1193 if (mFcFontsTrimmed == mFcFontSet->nfont) {
1194 // finished with this font set
1195 mFcFontSet.reset();
1196 }
1197 }
1198
1199 return mFonts[i].mPattern;
1200 }
1201
1202 #ifdef MOZ_WIDGET_GTK
1203 static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
1204 #endif
1205
1206 // Apply user settings and defaults to pattern in preparation for matching.
1207 static void
PrepareSortPattern(FcPattern * aPattern,double aFallbackSize,double aSizeAdjustFactor,bool aIsPrinterFont)1208 PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
1209 double aSizeAdjustFactor, bool aIsPrinterFont)
1210 {
1211 FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
1212
1213 // This gets cairo_font_options_t for the Screen. We should have
1214 // different font options for printing (no hinting) but we are not told
1215 // what we are measuring for.
1216 //
1217 // If cairo adds support for lcd_filter, gdk will not provide the default
1218 // setting for that option. We could get the default setting by creating
1219 // an xlib surface once, recording its font_options, and then merging the
1220 // gdk options.
1221 //
1222 // Using an xlib surface would also be an option to get Screen font
1223 // options for non-GTK X11 toolkits, but less efficient than using GDK to
1224 // pick up dynamic changes.
1225 if(aIsPrinterFont) {
1226 cairo_font_options_t *options = cairo_font_options_create();
1227 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
1228 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
1229 cairo_ft_font_options_substitute(options, aPattern);
1230 cairo_font_options_destroy(options);
1231 FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
1232 } else {
1233 #ifdef MOZ_WIDGET_GTK
1234 ApplyGdkScreenFontOptions(aPattern);
1235 #endif
1236 }
1237
1238 // Protect against any fontconfig settings that may have incorrectly
1239 // modified the pixelsize, and consider aSizeAdjustFactor.
1240 double size = aFallbackSize;
1241 if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
1242 || aSizeAdjustFactor != 1.0) {
1243 FcPatternDel(aPattern, FC_PIXEL_SIZE);
1244 FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
1245 }
1246
1247 FcDefaultSubstitute(aPattern);
1248 }
1249
1250 /**
1251 ** gfxPangoFontGroup
1252 **/
1253
gfxPangoFontGroup(const FontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)1254 gfxPangoFontGroup::gfxPangoFontGroup(const FontFamilyList& aFontFamilyList,
1255 const gfxFontStyle *aStyle,
1256 gfxUserFontSet *aUserFontSet,
1257 gfxFloat aDevToCssSize)
1258 : gfxFontGroup(aFontFamilyList, aStyle, nullptr, aUserFontSet, aDevToCssSize),
1259 mPangoLanguage(GuessPangoLanguage(aStyle->language))
1260 {
1261 // This language is passed to the font for shaping.
1262 // Shaping doesn't know about lang groups so make it a real language.
1263 if (mPangoLanguage) {
1264 mStyle.language = NS_Atomize(pango_language_to_string(mPangoLanguage));
1265 }
1266
1267 // dummy entry, will be replaced when actually needed
1268 mFonts.AppendElement(FamilyFace());
1269 mSkipUpdateUserFonts = true;
1270 }
1271
~gfxPangoFontGroup()1272 gfxPangoFontGroup::~gfxPangoFontGroup()
1273 {
1274 }
1275
1276 gfxFontGroup *
Copy(const gfxFontStyle * aStyle)1277 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
1278 {
1279 return new gfxPangoFontGroup(mFamilyList, aStyle, mUserFontSet, mDevToCssSize);
1280 }
1281
1282 void
FindGenericFontsPFG(FontFamilyType aGenericType,nsIAtom * aLanguage,void * aClosure)1283 gfxPangoFontGroup::FindGenericFontsPFG(FontFamilyType aGenericType,
1284 nsIAtom *aLanguage,
1285 void *aClosure)
1286 {
1287 AutoTArray<nsString, 5> resolvedGenerics;
1288 ResolveGenericFontNamesPFG(aGenericType, aLanguage, resolvedGenerics);
1289 uint32_t g = 0, numGenerics = resolvedGenerics.Length();
1290 for (g = 0; g < numGenerics; g++) {
1291 FindPlatformFontPFG(resolvedGenerics[g], false, aClosure);
1292 }
1293 }
1294
1295 /* static */ void
ResolveGenericFontNamesPFG(FontFamilyType aGenericType,nsIAtom * aLanguage,nsTArray<nsString> & aGenericFamilies)1296 gfxPangoFontGroup::ResolveGenericFontNamesPFG(FontFamilyType aGenericType,
1297 nsIAtom *aLanguage,
1298 nsTArray<nsString>& aGenericFamilies)
1299 {
1300 static const char kGeneric_serif[] = "serif";
1301 static const char kGeneric_sans_serif[] = "sans-serif";
1302 static const char kGeneric_monospace[] = "monospace";
1303 static const char kGeneric_cursive[] = "cursive";
1304 static const char kGeneric_fantasy[] = "fantasy";
1305
1306 // treat -moz-fixed as monospace
1307 if (aGenericType == eFamily_moz_fixed) {
1308 aGenericType = eFamily_monospace;
1309 }
1310
1311 // type should be standard generic type at this point
1312 NS_ASSERTION(aGenericType >= eFamily_serif &&
1313 aGenericType <= eFamily_fantasy,
1314 "standard generic font family type required");
1315
1316 // create the lang string
1317 nsIAtom *langGroupAtom = nullptr;
1318 nsAutoCString langGroupString;
1319 if (aLanguage) {
1320 if (!gLangService) {
1321 CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
1322 }
1323 if (gLangService) {
1324 nsresult rv;
1325 langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
1326 }
1327 }
1328 if (!langGroupAtom) {
1329 langGroupAtom = nsGkAtoms::Unicode;
1330 }
1331 langGroupAtom->ToUTF8String(langGroupString);
1332
1333 // map generic type to string
1334 const char *generic = nullptr;
1335 switch (aGenericType) {
1336 case eFamily_serif:
1337 generic = kGeneric_serif;
1338 break;
1339 case eFamily_sans_serif:
1340 generic = kGeneric_sans_serif;
1341 break;
1342 case eFamily_monospace:
1343 generic = kGeneric_monospace;
1344 break;
1345 case eFamily_cursive:
1346 generic = kGeneric_cursive;
1347 break;
1348 case eFamily_fantasy:
1349 generic = kGeneric_fantasy;
1350 break;
1351 default:
1352 break;
1353 }
1354
1355 if (!generic) {
1356 return;
1357 }
1358
1359 aGenericFamilies.Clear();
1360
1361 // load family for "font.name.generic.lang"
1362 nsAutoCString prefFontName("font.name.");
1363 prefFontName.Append(generic);
1364 prefFontName.Append('.');
1365 prefFontName.Append(langGroupString);
1366 gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
1367 aGenericFamilies);
1368
1369 // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
1370 if (!aGenericFamilies.IsEmpty()) {
1371 nsAutoCString prefFontListName("font.name-list.");
1372 prefFontListName.Append(generic);
1373 prefFontListName.Append('.');
1374 prefFontListName.Append(langGroupString);
1375 gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
1376 aGenericFamilies);
1377 }
1378
1379 #if 0 // dump out generic mappings
1380 printf("%s ===> ", prefFontName.get());
1381 for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
1382 if (k > 0) printf(", ");
1383 printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
1384 }
1385 printf("\n");
1386 #endif
1387 }
1388
EnumerateFontListPFG(nsIAtom * aLanguage,void * aClosure)1389 void gfxPangoFontGroup::EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure)
1390 {
1391 // initialize fonts in the font family list
1392 const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
1393
1394 // lookup fonts in the fontlist
1395 uint32_t i, numFonts = fontlist.Length();
1396 for (i = 0; i < numFonts; i++) {
1397 const FontFamilyName& name = fontlist[i];
1398 if (name.IsNamed()) {
1399 FindPlatformFontPFG(name.mName, true, aClosure);
1400 } else {
1401 FindGenericFontsPFG(name.mType, aLanguage, aClosure);
1402 }
1403 }
1404
1405 // if necessary, append default generic onto the end
1406 if (mFamilyList.GetDefaultFontType() != eFamily_none &&
1407 !mFamilyList.HasDefaultGeneric()) {
1408 FindGenericFontsPFG(mFamilyList.GetDefaultFontType(),
1409 aLanguage, aClosure);
1410 }
1411 }
1412
1413 void
FindPlatformFontPFG(const nsAString & fontName,bool aUseFontSet,void * aClosure)1414 gfxPangoFontGroup::FindPlatformFontPFG(const nsAString& fontName,
1415 bool aUseFontSet,
1416 void *aClosure)
1417 {
1418 nsTArray<nsString> *list = static_cast<nsTArray<nsString>*>(aClosure);
1419
1420 if (!list->Contains(fontName)) {
1421 // names present in the user fontset are not matched against system fonts
1422 if (aUseFontSet && mUserFontSet && mUserFontSet->HasFamily(fontName)) {
1423 nsAutoString userFontName =
1424 NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
1425 list->AppendElement(userFontName);
1426 } else {
1427 list->AppendElement(fontName);
1428 }
1429 }
1430 }
1431
1432 gfxFcFont *
GetBaseFont()1433 gfxPangoFontGroup::GetBaseFont()
1434 {
1435 if (mFonts[0].Font() == nullptr) {
1436 gfxFont* font = GetBaseFontSet()->GetFontAt(0, GetStyle());
1437 mFonts[0] = FamilyFace(nullptr, font);
1438 }
1439
1440 return static_cast<gfxFcFont*>(mFonts[0].Font());
1441 }
1442
1443 gfxFont*
GetFirstValidFont(uint32_t aCh)1444 gfxPangoFontGroup::GetFirstValidFont(uint32_t aCh)
1445 {
1446 return GetFontAt(0);
1447 }
1448
1449 gfxFont *
GetFontAt(int32_t i,uint32_t aCh)1450 gfxPangoFontGroup::GetFontAt(int32_t i, uint32_t aCh)
1451 {
1452 // If it turns out to be hard for all clients that cache font
1453 // groups to call UpdateUserFonts at appropriate times, we could
1454 // instead consider just calling UpdateUserFonts from someplace
1455 // more central (such as here).
1456 NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
1457 "Whoever was caching this font group should have "
1458 "called UpdateUserFonts on it");
1459
1460 NS_PRECONDITION(i == 0, "Only have one font");
1461
1462 return GetBaseFont();
1463 }
1464
1465 void
UpdateUserFonts()1466 gfxPangoFontGroup::UpdateUserFonts()
1467 {
1468 uint64_t newGeneration = GetGeneration();
1469 if (newGeneration == mCurrGeneration)
1470 return;
1471
1472 mFonts[0] = FamilyFace();
1473 mFontSets.Clear();
1474 ClearCachedData();
1475 mCurrGeneration = newGeneration;
1476 }
1477
1478 already_AddRefed<gfxFcFontSet>
MakeFontSet(PangoLanguage * aLang,gfxFloat aSizeAdjustFactor,nsAutoRef<FcPattern> * aMatchPattern)1479 gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
1480 nsAutoRef<FcPattern> *aMatchPattern)
1481 {
1482 const char *lang = pango_language_to_string(aLang);
1483
1484 RefPtr<nsIAtom> langGroup;
1485 if (aLang != mPangoLanguage) {
1486 // Set up langGroup for Mozilla's font prefs.
1487 langGroup = NS_Atomize(lang);
1488 }
1489
1490 AutoTArray<nsString, 20> fcFamilyList;
1491 EnumerateFontListPFG(langGroup ? langGroup.get() : mStyle.language.get(),
1492 &fcFamilyList);
1493
1494 // To consider: A fontset cache here could be helpful.
1495
1496 // Get a pattern suitable for matching.
1497 nsAutoRef<FcPattern> pattern
1498 (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
1499
1500 PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
1501
1502 RefPtr<gfxFcFontSet> fontset =
1503 new gfxFcFontSet(pattern, mUserFontSet);
1504
1505 mSkipDrawing = fontset->WaitingForUserFont();
1506
1507 if (aMatchPattern)
1508 aMatchPattern->steal(pattern);
1509
1510 return fontset.forget();
1511 }
1512
1513 gfxPangoFontGroup::
FontSetByLangEntry(PangoLanguage * aLang,gfxFcFontSet * aFontSet)1514 FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
1515 gfxFcFontSet *aFontSet)
1516 : mLang(aLang), mFontSet(aFontSet)
1517 {
1518 }
1519
1520 gfxFcFontSet *
GetFontSet(PangoLanguage * aLang)1521 gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
1522 {
1523 GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
1524
1525 if (!aLang)
1526 return mFontSets[0].mFontSet;
1527
1528 for (uint32_t i = 0; i < mFontSets.Length(); ++i) {
1529 if (mFontSets[i].mLang == aLang)
1530 return mFontSets[i].mFontSet;
1531 }
1532
1533 RefPtr<gfxFcFontSet> fontSet =
1534 MakeFontSet(aLang, mSizeAdjustFactor);
1535 mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
1536
1537 return fontSet;
1538 }
1539
1540 already_AddRefed<gfxFont>
FindFontForChar(uint32_t aCh,uint32_t aPrevCh,uint32_t aNextCh,Script aRunScript,gfxFont * aPrevMatchedFont,uint8_t * aMatchType)1541 gfxPangoFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
1542 uint32_t aNextCh, Script aRunScript,
1543 gfxFont *aPrevMatchedFont,
1544 uint8_t *aMatchType)
1545 {
1546 if (aPrevMatchedFont) {
1547 // Don't switch fonts for control characters, regardless of
1548 // whether they are present in the current font, as they won't
1549 // actually be rendered (see bug 716229)
1550 uint8_t category = GetGeneralCategory(aCh);
1551 if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
1552 return RefPtr<gfxFont>(aPrevMatchedFont).forget();
1553 }
1554
1555 // if this character is a join-control or the previous is a join-causer,
1556 // use the same font as the previous range if we can
1557 if (gfxFontUtils::IsJoinControl(aCh) ||
1558 gfxFontUtils::IsJoinCauser(aPrevCh)) {
1559 if (aPrevMatchedFont->HasCharacter(aCh)) {
1560 return RefPtr<gfxFont>(aPrevMatchedFont).forget();
1561 }
1562 }
1563 }
1564
1565 // if this character is a variation selector,
1566 // use the previous font regardless of whether it supports VS or not.
1567 // otherwise the text run will be divided.
1568 if (gfxFontUtils::IsVarSelector(aCh)) {
1569 if (aPrevMatchedFont) {
1570 return RefPtr<gfxFont>(aPrevMatchedFont).forget();
1571 }
1572 // VS alone. it's meaningless to search different fonts
1573 return nullptr;
1574 }
1575
1576 // The real fonts that fontconfig provides for generic/fallback families
1577 // depend on the language used, so a different FontSet is used for each
1578 // language (except for the variation below).
1579 //
1580 // With most fontconfig configurations any real family names prior to a
1581 // fontconfig generic with corresponding fonts installed will still lead
1582 // to the same leading fonts in each FontSet.
1583 //
1584 // There is an inefficiency here therefore because the same base FontSet
1585 // could often be used if these real families support the character.
1586 // However, with fontconfig aliases, it is difficult to distinguish
1587 // where exactly alias fonts end and generic/fallback fonts begin.
1588 //
1589 // The variation from pure language-based matching used here is that the
1590 // same primary/base font is always used irrespective of the language.
1591 // This provides that SCRIPT_COMMON characters are consistently rendered
1592 // with the same font (bug 339513 and bug 416725). This is particularly
1593 // important with the word cache as script can't be reliably determined
1594 // from surrounding words. It also often avoids the unnecessary extra
1595 // FontSet efficiency mentioned above.
1596 //
1597 // However, in two situations, the base font is not checked before the
1598 // language-specific FontSet.
1599 //
1600 // 1. When we don't have a language to make a good choice for
1601 // the base font.
1602 //
1603 // 2. For system fonts, use the default Pango behavior to give
1604 // consistency with other apps. This is relevant when un-localized
1605 // builds are run in non-Latin locales. This special-case probably
1606 // wouldn't be necessary but for bug 91190.
1607
1608 gfxFcFontSet *fontSet = GetBaseFontSet();
1609 uint32_t nextFont = 0;
1610 FcPattern *basePattern = nullptr;
1611 if (!mStyle.systemFont && mPangoLanguage) {
1612 basePattern = fontSet->GetFontPatternAt(0);
1613 if (HasChar(basePattern, aCh)) {
1614 *aMatchType = gfxTextRange::kFontGroup;
1615 return RefPtr<gfxFont>(GetBaseFont()).forget();
1616 }
1617
1618 nextFont = 1;
1619 }
1620
1621 // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values
1622 // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag.
1623 // Note that PangoScript is defined to be compatible with GUnicodeScript:
1624 // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript
1625 const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript);
1626 const PangoScript script =
1627 (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag));
1628
1629 // Might be nice to call pango_language_includes_script only once for the
1630 // run rather than for each character.
1631 PangoLanguage *scriptLang;
1632 if ((!basePattern ||
1633 !pango_language_includes_script(mPangoLanguage, script)) &&
1634 (scriptLang = pango_script_get_sample_language(script))) {
1635 fontSet = GetFontSet(scriptLang);
1636 nextFont = 0;
1637 }
1638
1639 for (uint32_t i = nextFont;
1640 FcPattern *pattern = fontSet->GetFontPatternAt(i);
1641 ++i) {
1642 if (pattern == basePattern) {
1643 continue; // already checked basePattern
1644 }
1645
1646 if (HasChar(pattern, aCh)) {
1647 *aMatchType = gfxTextRange::kFontGroup;
1648 return RefPtr<gfxFont>(fontSet->GetFontAt(i, GetStyle())).forget();
1649 }
1650 }
1651
1652 return nullptr;
1653 }
1654
1655 /**
1656 ** gfxFcFont
1657 **/
1658
1659 cairo_user_data_key_t gfxFcFont::sGfxFontKey;
1660
gfxFcFont(cairo_scaled_font_t * aCairoFont,FcPattern * aPattern,gfxFcFontEntry * aFontEntry,const gfxFontStyle * aFontStyle)1661 gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
1662 FcPattern *aPattern,
1663 gfxFcFontEntry *aFontEntry,
1664 const gfxFontStyle *aFontStyle)
1665 : gfxFontconfigFontBase(aCairoFont, aPattern, aFontEntry, aFontStyle)
1666 {
1667 cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, nullptr);
1668 }
1669
~gfxFcFont()1670 gfxFcFont::~gfxFcFont()
1671 {
1672 cairo_scaled_font_set_user_data(mScaledFont,
1673 &sGfxFontKey,
1674 nullptr,
1675 nullptr);
1676 }
1677
1678 already_AddRefed<gfxFont>
GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)1679 gfxFcFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
1680 {
1681 gfxFontStyle style(*GetStyle());
1682 style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
1683 return MakeScaledFont(&style, style.size / GetStyle()->size);
1684 }
1685
1686 already_AddRefed<gfxFont>
MakeScaledFont(gfxFontStyle * aFontStyle,gfxFloat aScaleFactor)1687 gfxFcFont::MakeScaledFont(gfxFontStyle *aFontStyle, gfxFloat aScaleFactor)
1688 {
1689 gfxFcFontEntry* fe = static_cast<gfxFcFontEntry*>(GetFontEntry());
1690 RefPtr<gfxFont> font =
1691 gfxFontCache::GetCache()->Lookup(fe, aFontStyle, nullptr);
1692 if (font) {
1693 return font.forget();
1694 }
1695
1696 cairo_matrix_t fontMatrix;
1697 cairo_scaled_font_get_font_matrix(mScaledFont, &fontMatrix);
1698 cairo_matrix_scale(&fontMatrix, aScaleFactor, aScaleFactor);
1699
1700 cairo_matrix_t ctm;
1701 cairo_scaled_font_get_ctm(mScaledFont, &ctm);
1702
1703 cairo_font_options_t *options = cairo_font_options_create();
1704 cairo_scaled_font_get_font_options(mScaledFont, options);
1705
1706 cairo_scaled_font_t *newFont =
1707 cairo_scaled_font_create(cairo_scaled_font_get_font_face(mScaledFont),
1708 &fontMatrix, &ctm, options);
1709 cairo_font_options_destroy(options);
1710
1711 font = new gfxFcFont(newFont, GetPattern(), fe, aFontStyle);
1712 gfxFontCache::GetCache()->AddNew(font);
1713 cairo_scaled_font_destroy(newFont);
1714
1715 return font.forget();
1716 }
1717
1718 already_AddRefed<gfxFont>
GetSmallCapsFont()1719 gfxFcFont::GetSmallCapsFont()
1720 {
1721 gfxFontStyle style(*GetStyle());
1722 style.size *= SMALL_CAPS_SCALE_FACTOR;
1723 style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
1724 return MakeScaledFont(&style, SMALL_CAPS_SCALE_FACTOR);
1725 }
1726
1727 /* static */ void
Shutdown()1728 gfxPangoFontGroup::Shutdown()
1729 {
1730 // Resetting gFTLibrary in case this is wanted again after a
1731 // cairo_debug_reset_static_data.
1732 gFTLibrary = nullptr;
1733 }
1734
1735 /* static */ gfxFontEntry *
NewFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle)1736 gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
1737 uint16_t aWeight,
1738 int16_t aStretch,
1739 uint8_t aStyle)
1740 {
1741 gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
1742 if (!utils)
1743 return nullptr;
1744
1745 // The font face name from @font-face { src: local() } is not well
1746 // defined.
1747 //
1748 // On MS Windows, this name gets compared with
1749 // ENUMLOGFONTEXW::elfFullName, which for OpenType fonts seems to be the
1750 // full font name from the name table. For CFF OpenType fonts this is the
1751 // same as the PostScript name, but for TrueType fonts it is usually
1752 // different.
1753 //
1754 // On Mac, the font face name is compared with the PostScript name, even
1755 // for TrueType fonts.
1756 //
1757 // Fontconfig only records the full font names, so the behavior here
1758 // follows that on MS Windows. However, to provide the possibility
1759 // of aliases to compensate for variations, the font face name is passed
1760 // through FcConfigSubstitute.
1761
1762 nsAutoRef<FcPattern> pattern(FcPatternCreate());
1763 if (!pattern)
1764 return nullptr;
1765
1766 NS_ConvertUTF16toUTF8 fullname(aFontName);
1767 FcPatternAddString(pattern, FC_FULLNAME,
1768 gfxFontconfigUtils::ToFcChar8(fullname));
1769 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
1770
1771 FcChar8 *name;
1772 for (int v = 0;
1773 FcPatternGetString(pattern, FC_FULLNAME, v, &name) == FcResultMatch;
1774 ++v) {
1775 const nsTArray< nsCountedRef<FcPattern> >& fonts =
1776 utils->GetFontsForFullname(name);
1777
1778 if (fonts.Length() != 0)
1779 return new gfxLocalFcFontEntry(aFontName,
1780 aWeight,
1781 aStretch,
1782 aStyle,
1783 fonts);
1784 }
1785
1786 return nullptr;
1787 }
1788
1789 /* static */ FT_Library
GetFTLibrary()1790 gfxPangoFontGroup::GetFTLibrary()
1791 {
1792 if (!gFTLibrary) {
1793 // Use cairo's FT_Library so that cairo takes care of shutdown of the
1794 // FT_Library after it has destroyed its font_faces, and FT_Done_Face
1795 // has been called on each FT_Face, at least until this bug is fixed:
1796 // https://bugs.freedesktop.org/show_bug.cgi?id=18857
1797 //
1798 // Cairo's FT_Library can be obtained from any cairo_scaled_font. The
1799 // font properties requested here are chosen to get an FT_Face that is
1800 // likely to be also used elsewhere.
1801 gfxFontStyle style;
1802 RefPtr<gfxPangoFontGroup> fontGroup =
1803 new gfxPangoFontGroup(FontFamilyList(eFamily_sans_serif),
1804 &style, nullptr, 1.0);
1805
1806 gfxFcFont *font = fontGroup->GetBaseFont();
1807 if (!font)
1808 return nullptr;
1809
1810 gfxFT2LockedFace face(font);
1811 if (!face.get())
1812 return nullptr;
1813
1814 gFTLibrary = face.get()->glyph->library;
1815 }
1816
1817 return gFTLibrary;
1818 }
1819
1820 /* static */ gfxFontEntry *
NewFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const uint8_t * aFontData,uint32_t aLength)1821 gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
1822 uint16_t aWeight,
1823 int16_t aStretch,
1824 uint8_t aStyle,
1825 const uint8_t* aFontData,
1826 uint32_t aLength)
1827 {
1828 // Ownership of aFontData is passed in here, and transferred to the
1829 // new fontEntry, which will release it when no longer needed.
1830
1831 // Using face_index = 0 for the first face in the font, as we have no
1832 // other information. FT_New_Memory_Face checks for a nullptr FT_Library.
1833 FT_Face face;
1834 FT_Error error =
1835 FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
1836 if (error != 0) {
1837 free((void*)aFontData);
1838 return nullptr;
1839 }
1840
1841 return new gfxDownloadedFcFontEntry(aFontName, aWeight,
1842 aStretch, aStyle,
1843 aFontData, face);
1844 }
1845
1846
1847 static double
GetPixelSize(FcPattern * aPattern)1848 GetPixelSize(FcPattern *aPattern)
1849 {
1850 double size;
1851 if (FcPatternGetDouble(aPattern,
1852 FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
1853 return size;
1854
1855 NS_NOTREACHED("No size on pattern");
1856 return 0.0;
1857 }
1858
1859 /**
1860 * The following gfxFcFonts are accessed from the cairo_scaled_font or created
1861 * from the FcPattern, not from the gfxFontCache hash table. The gfxFontCache
1862 * hash table is keyed by desired family and style, whereas here we only know
1863 * actual family and style. There may be more than one of these fonts with
1864 * the same family and style, but different PangoFont and actual font face.
1865 *
1866 * The point of this is to record the exact font face for gfxTextRun glyph
1867 * indices. The style of this font does not necessarily represent the exact
1868 * gfxFontStyle used to build the text run. Notably, the language is not
1869 * recorded.
1870 */
1871
1872 /* static */
1873 already_AddRefed<gfxFcFont>
GetOrMakeFont(FcPattern * aRequestedPattern,FcPattern * aFontPattern,const gfxFontStyle * aFontStyle)1874 gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
1875 const gfxFontStyle *aFontStyle)
1876 {
1877 nsAutoRef<FcPattern> renderPattern
1878 (FcFontRenderPrepare(nullptr, aRequestedPattern, aFontPattern));
1879
1880 // If synthetic bold/italic is not allowed by the style, adjust the
1881 // resulting pattern to match the actual properties of the font.
1882 if (!aFontStyle->allowSyntheticWeight) {
1883 int weight;
1884 if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0,
1885 &weight) == FcResultMatch) {
1886 FcPatternDel(renderPattern, FC_WEIGHT);
1887 FcPatternAddInteger(renderPattern, FC_WEIGHT, weight);
1888 }
1889 }
1890 if (!aFontStyle->allowSyntheticStyle) {
1891 int slant;
1892 if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0,
1893 &slant) == FcResultMatch) {
1894 FcPatternDel(renderPattern, FC_SLANT);
1895 FcPatternAddInteger(renderPattern, FC_SLANT, slant);
1896 }
1897 }
1898
1899 cairo_font_face_t *face =
1900 cairo_ft_font_face_create_for_pattern(renderPattern);
1901
1902 // Reuse an existing font entry if available.
1903 RefPtr<gfxFcFontEntry> fe = gfxFcFontEntry::LookupFontEntry(face);
1904 if (!fe) {
1905 gfxDownloadedFcFontEntry *downloadedFontEntry =
1906 GetDownloadedFontEntry(aFontPattern);
1907 if (downloadedFontEntry) {
1908 // Web font
1909 fe = downloadedFontEntry;
1910 if (cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
1911 // cairo_font_face_t is using the web font data.
1912 // Hold a reference to the font entry to keep the font face
1913 // data.
1914 if (!downloadedFontEntry->SetCairoFace(face)) {
1915 // OOM. Let cairo pick a fallback font
1916 cairo_font_face_destroy(face);
1917 face = cairo_ft_font_face_create_for_pattern(aRequestedPattern);
1918 fe = gfxFcFontEntry::LookupFontEntry(face);
1919 }
1920 }
1921 }
1922 if (!fe) {
1923 // Get a unique name for the font face from the file and id.
1924 nsAutoString name;
1925 FcChar8 *fc_file;
1926 if (FcPatternGetString(renderPattern,
1927 FC_FILE, 0, &fc_file) == FcResultMatch) {
1928 int index;
1929 if (FcPatternGetInteger(renderPattern,
1930 FC_INDEX, 0, &index) != FcResultMatch) {
1931 // cairo defaults to 0.
1932 index = 0;
1933 }
1934
1935 AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
1936 if (index != 0) {
1937 name.Append('/');
1938 name.AppendInt(index);
1939 }
1940 }
1941
1942 fe = new gfxSystemFcFontEntry(face, aFontPattern, name);
1943 }
1944 }
1945
1946 gfxFontStyle style(*aFontStyle);
1947 style.size = GetPixelSize(renderPattern);
1948 style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
1949 style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
1950
1951 RefPtr<gfxFont> font =
1952 gfxFontCache::GetCache()->Lookup(fe, &style, nullptr);
1953 if (!font) {
1954 // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
1955 // not necessarily enough to provide a key that will describe a unique
1956 // font. cairoFont contains information from renderPattern, which is a
1957 // fully resolved pattern from FcFontRenderPrepare.
1958 // FcFontRenderPrepare takes the requested pattern and the face
1959 // pattern as input and can modify elements of the resulting pattern
1960 // that affect rendering but are not included in the gfxFontStyle.
1961 cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
1962 font = new gfxFcFont(cairoFont, renderPattern, fe, &style);
1963 gfxFontCache::GetCache()->AddNew(font);
1964 cairo_scaled_font_destroy(cairoFont);
1965 }
1966
1967 cairo_font_face_destroy(face);
1968
1969 RefPtr<gfxFcFont> retval(static_cast<gfxFcFont*>(font.get()));
1970 return retval.forget();
1971 }
1972
1973 gfxFcFontSet *
GetBaseFontSet()1974 gfxPangoFontGroup::GetBaseFontSet()
1975 {
1976 if (mFontSets.Length() > 0)
1977 return mFontSets[0].mFontSet;
1978
1979 mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
1980 nsAutoRef<FcPattern> pattern;
1981 RefPtr<gfxFcFontSet> fontSet =
1982 MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
1983
1984 double size = GetPixelSize(pattern);
1985 if (size != 0.0 && mStyle.sizeAdjust > 0.0) {
1986 gfxFcFont *font = fontSet->GetFontAt(0, GetStyle());
1987 if (font) {
1988 const gfxFont::Metrics& metrics =
1989 font->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
1990
1991 // The factor of 0.1 ensures that xHeight is sane so fonts don't
1992 // become huge. Strictly ">" ensures that xHeight and emHeight are
1993 // not both zero.
1994 if (metrics.xHeight > 0.1 * metrics.emHeight) {
1995 mSizeAdjustFactor =
1996 mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
1997
1998 size *= mSizeAdjustFactor;
1999 FcPatternDel(pattern, FC_PIXEL_SIZE);
2000 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
2001
2002 fontSet = new gfxFcFontSet(pattern, mUserFontSet);
2003 }
2004 }
2005 }
2006
2007 PangoLanguage *pangoLang = mPangoLanguage;
2008 FcChar8 *fcLang;
2009 if (!pangoLang &&
2010 FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
2011 pangoLang =
2012 pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
2013 }
2014
2015 mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
2016
2017 return fontSet;
2018 }
2019
2020 /**
2021 ** gfxTextRun
2022 *
2023 * A serious problem:
2024 *
2025 * -- We draw with a font that's hinted for the CTM, but we measure with a font
2026 * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
2027 *
2028 **/
2029
2030 // This will fetch an existing scaled_font if one exists.
2031 static cairo_scaled_font_t *
CreateScaledFont(FcPattern * aPattern,cairo_font_face_t * aFace)2032 CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace)
2033 {
2034 double size = GetPixelSize(aPattern);
2035
2036 cairo_matrix_t fontMatrix;
2037 FcMatrix *fcMatrix;
2038 if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
2039 cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
2040 else
2041 cairo_matrix_init_identity(&fontMatrix);
2042 cairo_matrix_scale(&fontMatrix, size, size);
2043
2044 FcBool printing;
2045 if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != FcResultMatch) {
2046 printing = FcFalse;
2047 }
2048
2049 // The cairo_scaled_font is created with a unit ctm so that metrics and
2050 // positions are in user space, but this means that hinting effects will
2051 // not be estimated accurately for non-unit transformations.
2052 cairo_matrix_t identityMatrix;
2053 cairo_matrix_init_identity(&identityMatrix);
2054
2055 // Font options are set explicitly here to improve cairo's caching
2056 // behavior and to record the relevant parts of the pattern for
2057 // SetupCairoFont (so that the pattern can be released).
2058 //
2059 // Most font_options have already been set as defaults on the FcPattern
2060 // with cairo_ft_font_options_substitute(), then user and system
2061 // fontconfig configurations were applied. The resulting font_options
2062 // have been recorded on the face during
2063 // cairo_ft_font_face_create_for_pattern().
2064 //
2065 // None of the settings here cause this scaled_font to behave any
2066 // differently from how it would behave if it were created from the same
2067 // face with default font_options.
2068 //
2069 // We set options explicitly so that the same scaled_font will be found in
2070 // the cairo_scaled_font_map when cairo loads glyphs from a context with
2071 // the same font_face, font_matrix, ctm, and surface font_options.
2072 //
2073 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
2074 // font_options on the cairo_ft_font_face, and doesn't consider default
2075 // option values to not match any explicit values.
2076 //
2077 // Even after cairo_set_scaled_font is used to set font_options for the
2078 // cairo context, when cairo looks for a scaled_font for the context, it
2079 // will look for a font with some option values from the target surface if
2080 // any values are left default on the context font_options. If this
2081 // scaled_font is created with default font_options, cairo will not find
2082 // it.
2083 cairo_font_options_t *fontOptions = cairo_font_options_create();
2084
2085 // The one option not recorded in the pattern is hint_metrics, which will
2086 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
2087 // We should be considering the font_options of the surface on which this
2088 // font will be used, but currently we don't have different gfxFonts for
2089 // different surface font_options, so we'll create a font suitable for the
2090 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
2091 if (printing) {
2092 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
2093 } else {
2094 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON);
2095 }
2096
2097 // The remaining options have been recorded on the pattern and the face.
2098 // _cairo_ft_options_merge has some logic to decide which options from the
2099 // scaled_font or from the cairo_ft_font_face take priority in the way the
2100 // font behaves.
2101 //
2102 // In the majority of cases, _cairo_ft_options_merge uses the options from
2103 // the cairo_ft_font_face, so sometimes it is not so important which
2104 // values are set here so long as they are not defaults, but we'll set
2105 // them to the exact values that we expect from the font, to be consistent
2106 // and to protect against changes in cairo.
2107 //
2108 // In some cases, _cairo_ft_options_merge uses some options from the
2109 // scaled_font's font_options rather than options on the
2110 // cairo_ft_font_face (from fontconfig).
2111 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
2112 //
2113 // Surface font options were set on the pattern in
2114 // cairo_ft_font_options_substitute. If fontconfig has changed the
2115 // hint_style then that is what the user (or distribution) wants, so we
2116 // use the setting from the FcPattern.
2117 //
2118 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
2119 FcBool hinting = FcFalse;
2120 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
2121 hinting = FcTrue;
2122 }
2123
2124 cairo_hint_style_t hint_style;
2125 if (printing || !hinting) {
2126 hint_style = CAIRO_HINT_STYLE_NONE;
2127 } else {
2128 #ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
2129 int fc_hintstyle;
2130 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
2131 0, &fc_hintstyle ) != FcResultMatch) {
2132 fc_hintstyle = FC_HINT_FULL;
2133 }
2134 switch (fc_hintstyle) {
2135 case FC_HINT_NONE:
2136 hint_style = CAIRO_HINT_STYLE_NONE;
2137 break;
2138 case FC_HINT_SLIGHT:
2139 hint_style = CAIRO_HINT_STYLE_SLIGHT;
2140 break;
2141 case FC_HINT_MEDIUM:
2142 default: // This fallback mirrors _get_pattern_ft_options in cairo.
2143 hint_style = CAIRO_HINT_STYLE_MEDIUM;
2144 break;
2145 case FC_HINT_FULL:
2146 hint_style = CAIRO_HINT_STYLE_FULL;
2147 break;
2148 }
2149 #else // no FC_HINT_STYLE
2150 hint_style = CAIRO_HINT_STYLE_FULL;
2151 #endif
2152 }
2153 cairo_font_options_set_hint_style(fontOptions, hint_style);
2154
2155 int rgba;
2156 if (FcPatternGetInteger(aPattern,
2157 FC_RGBA, 0, &rgba) != FcResultMatch) {
2158 rgba = FC_RGBA_UNKNOWN;
2159 }
2160 cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
2161 switch (rgba) {
2162 case FC_RGBA_UNKNOWN:
2163 case FC_RGBA_NONE:
2164 default:
2165 // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
2166 // is disabled through cairo_antialias_t.
2167 rgba = FC_RGBA_NONE;
2168 // subpixel_order won't be used by the font as we won't use
2169 // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
2170 // caching reasons described above. Fall through:
2171 MOZ_FALLTHROUGH;
2172 case FC_RGBA_RGB:
2173 subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
2174 break;
2175 case FC_RGBA_BGR:
2176 subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
2177 break;
2178 case FC_RGBA_VRGB:
2179 subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
2180 break;
2181 case FC_RGBA_VBGR:
2182 subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
2183 break;
2184 }
2185 cairo_font_options_set_subpixel_order(fontOptions, subpixel_order);
2186
2187 FcBool fc_antialias;
2188 if (FcPatternGetBool(aPattern,
2189 FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
2190 fc_antialias = FcTrue;
2191 }
2192 cairo_antialias_t antialias;
2193 if (!fc_antialias) {
2194 antialias = CAIRO_ANTIALIAS_NONE;
2195 } else if (rgba == FC_RGBA_NONE) {
2196 antialias = CAIRO_ANTIALIAS_GRAY;
2197 } else {
2198 antialias = CAIRO_ANTIALIAS_SUBPIXEL;
2199 }
2200 cairo_font_options_set_antialias(fontOptions, antialias);
2201
2202 cairo_scaled_font_t *scaledFont =
2203 cairo_scaled_font_create(aFace, &fontMatrix, &identityMatrix,
2204 fontOptions);
2205
2206 cairo_font_options_destroy(fontOptions);
2207
2208 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
2209 "Failed to create scaled font");
2210 return scaledFont;
2211 }
2212
2213 /* static */
2214 PangoLanguage *
GuessPangoLanguage(nsIAtom * aLanguage)2215 GuessPangoLanguage(nsIAtom *aLanguage)
2216 {
2217 if (!aLanguage)
2218 return nullptr;
2219
2220 // Pango and fontconfig won't understand mozilla's internal langGroups, so
2221 // find a real language.
2222 nsAutoCString lang;
2223 gfxFontconfigUtils::GetSampleLangForGroup(aLanguage, &lang);
2224 if (lang.IsEmpty())
2225 return nullptr;
2226
2227 return pango_language_from_string(lang.get());
2228 }
2229
2230 #ifdef MOZ_WIDGET_GTK
2231 /***************************************************************************
2232 *
2233 * This function must be last in the file because it uses the system cairo
2234 * library. Above this point the cairo library used is the tree cairo if
2235 * MOZ_TREE_CAIRO.
2236 */
2237
2238 #if MOZ_TREE_CAIRO
2239 // Tree cairo symbols have different names. Disable their activation through
2240 // preprocessor macros.
2241 #undef cairo_ft_font_options_substitute
2242
2243 // The system cairo functions are not declared because the include paths cause
2244 // the gdk headers to pick up the tree cairo.h.
2245 extern "C" {
2246 NS_VISIBILITY_DEFAULT void
2247 cairo_ft_font_options_substitute (const cairo_font_options_t *options,
2248 FcPattern *pattern);
2249 }
2250 #endif
2251
2252 static void
ApplyGdkScreenFontOptions(FcPattern * aPattern)2253 ApplyGdkScreenFontOptions(FcPattern *aPattern)
2254 {
2255 const cairo_font_options_t *options =
2256 gdk_screen_get_font_options(gdk_screen_get_default());
2257
2258 cairo_ft_font_options_substitute(options, aPattern);
2259 }
2260
2261 #endif // MOZ_WIDGET_GTK
2262
2263