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"
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"
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"
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);
54 // Implementation of gfxPlatformFontList for Win32 GDI,
55 // using GDI font enumeration APIs to get the list of fonts
57 class WinUserFontData
: public gfxUserFontData
{
59 explicit WinUserFontData(HANDLE aFontRef
) : mFontRef(aFontRef
) {}
61 virtual ~WinUserFontData() {
62 DebugOnly
<BOOL
> success
;
63 success
= RemoveFontMemResourceEx(mFontRef
);
69 "error deleting font handle (%p) - RemoveFontMemResourceEx failed",
71 NS_ASSERTION(success
, buf
);
79 BYTE
FontTypeToOutPrecision(uint8_t fontType
) {
82 case GFX_FONT_TYPE_TT_OPENTYPE
:
83 case GFX_FONT_TYPE_TRUETYPE
:
84 ret
= OUT_TT_ONLY_PRECIS
;
86 case GFX_FONT_TYPE_PS_OPENTYPE
:
87 ret
= OUT_PS_ONLY_PRECIS
;
89 case GFX_FONT_TYPE_TYPE1
:
90 ret
= OUT_OUTLINE_PRECIS
;
92 case GFX_FONT_TYPE_RASTER
:
93 ret
= OUT_RASTER_PRECIS
;
95 case GFX_FONT_TYPE_DEVICE
:
96 ret
= OUT_DEVICE_PRECIS
;
99 ret
= OUT_DEFAULT_PRECIS
;
104 /***************************************************************
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
;
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(),
133 nsresult
GDIFontEntry::ReadCMAP(FontInfoData
* aFontInfoData
) {
134 AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER
);
136 // attempt this once, if errors occur leave a blank cmap
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
;
156 uint32_t uvsOffset
= 0;
158 (charmap
= GetCMAPFromFontInfo(aFontInfoData
, uvsOffset
))) {
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
,
172 if (NS_SUCCEEDED(rv
)) {
173 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
174 charmap
= pfl
->FindCharMap(charmap
);
175 mHasCmapTable
= true;
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()) {
192 SprintfLiteral(prefix
, "(cmapdata) name: %.220s", mName
.get());
193 charmap
->Dump(prefix
, eGfxLog_cmapdata
);
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
) {
206 return NS_ERROR_FAILURE
;
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
);
220 return NS_ERROR_OUT_OF_MEMORY
;
223 return NS_ERROR_FAILURE
;
226 already_AddRefed
<UnscaledFontGDI
> GDIFontEntry::LookupUnscaledFont(
228 RefPtr
<UnscaledFontGDI
> unscaledFont(mUnscaledFont
);
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
,
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)
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
) {
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
;
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
};
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
308 GetGlyphIndicesW(dc
, str
, 1, glyph
, GGI_MARK_NONEXISTING_GLYPHS
);
309 if (ret
!= GDI_ERROR
&& glyph
[0] != 0xFFFF &&
310 (IsType1() || glyph
[0] != MISSING_GLYPH
)) {
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
);
325 GetCharacterMap()->set(aCh
);
329 // font had a cmap so simply check that
330 return GetCharacterMap()->test(aCh
);
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
,
370 StretchRange aStretch
,
371 gfxUserFontData
* aUserFontData
) {
372 // jtdfix - need to set charset, pitch/family
374 return new GDIFontEntry(aName
, aFontType
, aStyle
, aWeight
, aStretch
,
378 void GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
379 FontListSizes
* aSizes
) const {
380 aSizes
->mFontListSize
+= aMallocSizeOf(this);
381 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
384 /***************************************************************
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") ||
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
)) {
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
);
426 } else if (feType
< fe
->mFontType
) {
427 // otherwise if the new type is worse, skip it
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
);
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);
459 ff
->AddFontEntryLocked(fe
);
461 if (LOG_FONTLIST_ENABLED()) {
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
));
471 void GDIFontFamily::FindStyleVariationsLocked(FontInfoData
* aFontInfoData
) {
477 HDC hdc
= GetDC(nullptr);
478 SetGraphicsMode(hdc
, GM_ADVANCED
);
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
,
491 if (LOG_FONTLIST_ENABLED() && mAvailableFonts
.Length() == 0) {
493 ("(fontlist) no styles available in family \"%s\"", mName
.get()));
496 ReleaseDC(nullptr, hdc
);
498 if (mIsBadUnderlineFamily
) {
499 SetBadUnderlineFonts();
502 CheckForSimpleFamily();
505 /***************************************************************
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());
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() {
533 DWORD i
, rv
, lenAlias
, lenActual
, valueType
;
534 WCHAR aliasName
[MAX_VALUE_NAME
];
535 WCHAR actualName
[MAX_VALUE_DATA
];
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
++) {
546 lenAlias
= ArrayLength(aliasName
);
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) {
556 if (aliasName
[0] == WCHAR('@')) {
560 nsAutoString
substituteName((char16_t
*)aliasName
);
561 nsAutoString
actualFontName((char16_t
*)actualName
);
562 RemoveCharsetFromFontSubstitute(substituteName
);
563 BuildKeyNameFromFontName(substituteName
);
564 RemoveCharsetFromFontSubstitute(actualFontName
);
565 BuildKeyNameFromFontName(actualFontName
);
567 NS_ConvertUTF16toUTF8
substitute(substituteName
);
568 NS_ConvertUTF16toUTF8
actual(actualFontName
);
569 if (!actual
.IsEmpty() && (ff
= mFontFamilies
.GetWeak(actual
))) {
570 mFontSubstitutes
.InsertOrUpdate(substitute
, RefPtr
{ff
});
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
)) {
585 nsAutoString actualFontName
;
586 actualFontName
.AssignLiteral("Courier New");
587 BuildKeyNameFromFontName(actualFontName
);
588 NS_ConvertUTF16toUTF8
actual(actualFontName
);
589 ff
= mFontFamilies
.GetWeak(actual
);
591 mFontSubstitutes
.InsertOrUpdate(substitute
, RefPtr
{ff
});
597 nsresult
gfxGDIFontList::InitFontListForPlatform() {
598 Telemetry::AutoTimer
<Telemetry::GDI_INITFONTLIST_TOTAL
> timer
;
600 mFontSubstitutes
.Clear();
601 mNonExistingFonts
.Clear();
603 // iterate over available families
605 memset(&logfont
, 0, sizeof(logfont
));
606 logfont
.lfCharSet
= DEFAULT_CHARSET
;
609 (void)EnumFontFamiliesExW(hdc
.GetDC(), &logfont
,
610 (FONTENUMPROCW
)&EnumFontFamExProc
, 0, 0);
612 GetFontSubstitutes();
614 GetPrefsAndStartLoader();
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] == '@') {
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
);
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
);
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(
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
;
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
) {
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
)) {
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.
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.
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
;
755 gfxFontEntry
* gfxGDIFontList::MakePlatformFont(const nsACString
& aFontName
,
756 WeightRange aWeightForEntry
,
757 StretchRange aStretchForEntry
,
758 SlantStyleRange aStyleForEntry
,
759 const uint8_t* aFontData
,
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
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
);
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;
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."
796 AddFontMemResourceEx(fontData
, fontLength
, 0 /* reserved */, &numFonts
);
798 if (FixupSymbolEncodedFont(fontData
, fontLength
)) {
799 fontRef
= AddFontMemResourceEx(fontData
, fontLength
, 0, &numFonts
);
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
);
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
);
824 fe
->mIsDataUserFont
= true;
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
));
847 if (mNonExistingFonts
.Contains(keyName
)) {
851 return gfxPlatformFontList::FindAndAddFamiliesLocked(
852 aPresContext
, aGeneric
, aFamily
, aOutput
, aFlags
, aStyle
, aLanguage
,
856 FontFamily
gfxGDIFontList::GetDefaultFontForPlatform(
857 nsPresContext
* aPresContext
, const gfxFontStyle
* aStyle
,
861 // this really shouldn't fail to find a font....
862 NONCLIENTMETRICSW ncm
;
863 ncm
.cbSize
= sizeof(ncm
);
865 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0);
867 ff
= FindFamily(aPresContext
,
868 NS_ConvertUTF16toUTF8(ncm
.lfMessageFont
.lfFaceName
));
874 // ...but just in case, try another (long-deprecated) approach as well
875 HGDIOBJ hGDI
= ::GetStockObject(DEFAULT_GUI_FONT
);
877 if (hGDI
&& ::GetObjectW(hGDI
, sizeof(logFont
), &logFont
)) {
878 ff
= FindFamily(aPresContext
, NS_ConvertUTF16toUTF8(logFont
.lfFaceName
));
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
{
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
);
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()) {
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
)) {
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
) {
971 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
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
);
981 if (famData
->mFontInfo
.mLoadFaceNames
) {
982 gfxFontUtils::ReadCanonicalName((const char*)(nameData
.Elements()),
983 nameSize
, gfxFontUtils::NAME_ID_FULL
,
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);
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
)) {
1009 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
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();
1020 if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData
.Elements(), cmapSize
,
1021 *charmap
, offset
))) {
1022 fontData
.mCharacterMap
= charmap
;
1023 fontData
.mUVSOffset
= offset
;
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
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
);
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
)) {
1083 if (NS_FAILED(localDir
->Append(u
"fonts"_ns
))) {
1087 if (NS_FAILED(localDir
->IsDirectory(&isDir
)) || !isDir
) {
1091 nsCOMPtr
<nsIDirectoryEnumerator
> e
;
1092 rv
= localDir
->GetDirectoryEntries(getter_AddRefs(e
));
1093 if (NS_FAILED(rv
)) {
1097 nsCOMPtr
<nsIFile
> file
;
1098 while (NS_SUCCEEDED(e
->GetNextFile(getter_AddRefs(file
))) && file
) {
1100 if (NS_FAILED(file
->GetPath(path
))) {
1103 AddFontResourceExW(path
.get(), FR_PRIVATE
, nullptr);