No bug - tagging 2af34b4c9adf8c8defd3251b569af9c38cc0a429 with FIREFOX_BETA_124_BASE...
[gecko.git] / gfx / thebes / gfxGDIFontList.cpp
blob3c4c7bce0725b8f2322f4dbeb5a71c2b0e481a16
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/TextUtils.h"
11 #include "mozilla/Sprintf.h"
13 #include "gfxGDIFontList.h"
14 #include "gfxWindowsPlatform.h"
15 #include "gfxUserFontSet.h"
16 #include "gfxFontUtils.h"
17 #include "gfxGDIFont.h"
19 #include "nsServiceManagerUtils.h"
20 #include "nsTArray.h"
21 #include "nsUnicharUtils.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsPresContext.h"
27 #include "gfxFontConstants.h"
29 #include "mozilla/MemoryReporting.h"
30 #include "mozilla/ProfilerLabels.h"
31 #include "mozilla/StaticPrefs_gfx.h"
32 #include "mozilla/Telemetry.h"
34 #include <usp10.h>
36 using namespace mozilla;
37 using namespace mozilla::gfx;
39 #define ROUND(x) floor((x) + 0.5)
41 #define LOG_FONTLIST(args) \
42 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
43 #define LOG_FONTLIST_ENABLED() \
44 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
46 #define LOG_CMAPDATA_ENABLED() \
47 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
49 static __inline void BuildKeyNameFromFontName(nsAString& aName) {
50 if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1);
51 ToLowerCase(aName);
54 // Implementation of gfxPlatformFontList for Win32 GDI,
55 // using GDI font enumeration APIs to get the list of fonts
57 class WinUserFontData : public gfxUserFontData {
58 public:
59 explicit WinUserFontData(HANDLE aFontRef) : mFontRef(aFontRef) {}
61 virtual ~WinUserFontData() {
62 DebugOnly<BOOL> success;
63 success = RemoveFontMemResourceEx(mFontRef);
64 #if DEBUG
65 if (!success) {
66 char buf[256];
67 SprintfLiteral(
68 buf,
69 "error deleting font handle (%p) - RemoveFontMemResourceEx failed",
70 mFontRef);
71 NS_ASSERTION(success, buf);
73 #endif
76 HANDLE mFontRef;
79 BYTE FontTypeToOutPrecision(uint8_t fontType) {
80 BYTE ret;
81 switch (fontType) {
82 case GFX_FONT_TYPE_TT_OPENTYPE:
83 case GFX_FONT_TYPE_TRUETYPE:
84 ret = OUT_TT_ONLY_PRECIS;
85 break;
86 case GFX_FONT_TYPE_PS_OPENTYPE:
87 ret = OUT_PS_ONLY_PRECIS;
88 break;
89 case GFX_FONT_TYPE_TYPE1:
90 ret = OUT_OUTLINE_PRECIS;
91 break;
92 case GFX_FONT_TYPE_RASTER:
93 ret = OUT_RASTER_PRECIS;
94 break;
95 case GFX_FONT_TYPE_DEVICE:
96 ret = OUT_DEVICE_PRECIS;
97 break;
98 default:
99 ret = OUT_DEFAULT_PRECIS;
101 return ret;
104 /***************************************************************
106 * GDIFontEntry
110 GDIFontEntry::GDIFontEntry(const nsACString& aFaceName,
111 gfxWindowsFontType aFontType, SlantStyleRange aStyle,
112 WeightRange aWeight, StretchRange aStretch,
113 gfxUserFontData* aUserFontData)
114 : gfxFontEntry(aFaceName), mFontType(aFontType), mForceGDI(false) {
115 mUserFontData.reset(aUserFontData);
116 mStyleRange = aStyle;
117 mWeightRange = aWeight;
118 mStretchRange = aStretch;
119 if (IsType1()) {
120 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 RefPtr<gfxCharacterMap> cmap = new gfxCharacterMap();
146 cmap->mBuildOnTheFly = true;
147 if (mCharacterMap.compareExchange(nullptr, cmap.get())) {
148 Unused << cmap.forget();
150 return NS_ERROR_FAILURE;
153 RefPtr<gfxCharacterMap> charmap;
154 nsresult rv;
156 uint32_t uvsOffset = 0;
157 if (aFontInfoData &&
158 (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
159 rv = NS_OK;
160 } else {
161 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
162 charmap = new gfxCharacterMap();
163 AutoTArray<uint8_t, 16384> cmap;
164 rv = CopyFontTable(kCMAP, cmap);
166 if (NS_SUCCEEDED(rv)) {
167 rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), *charmap,
168 uvsOffset);
172 if (NS_SUCCEEDED(rv)) {
173 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
174 charmap = pfl->FindCharMap(charmap);
175 mHasCmapTable = true;
176 } else {
177 // if error occurred, initialize to null cmap
178 charmap = new gfxCharacterMap();
179 // For fonts where we failed to read the character map,
180 // we can take a slow path to look up glyphs character by character
181 charmap->mBuildOnTheFly = true;
183 if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
184 charmap.get()->AddRef();
187 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zd hash: %8.8x%s\n",
188 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
189 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
190 if (LOG_CMAPDATA_ENABLED()) {
191 char prefix[256];
192 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
193 charmap->Dump(prefix, eGfxLog_cmapdata);
196 return rv;
199 gfxFont* GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
200 return new gfxGDIFont(this, aFontStyle);
203 nsresult GDIFontEntry::CopyFontTable(uint32_t aTableTag,
204 nsTArray<uint8_t>& aBuffer) {
205 if (!IsTrueType()) {
206 return NS_ERROR_FAILURE;
209 AutoDC dc;
210 AutoSelectFont font(dc.GetDC(), &mLogFont);
211 if (font.IsValid()) {
212 uint32_t tableSize = ::GetFontData(
213 dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0, nullptr, 0);
214 if (tableSize != GDI_ERROR) {
215 if (aBuffer.SetLength(tableSize, fallible)) {
216 ::GetFontData(dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0,
217 aBuffer.Elements(), tableSize);
218 return NS_OK;
220 return NS_ERROR_OUT_OF_MEMORY;
223 return NS_ERROR_FAILURE;
226 already_AddRefed<UnscaledFontGDI> GDIFontEntry::LookupUnscaledFont(
227 HFONT aFont) {
228 RefPtr<UnscaledFontGDI> unscaledFont(mUnscaledFont);
229 if (!unscaledFont) {
230 LOGFONT lf;
231 GetObject(aFont, sizeof(LOGFONT), &lf);
232 unscaledFont = new UnscaledFontGDI(lf);
233 mUnscaledFont = unscaledFont;
236 return unscaledFont.forget();
239 void GDIFontEntry::FillLogFont(LOGFONTW* aLogFont, LONG aWeight,
240 gfxFloat aSize) {
241 memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
243 aLogFont->lfHeight = (LONG)-ROUND(aSize);
245 if (aLogFont->lfHeight == 0) {
246 aLogFont->lfHeight = -1;
249 // If a non-zero weight is passed in, use this to override the original
250 // weight in the entry's logfont. This is used to control synthetic bolding
251 // for installed families with no bold face, and for downloaded fonts
252 // (but NOT for local user fonts, because it could cause a different,
253 // glyph-incompatible face to be used)
254 if (aWeight != 0) {
255 aLogFont->lfWeight = aWeight;
258 // for non-local() user fonts, we never want to apply italics here;
259 // if the face is described as italic, we should use it as-is,
260 // and if it's not, but then the element is styled italic, we'll use
261 // a cairo transform to create fake italic (oblique)
262 if (mIsDataUserFont) {
263 aLogFont->lfItalic = 0;
267 #define MISSING_GLYPH \
268 0x1F // glyph index returned for missing characters
269 // on WinXP with .fon fonts, but not Type1 (.pfb)
271 bool GDIFontEntry::TestCharacterMap(uint32_t aCh) {
272 if (!mCharacterMap) {
273 ReadCMAP();
274 NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
277 if (GetCharacterMap()->mBuildOnTheFly) {
278 if (aCh > 0xFFFF) return false;
280 // previous code was using the group style
281 gfxFontStyle fakeStyle;
282 if (!IsUpright()) {
283 fakeStyle.style = FontSlantStyle::ITALIC;
285 fakeStyle.weight = Weight().Min();
287 RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, nullptr);
288 if (!tempFont || !tempFont->Valid()) return false;
289 gfxGDIFont* font = static_cast<gfxGDIFont*>(tempFont.get());
291 HDC dc = GetDC((HWND) nullptr);
292 SetGraphicsMode(dc, GM_ADVANCED);
293 HFONT hfont = font->GetHFONT();
294 HFONT oldFont = (HFONT)SelectObject(dc, hfont);
296 wchar_t str[1] = {(wchar_t)aCh};
297 WORD glyph[1];
299 bool hasGlyph = false;
301 // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a
302 // missing glyph or 0x1F in other cases to indicate the "invalid"
303 // glyph. Map both cases to "not found"
304 if (IsType1() || mForceGDI) {
305 // Type1 fonts and uniscribe APIs don't get along.
306 // ScriptGetCMap will return E_HANDLE
307 DWORD ret =
308 GetGlyphIndicesW(dc, str, 1, glyph, GGI_MARK_NONEXISTING_GLYPHS);
309 if (ret != GDI_ERROR && glyph[0] != 0xFFFF &&
310 (IsType1() || glyph[0] != MISSING_GLYPH)) {
311 hasGlyph = true;
313 } else {
314 // ScriptGetCMap works better than GetGlyphIndicesW
315 // for things like bitmap/vector fonts
316 SCRIPT_CACHE sc = nullptr;
317 HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph);
318 if (rv == S_OK) hasGlyph = true;
321 SelectObject(dc, oldFont);
322 ReleaseDC(nullptr, dc);
324 if (hasGlyph) {
325 GetCharacterMap()->set(aCh);
326 return true;
328 } else {
329 // font had a cmap so simply check that
330 return GetCharacterMap()->test(aCh);
333 return false;
336 void GDIFontEntry::InitLogFont(const nsACString& aName,
337 gfxWindowsFontType aFontType) {
338 #define CLIP_TURNOFF_FONTASSOCIATION 0x40
340 mLogFont.lfHeight = -1;
342 // Fill in logFont structure
343 mLogFont.lfWidth = 0;
344 mLogFont.lfEscapement = 0;
345 mLogFont.lfOrientation = 0;
346 mLogFont.lfUnderline = FALSE;
347 mLogFont.lfStrikeOut = FALSE;
348 mLogFont.lfCharSet = DEFAULT_CHARSET;
349 mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType);
350 mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
351 mLogFont.lfQuality = DEFAULT_QUALITY;
352 mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
353 // always force lfItalic if we want it. Font selection code will
354 // do its best to give us an italic font entry, but if no face exists
355 // it may give us a regular one based on weight. Windows should
356 // do fake italic for us in that case.
357 mLogFont.lfItalic = !IsUpright();
358 mLogFont.lfWeight = Weight().Min().ToIntRounded();
360 NS_ConvertUTF8toUTF16 name(aName);
361 int len = std::min<int>(name.Length(), LF_FACESIZE - 1);
362 memcpy(&mLogFont.lfFaceName, name.BeginReading(), len * sizeof(char16_t));
363 mLogFont.lfFaceName[len] = '\0';
366 GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsACString& aName,
367 gfxWindowsFontType aFontType,
368 SlantStyleRange aStyle,
369 WeightRange aWeight,
370 StretchRange aStretch,
371 gfxUserFontData* aUserFontData) {
372 // jtdfix - need to set charset, pitch/family
374 return new GDIFontEntry(aName, aFontType, aStyle, aWeight, aStretch,
375 aUserFontData);
378 void GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
379 FontListSizes* aSizes) const {
380 aSizes->mFontListSize += aMallocSizeOf(this);
381 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
384 /***************************************************************
386 * GDIFontFamily
390 static bool ShouldIgnoreItalicStyle(const nsACString& aName) {
391 // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
392 // non-italic style glyphs as Japanese characters. However, using it
393 // causes serious problem if web pages wants some elements to be
394 // different style from others only with font-style. For example,
395 // <em> and <i> should be rendered as italic in the default style.
396 return aName.EqualsLiteral("Meiryo") ||
397 aName.EqualsLiteral(
398 "\xe3\x83\xa1\xe3\x82\xa4\xe3\x83\xaa\xe3\x82\xaa");
401 int CALLBACK GDIFontFamily::FamilyAddStylesProc(
402 const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
403 DWORD fontType, LPARAM data) MOZ_NO_THREAD_SAFETY_ANALYSIS {
404 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
405 LOGFONTW logFont = lpelfe->elfLogFont;
406 GDIFontFamily* ff = reinterpret_cast<GDIFontFamily*>(data);
407 MOZ_ASSERT(ff->mLock.LockedForWritingByCurrentThread());
409 if (logFont.lfItalic && ShouldIgnoreItalicStyle(ff->mName)) {
410 return 1;
413 // Some fonts claim to support things > 900, but we don't so clamp the sizes
414 logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900));
416 gfxWindowsFontType feType =
417 GDIFontEntry::DetermineFontType(metrics, fontType);
419 GDIFontEntry* fe = nullptr;
420 for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
421 fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
422 if (feType > fe->mFontType) {
423 // if the new type is better than the old one, remove the old entries
424 ff->mAvailableFonts.RemoveElementAt(i);
425 --i;
426 } else if (feType < fe->mFontType) {
427 // otherwise if the new type is worse, skip it
428 return 1;
432 for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
433 fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
434 // check if we already know about this face
435 if (fe->Weight().Min() == FontWeight::FromInt(int32_t(logFont.lfWeight)) &&
436 fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
437 // update the charset bit here since this could be different
438 // XXX Can we still do this now that we store mCharset
439 // on the font family rather than the font entry?
440 ff->mCharset.set(metrics.tmCharSet);
441 return 1;
445 // We can't set the hasItalicFace flag correctly here,
446 // because we might not have seen the family's italic face(s) yet.
447 // So we'll set that flag for all members after loading all the faces.
448 auto italicStyle = (logFont.lfItalic == 0xFF ? FontSlantStyle::ITALIC
449 : FontSlantStyle::NORMAL);
450 fe = GDIFontEntry::CreateFontEntry(
451 NS_ConvertUTF16toUTF8(lpelfe->elfFullName), feType,
452 SlantStyleRange(italicStyle),
453 WeightRange(FontWeight::FromInt(int32_t(logFont.lfWeight))),
454 StretchRange(FontStretch::NORMAL), nullptr);
455 if (!fe) {
456 return 1;
459 ff->AddFontEntryLocked(fe);
461 if (LOG_FONTLIST_ENABLED()) {
462 LOG_FONTLIST(
463 ("(fontlist) added (%s) to family (%s)"
464 " with style: %s weight: %ld stretch: normal",
465 fe->Name().get(), ff->Name().get(),
466 (logFont.lfItalic == 0xff) ? "italic" : "normal", logFont.lfWeight));
468 return 1;
471 void GDIFontFamily::FindStyleVariationsLocked(FontInfoData* aFontInfoData) {
472 if (mHasStyles) {
473 return;
475 mHasStyles = true;
477 HDC hdc = GetDC(nullptr);
478 SetGraphicsMode(hdc, GM_ADVANCED);
480 LOGFONTW logFont;
481 memset(&logFont, 0, sizeof(LOGFONTW));
482 logFont.lfCharSet = DEFAULT_CHARSET;
483 logFont.lfPitchAndFamily = 0;
484 NS_ConvertUTF8toUTF16 name(mName);
485 uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
486 memcpy(logFont.lfFaceName, name.get(), l * sizeof(char16_t));
488 EnumFontFamiliesExW(hdc, &logFont,
489 (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc,
490 (LPARAM)this, 0);
491 if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) {
492 LOG_FONTLIST(
493 ("(fontlist) no styles available in family \"%s\"", mName.get()));
496 ReleaseDC(nullptr, hdc);
498 if (mIsBadUnderlineFamily) {
499 SetBadUnderlineFonts();
502 CheckForSimpleFamily();
505 /***************************************************************
507 * gfxGDIFontList
511 gfxGDIFontList::gfxGDIFontList() : mFontSubstitutes(32) {
512 #ifdef MOZ_BUNDLED_FONTS
513 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
514 TimeStamp start = TimeStamp::Now();
515 ActivateBundledFonts();
516 TimeStamp end = TimeStamp::Now();
517 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
518 (end - start).ToMilliseconds());
520 #endif
523 static void RemoveCharsetFromFontSubstitute(nsAString& aName) {
524 int32_t comma = aName.FindChar(char16_t(','));
525 if (comma >= 0) aName.Truncate(comma);
528 #define MAX_VALUE_NAME 512
529 #define MAX_VALUE_DATA 512
531 nsresult gfxGDIFontList::GetFontSubstitutes() {
532 HKEY hKey;
533 DWORD i, rv, lenAlias, lenActual, valueType;
534 WCHAR aliasName[MAX_VALUE_NAME];
535 WCHAR actualName[MAX_VALUE_DATA];
537 if (RegOpenKeyExW(
538 HKEY_LOCAL_MACHINE,
539 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
540 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
541 return NS_ERROR_FAILURE;
544 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
545 aliasName[0] = 0;
546 lenAlias = ArrayLength(aliasName);
547 actualName[0] = 0;
548 lenActual = sizeof(actualName);
549 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
550 (LPBYTE)actualName, &lenActual);
552 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
553 continue;
556 if (aliasName[0] == WCHAR('@')) {
557 continue;
560 nsAutoString substituteName((char16_t*)aliasName);
561 nsAutoString actualFontName((char16_t*)actualName);
562 RemoveCharsetFromFontSubstitute(substituteName);
563 BuildKeyNameFromFontName(substituteName);
564 RemoveCharsetFromFontSubstitute(actualFontName);
565 BuildKeyNameFromFontName(actualFontName);
566 gfxFontFamily* ff;
567 NS_ConvertUTF16toUTF8 substitute(substituteName);
568 NS_ConvertUTF16toUTF8 actual(actualFontName);
569 if (!actual.IsEmpty() && (ff = mFontFamilies.GetWeak(actual))) {
570 mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
571 } else {
572 mNonExistingFonts.AppendElement(substitute);
576 // "Courier" on a default Windows install is an ugly bitmap font.
577 // If there is no substitution for Courier in the registry
578 // substitute "Courier" with "Courier New".
579 nsAutoString substituteName;
580 substituteName.AssignLiteral("Courier");
581 BuildKeyNameFromFontName(substituteName);
582 NS_ConvertUTF16toUTF8 substitute(substituteName);
583 if (!mFontSubstitutes.GetWeak(substitute)) {
584 gfxFontFamily* ff;
585 nsAutoString actualFontName;
586 actualFontName.AssignLiteral("Courier New");
587 BuildKeyNameFromFontName(actualFontName);
588 NS_ConvertUTF16toUTF8 actual(actualFontName);
589 ff = mFontFamilies.GetWeak(actual);
590 if (ff) {
591 mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
594 return NS_OK;
597 nsresult gfxGDIFontList::InitFontListForPlatform() {
598 Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
600 mFontSubstitutes.Clear();
601 mNonExistingFonts.Clear();
603 // iterate over available families
604 LOGFONTW logfont;
605 memset(&logfont, 0, sizeof(logfont));
606 logfont.lfCharSet = DEFAULT_CHARSET;
608 AutoDC hdc;
609 (void)EnumFontFamiliesExW(hdc.GetDC(), &logfont,
610 (FONTENUMPROCW)&EnumFontFamExProc, 0, 0);
612 GetFontSubstitutes();
614 GetPrefsAndStartLoader();
616 return NS_OK;
619 int CALLBACK gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
620 NEWTEXTMETRICEXW* lpntme,
621 DWORD fontType, LPARAM lParam) {
622 const NEWTEXTMETRICW& metrics = lpntme->ntmTm;
623 const LOGFONTW& lf = lpelfe->elfLogFont;
625 if (lf.lfFaceName[0] == '@') {
626 return 1;
629 nsAutoString name(lf.lfFaceName);
630 BuildKeyNameFromFontName(name);
632 NS_ConvertUTF16toUTF8 key(name);
634 gfxGDIFontList* fontList = PlatformFontList();
635 fontList->mLock.AssertCurrentThreadIn();
637 if (!fontList->mFontFamilies.Contains(key)) {
638 NS_ConvertUTF16toUTF8 faceName(lf.lfFaceName);
639 FontVisibility visibility = FontVisibility::Unknown; // TODO
640 RefPtr<GDIFontFamily> family = new GDIFontFamily(faceName, visibility);
641 fontList->mFontFamilies.InsertOrUpdate(key, RefPtr{family});
643 // if locale is such that CJK font names are the default coming from
644 // GDI, then if a family name is non-ASCII immediately read in other
645 // family names. This assures that MS Gothic, MS Mincho are all found
646 // before lookups begin.
647 if (!IsAscii(faceName)) {
648 family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());
651 if (fontList->mBadUnderlineFamilyNames.ContainsSorted(key)) {
652 family->SetBadUnderlineFamily();
655 family->mWindowsFamily = lf.lfPitchAndFamily & 0xF0;
656 family->mWindowsPitch = lf.lfPitchAndFamily & 0x0F;
658 // mark the charset bit
659 family->mCharset.set(metrics.tmCharSet);
662 return 1;
665 gfxFontEntry* gfxGDIFontList::LookupLocalFont(nsPresContext* aPresContext,
666 const nsACString& aFontName,
667 WeightRange aWeightForEntry,
668 StretchRange aStretchForEntry,
669 SlantStyleRange aStyleForEntry) {
670 AutoLock lock(mLock);
672 gfxFontEntry* lookup = LookupInFaceNameLists(aFontName);
673 if (!lookup) {
674 return nullptr;
677 bool isCFF = false; // jtdfix -- need to determine this
679 // use the face name from the lookup font entry, which will be the localized
680 // face name which GDI mapping tables use (e.g. with the system locale set to
681 // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
682 // 'Arial Vet' which can be used as a key in GDI font lookups).
683 GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
684 lookup->Name(),
685 gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
686 : GFX_FONT_TYPE_TRUETYPE) /*type*/,
687 lookup->SlantStyle(), lookup->Weight(), aStretchForEntry, nullptr);
689 if (!fe) return nullptr;
691 fe->mIsLocalUserFont = true;
693 // make the new font entry match the userfont entry style characteristics
694 fe->mWeightRange = aWeightForEntry;
695 fe->mStyleRange = aStyleForEntry;
696 fe->mStretchRange = aStretchForEntry;
698 return fe;
701 // If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
702 // we modify the subtable header to mark it as Unicode instead, because
703 // otherwise GDI will refuse to load the font.
704 // NOTE that this function does not bounds-check every access to the font data.
705 // This is OK because we only use it on data that has already been validated
706 // by OTS, and therefore we will not hit out-of-bounds accesses here.
707 static bool FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength) {
708 struct CmapHeader {
709 AutoSwap_PRUint16 version;
710 AutoSwap_PRUint16 numTables;
712 struct CmapEncodingRecord {
713 AutoSwap_PRUint16 platformID;
714 AutoSwap_PRUint16 encodingID;
715 AutoSwap_PRUint32 offset;
717 const uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
718 const TableDirEntry* dir = gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
719 if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
720 CmapHeader* cmap =
721 reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
722 CmapEncodingRecord* encRec =
723 reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
724 int32_t symbolSubtable = -1;
725 for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
726 if (uint16_t(encRec[i].platformID) !=
727 gfxFontUtils::PLATFORM_ID_MICROSOFT) {
728 continue; // only interested in MS platform
730 if (uint16_t(encRec[i].encodingID) ==
731 gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
732 // We've got a Microsoft/Unicode table, so don't interfere.
733 symbolSubtable = -1;
734 break;
736 if (uint16_t(encRec[i].encodingID) ==
737 gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
738 // Found a symbol subtable; remember it for possible fixup,
739 // but if we subsequently find a Microsoft/Unicode subtable,
740 // we'll cancel this.
741 symbolSubtable = i;
744 if (symbolSubtable != -1) {
745 // We found a windows/symbol cmap table, and no windows/unicode one;
746 // change the encoding ID so that AddFontMemResourceEx will accept it
747 encRec[symbolSubtable].encodingID =
748 gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
749 return true;
752 return false;
755 gfxFontEntry* gfxGDIFontList::MakePlatformFont(const nsACString& aFontName,
756 WeightRange aWeightForEntry,
757 StretchRange aStretchForEntry,
758 SlantStyleRange aStyleForEntry,
759 const uint8_t* aFontData,
760 uint32_t aLength) {
761 // MakePlatformFont is responsible for deleting the font data with free
762 // so we set up a stack object to ensure it is freed even if we take an
763 // early exit
764 struct FontDataDeleter {
765 explicit FontDataDeleter(const uint8_t* aFontData) : mFontData(aFontData) {}
766 ~FontDataDeleter() { free((void*)mFontData); }
767 const uint8_t* mFontData;
769 FontDataDeleter autoDelete(aFontData);
771 bool isCFF = gfxFontUtils::IsCffFont(aFontData);
773 nsresult rv;
774 HANDLE fontRef = nullptr;
776 nsAutoString uniqueName;
777 rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
778 if (NS_FAILED(rv)) return nullptr;
780 FallibleTArray<uint8_t> newFontData;
782 rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
784 if (NS_FAILED(rv)) return nullptr;
786 DWORD numFonts = 0;
788 uint8_t* fontData = reinterpret_cast<uint8_t*>(newFontData.Elements());
789 uint32_t fontLength = newFontData.Length();
790 NS_ASSERTION(fontData, "null font data after renaming");
792 // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
793 // "A font that is added by AddFontMemResourceEx is always private
794 // to the process that made the call and is not enumerable."
795 fontRef =
796 AddFontMemResourceEx(fontData, fontLength, 0 /* reserved */, &numFonts);
797 if (!fontRef) {
798 if (FixupSymbolEncodedFont(fontData, fontLength)) {
799 fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
802 if (!fontRef) {
803 return nullptr;
806 // only load fonts with a single face contained in the data
807 // AddFontMemResourceEx generates an additional face name for
808 // vertical text if the font supports vertical writing but since
809 // the font is referenced via the name this can be ignored
810 if (fontRef && numFonts > 2) {
811 RemoveFontMemResourceEx(fontRef);
812 return nullptr;
815 // make a new font entry using the unique name
816 WinUserFontData* winUserFontData = new WinUserFontData(fontRef);
817 GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
818 NS_ConvertUTF16toUTF8(uniqueName),
819 gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
820 : GFX_FONT_TYPE_TRUETYPE) /*type*/,
821 aStyleForEntry, aWeightForEntry, aStretchForEntry, winUserFontData);
823 if (fe) {
824 fe->mIsDataUserFont = true;
827 return fe;
830 bool gfxGDIFontList::FindAndAddFamiliesLocked(
831 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
832 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
833 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
834 gfxFloat aDevToCssSize) {
835 NS_ConvertUTF8toUTF16 key16(aFamily);
836 BuildKeyNameFromFontName(key16);
837 NS_ConvertUTF16toUTF8 keyName(key16);
839 gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
840 FontVisibility level =
841 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
842 if (ff && IsVisibleToCSS(*ff, level)) {
843 aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
844 return true;
847 if (mNonExistingFonts.Contains(keyName)) {
848 return false;
851 return gfxPlatformFontList::FindAndAddFamiliesLocked(
852 aPresContext, aGeneric, aFamily, aOutput, aFlags, aStyle, aLanguage,
853 aDevToCssSize);
856 FontFamily gfxGDIFontList::GetDefaultFontForPlatform(
857 nsPresContext* aPresContext, const gfxFontStyle* aStyle,
858 nsAtom* aLanguage) {
859 FontFamily ff;
861 // this really shouldn't fail to find a font....
862 NONCLIENTMETRICSW ncm;
863 ncm.cbSize = sizeof(ncm);
864 BOOL status =
865 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
866 if (status) {
867 ff = FindFamily(aPresContext,
868 NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
869 if (!ff.IsNull()) {
870 return ff;
874 // ...but just in case, try another (long-deprecated) approach as well
875 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
876 LOGFONTW logFont;
877 if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
878 ff = FindFamily(aPresContext, NS_ConvertUTF16toUTF8(logFont.lfFaceName));
881 return ff;
884 void gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
885 FontListSizes* aSizes) const {
886 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
888 AutoLock lock(mLock);
890 aSizes->mFontListSize +=
891 SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
892 aSizes->mFontListSize +=
893 mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
894 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
895 aSizes->mFontListSize +=
896 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
900 void gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
901 FontListSizes* aSizes) const {
902 aSizes->mFontListSize += aMallocSizeOf(this);
903 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
906 // used to load system-wide font info on off-main thread
907 class GDIFontInfo : public FontInfoData {
908 public:
909 GDIFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps)
910 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {}
912 virtual ~GDIFontInfo() = default;
914 virtual void Load() {
915 mHdc = GetDC(nullptr);
916 SetGraphicsMode(mHdc, GM_ADVANCED);
917 FontInfoData::Load();
918 ReleaseDC(nullptr, mHdc);
921 // loads font data for all members of a given family
922 virtual void LoadFontFamilyData(const nsACString& aFamilyName);
924 // callback for GDI EnumFontFamiliesExW call
925 static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW* lpelfe,
926 const NEWTEXTMETRICEXW* nmetrics,
927 DWORD fontType, LPARAM data);
929 HDC mHdc;
932 struct EnumerateFontsForFamilyData {
933 EnumerateFontsForFamilyData(const nsACString& aFamilyName,
934 GDIFontInfo& aFontInfo)
935 : mFamilyName(aFamilyName), mFontInfo(aFontInfo) {}
937 nsCString mFamilyName;
938 nsTArray<nsCString> mOtherFamilyNames;
939 GDIFontInfo& mFontInfo;
940 nsCString mPreviousFontName;
943 int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
944 const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
945 DWORD fontType, LPARAM data) {
946 EnumerateFontsForFamilyData* famData =
947 reinterpret_cast<EnumerateFontsForFamilyData*>(data);
948 HDC hdc = famData->mFontInfo.mHdc;
949 LOGFONTW logFont = lpelfe->elfLogFont;
950 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
952 AutoSelectFont font(hdc, &logFont);
953 if (!font.IsValid()) {
954 return 1;
957 FontFaceData fontData;
958 NS_ConvertUTF16toUTF8 fontName(lpelfe->elfFullName);
960 // callback called for each style-charset so return if style already seen
961 if (fontName.Equals(famData->mPreviousFontName)) {
962 return 1;
964 famData->mPreviousFontName = fontName;
965 famData->mFontInfo.mLoadStats.fonts++;
967 // read name table info
968 bool nameDataLoaded = false;
969 if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
970 uint32_t kNAME =
971 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
972 uint32_t nameSize;
973 AutoTArray<uint8_t, 1024> nameData;
975 nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
976 if (nameSize != GDI_ERROR && nameSize > 0 &&
977 nameData.SetLength(nameSize, fallible)) {
978 ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);
980 // face names
981 if (famData->mFontInfo.mLoadFaceNames) {
982 gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()),
983 nameSize, gfxFontUtils::NAME_ID_FULL,
984 fontData.mFullName);
985 gfxFontUtils::ReadCanonicalName(
986 (const char*)(nameData.Elements()), nameSize,
987 gfxFontUtils::NAME_ID_POSTSCRIPT, fontData.mPostscriptName);
988 nameDataLoaded = true;
989 famData->mFontInfo.mLoadStats.facenames++;
992 // other family names
993 if (famData->mFontInfo.mLoadOtherNames) {
994 gfxFontUtils::ReadOtherFamilyNamesForFace(
995 famData->mFamilyName, (const char*)(nameData.Elements()), nameSize,
996 famData->mOtherFamilyNames, false);
1001 // read cmap
1002 bool cmapLoaded = false;
1003 gfxWindowsFontType feType =
1004 GDIFontEntry::DetermineFontType(metrics, fontType);
1005 if (famData->mFontInfo.mLoadCmaps && (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
1006 feType == GFX_FONT_TYPE_TT_OPENTYPE ||
1007 feType == GFX_FONT_TYPE_TRUETYPE)) {
1008 uint32_t kCMAP =
1009 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1010 uint32_t cmapSize;
1011 AutoTArray<uint8_t, 1024> cmapData;
1013 cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
1014 if (cmapSize != GDI_ERROR && cmapSize > 0 &&
1015 cmapData.SetLength(cmapSize, fallible)) {
1016 ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
1017 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1018 uint32_t offset;
1020 if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), cmapSize,
1021 *charmap, offset))) {
1022 fontData.mCharacterMap = charmap;
1023 fontData.mUVSOffset = offset;
1024 cmapLoaded = true;
1025 famData->mFontInfo.mLoadStats.cmaps++;
1030 if (cmapLoaded || nameDataLoaded) {
1031 famData->mFontInfo.mFontFaceData.InsertOrUpdate(fontName, fontData);
1034 return famData->mFontInfo.mCanceled ? 0 : 1;
1037 void GDIFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
1038 // iterate over the family
1039 LOGFONTW logFont;
1040 memset(&logFont, 0, sizeof(LOGFONTW));
1041 logFont.lfCharSet = DEFAULT_CHARSET;
1042 logFont.lfPitchAndFamily = 0;
1043 NS_ConvertUTF8toUTF16 name(aFamilyName);
1044 uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
1045 memcpy(logFont.lfFaceName, name.BeginReading(), l * sizeof(char16_t));
1047 EnumerateFontsForFamilyData data(aFamilyName, *this);
1049 EnumFontFamiliesExW(mHdc, &logFont,
1050 (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
1051 (LPARAM)(&data), 0);
1053 // if found other names, insert them
1054 if (data.mOtherFamilyNames.Length() != 0) {
1055 mOtherFamilyNames.InsertOrUpdate(aFamilyName, data.mOtherFamilyNames);
1056 mLoadStats.othernames += data.mOtherFamilyNames.Length();
1060 already_AddRefed<FontInfoData> gfxGDIFontList::CreateFontInfoData() {
1061 bool loadCmaps = !UsesSystemFallback() ||
1062 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1064 RefPtr<GDIFontInfo> fi =
1065 new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1067 return fi.forget();
1070 gfxFontFamily* gfxGDIFontList::CreateFontFamily(
1071 const nsACString& aName, FontVisibility aVisibility) const {
1072 return new GDIFontFamily(aName, aVisibility);
1075 #ifdef MOZ_BUNDLED_FONTS
1077 void gfxGDIFontList::ActivateBundledFonts() {
1078 nsCOMPtr<nsIFile> localDir;
1079 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1080 if (NS_FAILED(rv)) {
1081 return;
1083 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
1084 return;
1086 bool isDir;
1087 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1088 return;
1091 nsCOMPtr<nsIDirectoryEnumerator> e;
1092 rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
1093 if (NS_FAILED(rv)) {
1094 return;
1097 nsCOMPtr<nsIFile> file;
1098 while (NS_SUCCEEDED(e->GetNextFile(getter_AddRefs(file))) && file) {
1099 nsAutoString path;
1100 if (NS_FAILED(file->GetPath(path))) {
1101 continue;
1103 AddFontResourceExW(path.get(), FR_PRIVATE, nullptr);
1107 #endif