gfx/thebes/gfxDWriteFontList.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 "mozilla/ArrayUtils.h"
michael@0 7 #include "mozilla/MemoryReporting.h"
michael@0 8
michael@0 9 #ifdef MOZ_LOGGING
michael@0 10 #define FORCE_PR_LOG /* Allow logging in the release build */
michael@0 11 #endif /* MOZ_LOGGING */
michael@0 12
michael@0 13 #include "gfxDWriteFontList.h"
michael@0 14 #include "gfxDWriteFonts.h"
michael@0 15 #include "nsUnicharUtils.h"
michael@0 16 #include "nsILocaleService.h"
michael@0 17 #include "nsServiceManagerUtils.h"
michael@0 18 #include "nsCharSeparatedTokenizer.h"
michael@0 19 #include "mozilla/Preferences.h"
michael@0 20 #include "mozilla/Telemetry.h"
michael@0 21
michael@0 22 #include "gfxGDIFontList.h"
michael@0 23
michael@0 24 #include "nsIWindowsRegKey.h"
michael@0 25
michael@0 26 #include "harfbuzz/hb.h"
michael@0 27
michael@0 28 using namespace mozilla;
michael@0 29
michael@0 30 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
michael@0 31 PR_LOG_DEBUG, args)
michael@0 32 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
michael@0 33 gfxPlatform::GetLog(eGfxLog_fontlist), \
michael@0 34 PR_LOG_DEBUG)
michael@0 35
michael@0 36 #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
michael@0 37 PR_LOG_DEBUG, args)
michael@0 38 #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
michael@0 39 gfxPlatform::GetLog(eGfxLog_fontinit), \
michael@0 40 PR_LOG_DEBUG)
michael@0 41
michael@0 42 #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
michael@0 43 gfxPlatform::GetLog(eGfxLog_cmapdata), \
michael@0 44 PR_LOG_DEBUG)
michael@0 45
michael@0 46 static __inline void
michael@0 47 BuildKeyNameFromFontName(nsAString &aName)
michael@0 48 {
michael@0 49 if (aName.Length() >= LF_FACESIZE)
michael@0 50 aName.Truncate(LF_FACESIZE - 1);
michael@0 51 ToLowerCase(aName);
michael@0 52 }
michael@0 53
michael@0 54 ////////////////////////////////////////////////////////////////////////////////
michael@0 55 // gfxDWriteFontFamily
michael@0 56
michael@0 57 gfxDWriteFontFamily::~gfxDWriteFontFamily()
michael@0 58 {
michael@0 59 }
michael@0 60
michael@0 61 static HRESULT
michael@0 62 GetDirectWriteFontName(IDWriteFont *aFont, nsAString& aFontName)
michael@0 63 {
michael@0 64 HRESULT hr;
michael@0 65
michael@0 66 nsRefPtr<IDWriteLocalizedStrings> names;
michael@0 67 hr = aFont->GetFaceNames(getter_AddRefs(names));
michael@0 68 if (FAILED(hr)) {
michael@0 69 return hr;
michael@0 70 }
michael@0 71
michael@0 72 BOOL exists;
michael@0 73 nsAutoTArray<wchar_t,32> faceName;
michael@0 74 UINT32 englishIdx = 0;
michael@0 75 hr = names->FindLocaleName(L"en-us", &englishIdx, &exists);
michael@0 76 if (FAILED(hr)) {
michael@0 77 return hr;
michael@0 78 }
michael@0 79
michael@0 80 if (!exists) {
michael@0 81 // No english found, use whatever is first in the list.
michael@0 82 englishIdx = 0;
michael@0 83 }
michael@0 84 UINT32 length;
michael@0 85 hr = names->GetStringLength(englishIdx, &length);
michael@0 86 if (FAILED(hr)) {
michael@0 87 return hr;
michael@0 88 }
michael@0 89 faceName.SetLength(length + 1);
michael@0 90 hr = names->GetString(englishIdx, faceName.Elements(), length + 1);
michael@0 91 if (FAILED(hr)) {
michael@0 92 return hr;
michael@0 93 }
michael@0 94
michael@0 95 aFontName.Assign(faceName.Elements());
michael@0 96 return S_OK;
michael@0 97 }
michael@0 98
michael@0 99 // These strings are only defined in Win SDK 8+, so use #ifdef for now
michael@0 100 #if MOZ_WINSDK_TARGETVER > 0x08000000
michael@0 101 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
michael@0 102 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
michael@0 103 #else
michael@0 104 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_ID(DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT + 1)
michael@0 105 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_ID(DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT + 2)
michael@0 106 #endif
michael@0 107
michael@0 108 // for use in reading postscript or fullname
michael@0 109 static HRESULT
michael@0 110 GetDirectWriteFaceName(IDWriteFont *aFont,
michael@0 111 DWRITE_INFORMATIONAL_STRING_ID aWhichName,
michael@0 112 nsAString& aFontName)
michael@0 113 {
michael@0 114 HRESULT hr;
michael@0 115
michael@0 116 BOOL exists;
michael@0 117 nsRefPtr<IDWriteLocalizedStrings> infostrings;
michael@0 118 hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings), &exists);
michael@0 119 if (FAILED(hr) || !exists) {
michael@0 120 return E_FAIL;
michael@0 121 }
michael@0 122
michael@0 123 nsAutoTArray<wchar_t,32> faceName;
michael@0 124 UINT32 englishIdx = 0;
michael@0 125 hr = infostrings->FindLocaleName(L"en-us", &englishIdx, &exists);
michael@0 126 if (FAILED(hr)) {
michael@0 127 return hr;
michael@0 128 }
michael@0 129
michael@0 130 if (!exists) {
michael@0 131 // No english found, use whatever is first in the list.
michael@0 132 englishIdx = 0;
michael@0 133 }
michael@0 134 UINT32 length;
michael@0 135 hr = infostrings->GetStringLength(englishIdx, &length);
michael@0 136 if (FAILED(hr)) {
michael@0 137 return hr;
michael@0 138 }
michael@0 139 faceName.SetLength(length + 1);
michael@0 140 hr = infostrings->GetString(englishIdx, faceName.Elements(), length + 1);
michael@0 141 if (FAILED(hr)) {
michael@0 142 return hr;
michael@0 143 }
michael@0 144
michael@0 145 aFontName.Assign(faceName.Elements());
michael@0 146 return S_OK;
michael@0 147 }
michael@0 148
michael@0 149 void
michael@0 150 gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
michael@0 151 {
michael@0 152 HRESULT hr;
michael@0 153 if (mHasStyles) {
michael@0 154 return;
michael@0 155 }
michael@0 156 mHasStyles = true;
michael@0 157
michael@0 158 gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
michael@0 159
michael@0 160 bool skipFaceNames = mFaceNamesInitialized ||
michael@0 161 !fp->NeedFullnamePostscriptNames();
michael@0 162 bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
michael@0 163 fp->NeedFullnamePostscriptNames() &&
michael@0 164 aFontInfoData;
michael@0 165
michael@0 166 for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
michael@0 167 nsRefPtr<IDWriteFont> font;
michael@0 168 hr = mDWFamily->GetFont(i, getter_AddRefs(font));
michael@0 169 if (FAILED(hr)) {
michael@0 170 // This should never happen.
michael@0 171 NS_WARNING("Failed to get existing font from family.");
michael@0 172 continue;
michael@0 173 }
michael@0 174
michael@0 175 if (font->GetSimulations() & DWRITE_FONT_SIMULATIONS_OBLIQUE) {
michael@0 176 // We don't want these.
michael@0 177 continue;
michael@0 178 }
michael@0 179
michael@0 180 // name
michael@0 181 nsString fullID(mName);
michael@0 182 nsAutoString faceName;
michael@0 183 hr = GetDirectWriteFontName(font, faceName);
michael@0 184 if (FAILED(hr)) {
michael@0 185 continue;
michael@0 186 }
michael@0 187 fullID.Append(NS_LITERAL_STRING(" "));
michael@0 188 fullID.Append(faceName);
michael@0 189
michael@0 190 gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font);
michael@0 191 fe->SetForceGDIClassic(mForceGDIClassic);
michael@0 192 AddFontEntry(fe);
michael@0 193
michael@0 194 // postscript/fullname if needed
michael@0 195 nsAutoString psname, fullname;
michael@0 196 if (fontInfoShouldHaveFaceNames) {
michael@0 197 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
michael@0 198 if (!fullname.IsEmpty()) {
michael@0 199 fp->AddFullname(fe, fullname);
michael@0 200 }
michael@0 201 if (!psname.IsEmpty()) {
michael@0 202 fp->AddPostscriptName(fe, psname);
michael@0 203 }
michael@0 204 } else if (!skipFaceNames) {
michael@0 205 hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
michael@0 206 if (FAILED(hr)) {
michael@0 207 skipFaceNames = true;
michael@0 208 } else if (psname.Length() > 0) {
michael@0 209 fp->AddPostscriptName(fe, psname);
michael@0 210 }
michael@0 211
michael@0 212 hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname);
michael@0 213 if (FAILED(hr)) {
michael@0 214 skipFaceNames = true;
michael@0 215 } else if (fullname.Length() > 0) {
michael@0 216 fp->AddFullname(fe, fullname);
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 #ifdef PR_LOGGING
michael@0 221 if (LOG_FONTLIST_ENABLED()) {
michael@0 222 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
michael@0 223 " with style: %s weight: %d stretch: %d psname: %s fullname: %s",
michael@0 224 NS_ConvertUTF16toUTF8(fe->Name()).get(),
michael@0 225 NS_ConvertUTF16toUTF8(Name()).get(),
michael@0 226 (fe->IsItalic()) ? "italic" : "normal",
michael@0 227 fe->Weight(), fe->Stretch(),
michael@0 228 NS_ConvertUTF16toUTF8(psname).get(),
michael@0 229 NS_ConvertUTF16toUTF8(fullname).get()));
michael@0 230 }
michael@0 231 #endif
michael@0 232 }
michael@0 233
michael@0 234 // assume that if no error, all postscript/fullnames were initialized
michael@0 235 if (!skipFaceNames) {
michael@0 236 mFaceNamesInitialized = true;
michael@0 237 }
michael@0 238
michael@0 239 if (!mAvailableFonts.Length()) {
michael@0 240 NS_WARNING("Family with no font faces in it.");
michael@0 241 }
michael@0 242
michael@0 243 if (mIsBadUnderlineFamily) {
michael@0 244 SetBadUnderlineFonts();
michael@0 245 }
michael@0 246 }
michael@0 247
michael@0 248 void
michael@0 249 gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
michael@0 250 bool aNeedFullnamePostscriptNames)
michael@0 251 {
michael@0 252 // if all needed names have already been read, skip
michael@0 253 if (mOtherFamilyNamesInitialized &&
michael@0 254 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
michael@0 255 return;
michael@0 256 }
michael@0 257
michael@0 258 // DirectWrite version of this will try to read
michael@0 259 // postscript/fullnames via DirectWrite API
michael@0 260 FindStyleVariations();
michael@0 261
michael@0 262 // fallback to looking up via name table
michael@0 263 if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
michael@0 264 gfxFontFamily::ReadFaceNames(aPlatformFontList,
michael@0 265 aNeedFullnamePostscriptNames);
michael@0 266 }
michael@0 267 }
michael@0 268
michael@0 269 void
michael@0 270 gfxDWriteFontFamily::LocalizedName(nsAString &aLocalizedName)
michael@0 271 {
michael@0 272 aLocalizedName.AssignLiteral("Unknown Font");
michael@0 273 HRESULT hr;
michael@0 274 nsresult rv;
michael@0 275 nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
michael@0 276 &rv);
michael@0 277 nsCOMPtr<nsILocale> locale;
michael@0 278 rv = ls->GetApplicationLocale(getter_AddRefs(locale));
michael@0 279 nsString localeName;
michael@0 280 if (NS_SUCCEEDED(rv)) {
michael@0 281 rv = locale->GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE),
michael@0 282 localeName);
michael@0 283 }
michael@0 284 if (NS_FAILED(rv)) {
michael@0 285 localeName.AssignLiteral("en-us");
michael@0 286 }
michael@0 287
michael@0 288 nsRefPtr<IDWriteLocalizedStrings> names;
michael@0 289
michael@0 290 hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
michael@0 291 if (FAILED(hr)) {
michael@0 292 return;
michael@0 293 }
michael@0 294 UINT32 idx = 0;
michael@0 295 BOOL exists;
michael@0 296 hr = names->FindLocaleName(localeName.get(),
michael@0 297 &idx,
michael@0 298 &exists);
michael@0 299 if (FAILED(hr)) {
michael@0 300 return;
michael@0 301 }
michael@0 302 if (!exists) {
michael@0 303 // Use english is localized is not found.
michael@0 304 hr = names->FindLocaleName(L"en-us", &idx, &exists);
michael@0 305 if (FAILED(hr)) {
michael@0 306 return;
michael@0 307 }
michael@0 308 if (!exists) {
michael@0 309 // Use 0 index if english is not found.
michael@0 310 idx = 0;
michael@0 311 }
michael@0 312 }
michael@0 313 AutoFallibleTArray<WCHAR, 32> famName;
michael@0 314 UINT32 length;
michael@0 315
michael@0 316 hr = names->GetStringLength(idx, &length);
michael@0 317 if (FAILED(hr)) {
michael@0 318 return;
michael@0 319 }
michael@0 320
michael@0 321 if (!famName.SetLength(length + 1)) {
michael@0 322 // Eeep - running out of memory. Unlikely to end well.
michael@0 323 return;
michael@0 324 }
michael@0 325
michael@0 326 hr = names->GetString(idx, famName.Elements(), length + 1);
michael@0 327 if (FAILED(hr)) {
michael@0 328 return;
michael@0 329 }
michael@0 330
michael@0 331 aLocalizedName = nsDependentString(famName.Elements());
michael@0 332 }
michael@0 333
michael@0 334 void
michael@0 335 gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 336 FontListSizes* aSizes) const
michael@0 337 {
michael@0 338 gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 339 // TODO:
michael@0 340 // This doesn't currently account for |mDWFamily|
michael@0 341 }
michael@0 342
michael@0 343 void
michael@0 344 gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 345 FontListSizes* aSizes) const
michael@0 346 {
michael@0 347 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 348 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 349 }
michael@0 350
michael@0 351 ////////////////////////////////////////////////////////////////////////////////
michael@0 352 // gfxDWriteFontEntry
michael@0 353
michael@0 354 gfxDWriteFontEntry::~gfxDWriteFontEntry()
michael@0 355 {
michael@0 356 }
michael@0 357
michael@0 358 bool
michael@0 359 gfxDWriteFontEntry::IsSymbolFont()
michael@0 360 {
michael@0 361 if (mFont) {
michael@0 362 return mFont->IsSymbolFont();
michael@0 363 } else {
michael@0 364 return false;
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 static bool
michael@0 369 UsingArabicOrHebrewScriptSystemLocale()
michael@0 370 {
michael@0 371 LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID());
michael@0 372 switch (langid) {
michael@0 373 case LANG_ARABIC:
michael@0 374 case LANG_DARI:
michael@0 375 case LANG_PASHTO:
michael@0 376 case LANG_PERSIAN:
michael@0 377 case LANG_SINDHI:
michael@0 378 case LANG_UIGHUR:
michael@0 379 case LANG_URDU:
michael@0 380 case LANG_HEBREW:
michael@0 381 return true;
michael@0 382 default:
michael@0 383 return false;
michael@0 384 }
michael@0 385 }
michael@0 386
michael@0 387 nsresult
michael@0 388 gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag,
michael@0 389 FallibleTArray<uint8_t> &aBuffer)
michael@0 390 {
michael@0 391 gfxDWriteFontList *pFontList = gfxDWriteFontList::PlatformFontList();
michael@0 392
michael@0 393 // Don't use GDI table loading for symbol fonts or for
michael@0 394 // italic fonts in Arabic-script system locales because of
michael@0 395 // potential cmap discrepancies, see bug 629386.
michael@0 396 // Ditto for Hebrew, bug 837498.
michael@0 397 if (mFont && pFontList->UseGDIFontTableAccess() &&
michael@0 398 !(mItalic && UsingArabicOrHebrewScriptSystemLocale()) &&
michael@0 399 !mFont->IsSymbolFont())
michael@0 400 {
michael@0 401 LOGFONTW logfont = { 0 };
michael@0 402 if (!InitLogFont(mFont, &logfont))
michael@0 403 return NS_ERROR_FAILURE;
michael@0 404
michael@0 405 AutoDC dc;
michael@0 406 AutoSelectFont font(dc.GetDC(), &logfont);
michael@0 407 if (font.IsValid()) {
michael@0 408 uint32_t tableSize =
michael@0 409 ::GetFontData(dc.GetDC(),
michael@0 410 NativeEndian::swapToBigEndian(aTableTag), 0,
michael@0 411 nullptr, 0);
michael@0 412 if (tableSize != GDI_ERROR) {
michael@0 413 if (aBuffer.SetLength(tableSize)) {
michael@0 414 ::GetFontData(dc.GetDC(),
michael@0 415 NativeEndian::swapToBigEndian(aTableTag), 0,
michael@0 416 aBuffer.Elements(), aBuffer.Length());
michael@0 417 return NS_OK;
michael@0 418 }
michael@0 419 return NS_ERROR_OUT_OF_MEMORY;
michael@0 420 }
michael@0 421 }
michael@0 422 return NS_ERROR_FAILURE;
michael@0 423 }
michael@0 424
michael@0 425 nsRefPtr<IDWriteFontFace> fontFace;
michael@0 426 nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
michael@0 427 if (NS_FAILED(rv)) {
michael@0 428 return rv;
michael@0 429 }
michael@0 430
michael@0 431 uint8_t *tableData;
michael@0 432 uint32_t len;
michael@0 433 void *tableContext = nullptr;
michael@0 434 BOOL exists;
michael@0 435 HRESULT hr =
michael@0 436 fontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTableTag),
michael@0 437 (const void**)&tableData, &len,
michael@0 438 &tableContext, &exists);
michael@0 439 if (FAILED(hr) || !exists) {
michael@0 440 return NS_ERROR_FAILURE;
michael@0 441 }
michael@0 442
michael@0 443 if (aBuffer.SetLength(len)) {
michael@0 444 memcpy(aBuffer.Elements(), tableData, len);
michael@0 445 rv = NS_OK;
michael@0 446 } else {
michael@0 447 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 448 }
michael@0 449
michael@0 450 if (tableContext) {
michael@0 451 fontFace->ReleaseFontTable(&tableContext);
michael@0 452 }
michael@0 453
michael@0 454 return rv;
michael@0 455 }
michael@0 456
michael@0 457 // Access to font tables packaged in hb_blob_t form
michael@0 458
michael@0 459 // object attached to the Harfbuzz blob, used to release
michael@0 460 // the table when the blob is destroyed
michael@0 461 class FontTableRec {
michael@0 462 public:
michael@0 463 FontTableRec(IDWriteFontFace *aFontFace, void *aContext)
michael@0 464 : mFontFace(aFontFace), mContext(aContext)
michael@0 465 { }
michael@0 466
michael@0 467 ~FontTableRec() {
michael@0 468 mFontFace->ReleaseFontTable(mContext);
michael@0 469 }
michael@0 470
michael@0 471 private:
michael@0 472 nsRefPtr<IDWriteFontFace> mFontFace;
michael@0 473 void *mContext;
michael@0 474 };
michael@0 475
michael@0 476 static void
michael@0 477 DestroyBlobFunc(void* aUserData)
michael@0 478 {
michael@0 479 FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
michael@0 480 delete ftr;
michael@0 481 }
michael@0 482
michael@0 483 hb_blob_t *
michael@0 484 gfxDWriteFontEntry::GetFontTable(uint32_t aTag)
michael@0 485 {
michael@0 486 // try to avoid potentially expensive DWrite call if we haven't actually
michael@0 487 // created the font face yet, by using the gfxFontEntry method that will
michael@0 488 // use CopyFontTable and then cache the data
michael@0 489 if (!mFontFace) {
michael@0 490 return gfxFontEntry::GetFontTable(aTag);
michael@0 491 }
michael@0 492
michael@0 493 const void *data;
michael@0 494 UINT32 size;
michael@0 495 void *context;
michael@0 496 BOOL exists;
michael@0 497 HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag),
michael@0 498 &data, &size, &context, &exists);
michael@0 499 if (SUCCEEDED(hr) && exists) {
michael@0 500 FontTableRec *ftr = new FontTableRec(mFontFace, context);
michael@0 501 return hb_blob_create(static_cast<const char*>(data), size,
michael@0 502 HB_MEMORY_MODE_READONLY,
michael@0 503 ftr, DestroyBlobFunc);
michael@0 504 }
michael@0 505
michael@0 506 return nullptr;
michael@0 507 }
michael@0 508
michael@0 509 nsresult
michael@0 510 gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
michael@0 511 {
michael@0 512 // attempt this once, if errors occur leave a blank cmap
michael@0 513 if (mCharacterMap) {
michael@0 514 return NS_OK;
michael@0 515 }
michael@0 516
michael@0 517 nsRefPtr<gfxCharacterMap> charmap;
michael@0 518 nsresult rv;
michael@0 519 bool symbolFont;
michael@0 520
michael@0 521 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
michael@0 522 mUVSOffset,
michael@0 523 symbolFont))) {
michael@0 524 rv = NS_OK;
michael@0 525 } else {
michael@0 526 uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
michael@0 527 charmap = new gfxCharacterMap();
michael@0 528 AutoTable cmapTable(this, kCMAP);
michael@0 529
michael@0 530 if (cmapTable) {
michael@0 531 bool unicodeFont = false, symbolFont = false; // currently ignored
michael@0 532 uint32_t cmapLen;
michael@0 533 const uint8_t* cmapData =
michael@0 534 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
michael@0 535 &cmapLen));
michael@0 536 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
michael@0 537 *charmap, mUVSOffset,
michael@0 538 unicodeFont, symbolFont);
michael@0 539 } else {
michael@0 540 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 541 }
michael@0 542 }
michael@0 543
michael@0 544 mHasCmapTable = NS_SUCCEEDED(rv);
michael@0 545 if (mHasCmapTable) {
michael@0 546 // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used
michael@0 547 // by sites to represent a "Play" icon, but the glyph in Segoe UI Light
michael@0 548 // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.)
michael@0 549 // Fallback to Segoe UI Symbol is preferred.
michael@0 550 if (FamilyName().EqualsLiteral("Segoe UI")) {
michael@0 551 charmap->clear(0x25b6);
michael@0 552 charmap->clear(0x25c0);
michael@0 553 }
michael@0 554 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
michael@0 555 mCharacterMap = pfl->FindCharMap(charmap);
michael@0 556 } else {
michael@0 557 // if error occurred, initialize to null cmap
michael@0 558 mCharacterMap = new gfxCharacterMap();
michael@0 559 }
michael@0 560
michael@0 561 #ifdef PR_LOGGING
michael@0 562 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
michael@0 563 NS_ConvertUTF16toUTF8(mName).get(),
michael@0 564 charmap->SizeOfIncludingThis(moz_malloc_size_of),
michael@0 565 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
michael@0 566 if (LOG_CMAPDATA_ENABLED()) {
michael@0 567 char prefix[256];
michael@0 568 sprintf(prefix, "(cmapdata) name: %.220s",
michael@0 569 NS_ConvertUTF16toUTF8(mName).get());
michael@0 570 charmap->Dump(prefix, eGfxLog_cmapdata);
michael@0 571 }
michael@0 572 #endif
michael@0 573
michael@0 574 return rv;
michael@0 575 }
michael@0 576
michael@0 577 gfxFont *
michael@0 578 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
michael@0 579 bool aNeedsBold)
michael@0 580 {
michael@0 581 return new gfxDWriteFont(this, aFontStyle, aNeedsBold);
michael@0 582 }
michael@0 583
michael@0 584 nsresult
michael@0 585 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
michael@0 586 DWRITE_FONT_SIMULATIONS aSimulations)
michael@0 587 {
michael@0 588 // initialize mFontFace if this hasn't been done before
michael@0 589 if (!mFontFace) {
michael@0 590 HRESULT hr;
michael@0 591 if (mFont) {
michael@0 592 hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
michael@0 593 } else if (mFontFile) {
michael@0 594 IDWriteFontFile *fontFile = mFontFile.get();
michael@0 595 hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 596 CreateFontFace(mFaceType,
michael@0 597 1,
michael@0 598 &fontFile,
michael@0 599 0,
michael@0 600 DWRITE_FONT_SIMULATIONS_NONE,
michael@0 601 getter_AddRefs(mFontFace));
michael@0 602 } else {
michael@0 603 NS_NOTREACHED("invalid font entry");
michael@0 604 return NS_ERROR_FAILURE;
michael@0 605 }
michael@0 606 if (FAILED(hr)) {
michael@0 607 return NS_ERROR_FAILURE;
michael@0 608 }
michael@0 609 }
michael@0 610
michael@0 611 // check whether we need to add a DWrite simulated style
michael@0 612 if ((aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
michael@0 613 !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) {
michael@0 614 // if so, we need to return not mFontFace itself but a version that
michael@0 615 // has the Bold simulation - unfortunately, DWrite doesn't provide
michael@0 616 // a simple API for this
michael@0 617 UINT32 numberOfFiles = 0;
michael@0 618 if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
michael@0 619 return NS_ERROR_FAILURE;
michael@0 620 }
michael@0 621 nsAutoTArray<IDWriteFontFile*,1> files;
michael@0 622 files.AppendElements(numberOfFiles);
michael@0 623 if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
michael@0 624 return NS_ERROR_FAILURE;
michael@0 625 }
michael@0 626 HRESULT hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 627 CreateFontFace(mFontFace->GetType(),
michael@0 628 numberOfFiles,
michael@0 629 files.Elements(),
michael@0 630 mFontFace->GetIndex(),
michael@0 631 aSimulations,
michael@0 632 aFontFace);
michael@0 633 for (UINT32 i = 0; i < numberOfFiles; ++i) {
michael@0 634 files[i]->Release();
michael@0 635 }
michael@0 636 return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
michael@0 637 }
michael@0 638
michael@0 639 // no simulation: we can just add a reference to mFontFace and return that
michael@0 640 *aFontFace = mFontFace;
michael@0 641 (*aFontFace)->AddRef();
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 bool
michael@0 646 gfxDWriteFontEntry::InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont)
michael@0 647 {
michael@0 648 HRESULT hr;
michael@0 649
michael@0 650 BOOL isInSystemCollection;
michael@0 651 IDWriteGdiInterop *gdi =
michael@0 652 gfxDWriteFontList::PlatformFontList()->GetGDIInterop();
michael@0 653 hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
michael@0 654 return (FAILED(hr) ? false : true);
michael@0 655 }
michael@0 656
michael@0 657 bool
michael@0 658 gfxDWriteFontEntry::IsCJKFont()
michael@0 659 {
michael@0 660 if (mIsCJK != UNINITIALIZED_VALUE) {
michael@0 661 return mIsCJK;
michael@0 662 }
michael@0 663
michael@0 664 mIsCJK = false;
michael@0 665
michael@0 666 const uint32_t kOS2Tag = TRUETYPE_TAG('O','S','/','2');
michael@0 667 AutoFallibleTArray<uint8_t,128> buffer;
michael@0 668 if (CopyFontTable(kOS2Tag, buffer) != NS_OK) {
michael@0 669 return mIsCJK;
michael@0 670 }
michael@0 671
michael@0 672 // ulCodePageRange bit definitions for the CJK codepages,
michael@0 673 // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
michael@0 674 const uint32_t CJK_CODEPAGE_BITS =
michael@0 675 (1 << 17) | // codepage 932 - JIS/Japan
michael@0 676 (1 << 18) | // codepage 936 - Chinese (simplified)
michael@0 677 (1 << 19) | // codepage 949 - Korean Wansung
michael@0 678 (1 << 20) | // codepage 950 - Chinese (traditional)
michael@0 679 (1 << 21); // codepage 1361 - Korean Johab
michael@0 680
michael@0 681 if (buffer.Length() >= offsetof(OS2Table, sxHeight)) {
michael@0 682 const OS2Table* os2 =
michael@0 683 reinterpret_cast<const OS2Table*>(buffer.Elements());
michael@0 684 if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
michael@0 685 mIsCJK = true;
michael@0 686 }
michael@0 687 }
michael@0 688
michael@0 689 return mIsCJK;
michael@0 690 }
michael@0 691
michael@0 692 void
michael@0 693 gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 694 FontListSizes* aSizes) const
michael@0 695 {
michael@0 696 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 697 // TODO:
michael@0 698 // This doesn't currently account for the |mFont| and |mFontFile| members
michael@0 699 }
michael@0 700
michael@0 701 void
michael@0 702 gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 703 FontListSizes* aSizes) const
michael@0 704 {
michael@0 705 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 706 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 707 }
michael@0 708
michael@0 709 ////////////////////////////////////////////////////////////////////////////////
michael@0 710 // gfxDWriteFontList
michael@0 711
michael@0 712 gfxDWriteFontList::gfxDWriteFontList()
michael@0 713 : mInitialized(false), mForceGDIClassicMaxFontSize(0.0)
michael@0 714 {
michael@0 715 }
michael@0 716
michael@0 717 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
michael@0 718 // I/O strain during cold startup due to dwrite caching bugs. Default to
michael@0 719 // Arial to avoid this.
michael@0 720
michael@0 721 gfxFontFamily *
michael@0 722 gfxDWriteFontList::GetDefaultFont(const gfxFontStyle *aStyle)
michael@0 723 {
michael@0 724 nsAutoString resolvedName;
michael@0 725
michael@0 726 // try Arial first
michael@0 727 if (ResolveFontName(NS_LITERAL_STRING("Arial"), resolvedName)) {
michael@0 728 return FindFamily(resolvedName);
michael@0 729 }
michael@0 730
michael@0 731 // otherwise, use local default
michael@0 732 NONCLIENTMETRICSW ncm;
michael@0 733 ncm.cbSize = sizeof(ncm);
michael@0 734 BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
michael@0 735 sizeof(ncm), &ncm, 0);
michael@0 736 if (status) {
michael@0 737 if (ResolveFontName(nsDependentString(ncm.lfMessageFont.lfFaceName),
michael@0 738 resolvedName)) {
michael@0 739 return FindFamily(resolvedName);
michael@0 740 }
michael@0 741 }
michael@0 742
michael@0 743 return nullptr;
michael@0 744 }
michael@0 745
michael@0 746 gfxFontEntry *
michael@0 747 gfxDWriteFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
michael@0 748 const nsAString& aFullname)
michael@0 749 {
michael@0 750 gfxFontEntry *lookup;
michael@0 751
michael@0 752 lookup = LookupInFaceNameLists(aFullname);
michael@0 753 if (!lookup) {
michael@0 754 return nullptr;
michael@0 755 }
michael@0 756
michael@0 757 gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
michael@0 758 gfxDWriteFontEntry *fe =
michael@0 759 new gfxDWriteFontEntry(lookup->Name(),
michael@0 760 dwriteLookup->mFont,
michael@0 761 aProxyEntry->Weight(),
michael@0 762 aProxyEntry->Stretch(),
michael@0 763 aProxyEntry->IsItalic());
michael@0 764 fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
michael@0 765 return fe;
michael@0 766 }
michael@0 767
michael@0 768 gfxFontEntry *
michael@0 769 gfxDWriteFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
michael@0 770 const uint8_t *aFontData,
michael@0 771 uint32_t aLength)
michael@0 772 {
michael@0 773 nsresult rv;
michael@0 774 nsAutoString uniqueName;
michael@0 775 rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
michael@0 776 if (NS_FAILED(rv)) {
michael@0 777 NS_Free((void*)aFontData);
michael@0 778 return nullptr;
michael@0 779 }
michael@0 780
michael@0 781 FallibleTArray<uint8_t> newFontData;
michael@0 782
michael@0 783 rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
michael@0 784 NS_Free((void*)aFontData);
michael@0 785
michael@0 786 if (NS_FAILED(rv)) {
michael@0 787 return nullptr;
michael@0 788 }
michael@0 789
michael@0 790 nsRefPtr<IDWriteFontFile> fontFile;
michael@0 791 HRESULT hr;
michael@0 792
michael@0 793 /**
michael@0 794 * We pass in a pointer to a structure containing a pointer to the array
michael@0 795 * containing the font data and a unique identifier. DWrite will
michael@0 796 * internally copy what is at that pointer, and pass that to
michael@0 797 * CreateStreamFromKey. The array will be empty when the function
michael@0 798 * succesfully returns since it swaps out the data.
michael@0 799 */
michael@0 800 ffReferenceKey key;
michael@0 801 key.mArray = &newFontData;
michael@0 802 nsCOMPtr<nsIUUIDGenerator> uuidgen =
michael@0 803 do_GetService("@mozilla.org/uuid-generator;1");
michael@0 804 if (!uuidgen) {
michael@0 805 return nullptr;
michael@0 806 }
michael@0 807
michael@0 808 rv = uuidgen->GenerateUUIDInPlace(&key.mGUID);
michael@0 809
michael@0 810 if (NS_FAILED(rv)) {
michael@0 811 return nullptr;
michael@0 812 }
michael@0 813
michael@0 814 hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 815 CreateCustomFontFileReference(&key,
michael@0 816 sizeof(key),
michael@0 817 gfxDWriteFontFileLoader::Instance(),
michael@0 818 getter_AddRefs(fontFile));
michael@0 819
michael@0 820 if (FAILED(hr)) {
michael@0 821 NS_WARNING("Failed to create custom font file reference.");
michael@0 822 return nullptr;
michael@0 823 }
michael@0 824
michael@0 825 BOOL isSupported;
michael@0 826 DWRITE_FONT_FILE_TYPE fileType;
michael@0 827 UINT32 numFaces;
michael@0 828
michael@0 829 gfxDWriteFontEntry *entry =
michael@0 830 new gfxDWriteFontEntry(uniqueName,
michael@0 831 fontFile,
michael@0 832 aProxyEntry->Weight(),
michael@0 833 aProxyEntry->Stretch(),
michael@0 834 aProxyEntry->IsItalic());
michael@0 835
michael@0 836 fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
michael@0 837 if (!isSupported || numFaces > 1) {
michael@0 838 // We don't know how to deal with 0 faces either.
michael@0 839 delete entry;
michael@0 840 return nullptr;
michael@0 841 }
michael@0 842
michael@0 843 return entry;
michael@0 844 }
michael@0 845
michael@0 846 #ifdef DEBUG_DWRITE_STARTUP
michael@0 847
michael@0 848 #define LOGREGISTRY(msg) LogRegistryEvent(msg)
michael@0 849
michael@0 850 // for use when monitoring process
michael@0 851 static void LogRegistryEvent(const wchar_t *msg)
michael@0 852 {
michael@0 853 HKEY dummyKey;
michael@0 854 HRESULT hr;
michael@0 855 wchar_t buf[512];
michael@0 856
michael@0 857 wsprintfW(buf, L" log %s", msg);
michael@0 858 hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
michael@0 859 if (SUCCEEDED(hr)) {
michael@0 860 RegCloseKey(dummyKey);
michael@0 861 }
michael@0 862 }
michael@0 863 #else
michael@0 864
michael@0 865 #define LOGREGISTRY(msg)
michael@0 866
michael@0 867 #endif
michael@0 868
michael@0 869 nsresult
michael@0 870 gfxDWriteFontList::InitFontList()
michael@0 871 {
michael@0 872 LOGREGISTRY(L"InitFontList start");
michael@0 873
michael@0 874 mInitialized = false;
michael@0 875
michael@0 876 LARGE_INTEGER frequency; // ticks per second
michael@0 877 LARGE_INTEGER t1, t2, t3; // ticks
michael@0 878 double elapsedTime, upTime;
michael@0 879 char nowTime[256], nowDate[256];
michael@0 880
michael@0 881 if (LOG_FONTINIT_ENABLED()) {
michael@0 882 GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT,
michael@0 883 nullptr, nullptr, nowTime, 256);
michael@0 884 GetDateFormat(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
michael@0 885 }
michael@0 886 upTime = (double) GetTickCount();
michael@0 887 QueryPerformanceFrequency(&frequency);
michael@0 888 QueryPerformanceCounter(&t1);
michael@0 889
michael@0 890 HRESULT hr;
michael@0 891 gfxFontCache *fc = gfxFontCache::GetCache();
michael@0 892 if (fc) {
michael@0 893 fc->AgeAllGenerations();
michael@0 894 }
michael@0 895
michael@0 896 mGDIFontTableAccess = Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading", false);
michael@0 897
michael@0 898 gfxPlatformFontList::InitFontList();
michael@0 899
michael@0 900 mFontSubstitutes.Clear();
michael@0 901 mNonExistingFonts.Clear();
michael@0 902
michael@0 903 QueryPerformanceCounter(&t2);
michael@0 904
michael@0 905 hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 906 GetGdiInterop(getter_AddRefs(mGDIInterop));
michael@0 907 if (FAILED(hr)) {
michael@0 908 return NS_ERROR_FAILURE;
michael@0 909 }
michael@0 910
michael@0 911 LOGREGISTRY(L"InitFontList end");
michael@0 912
michael@0 913 QueryPerformanceCounter(&t3);
michael@0 914
michael@0 915 if (LOG_FONTINIT_ENABLED()) {
michael@0 916 // determine dwrite version
michael@0 917 nsAutoString dwriteVers;
michael@0 918 gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
michael@0 919 LOG_FONTINIT(("InitFontList\n"));
michael@0 920 LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime));
michael@0 921 LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000));
michael@0 922 LOG_FONTINIT(("dwrite version: %s\n",
michael@0 923 NS_ConvertUTF16toUTF8(dwriteVers).get()));
michael@0 924 }
michael@0 925
michael@0 926 elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 927 Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_TOTAL, elapsedTime);
michael@0 928 LOG_FONTINIT(("Total time in InitFontList: %9.3f ms\n", elapsedTime));
michael@0 929 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 930 Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_INIT, elapsedTime);
michael@0 931 LOG_FONTINIT((" --- gfxPlatformFontList init: %9.3f ms\n", elapsedTime));
michael@0 932 elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 933 Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_GDI, elapsedTime);
michael@0 934 LOG_FONTINIT((" --- GdiInterop object: %9.3f ms\n", elapsedTime));
michael@0 935
michael@0 936 return NS_OK;
michael@0 937 }
michael@0 938
michael@0 939 nsresult
michael@0 940 gfxDWriteFontList::DelayedInitFontList()
michael@0 941 {
michael@0 942 LOGREGISTRY(L"DelayedInitFontList start");
michael@0 943
michael@0 944 LARGE_INTEGER frequency; // ticks per second
michael@0 945 LARGE_INTEGER t1, t2, t3; // ticks
michael@0 946 double elapsedTime, upTime;
michael@0 947 char nowTime[256], nowDate[256];
michael@0 948
michael@0 949 if (LOG_FONTINIT_ENABLED()) {
michael@0 950 GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT,
michael@0 951 nullptr, nullptr, nowTime, 256);
michael@0 952 GetDateFormat(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
michael@0 953 }
michael@0 954
michael@0 955 upTime = (double) GetTickCount();
michael@0 956 QueryPerformanceFrequency(&frequency);
michael@0 957 QueryPerformanceCounter(&t1);
michael@0 958
michael@0 959 HRESULT hr;
michael@0 960
michael@0 961 LOGREGISTRY(L"calling GetSystemFontCollection");
michael@0 962 nsRefPtr<IDWriteFontCollection> systemFonts;
michael@0 963 hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 964 GetSystemFontCollection(getter_AddRefs(systemFonts));
michael@0 965 NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
michael@0 966 LOGREGISTRY(L"GetSystemFontCollection done");
michael@0 967
michael@0 968 if (FAILED(hr)) {
michael@0 969 return NS_ERROR_FAILURE;
michael@0 970 }
michael@0 971
michael@0 972 QueryPerformanceCounter(&t2);
michael@0 973
michael@0 974 for (UINT32 i = 0; i < systemFonts->GetFontFamilyCount(); i++) {
michael@0 975 nsRefPtr<IDWriteFontFamily> family;
michael@0 976 systemFonts->GetFontFamily(i, getter_AddRefs(family));
michael@0 977
michael@0 978 nsRefPtr<IDWriteLocalizedStrings> names;
michael@0 979 hr = family->GetFamilyNames(getter_AddRefs(names));
michael@0 980 if (FAILED(hr)) {
michael@0 981 continue;
michael@0 982 }
michael@0 983
michael@0 984 UINT32 englishIdx = 0;
michael@0 985
michael@0 986 BOOL exists;
michael@0 987 hr = names->FindLocaleName(L"en-us", &englishIdx, &exists);
michael@0 988 if (FAILED(hr)) {
michael@0 989 continue;
michael@0 990 }
michael@0 991 if (!exists) {
michael@0 992 // Use 0 index if english is not found.
michael@0 993 englishIdx = 0;
michael@0 994 }
michael@0 995
michael@0 996 AutoFallibleTArray<WCHAR, 32> enName;
michael@0 997 UINT32 length;
michael@0 998
michael@0 999 hr = names->GetStringLength(englishIdx, &length);
michael@0 1000 if (FAILED(hr)) {
michael@0 1001 continue;
michael@0 1002 }
michael@0 1003
michael@0 1004 if (!enName.SetLength(length + 1)) {
michael@0 1005 // Eeep - running out of memory. Unlikely to end well.
michael@0 1006 continue;
michael@0 1007 }
michael@0 1008
michael@0 1009 hr = names->GetString(englishIdx, enName.Elements(), length + 1);
michael@0 1010 if (FAILED(hr)) {
michael@0 1011 continue;
michael@0 1012 }
michael@0 1013
michael@0 1014 nsAutoString name(enName.Elements());
michael@0 1015 BuildKeyNameFromFontName(name);
michael@0 1016
michael@0 1017 nsRefPtr<gfxFontFamily> fam;
michael@0 1018
michael@0 1019 if (mFontFamilies.GetWeak(name)) {
michael@0 1020 continue;
michael@0 1021 }
michael@0 1022
michael@0 1023 nsDependentString familyName(enName.Elements());
michael@0 1024
michael@0 1025 fam = new gfxDWriteFontFamily(familyName, family);
michael@0 1026 if (!fam) {
michael@0 1027 continue;
michael@0 1028 }
michael@0 1029
michael@0 1030 if (mBadUnderlineFamilyNames.Contains(name)) {
michael@0 1031 fam->SetBadUnderlineFamily();
michael@0 1032 }
michael@0 1033 mFontFamilies.Put(name, fam);
michael@0 1034
michael@0 1035 // now add other family name localizations, if present
michael@0 1036 uint32_t nameCount = names->GetCount();
michael@0 1037 uint32_t nameIndex;
michael@0 1038
michael@0 1039 for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
michael@0 1040 UINT32 nameLen;
michael@0 1041 AutoFallibleTArray<WCHAR, 32> localizedName;
michael@0 1042
michael@0 1043 // only add other names
michael@0 1044 if (nameIndex == englishIdx) {
michael@0 1045 continue;
michael@0 1046 }
michael@0 1047
michael@0 1048 hr = names->GetStringLength(nameIndex, &nameLen);
michael@0 1049 if (FAILED(hr)) {
michael@0 1050 continue;
michael@0 1051 }
michael@0 1052
michael@0 1053 if (!localizedName.SetLength(nameLen + 1)) {
michael@0 1054 continue;
michael@0 1055 }
michael@0 1056
michael@0 1057 hr = names->GetString(nameIndex, localizedName.Elements(),
michael@0 1058 nameLen + 1);
michael@0 1059 if (FAILED(hr)) {
michael@0 1060 continue;
michael@0 1061 }
michael@0 1062
michael@0 1063 nsDependentString locName(localizedName.Elements());
michael@0 1064
michael@0 1065 if (!familyName.Equals(locName)) {
michael@0 1066 AddOtherFamilyName(fam, locName);
michael@0 1067 }
michael@0 1068
michael@0 1069 }
michael@0 1070
michael@0 1071 // at this point, all family names have been read in
michael@0 1072 fam->SetOtherFamilyNamesInitialized();
michael@0 1073 }
michael@0 1074
michael@0 1075 mOtherFamilyNamesInitialized = true;
michael@0 1076 GetFontSubstitutes();
michael@0 1077
michael@0 1078 // bug 642093 - DirectWrite does not support old bitmap (.fon)
michael@0 1079 // font files, but a few of these such as "Courier" and "MS Sans Serif"
michael@0 1080 // are frequently specified in shoddy CSS, without appropriate fallbacks.
michael@0 1081 // By mapping these to TrueType equivalents, we provide better consistency
michael@0 1082 // with both pre-DW systems and with IE9, which appears to do the same.
michael@0 1083 GetDirectWriteSubstitutes();
michael@0 1084
michael@0 1085 // bug 551313 - DirectWrite creates a Gill Sans family out of
michael@0 1086 // poorly named members of the Gill Sans MT family containing
michael@0 1087 // only Ultra Bold weights. This causes big problems for pages
michael@0 1088 // using Gill Sans which is usually only available on OSX
michael@0 1089
michael@0 1090 nsAutoString nameGillSans(L"Gill Sans");
michael@0 1091 nsAutoString nameGillSansMT(L"Gill Sans MT");
michael@0 1092 BuildKeyNameFromFontName(nameGillSans);
michael@0 1093 BuildKeyNameFromFontName(nameGillSansMT);
michael@0 1094
michael@0 1095 gfxFontFamily *gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
michael@0 1096 gfxFontFamily *gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
michael@0 1097
michael@0 1098 if (gillSansFamily && gillSansMTFamily) {
michael@0 1099 gillSansFamily->FindStyleVariations();
michael@0 1100 nsTArray<nsRefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList();
michael@0 1101 uint32_t i;
michael@0 1102
michael@0 1103 bool allUltraBold = true;
michael@0 1104 for (i = 0; i < faces.Length(); i++) {
michael@0 1105 // does the face have 'Ultra Bold' in the name?
michael@0 1106 if (faces[i]->Name().Find(NS_LITERAL_STRING("Ultra Bold")) == -1) {
michael@0 1107 allUltraBold = false;
michael@0 1108 break;
michael@0 1109 }
michael@0 1110 }
michael@0 1111
michael@0 1112 // if all the Gill Sans faces are Ultra Bold ==> move faces
michael@0 1113 // for Gill Sans into Gill Sans MT family
michael@0 1114 if (allUltraBold) {
michael@0 1115
michael@0 1116 // add faces to Gill Sans MT
michael@0 1117 for (i = 0; i < faces.Length(); i++) {
michael@0 1118 gillSansMTFamily->AddFontEntry(faces[i]);
michael@0 1119
michael@0 1120 #ifdef PR_LOGGING
michael@0 1121 if (LOG_FONTLIST_ENABLED()) {
michael@0 1122 gfxFontEntry *fe = faces[i];
michael@0 1123 LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)"
michael@0 1124 " with style: %s weight: %d stretch: %d",
michael@0 1125 NS_ConvertUTF16toUTF8(fe->Name()).get(),
michael@0 1126 NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(),
michael@0 1127 (fe->IsItalic()) ? "italic" : "normal",
michael@0 1128 fe->Weight(), fe->Stretch()));
michael@0 1129 }
michael@0 1130 #endif
michael@0 1131 }
michael@0 1132
michael@0 1133 // remove Gills Sans
michael@0 1134 mFontFamilies.Remove(nameGillSans);
michael@0 1135 }
michael@0 1136 }
michael@0 1137
michael@0 1138 nsAdoptingCString classicFamilies =
michael@0 1139 Preferences::GetCString("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families");
michael@0 1140 if (classicFamilies) {
michael@0 1141 nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
michael@0 1142 while (tokenizer.hasMoreTokens()) {
michael@0 1143 NS_ConvertUTF8toUTF16 name(tokenizer.nextToken());
michael@0 1144 BuildKeyNameFromFontName(name);
michael@0 1145 gfxFontFamily *family = mFontFamilies.GetWeak(name);
michael@0 1146 if (family) {
michael@0 1147 static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
michael@0 1148 }
michael@0 1149 }
michael@0 1150 }
michael@0 1151 mForceGDIClassicMaxFontSize =
michael@0 1152 Preferences::GetInt("gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
michael@0 1153 mForceGDIClassicMaxFontSize);
michael@0 1154
michael@0 1155 GetPrefsAndStartLoader();
michael@0 1156
michael@0 1157 LOGREGISTRY(L"DelayedInitFontList end");
michael@0 1158
michael@0 1159 QueryPerformanceCounter(&t3);
michael@0 1160
michael@0 1161 if (LOG_FONTINIT_ENABLED()) {
michael@0 1162 // determine dwrite version
michael@0 1163 nsAutoString dwriteVers;
michael@0 1164 gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
michael@0 1165 LOG_FONTINIT(("DelayedInitFontList\n"));
michael@0 1166 LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime));
michael@0 1167 LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000));
michael@0 1168 LOG_FONTINIT(("dwrite version: %s\n",
michael@0 1169 NS_ConvertUTF16toUTF8(dwriteVers).get()));
michael@0 1170 }
michael@0 1171
michael@0 1172 elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 1173 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL, elapsedTime);
michael@0 1174 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT,
michael@0 1175 systemFonts->GetFontFamilyCount());
michael@0 1176 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_GDI_TABLE, mGDIFontTableAccess);
michael@0 1177 LOG_FONTINIT((
michael@0 1178 "Total time in DelayedInitFontList: %9.3f ms (families: %d, %s)\n",
michael@0 1179 elapsedTime, systemFonts->GetFontFamilyCount(),
michael@0 1180 (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
michael@0 1181
michael@0 1182 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 1183 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT, elapsedTime);
michael@0 1184 LOG_FONTINIT((" --- GetSystemFontCollection: %9.3f ms\n", elapsedTime));
michael@0 1185
michael@0 1186 elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
michael@0 1187 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_ITERATE, elapsedTime);
michael@0 1188 LOG_FONTINIT((" --- iterate over families: %9.3f ms\n", elapsedTime));
michael@0 1189
michael@0 1190 return NS_OK;
michael@0 1191 }
michael@0 1192
michael@0 1193 static void
michael@0 1194 RemoveCharsetFromFontSubstitute(nsAString &aName)
michael@0 1195 {
michael@0 1196 int32_t comma = aName.FindChar(char16_t(','));
michael@0 1197 if (comma >= 0)
michael@0 1198 aName.Truncate(comma);
michael@0 1199 }
michael@0 1200
michael@0 1201 #define MAX_VALUE_NAME 512
michael@0 1202 #define MAX_VALUE_DATA 512
michael@0 1203
michael@0 1204 nsresult
michael@0 1205 gfxDWriteFontList::GetFontSubstitutes()
michael@0 1206 {
michael@0 1207 HKEY hKey;
michael@0 1208 DWORD i, rv, lenAlias, lenActual, valueType;
michael@0 1209 WCHAR aliasName[MAX_VALUE_NAME];
michael@0 1210 WCHAR actualName[MAX_VALUE_DATA];
michael@0 1211
michael@0 1212 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
michael@0 1213 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
michael@0 1214 0, KEY_READ, &hKey) != ERROR_SUCCESS)
michael@0 1215 {
michael@0 1216 return NS_ERROR_FAILURE;
michael@0 1217 }
michael@0 1218
michael@0 1219 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
michael@0 1220 aliasName[0] = 0;
michael@0 1221 lenAlias = ArrayLength(aliasName);
michael@0 1222 actualName[0] = 0;
michael@0 1223 lenActual = sizeof(actualName);
michael@0 1224 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
michael@0 1225 (LPBYTE)actualName, &lenActual);
michael@0 1226
michael@0 1227 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
michael@0 1228 continue;
michael@0 1229 }
michael@0 1230
michael@0 1231 if (aliasName[0] == WCHAR('@')) {
michael@0 1232 continue;
michael@0 1233 }
michael@0 1234
michael@0 1235 nsAutoString substituteName((char16_t*) aliasName);
michael@0 1236 nsAutoString actualFontName((char16_t*) actualName);
michael@0 1237 RemoveCharsetFromFontSubstitute(substituteName);
michael@0 1238 BuildKeyNameFromFontName(substituteName);
michael@0 1239 RemoveCharsetFromFontSubstitute(actualFontName);
michael@0 1240 BuildKeyNameFromFontName(actualFontName);
michael@0 1241 gfxFontFamily *ff;
michael@0 1242 if (!actualFontName.IsEmpty() &&
michael@0 1243 (ff = mFontFamilies.GetWeak(actualFontName))) {
michael@0 1244 mFontSubstitutes.Put(substituteName, ff);
michael@0 1245 } else {
michael@0 1246 mNonExistingFonts.AppendElement(substituteName);
michael@0 1247 }
michael@0 1248 }
michael@0 1249 return NS_OK;
michael@0 1250 }
michael@0 1251
michael@0 1252 struct FontSubstitution {
michael@0 1253 const WCHAR* aliasName;
michael@0 1254 const WCHAR* actualName;
michael@0 1255 };
michael@0 1256
michael@0 1257 static const FontSubstitution sDirectWriteSubs[] = {
michael@0 1258 { L"MS Sans Serif", L"Microsoft Sans Serif" },
michael@0 1259 { L"MS Serif", L"Times New Roman" },
michael@0 1260 { L"Courier", L"Courier New" },
michael@0 1261 { L"Small Fonts", L"Arial" },
michael@0 1262 { L"Roman", L"Times New Roman" },
michael@0 1263 { L"Script", L"Mistral" }
michael@0 1264 };
michael@0 1265
michael@0 1266 void
michael@0 1267 gfxDWriteFontList::GetDirectWriteSubstitutes()
michael@0 1268 {
michael@0 1269 for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
michael@0 1270 const FontSubstitution& sub(sDirectWriteSubs[i]);
michael@0 1271 nsAutoString substituteName((char16_t*)sub.aliasName);
michael@0 1272 BuildKeyNameFromFontName(substituteName);
michael@0 1273 if (nullptr != mFontFamilies.GetWeak(substituteName)) {
michael@0 1274 // don't do the substitution if user actually has a usable font
michael@0 1275 // with this name installed
michael@0 1276 continue;
michael@0 1277 }
michael@0 1278 nsAutoString actualFontName((char16_t*)sub.actualName);
michael@0 1279 BuildKeyNameFromFontName(actualFontName);
michael@0 1280 gfxFontFamily *ff;
michael@0 1281 if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
michael@0 1282 mFontSubstitutes.Put(substituteName, ff);
michael@0 1283 } else {
michael@0 1284 mNonExistingFonts.AppendElement(substituteName);
michael@0 1285 }
michael@0 1286 }
michael@0 1287 }
michael@0 1288
michael@0 1289 bool
michael@0 1290 gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName,
michael@0 1291 nsAString& aFamilyName)
michael@0 1292 {
michael@0 1293 gfxFontFamily *family = FindFamily(aFontName);
michael@0 1294 if (family) {
michael@0 1295 family->LocalizedName(aFamilyName);
michael@0 1296 return true;
michael@0 1297 }
michael@0 1298
michael@0 1299 return false;
michael@0 1300 }
michael@0 1301
michael@0 1302 gfxFontFamily* gfxDWriteFontList::FindFamily(const nsAString& aFamily)
michael@0 1303 {
michael@0 1304 if (!mInitialized) {
michael@0 1305 mInitialized = true;
michael@0 1306 DelayedInitFontList();
michael@0 1307 }
michael@0 1308
michael@0 1309 return gfxPlatformFontList::FindFamily(aFamily);
michael@0 1310 }
michael@0 1311
michael@0 1312 void
michael@0 1313 gfxDWriteFontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
michael@0 1314 {
michael@0 1315 if (!mInitialized) {
michael@0 1316 mInitialized = true;
michael@0 1317 DelayedInitFontList();
michael@0 1318 }
michael@0 1319
michael@0 1320 return gfxPlatformFontList::GetFontFamilyList(aFamilyArray);
michael@0 1321 }
michael@0 1322
michael@0 1323 bool
michael@0 1324 gfxDWriteFontList::ResolveFontName(const nsAString& aFontName,
michael@0 1325 nsAString& aResolvedFontName)
michael@0 1326 {
michael@0 1327 if (!mInitialized) {
michael@0 1328 mInitialized = true;
michael@0 1329 DelayedInitFontList();
michael@0 1330 }
michael@0 1331
michael@0 1332 nsAutoString keyName(aFontName);
michael@0 1333 BuildKeyNameFromFontName(keyName);
michael@0 1334
michael@0 1335 gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
michael@0 1336 if (ff) {
michael@0 1337 aResolvedFontName = ff->Name();
michael@0 1338 return true;
michael@0 1339 }
michael@0 1340
michael@0 1341 if (mNonExistingFonts.Contains(keyName)) {
michael@0 1342 return false;
michael@0 1343 }
michael@0 1344
michael@0 1345 return gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName);
michael@0 1346 }
michael@0 1347
michael@0 1348 void
michael@0 1349 gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1350 FontListSizes* aSizes) const
michael@0 1351 {
michael@0 1352 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 1353
michael@0 1354 aSizes->mFontListSize +=
michael@0 1355 mFontSubstitutes.SizeOfExcludingThis(SizeOfFamilyNameEntryExcludingThis,
michael@0 1356 aMallocSizeOf);
michael@0 1357
michael@0 1358 aSizes->mFontListSize +=
michael@0 1359 mNonExistingFonts.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1360 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
michael@0 1361 aSizes->mFontListSize +=
michael@0 1362 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 1363 }
michael@0 1364 }
michael@0 1365
michael@0 1366 void
michael@0 1367 gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1368 FontListSizes* aSizes) const
michael@0 1369 {
michael@0 1370 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 1371 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 1372 }
michael@0 1373
michael@0 1374 static HRESULT GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName)
michael@0 1375 {
michael@0 1376 HRESULT hr;
michael@0 1377 nsRefPtr<IDWriteFontFamily> family;
michael@0 1378
michael@0 1379 // clean out previous value
michael@0 1380 aFamilyName.Truncate();
michael@0 1381
michael@0 1382 hr = aFont->GetFontFamily(getter_AddRefs(family));
michael@0 1383 if (FAILED(hr)) {
michael@0 1384 return hr;
michael@0 1385 }
michael@0 1386
michael@0 1387 nsRefPtr<IDWriteLocalizedStrings> familyNames;
michael@0 1388
michael@0 1389 hr = family->GetFamilyNames(getter_AddRefs(familyNames));
michael@0 1390 if (FAILED(hr)) {
michael@0 1391 return hr;
michael@0 1392 }
michael@0 1393
michael@0 1394 UINT32 index = 0;
michael@0 1395 BOOL exists = false;
michael@0 1396
michael@0 1397 hr = familyNames->FindLocaleName(L"en-us", &index, &exists);
michael@0 1398 if (FAILED(hr)) {
michael@0 1399 return hr;
michael@0 1400 }
michael@0 1401
michael@0 1402 // If the specified locale doesn't exist, select the first on the list.
michael@0 1403 if (!exists) {
michael@0 1404 index = 0;
michael@0 1405 }
michael@0 1406
michael@0 1407 AutoFallibleTArray<WCHAR, 32> name;
michael@0 1408 UINT32 length;
michael@0 1409
michael@0 1410 hr = familyNames->GetStringLength(index, &length);
michael@0 1411 if (FAILED(hr)) {
michael@0 1412 return hr;
michael@0 1413 }
michael@0 1414
michael@0 1415 if (!name.SetLength(length + 1)) {
michael@0 1416 return E_FAIL;
michael@0 1417 }
michael@0 1418 hr = familyNames->GetString(index, name.Elements(), length + 1);
michael@0 1419 if (FAILED(hr)) {
michael@0 1420 return hr;
michael@0 1421 }
michael@0 1422
michael@0 1423 aFamilyName.Assign(name.Elements());
michael@0 1424 return S_OK;
michael@0 1425 }
michael@0 1426
michael@0 1427 // bug 705594 - the method below doesn't actually do any "drawing", it's only
michael@0 1428 // used to invoke the DirectWrite layout engine to determine the fallback font
michael@0 1429 // for a given character.
michael@0 1430
michael@0 1431 IFACEMETHODIMP FontFallbackRenderer::DrawGlyphRun(
michael@0 1432 void* clientDrawingContext,
michael@0 1433 FLOAT baselineOriginX,
michael@0 1434 FLOAT baselineOriginY,
michael@0 1435 DWRITE_MEASURING_MODE measuringMode,
michael@0 1436 DWRITE_GLYPH_RUN const* glyphRun,
michael@0 1437 DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
michael@0 1438 IUnknown* clientDrawingEffect
michael@0 1439 )
michael@0 1440 {
michael@0 1441 if (!mSystemFonts) {
michael@0 1442 return E_FAIL;
michael@0 1443 }
michael@0 1444
michael@0 1445 HRESULT hr = S_OK;
michael@0 1446
michael@0 1447 nsRefPtr<IDWriteFont> font;
michael@0 1448 hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
michael@0 1449 getter_AddRefs(font));
michael@0 1450 if (FAILED(hr)) {
michael@0 1451 return hr;
michael@0 1452 }
michael@0 1453
michael@0 1454 // copy the family name
michael@0 1455 hr = GetFamilyName(font, mFamilyName);
michael@0 1456 if (FAILED(hr)) {
michael@0 1457 return hr;
michael@0 1458 }
michael@0 1459
michael@0 1460 // Arial is used as the default fallback font
michael@0 1461 // so if it matches ==> no font found
michael@0 1462 if (mFamilyName.EqualsLiteral("Arial")) {
michael@0 1463 mFamilyName.Truncate();
michael@0 1464 return E_FAIL;
michael@0 1465 }
michael@0 1466 return hr;
michael@0 1467 }
michael@0 1468
michael@0 1469 gfxFontEntry*
michael@0 1470 gfxDWriteFontList::GlobalFontFallback(const uint32_t aCh,
michael@0 1471 int32_t aRunScript,
michael@0 1472 const gfxFontStyle* aMatchStyle,
michael@0 1473 uint32_t& aCmapCount,
michael@0 1474 gfxFontFamily** aMatchedFamily)
michael@0 1475 {
michael@0 1476 bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
michael@0 1477
michael@0 1478 if (useCmaps) {
michael@0 1479 return gfxPlatformFontList::GlobalFontFallback(aCh,
michael@0 1480 aRunScript,
michael@0 1481 aMatchStyle,
michael@0 1482 aCmapCount,
michael@0 1483 aMatchedFamily);
michael@0 1484 }
michael@0 1485
michael@0 1486 HRESULT hr;
michael@0 1487
michael@0 1488 nsRefPtr<IDWriteFactory> dwFactory =
michael@0 1489 gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
michael@0 1490 if (!dwFactory) {
michael@0 1491 return nullptr;
michael@0 1492 }
michael@0 1493
michael@0 1494 // initialize fallback renderer
michael@0 1495 if (!mFallbackRenderer) {
michael@0 1496 mFallbackRenderer = new FontFallbackRenderer(dwFactory);
michael@0 1497 }
michael@0 1498
michael@0 1499 // initialize text format
michael@0 1500 if (!mFallbackFormat) {
michael@0 1501 hr = dwFactory->CreateTextFormat(L"Arial", nullptr,
michael@0 1502 DWRITE_FONT_WEIGHT_REGULAR,
michael@0 1503 DWRITE_FONT_STYLE_NORMAL,
michael@0 1504 DWRITE_FONT_STRETCH_NORMAL,
michael@0 1505 72.0f, L"en-us",
michael@0 1506 getter_AddRefs(mFallbackFormat));
michael@0 1507 if (FAILED(hr)) {
michael@0 1508 return nullptr;
michael@0 1509 }
michael@0 1510 }
michael@0 1511
michael@0 1512 // set up string with fallback character
michael@0 1513 wchar_t str[16];
michael@0 1514 uint32_t strLen;
michael@0 1515
michael@0 1516 if (IS_IN_BMP(aCh)) {
michael@0 1517 str[0] = static_cast<wchar_t> (aCh);
michael@0 1518 str[1] = 0;
michael@0 1519 strLen = 1;
michael@0 1520 } else {
michael@0 1521 str[0] = static_cast<wchar_t> (H_SURROGATE(aCh));
michael@0 1522 str[1] = static_cast<wchar_t> (L_SURROGATE(aCh));
michael@0 1523 str[2] = 0;
michael@0 1524 strLen = 2;
michael@0 1525 }
michael@0 1526
michael@0 1527 // set up layout
michael@0 1528 nsRefPtr<IDWriteTextLayout> fallbackLayout;
michael@0 1529
michael@0 1530 hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat,
michael@0 1531 200.0f, 200.0f,
michael@0 1532 getter_AddRefs(fallbackLayout));
michael@0 1533 if (FAILED(hr)) {
michael@0 1534 return nullptr;
michael@0 1535 }
michael@0 1536
michael@0 1537 // call the draw method to invoke the DirectWrite layout functions
michael@0 1538 // which determine the fallback font
michael@0 1539 hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
michael@0 1540 if (FAILED(hr)) {
michael@0 1541 return nullptr;
michael@0 1542 }
michael@0 1543
michael@0 1544 gfxFontFamily *family = FindFamily(mFallbackRenderer->FallbackFamilyName());
michael@0 1545 if (family) {
michael@0 1546 gfxFontEntry *fontEntry;
michael@0 1547 bool needsBold; // ignored in the system fallback case
michael@0 1548 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
michael@0 1549 if (fontEntry && fontEntry->TestCharacterMap(aCh)) {
michael@0 1550 *aMatchedFamily = family;
michael@0 1551 return fontEntry;
michael@0 1552 }
michael@0 1553 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true);
michael@0 1554 }
michael@0 1555
michael@0 1556 return nullptr;
michael@0 1557 }
michael@0 1558
michael@0 1559 // used to load system-wide font info on off-main thread
michael@0 1560 class DirectWriteFontInfo : public FontInfoData {
michael@0 1561 public:
michael@0 1562 DirectWriteFontInfo(bool aLoadOtherNames,
michael@0 1563 bool aLoadFaceNames,
michael@0 1564 bool aLoadCmaps) :
michael@0 1565 FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
michael@0 1566 {}
michael@0 1567
michael@0 1568 virtual ~DirectWriteFontInfo() {}
michael@0 1569
michael@0 1570 // loads font data for all members of a given family
michael@0 1571 virtual void LoadFontFamilyData(const nsAString& aFamilyName);
michael@0 1572
michael@0 1573 nsRefPtr<IDWriteFontCollection> mSystemFonts;
michael@0 1574 };
michael@0 1575
michael@0 1576 void
michael@0 1577 DirectWriteFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
michael@0 1578 {
michael@0 1579 // lookup the family
michael@0 1580 nsAutoTArray<wchar_t, 32> famName;
michael@0 1581
michael@0 1582 uint32_t len = aFamilyName.Length();
michael@0 1583 famName.SetLength(len + 1);
michael@0 1584 memcpy(famName.Elements(), aFamilyName.BeginReading(), len * sizeof(char16_t));
michael@0 1585 famName[len] = 0;
michael@0 1586
michael@0 1587 HRESULT hr;
michael@0 1588 BOOL exists = false;
michael@0 1589
michael@0 1590 uint32_t index;
michael@0 1591 hr = mSystemFonts->FindFamilyName(famName.Elements(), &index, &exists);
michael@0 1592 if (FAILED(hr) || !exists) {
michael@0 1593 return;
michael@0 1594 }
michael@0 1595
michael@0 1596 nsRefPtr<IDWriteFontFamily> family;
michael@0 1597 mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
michael@0 1598 if (!family) {
michael@0 1599 return;
michael@0 1600 }
michael@0 1601
michael@0 1602 // later versions of DirectWrite support querying the fullname/psname
michael@0 1603 bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
michael@0 1604
michael@0 1605 for (uint32_t i = 0; i < family->GetFontCount(); i++) {
michael@0 1606 // get the font
michael@0 1607 nsRefPtr<IDWriteFont> dwFont;
michael@0 1608 hr = family->GetFont(i, getter_AddRefs(dwFont));
michael@0 1609 if (FAILED(hr)) {
michael@0 1610 // This should never happen.
michael@0 1611 NS_WARNING("Failed to get existing font from family.");
michael@0 1612 continue;
michael@0 1613 }
michael@0 1614
michael@0 1615 if (dwFont->GetSimulations() & DWRITE_FONT_SIMULATIONS_OBLIQUE) {
michael@0 1616 // We don't want these.
michael@0 1617 continue;
michael@0 1618 }
michael@0 1619
michael@0 1620 mLoadStats.fonts++;
michael@0 1621
michael@0 1622 // get the name of the face
michael@0 1623 nsString fullID(aFamilyName);
michael@0 1624 nsAutoString fontName;
michael@0 1625 hr = GetDirectWriteFontName(dwFont, fontName);
michael@0 1626 if (FAILED(hr)) {
michael@0 1627 continue;
michael@0 1628 }
michael@0 1629 fullID.Append(NS_LITERAL_STRING(" "));
michael@0 1630 fullID.Append(fontName);
michael@0 1631
michael@0 1632 FontFaceData fontData;
michael@0 1633 bool haveData = true;
michael@0 1634 nsRefPtr<IDWriteFontFace> dwFontFace;
michael@0 1635
michael@0 1636 if (mLoadFaceNames) {
michael@0 1637 // try to load using DirectWrite first
michael@0 1638 if (loadFaceNamesUsingDirectWrite) {
michael@0 1639 hr = GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
michael@0 1640 if (FAILED(hr)) {
michael@0 1641 loadFaceNamesUsingDirectWrite = false;
michael@0 1642 }
michael@0 1643 hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
michael@0 1644 if (FAILED(hr)) {
michael@0 1645 loadFaceNamesUsingDirectWrite = false;
michael@0 1646 }
michael@0 1647 }
michael@0 1648
michael@0 1649 // if DirectWrite read fails, load directly from name table
michael@0 1650 if (!loadFaceNamesUsingDirectWrite) {
michael@0 1651 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
michael@0 1652 if (SUCCEEDED(hr)) {
michael@0 1653 uint32_t kNAME =
michael@0 1654 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
michael@0 1655 const char *nameData;
michael@0 1656 BOOL exists;
michael@0 1657 void* ctx;
michael@0 1658 uint32_t nameSize;
michael@0 1659
michael@0 1660 hr = dwFontFace->TryGetFontTable(
michael@0 1661 kNAME,
michael@0 1662 (const void**)&nameData, &nameSize, &ctx, &exists);
michael@0 1663
michael@0 1664 if (SUCCEEDED(hr) && nameData && nameSize > 0) {
michael@0 1665 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
michael@0 1666 gfxFontUtils::NAME_ID_FULL,
michael@0 1667 fontData.mFullName);
michael@0 1668 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
michael@0 1669 gfxFontUtils::NAME_ID_POSTSCRIPT,
michael@0 1670 fontData.mPostscriptName);
michael@0 1671 dwFontFace->ReleaseFontTable(ctx);
michael@0 1672 }
michael@0 1673 }
michael@0 1674 }
michael@0 1675
michael@0 1676 haveData = !fontData.mPostscriptName.IsEmpty() ||
michael@0 1677 !fontData.mFullName.IsEmpty();
michael@0 1678 if (haveData) {
michael@0 1679 mLoadStats.facenames++;
michael@0 1680 }
michael@0 1681 }
michael@0 1682
michael@0 1683 // cmaps
michael@0 1684 if (mLoadCmaps) {
michael@0 1685 if (!dwFontFace) {
michael@0 1686 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
michael@0 1687 if (!SUCCEEDED(hr)) {
michael@0 1688 continue;
michael@0 1689 }
michael@0 1690 }
michael@0 1691
michael@0 1692 uint32_t kCMAP =
michael@0 1693 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
michael@0 1694 const uint8_t *cmapData;
michael@0 1695 BOOL exists;
michael@0 1696 void* ctx;
michael@0 1697 uint32_t cmapSize;
michael@0 1698
michael@0 1699 hr = dwFontFace->TryGetFontTable(kCMAP,
michael@0 1700 (const void**)&cmapData, &cmapSize, &ctx, &exists);
michael@0 1701
michael@0 1702 if (SUCCEEDED(hr)) {
michael@0 1703 bool cmapLoaded = false;
michael@0 1704 bool unicodeFont = false, symbolFont = false;
michael@0 1705 nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
michael@0 1706 uint32_t offset;
michael@0 1707
michael@0 1708 if (cmapData &&
michael@0 1709 cmapSize > 0 &&
michael@0 1710 NS_SUCCEEDED(
michael@0 1711 gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
michael@0 1712 offset, unicodeFont, symbolFont))) {
michael@0 1713 fontData.mCharacterMap = charmap;
michael@0 1714 fontData.mUVSOffset = offset;
michael@0 1715 fontData.mSymbolFont = symbolFont;
michael@0 1716 cmapLoaded = true;
michael@0 1717 mLoadStats.cmaps++;
michael@0 1718 }
michael@0 1719 dwFontFace->ReleaseFontTable(ctx);
michael@0 1720 haveData = haveData || cmapLoaded;
michael@0 1721 }
michael@0 1722 }
michael@0 1723
michael@0 1724 // if have data, load
michael@0 1725 if (haveData) {
michael@0 1726 mFontFaceData.Put(fullID, fontData);
michael@0 1727 }
michael@0 1728 }
michael@0 1729 }
michael@0 1730
michael@0 1731 already_AddRefed<FontInfoData>
michael@0 1732 gfxDWriteFontList::CreateFontInfoData()
michael@0 1733 {
michael@0 1734 bool loadCmaps = !UsesSystemFallback() ||
michael@0 1735 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
michael@0 1736
michael@0 1737 nsRefPtr<DirectWriteFontInfo> fi =
michael@0 1738 new DirectWriteFontInfo(false, NeedFullnamePostscriptNames(), loadCmaps);
michael@0 1739 gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
michael@0 1740 GetSystemFontCollection(getter_AddRefs(fi->mSystemFonts));
michael@0 1741
michael@0 1742 return fi.forget();
michael@0 1743 }

mercurial