gfx/thebes/gfxPangoFonts.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial