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