Tue, 06 Jan 2015 21:39:09 +0100
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 |