Bug 1567650 [wpt PR 17950] - [ElementTiming] Replace responseEnd with loadTime, a...
[gecko.git] / gfx / thebes / gfxGDIFontList.cpp
blobf36f46ff1593afe902d5f5f7db98d5403a5d8ab1
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/DebugOnly.h"
7 #include <algorithm>
9 #include "mozilla/Logging.h"
10 #include "mozilla/Sprintf.h"
12 #include "gfxGDIFontList.h"
13 #include "gfxWindowsPlatform.h"
14 #include "gfxUserFontSet.h"
15 #include "gfxFontUtils.h"
16 #include "gfxGDIFont.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsTArray.h"
20 #include "nsUnicharUtils.h"
22 #include "nsDirectoryServiceUtils.h"
23 #include "nsDirectoryServiceDefs.h"
24 #include "nsAppDirectoryServiceDefs.h"
25 #include "nsISimpleEnumerator.h"
26 #include "nsIWindowsRegKey.h"
27 #include "gfxFontConstants.h"
28 #include "GeckoProfiler.h"
30 #include "mozilla/MemoryReporting.h"
31 #include "mozilla/Telemetry.h"
33 #include <usp10.h>
35 using namespace mozilla;
36 using namespace mozilla::gfx;
38 #define ROUND(x) floor((x) + 0.5)
40 #define LOG_FONTLIST(args) \
41 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
42 #define LOG_FONTLIST_ENABLED() \
43 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
45 #define LOG_CMAPDATA_ENABLED() \
46 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
48 static __inline void BuildKeyNameFromFontName(nsAString& aName) {
49 if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1);
50 ToLowerCase(aName);
53 // Implementation of gfxPlatformFontList for Win32 GDI,
54 // using GDI font enumeration APIs to get the list of fonts
56 class WinUserFontData : public gfxUserFontData {
57 public:
58 explicit WinUserFontData(HANDLE aFontRef) : mFontRef(aFontRef) {}
60 virtual ~WinUserFontData() {
61 DebugOnly<BOOL> success;
62 success = RemoveFontMemResourceEx(mFontRef);
63 #if DEBUG
64 if (!success) {
65 char buf[256];
66 SprintfLiteral(
67 buf,
68 "error deleting font handle (%p) - RemoveFontMemResourceEx failed",
69 mFontRef);
70 NS_ASSERTION(success, buf);
72 #endif
75 HANDLE mFontRef;
78 BYTE FontTypeToOutPrecision(uint8_t fontType) {
79 BYTE ret;
80 switch (fontType) {
81 case GFX_FONT_TYPE_TT_OPENTYPE:
82 case GFX_FONT_TYPE_TRUETYPE:
83 ret = OUT_TT_ONLY_PRECIS;
84 break;
85 case GFX_FONT_TYPE_PS_OPENTYPE:
86 ret = OUT_PS_ONLY_PRECIS;
87 break;
88 case GFX_FONT_TYPE_TYPE1:
89 ret = OUT_OUTLINE_PRECIS;
90 break;
91 case GFX_FONT_TYPE_RASTER:
92 ret = OUT_RASTER_PRECIS;
93 break;
94 case GFX_FONT_TYPE_DEVICE:
95 ret = OUT_DEVICE_PRECIS;
96 break;
97 default:
98 ret = OUT_DEFAULT_PRECIS;
100 return ret;
103 /***************************************************************
105 * GDIFontEntry
109 GDIFontEntry::GDIFontEntry(const nsACString& aFaceName,
110 gfxWindowsFontType aFontType, SlantStyleRange aStyle,
111 WeightRange aWeight, StretchRange aStretch,
112 gfxUserFontData* aUserFontData)
113 : gfxFontEntry(aFaceName),
114 mFontType(aFontType),
115 mForceGDI(false),
116 mUnicodeRanges() {
117 mUserFontData.reset(aUserFontData);
118 mStyleRange = aStyle;
119 mWeightRange = aWeight;
120 mStretchRange = aStretch;
121 if (IsType1()) mForceGDI = true;
122 mIsDataUserFont = aUserFontData != nullptr;
124 InitLogFont(aFaceName, aFontType);
127 gfxFontEntry* GDIFontEntry::Clone() const {
128 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
129 return new GDIFontEntry(Name(), mFontType, SlantStyle(), Weight(), Stretch(),
130 nullptr);
133 nsresult GDIFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
134 AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER);
136 // attempt this once, if errors occur leave a blank cmap
137 if (mCharacterMap) {
138 return NS_OK;
141 // skip non-SFNT fonts completely
142 if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE &&
143 mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
144 mFontType != GFX_FONT_TYPE_TRUETYPE) {
145 mCharacterMap = new gfxCharacterMap();
146 mCharacterMap->mBuildOnTheFly = true;
147 return NS_ERROR_FAILURE;
150 RefPtr<gfxCharacterMap> charmap;
151 nsresult rv;
153 if (aFontInfoData &&
154 (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
155 rv = NS_OK;
156 } else {
157 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
158 charmap = new gfxCharacterMap();
159 AutoTArray<uint8_t, 16384> cmap;
160 rv = CopyFontTable(kCMAP, cmap);
162 if (NS_SUCCEEDED(rv)) {
163 rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), *charmap,
164 mUVSOffset);
168 mHasCmapTable = NS_SUCCEEDED(rv);
169 if (mHasCmapTable) {
170 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
171 mCharacterMap = pfl->FindCharMap(charmap);
172 } else {
173 // if error occurred, initialize to null cmap
174 mCharacterMap = new gfxCharacterMap();
175 // For fonts where we failed to read the character map,
176 // we can take a slow path to look up glyphs character by character
177 mCharacterMap->mBuildOnTheFly = true;
180 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
181 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
182 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
183 if (LOG_CMAPDATA_ENABLED()) {
184 char prefix[256];
185 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
186 charmap->Dump(prefix, eGfxLog_cmapdata);
189 return rv;
192 gfxFont* GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
193 return new gfxGDIFont(this, aFontStyle);
196 nsresult GDIFontEntry::CopyFontTable(uint32_t aTableTag,
197 nsTArray<uint8_t>& aBuffer) {
198 if (!IsTrueType()) {
199 return NS_ERROR_FAILURE;
202 AutoDC dc;
203 AutoSelectFont font(dc.GetDC(), &mLogFont);
204 if (font.IsValid()) {
205 uint32_t tableSize = ::GetFontData(
206 dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0, nullptr, 0);
207 if (tableSize != GDI_ERROR) {
208 if (aBuffer.SetLength(tableSize, fallible)) {
209 ::GetFontData(dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0,
210 aBuffer.Elements(), tableSize);
211 return NS_OK;
213 return NS_ERROR_OUT_OF_MEMORY;
216 return NS_ERROR_FAILURE;
219 already_AddRefed<UnscaledFontGDI> GDIFontEntry::LookupUnscaledFont(
220 HFONT aFont) {
221 RefPtr<UnscaledFontGDI> unscaledFont(mUnscaledFont);
222 if (!unscaledFont) {
223 LOGFONT lf;
224 GetObject(aFont, sizeof(LOGFONT), &lf);
225 unscaledFont = new UnscaledFontGDI(lf);
226 mUnscaledFont = unscaledFont;
229 return unscaledFont.forget();
232 void GDIFontEntry::FillLogFont(LOGFONTW* aLogFont, LONG aWeight,
233 gfxFloat aSize) {
234 memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
236 aLogFont->lfHeight = (LONG)-ROUND(aSize);
238 if (aLogFont->lfHeight == 0) {
239 aLogFont->lfHeight = -1;
242 // If a non-zero weight is passed in, use this to override the original
243 // weight in the entry's logfont. This is used to control synthetic bolding
244 // for installed families with no bold face, and for downloaded fonts
245 // (but NOT for local user fonts, because it could cause a different,
246 // glyph-incompatible face to be used)
247 if (aWeight != 0) {
248 aLogFont->lfWeight = aWeight;
251 // for non-local() user fonts, we never want to apply italics here;
252 // if the face is described as italic, we should use it as-is,
253 // and if it's not, but then the element is styled italic, we'll use
254 // a cairo transform to create fake italic (oblique)
255 if (mIsDataUserFont) {
256 aLogFont->lfItalic = 0;
260 #define MISSING_GLYPH \
261 0x1F // glyph index returned for missing characters
262 // on WinXP with .fon fonts, but not Type1 (.pfb)
264 bool GDIFontEntry::TestCharacterMap(uint32_t aCh) {
265 if (!mCharacterMap) {
266 ReadCMAP();
267 NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
270 if (mCharacterMap->mBuildOnTheFly) {
271 if (aCh > 0xFFFF) return false;
273 // previous code was using the group style
274 gfxFontStyle fakeStyle;
275 if (!IsUpright()) {
276 fakeStyle.style = FontSlantStyle::Italic();
278 fakeStyle.weight = Weight().Min();
280 RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, nullptr);
281 if (!tempFont || !tempFont->Valid()) return false;
282 gfxGDIFont* font = static_cast<gfxGDIFont*>(tempFont.get());
284 HDC dc = GetDC((HWND) nullptr);
285 SetGraphicsMode(dc, GM_ADVANCED);
286 HFONT hfont = font->GetHFONT();
287 HFONT oldFont = (HFONT)SelectObject(dc, hfont);
289 wchar_t str[1] = {(wchar_t)aCh};
290 WORD glyph[1];
292 bool hasGlyph = false;
294 // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a
295 // missing glyph or 0x1F in other cases to indicate the "invalid"
296 // glyph. Map both cases to "not found"
297 if (IsType1() || mForceGDI) {
298 // Type1 fonts and uniscribe APIs don't get along.
299 // ScriptGetCMap will return E_HANDLE
300 DWORD ret =
301 GetGlyphIndicesW(dc, str, 1, glyph, GGI_MARK_NONEXISTING_GLYPHS);
302 if (ret != GDI_ERROR && glyph[0] != 0xFFFF &&
303 (IsType1() || glyph[0] != MISSING_GLYPH)) {
304 hasGlyph = true;
306 } else {
307 // ScriptGetCMap works better than GetGlyphIndicesW
308 // for things like bitmap/vector fonts
309 SCRIPT_CACHE sc = nullptr;
310 HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph);
311 if (rv == S_OK) hasGlyph = true;
314 SelectObject(dc, oldFont);
315 ReleaseDC(nullptr, dc);
317 if (hasGlyph) {
318 mCharacterMap->set(aCh);
319 return true;
321 } else {
322 // font had a cmap so simply check that
323 return mCharacterMap->test(aCh);
326 return false;
329 void GDIFontEntry::InitLogFont(const nsACString& aName,
330 gfxWindowsFontType aFontType) {
331 #define CLIP_TURNOFF_FONTASSOCIATION 0x40
333 mLogFont.lfHeight = -1;
335 // Fill in logFont structure
336 mLogFont.lfWidth = 0;
337 mLogFont.lfEscapement = 0;
338 mLogFont.lfOrientation = 0;
339 mLogFont.lfUnderline = FALSE;
340 mLogFont.lfStrikeOut = FALSE;
341 mLogFont.lfCharSet = DEFAULT_CHARSET;
342 mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType);
343 mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
344 mLogFont.lfQuality = DEFAULT_QUALITY;
345 mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
346 // always force lfItalic if we want it. Font selection code will
347 // do its best to give us an italic font entry, but if no face exists
348 // it may give us a regular one based on weight. Windows should
349 // do fake italic for us in that case.
350 mLogFont.lfItalic = !IsUpright();
351 mLogFont.lfWeight = Weight().Min().ToIntRounded();
353 NS_ConvertUTF8toUTF16 name(aName);
354 int len = std::min<int>(name.Length(), LF_FACESIZE - 1);
355 memcpy(&mLogFont.lfFaceName, name.BeginReading(), len * sizeof(char16_t));
356 mLogFont.lfFaceName[len] = '\0';
359 GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsACString& aName,
360 gfxWindowsFontType aFontType,
361 SlantStyleRange aStyle,
362 WeightRange aWeight,
363 StretchRange aStretch,
364 gfxUserFontData* aUserFontData) {
365 // jtdfix - need to set charset, unicode ranges, pitch/family
367 GDIFontEntry* fe = new GDIFontEntry(aName, aFontType, aStyle, aWeight,
368 aStretch, aUserFontData);
370 return fe;
373 void GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
374 FontListSizes* aSizes) const {
375 aSizes->mFontListSize += aMallocSizeOf(this);
376 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
379 /***************************************************************
381 * GDIFontFamily
385 static bool ShouldIgnoreItalicStyle(const nsACString& aName) {
386 // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
387 // non-italic style glyphs as Japanese characters. However, using it
388 // causes serious problem if web pages wants some elements to be
389 // different style from others only with font-style. For example,
390 // <em> and <i> should be rendered as italic in the default style.
391 return aName.EqualsLiteral("Meiryo") ||
392 aName.EqualsLiteral(
393 "\xe3\x83\xa1\xe3\x82\xa4\xe3\x83\xaa\xe3\x82\xaa");
396 int CALLBACK GDIFontFamily::FamilyAddStylesProc(
397 const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
398 DWORD fontType, LPARAM data) {
399 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
400 LOGFONTW logFont = lpelfe->elfLogFont;
401 GDIFontFamily* ff = reinterpret_cast<GDIFontFamily*>(data);
403 if (logFont.lfItalic && ShouldIgnoreItalicStyle(ff->mName)) {
404 return 1;
407 // Some fonts claim to support things > 900, but we don't so clamp the sizes
408 logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900));
410 gfxWindowsFontType feType =
411 GDIFontEntry::DetermineFontType(metrics, fontType);
413 GDIFontEntry* fe = nullptr;
414 for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
415 fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
416 if (feType > fe->mFontType) {
417 // if the new type is better than the old one, remove the old entries
418 ff->mAvailableFonts.RemoveElementAt(i);
419 --i;
420 } else if (feType < fe->mFontType) {
421 // otherwise if the new type is worse, skip it
422 return 1;
426 for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
427 fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
428 // check if we already know about this face
429 if (fe->Weight().Min() == FontWeight(int32_t(logFont.lfWeight)) &&
430 fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
431 // update the charset bit here since this could be different
432 // XXX Can we still do this now that we store mCharset
433 // on the font family rather than the font entry?
434 ff->mCharset.set(metrics.tmCharSet);
435 return 1;
439 // We can't set the hasItalicFace flag correctly here,
440 // because we might not have seen the family's italic face(s) yet.
441 // So we'll set that flag for all members after loading all the faces.
442 auto italicStyle = (logFont.lfItalic == 0xFF ? FontSlantStyle::Italic()
443 : FontSlantStyle::Normal());
444 fe = GDIFontEntry::CreateFontEntry(
445 NS_ConvertUTF16toUTF8(lpelfe->elfFullName), feType,
446 SlantStyleRange(italicStyle),
447 WeightRange(FontWeight(int32_t(logFont.lfWeight))),
448 StretchRange(FontStretch::Normal()), nullptr);
449 if (!fe) return 1;
451 ff->AddFontEntry(fe);
453 if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
454 nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
455 nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 &&
456 nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) {
457 // set the unicode ranges
458 uint32_t x = 0;
459 for (uint32_t i = 0; i < 4; ++i) {
460 DWORD range = nmetrics->ntmFontSig.fsUsb[i];
461 for (uint32_t k = 0; k < 32; ++k) {
462 fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0);
467 if (LOG_FONTLIST_ENABLED()) {
468 LOG_FONTLIST(
469 ("(fontlist) added (%s) to family (%s)"
470 " with style: %s weight: %d stretch: normal",
471 fe->Name().get(), ff->Name().get(),
472 (logFont.lfItalic == 0xff) ? "italic" : "normal", logFont.lfWeight));
474 return 1;
477 void GDIFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
478 if (mHasStyles) return;
479 mHasStyles = true;
481 HDC hdc = GetDC(nullptr);
482 SetGraphicsMode(hdc, GM_ADVANCED);
484 LOGFONTW logFont;
485 memset(&logFont, 0, sizeof(LOGFONTW));
486 logFont.lfCharSet = DEFAULT_CHARSET;
487 logFont.lfPitchAndFamily = 0;
488 NS_ConvertUTF8toUTF16 name(mName);
489 uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
490 memcpy(logFont.lfFaceName, name.get(), l * sizeof(char16_t));
492 EnumFontFamiliesExW(hdc, &logFont,
493 (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc,
494 (LPARAM)this, 0);
495 if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) {
496 LOG_FONTLIST(
497 ("(fontlist) no styles available in family \"%s\"", mName.get()));
500 ReleaseDC(nullptr, hdc);
502 if (mIsBadUnderlineFamily) {
503 SetBadUnderlineFonts();
507 /***************************************************************
509 * gfxGDIFontList
513 gfxGDIFontList::gfxGDIFontList() : mFontSubstitutes(32) {
514 #ifdef MOZ_BUNDLED_FONTS
515 ActivateBundledFonts();
516 #endif
519 static void RemoveCharsetFromFontSubstitute(nsAString& aName) {
520 int32_t comma = aName.FindChar(char16_t(','));
521 if (comma >= 0) aName.Truncate(comma);
524 #define MAX_VALUE_NAME 512
525 #define MAX_VALUE_DATA 512
527 nsresult gfxGDIFontList::GetFontSubstitutes() {
528 HKEY hKey;
529 DWORD i, rv, lenAlias, lenActual, valueType;
530 WCHAR aliasName[MAX_VALUE_NAME];
531 WCHAR actualName[MAX_VALUE_DATA];
533 if (RegOpenKeyExW(
534 HKEY_LOCAL_MACHINE,
535 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
536 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
537 return NS_ERROR_FAILURE;
540 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
541 aliasName[0] = 0;
542 lenAlias = ArrayLength(aliasName);
543 actualName[0] = 0;
544 lenActual = sizeof(actualName);
545 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
546 (LPBYTE)actualName, &lenActual);
548 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
549 continue;
552 if (aliasName[0] == WCHAR('@')) {
553 continue;
556 nsAutoString substituteName((char16_t*)aliasName);
557 nsAutoString actualFontName((char16_t*)actualName);
558 RemoveCharsetFromFontSubstitute(substituteName);
559 BuildKeyNameFromFontName(substituteName);
560 RemoveCharsetFromFontSubstitute(actualFontName);
561 BuildKeyNameFromFontName(actualFontName);
562 gfxFontFamily* ff;
563 NS_ConvertUTF16toUTF8 substitute(substituteName);
564 NS_ConvertUTF16toUTF8 actual(actualFontName);
565 if (!actual.IsEmpty() && (ff = mFontFamilies.GetWeak(actual))) {
566 mFontSubstitutes.Put(substitute, ff);
567 } else {
568 mNonExistingFonts.AppendElement(substitute);
572 // "Courier" on a default Windows install is an ugly bitmap font.
573 // If there is no substitution for Courier in the registry
574 // substitute "Courier" with "Courier New".
575 nsAutoString substituteName;
576 substituteName.AssignLiteral("Courier");
577 BuildKeyNameFromFontName(substituteName);
578 NS_ConvertUTF16toUTF8 substitute(substituteName);
579 if (!mFontSubstitutes.GetWeak(substitute)) {
580 gfxFontFamily* ff;
581 nsAutoString actualFontName;
582 actualFontName.AssignLiteral("Courier New");
583 BuildKeyNameFromFontName(actualFontName);
584 NS_ConvertUTF16toUTF8 actual(actualFontName);
585 ff = mFontFamilies.GetWeak(actual);
586 if (ff) {
587 mFontSubstitutes.Put(substitute, ff);
590 return NS_OK;
593 nsresult gfxGDIFontList::InitFontListForPlatform() {
594 Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
596 mFontSubstitutes.Clear();
597 mNonExistingFonts.Clear();
599 // iterate over available families
600 LOGFONTW logfont;
601 memset(&logfont, 0, sizeof(logfont));
602 logfont.lfCharSet = DEFAULT_CHARSET;
604 AutoDC hdc;
605 (void)EnumFontFamiliesExW(hdc.GetDC(), &logfont,
606 (FONTENUMPROCW)&EnumFontFamExProc, 0, 0);
608 GetFontSubstitutes();
610 GetPrefsAndStartLoader();
612 return NS_OK;
615 int CALLBACK gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
616 NEWTEXTMETRICEXW* lpntme,
617 DWORD fontType, LPARAM lParam) {
618 const NEWTEXTMETRICW& metrics = lpntme->ntmTm;
619 const LOGFONTW& lf = lpelfe->elfLogFont;
621 if (lf.lfFaceName[0] == '@') {
622 return 1;
625 nsAutoString name(lf.lfFaceName);
626 BuildKeyNameFromFontName(name);
628 NS_ConvertUTF16toUTF8 key(name);
630 gfxGDIFontList* fontList = PlatformFontList();
632 if (!fontList->mFontFamilies.GetWeak(key)) {
633 NS_ConvertUTF16toUTF8 faceName(lf.lfFaceName);
634 RefPtr<GDIFontFamily> family = new GDIFontFamily(faceName);
635 fontList->mFontFamilies.Put(key, family);
637 // if locale is such that CJK font names are the default coming from
638 // GDI, then if a family name is non-ASCII immediately read in other
639 // family names. This assures that MS Gothic, MS Mincho are all found
640 // before lookups begin.
641 if (!IsASCII(faceName)) {
642 family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());
645 if (fontList->mBadUnderlineFamilyNames.ContainsSorted(key)) {
646 family->SetBadUnderlineFamily();
649 family->mWindowsFamily = lf.lfPitchAndFamily & 0xF0;
650 family->mWindowsPitch = lf.lfPitchAndFamily & 0x0F;
652 // mark the charset bit
653 family->mCharset.set(metrics.tmCharSet);
656 return 1;
659 gfxFontEntry* gfxGDIFontList::LookupLocalFont(const nsACString& aFontName,
660 WeightRange aWeightForEntry,
661 StretchRange aStretchForEntry,
662 SlantStyleRange aStyleForEntry) {
663 gfxFontEntry* lookup;
665 lookup = LookupInFaceNameLists(aFontName);
666 if (!lookup) {
667 return nullptr;
670 bool isCFF = false; // jtdfix -- need to determine this
672 // use the face name from the lookup font entry, which will be the localized
673 // face name which GDI mapping tables use (e.g. with the system locale set to
674 // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
675 // 'Arial Vet' which can be used as a key in GDI font lookups).
676 GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
677 lookup->Name(),
678 gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
679 : GFX_FONT_TYPE_TRUETYPE) /*type*/,
680 lookup->SlantStyle(), lookup->Weight(), aStretchForEntry, nullptr);
682 if (!fe) return nullptr;
684 fe->mIsLocalUserFont = true;
686 // make the new font entry match the userfont entry style characteristics
687 fe->mWeightRange = aWeightForEntry;
688 fe->mStyleRange = aStyleForEntry;
689 fe->mStretchRange = aStretchForEntry;
691 return fe;
694 // If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
695 // we modify the subtable header to mark it as Unicode instead, because
696 // otherwise GDI will refuse to load the font.
697 // NOTE that this function does not bounds-check every access to the font data.
698 // This is OK because we only use it on data that has already been validated
699 // by OTS, and therefore we will not hit out-of-bounds accesses here.
700 static bool FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength) {
701 struct CmapHeader {
702 AutoSwap_PRUint16 version;
703 AutoSwap_PRUint16 numTables;
705 struct CmapEncodingRecord {
706 AutoSwap_PRUint16 platformID;
707 AutoSwap_PRUint16 encodingID;
708 AutoSwap_PRUint32 offset;
710 const uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
711 const TableDirEntry* dir = gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
712 if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
713 CmapHeader* cmap =
714 reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
715 CmapEncodingRecord* encRec =
716 reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
717 int32_t symbolSubtable = -1;
718 for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
719 if (uint16_t(encRec[i].platformID) !=
720 gfxFontUtils::PLATFORM_ID_MICROSOFT) {
721 continue; // only interested in MS platform
723 if (uint16_t(encRec[i].encodingID) ==
724 gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
725 // We've got a Microsoft/Unicode table, so don't interfere.
726 symbolSubtable = -1;
727 break;
729 if (uint16_t(encRec[i].encodingID) ==
730 gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
731 // Found a symbol subtable; remember it for possible fixup,
732 // but if we subsequently find a Microsoft/Unicode subtable,
733 // we'll cancel this.
734 symbolSubtable = i;
737 if (symbolSubtable != -1) {
738 // We found a windows/symbol cmap table, and no windows/unicode one;
739 // change the encoding ID so that AddFontMemResourceEx will accept it
740 encRec[symbolSubtable].encodingID =
741 gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
742 return true;
745 return false;
748 gfxFontEntry* gfxGDIFontList::MakePlatformFont(const nsACString& aFontName,
749 WeightRange aWeightForEntry,
750 StretchRange aStretchForEntry,
751 SlantStyleRange aStyleForEntry,
752 const uint8_t* aFontData,
753 uint32_t aLength) {
754 // MakePlatformFont is responsible for deleting the font data with free
755 // so we set up a stack object to ensure it is freed even if we take an
756 // early exit
757 struct FontDataDeleter {
758 explicit FontDataDeleter(const uint8_t* aFontData) : mFontData(aFontData) {}
759 ~FontDataDeleter() { free((void*)mFontData); }
760 const uint8_t* mFontData;
762 FontDataDeleter autoDelete(aFontData);
764 bool isCFF = gfxFontUtils::IsCffFont(aFontData);
766 nsresult rv;
767 HANDLE fontRef = nullptr;
769 nsAutoString uniqueName;
770 rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
771 if (NS_FAILED(rv)) return nullptr;
773 FallibleTArray<uint8_t> newFontData;
775 rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
777 if (NS_FAILED(rv)) return nullptr;
779 DWORD numFonts = 0;
781 uint8_t* fontData = reinterpret_cast<uint8_t*>(newFontData.Elements());
782 uint32_t fontLength = newFontData.Length();
783 NS_ASSERTION(fontData, "null font data after renaming");
785 // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
786 // "A font that is added by AddFontMemResourceEx is always private
787 // to the process that made the call and is not enumerable."
788 fontRef =
789 AddFontMemResourceEx(fontData, fontLength, 0 /* reserved */, &numFonts);
790 if (!fontRef) {
791 if (FixupSymbolEncodedFont(fontData, fontLength)) {
792 fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
795 if (!fontRef) {
796 return nullptr;
799 // only load fonts with a single face contained in the data
800 // AddFontMemResourceEx generates an additional face name for
801 // vertical text if the font supports vertical writing but since
802 // the font is referenced via the name this can be ignored
803 if (fontRef && numFonts > 2) {
804 RemoveFontMemResourceEx(fontRef);
805 return nullptr;
808 // make a new font entry using the unique name
809 WinUserFontData* winUserFontData = new WinUserFontData(fontRef);
810 GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
811 NS_ConvertUTF16toUTF8(uniqueName),
812 gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
813 : GFX_FONT_TYPE_TRUETYPE) /*type*/,
814 aStyleForEntry, aWeightForEntry, aStretchForEntry, winUserFontData);
816 if (fe) {
817 fe->mIsDataUserFont = true;
820 return fe;
823 bool gfxGDIFontList::FindAndAddFamilies(StyleGenericFontFamily aGeneric,
824 const nsACString& aFamily,
825 nsTArray<FamilyAndGeneric>* aOutput,
826 FindFamiliesFlags aFlags,
827 gfxFontStyle* aStyle,
828 gfxFloat aDevToCssSize) {
829 NS_ConvertUTF8toUTF16 key16(aFamily);
830 BuildKeyNameFromFontName(key16);
831 NS_ConvertUTF16toUTF8 keyName(key16);
833 gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
834 if (ff) {
835 aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
836 return true;
839 if (mNonExistingFonts.Contains(keyName)) {
840 return false;
843 return gfxPlatformFontList::FindAndAddFamilies(aGeneric, aFamily, aOutput,
844 aFlags, aStyle, aDevToCssSize);
847 FontFamily gfxGDIFontList::GetDefaultFontForPlatform(
848 const gfxFontStyle* aStyle) {
849 FontFamily ff;
851 // this really shouldn't fail to find a font....
852 NONCLIENTMETRICSW ncm;
853 ncm.cbSize = sizeof(ncm);
854 BOOL status =
855 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
856 if (status) {
857 ff = FindFamily(NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
858 if (!ff.IsNull()) {
859 return ff;
863 // ...but just in case, try another (long-deprecated) approach as well
864 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
865 LOGFONTW logFont;
866 if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
867 ff = FindFamily(NS_ConvertUTF16toUTF8(logFont.lfFaceName));
870 return ff;
873 void gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
874 FontListSizes* aSizes) const {
875 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
876 aSizes->mFontListSize +=
877 SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
878 aSizes->mFontListSize +=
879 mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
880 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
881 aSizes->mFontListSize +=
882 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
886 void gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
887 FontListSizes* aSizes) const {
888 aSizes->mFontListSize += aMallocSizeOf(this);
889 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
892 // used to load system-wide font info on off-main thread
893 class GDIFontInfo : public FontInfoData {
894 public:
895 GDIFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps)
896 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {}
898 virtual ~GDIFontInfo() = default;
900 virtual void Load() {
901 mHdc = GetDC(nullptr);
902 SetGraphicsMode(mHdc, GM_ADVANCED);
903 FontInfoData::Load();
904 ReleaseDC(nullptr, mHdc);
907 // loads font data for all members of a given family
908 virtual void LoadFontFamilyData(const nsACString& aFamilyName);
910 // callback for GDI EnumFontFamiliesExW call
911 static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW* lpelfe,
912 const NEWTEXTMETRICEXW* nmetrics,
913 DWORD fontType, LPARAM data);
915 HDC mHdc;
918 struct EnumerateFontsForFamilyData {
919 EnumerateFontsForFamilyData(const nsACString& aFamilyName,
920 GDIFontInfo& aFontInfo)
921 : mFamilyName(aFamilyName), mFontInfo(aFontInfo) {}
923 nsCString mFamilyName;
924 nsTArray<nsCString> mOtherFamilyNames;
925 GDIFontInfo& mFontInfo;
926 nsCString mPreviousFontName;
929 int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
930 const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
931 DWORD fontType, LPARAM data) {
932 EnumerateFontsForFamilyData* famData =
933 reinterpret_cast<EnumerateFontsForFamilyData*>(data);
934 HDC hdc = famData->mFontInfo.mHdc;
935 LOGFONTW logFont = lpelfe->elfLogFont;
936 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
938 AutoSelectFont font(hdc, &logFont);
939 if (!font.IsValid()) {
940 return 1;
943 FontFaceData fontData;
944 NS_ConvertUTF16toUTF8 fontName(lpelfe->elfFullName);
946 // callback called for each style-charset so return if style already seen
947 if (fontName.Equals(famData->mPreviousFontName)) {
948 return 1;
950 famData->mPreviousFontName = fontName;
951 famData->mFontInfo.mLoadStats.fonts++;
953 // read name table info
954 bool nameDataLoaded = false;
955 if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
956 uint32_t kNAME =
957 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
958 uint32_t nameSize;
959 AutoTArray<uint8_t, 1024> nameData;
961 nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
962 if (nameSize != GDI_ERROR && nameSize > 0 &&
963 nameData.SetLength(nameSize, fallible)) {
964 ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);
966 // face names
967 if (famData->mFontInfo.mLoadFaceNames) {
968 gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()),
969 nameSize, gfxFontUtils::NAME_ID_FULL,
970 fontData.mFullName);
971 gfxFontUtils::ReadCanonicalName(
972 (const char*)(nameData.Elements()), nameSize,
973 gfxFontUtils::NAME_ID_POSTSCRIPT, fontData.mPostscriptName);
974 nameDataLoaded = true;
975 famData->mFontInfo.mLoadStats.facenames++;
978 // other family names
979 if (famData->mFontInfo.mLoadOtherNames) {
980 gfxFontUtils::ReadOtherFamilyNamesForFace(
981 famData->mFamilyName, (const char*)(nameData.Elements()), nameSize,
982 famData->mOtherFamilyNames, false);
987 // read cmap
988 bool cmapLoaded = false;
989 gfxWindowsFontType feType =
990 GDIFontEntry::DetermineFontType(metrics, fontType);
991 if (famData->mFontInfo.mLoadCmaps && (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
992 feType == GFX_FONT_TYPE_TT_OPENTYPE ||
993 feType == GFX_FONT_TYPE_TRUETYPE)) {
994 uint32_t kCMAP =
995 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
996 uint32_t cmapSize;
997 AutoTArray<uint8_t, 1024> cmapData;
999 cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
1000 if (cmapSize != GDI_ERROR && cmapSize > 0 &&
1001 cmapData.SetLength(cmapSize, fallible)) {
1002 ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
1003 bool cmapLoaded = false;
1004 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1005 uint32_t offset;
1007 if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), cmapSize,
1008 *charmap, offset))) {
1009 fontData.mCharacterMap = charmap;
1010 fontData.mUVSOffset = offset;
1011 cmapLoaded = true;
1012 famData->mFontInfo.mLoadStats.cmaps++;
1017 if (cmapLoaded || nameDataLoaded) {
1018 famData->mFontInfo.mFontFaceData.Put(fontName, fontData);
1021 return famData->mFontInfo.mCanceled ? 0 : 1;
1024 void GDIFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
1025 // iterate over the family
1026 LOGFONTW logFont;
1027 memset(&logFont, 0, sizeof(LOGFONTW));
1028 logFont.lfCharSet = DEFAULT_CHARSET;
1029 logFont.lfPitchAndFamily = 0;
1030 NS_ConvertUTF8toUTF16 name(aFamilyName);
1031 uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
1032 memcpy(logFont.lfFaceName, name.BeginReading(), l * sizeof(char16_t));
1034 EnumerateFontsForFamilyData data(aFamilyName, *this);
1036 EnumFontFamiliesExW(mHdc, &logFont,
1037 (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
1038 (LPARAM)(&data), 0);
1040 // if found other names, insert them
1041 if (data.mOtherFamilyNames.Length() != 0) {
1042 mOtherFamilyNames.Put(aFamilyName, data.mOtherFamilyNames);
1043 mLoadStats.othernames += data.mOtherFamilyNames.Length();
1047 already_AddRefed<FontInfoData> gfxGDIFontList::CreateFontInfoData() {
1048 bool loadCmaps = !UsesSystemFallback() ||
1049 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1051 RefPtr<GDIFontInfo> fi =
1052 new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1054 return fi.forget();
1057 gfxFontFamily* gfxGDIFontList::CreateFontFamily(const nsACString& aName) const {
1058 return new GDIFontFamily(aName);
1061 #ifdef MOZ_BUNDLED_FONTS
1063 void gfxGDIFontList::ActivateBundledFonts() {
1064 nsCOMPtr<nsIFile> localDir;
1065 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1066 if (NS_FAILED(rv)) {
1067 return;
1069 if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1070 return;
1072 bool isDir;
1073 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1074 return;
1077 nsCOMPtr<nsIDirectoryEnumerator> e;
1078 rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
1079 if (NS_FAILED(rv)) {
1080 return;
1083 nsCOMPtr<nsIFile> file;
1084 while (NS_SUCCEEDED(e->GetNextFile(getter_AddRefs(file))) && file) {
1085 nsAutoString path;
1086 if (NS_FAILED(file->GetPath(path))) {
1087 continue;
1089 AddFontResourceExW(path.get(), FR_PRIVATE, nullptr);
1093 #endif