1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Foundation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
22 * Stuart Parmenter <stuart@mozilla.com>
23 * Vladimir Vukicevic <vladimir@pobox.com>
24 * Masayuki Nakano <masayuki@d-toybox.com>
25 * Masatoshi Kimura <VYV03354@nifty.ne.jp>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "gfxWindowsPlatform.h"
43 #include "gfxImageSurface.h"
44 #include "gfxWindowsSurface.h"
46 #include "nsUnicharUtils.h"
49 #include "nsServiceManagerUtils.h"
51 #include "nsIWindowsRegKey.h"
53 #include "gfxWindowsFonts.h"
59 //#define DEBUG_CMAP_SIZE 1
61 // font info loader constants
62 static const PRUint32 kDelayBeforeLoadingCmaps
= 8 * 1000; // 8secs
63 static const PRUint32 kIntervalBetweenLoadingCmaps
= 150; // 150ms
64 static const PRUint32 kNumFontsPerSlice
= 10; // read in info 10 fonts at a time
67 BuildKeyNameFromFontName(nsAString
&aName
)
69 if (aName
.Length() >= LF_FACESIZE
)
70 aName
.Truncate(LF_FACESIZE
- 1);
75 gfxWindowsPlatform::PrefChangedCallback(const char *aPrefName
, void *closure
)
77 // XXX this could be made to only clear out the cache for the prefs that were changed
78 // but it probably isn't that big a deal.
79 gfxWindowsPlatform
*plat
= static_cast<gfxWindowsPlatform
*>(closure
);
80 plat
->mPrefFonts
.Clear();
84 gfxWindowsPlatform::gfxWindowsPlatform()
85 : mStartIndex(0), mIncrement(kNumFontsPerSlice
), mNumFamilies(0)
88 mFontAliases
.Init(20);
89 mFontSubstitutes
.Init(50);
94 nsCOMPtr
<nsIPref
> pref
= do_GetService(NS_PREF_CONTRACTID
);
95 pref
->RegisterCallback("font.", PrefChangedCallback
, this);
96 pref
->RegisterCallback("font.name-list.", PrefChangedCallback
, this);
97 pref
->RegisterCallback("intl.accept_languages", PrefChangedCallback
, this);
98 // don't bother unregistering. We'll get shutdown after the pref service
101 gfxWindowsPlatform::~gfxWindowsPlatform()
105 already_AddRefed
<gfxASurface
>
106 gfxWindowsPlatform::CreateOffscreenSurface(const gfxIntSize
& size
,
107 gfxASurface::gfxImageFormat imageFormat
)
109 gfxASurface
*surf
= new gfxWindowsSurface(size
, imageFormat
);
115 gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW
*lpelfe
,
116 const NEWTEXTMETRICEXW
*nmetrics
,
117 DWORD fontType
, LPARAM data
)
119 FontTable
*ht
= reinterpret_cast<FontTable
*>(data
);
121 const NEWTEXTMETRICW
& metrics
= nmetrics
->ntmTm
;
122 const LOGFONTW
& logFont
= lpelfe
->elfLogFont
;
124 // Ignore vertical fonts
125 if (logFont
.lfFaceName
[0] == L
'@')
128 nsAutoString
name(logFont
.lfFaceName
);
129 BuildKeyNameFromFontName(name
);
131 nsRefPtr
<FontFamily
> ff
;
132 if (!ht
->Get(name
, &ff
)) {
133 ff
= new FontFamily(nsDependentString(logFont
.lfFaceName
));
141 // general cmap reading routines moved to gfxFontUtils.cpp
143 struct FontListData
{
144 FontListData(const nsACString
& aLangGroup
, const nsACString
& aGenericFamily
, nsStringArray
& aListOfFonts
) :
145 mLangGroup(aLangGroup
), mGenericFamily(aGenericFamily
), mStringArray(aListOfFonts
) {}
146 const nsACString
& mLangGroup
;
147 const nsACString
& mGenericFamily
;
148 nsStringArray
& mStringArray
;
151 PLDHashOperator PR_CALLBACK
152 gfxWindowsPlatform::HashEnumFunc(nsStringHashKey::KeyType aKey
,
153 nsRefPtr
<FontFamily
>& aFontFamily
,
156 FontListData
*data
= (FontListData
*)userArg
;
158 // use the first variation for now. This data should be the same
159 // for all the variations and should probably be moved up to
162 style
.langGroup
= data
->mLangGroup
;
163 nsRefPtr
<FontEntry
> aFontEntry
= aFontFamily
->FindFontEntry(style
);
165 /* skip symbol fonts */
166 if (aFontEntry
->mSymbolFont
)
167 return PL_DHASH_NEXT
;
169 if (aFontEntry
->SupportsLangGroup(data
->mLangGroup
) &&
170 aFontEntry
->MatchesGenericFamily(data
->mGenericFamily
))
171 data
->mStringArray
.AppendString(aFontFamily
->mName
);
173 return PL_DHASH_NEXT
;
177 gfxWindowsPlatform::GetFontList(const nsACString
& aLangGroup
,
178 const nsACString
& aGenericFamily
,
179 nsStringArray
& aListOfFonts
)
181 FontListData
data(aLangGroup
, aGenericFamily
, aListOfFonts
);
183 mFonts
.Enumerate(gfxWindowsPlatform::HashEnumFunc
, &data
);
186 aListOfFonts
.Compact();
192 RemoveCharsetFromFontSubstitute(nsAString
&aName
)
194 PRInt32 comma
= aName
.FindChar(PRUnichar(','));
196 aName
.Truncate(comma
);
200 gfxWindowsPlatform::UpdateFontList()
202 gfxFontCache
*fc
= gfxFontCache::GetCache();
204 fc
->AgeAllGenerations();
206 mFontAliases
.Clear();
207 mNonExistingFonts
.Clear();
208 mFontSubstitutes
.Clear();
210 mCodepointsWithNoFonts
.reset();
214 logFont
.lfCharSet
= DEFAULT_CHARSET
;
215 logFont
.lfFaceName
[0] = 0;
216 logFont
.lfPitchAndFamily
= 0;
218 // Use the screen DC here.. should we use something else for printing?
219 HDC dc
= ::GetDC(nsnull
);
220 EnumFontFamiliesExW(dc
, &logFont
, (FONTENUMPROCW
)gfxWindowsPlatform::FontEnumProc
, (LPARAM
)&mFonts
, 0);
221 ::ReleaseDC(nsnull
, dc
);
223 // initialize the cmap loading process after font list has been initialized
224 StartLoader(kDelayBeforeLoadingCmaps
, kIntervalBetweenLoadingCmaps
);
226 // Create the list of FontSubstitutes
227 nsCOMPtr
<nsIWindowsRegKey
> regKey
= do_CreateInstance("@mozilla.org/windows-registry-key;1");
229 return NS_ERROR_FAILURE
;
230 NS_NAMED_LITERAL_STRING(kFontSubstitutesKey
, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
232 nsresult rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
233 kFontSubstitutesKey
, nsIWindowsRegKey::ACCESS_READ
);
238 rv
= regKey
->GetValueCount(&count
);
239 if (NS_FAILED(rv
) || count
== 0)
241 for (PRUint32 i
= 0; i
< count
; i
++) {
242 nsAutoString substituteName
;
243 rv
= regKey
->GetValueName(i
, substituteName
);
244 if (NS_FAILED(rv
) || substituteName
.IsEmpty() ||
245 substituteName
.CharAt(1) == PRUnichar('@'))
248 rv
= regKey
->GetValueType(substituteName
, &valueType
);
249 if (NS_FAILED(rv
) || valueType
!= nsIWindowsRegKey::TYPE_STRING
)
251 nsAutoString actualFontName
;
252 rv
= regKey
->ReadStringValue(substituteName
, actualFontName
);
256 RemoveCharsetFromFontSubstitute(substituteName
);
257 BuildKeyNameFromFontName(substituteName
);
258 RemoveCharsetFromFontSubstitute(actualFontName
);
259 BuildKeyNameFromFontName(actualFontName
);
260 nsRefPtr
<FontFamily
> ff
;
261 if (!actualFontName
.IsEmpty() && mFonts
.Get(actualFontName
, &ff
))
262 mFontSubstitutes
.Put(substituteName
, ff
);
264 mNonExistingFonts
.AppendString(substituteName
);
267 // initialize ranges of characters for which system-wide font search should be skipped
268 mCodepointsWithNoFonts
.SetRange(0,0x1f); // C0 controls
269 mCodepointsWithNoFonts
.SetRange(0x7f,0x9f); // C1 controls
271 InitBadUnderlineList();
276 struct FontFamilyListData
{
277 FontFamilyListData(nsTArray
<nsRefPtr
<FontFamily
> >& aFamilyArray
)
278 : mFamilyArray(aFamilyArray
)
281 static PLDHashOperator PR_CALLBACK
AppendFamily(nsStringHashKey::KeyType aKey
,
282 nsRefPtr
<FontFamily
>& aFamilyEntry
,
285 FontFamilyListData
*data
= (FontFamilyListData
*)aUserArg
;
286 data
->mFamilyArray
.AppendElement(aFamilyEntry
);
287 return PL_DHASH_NEXT
;
290 nsTArray
<nsRefPtr
<FontFamily
> >& mFamilyArray
;
294 gfxWindowsPlatform::GetFontFamilyList(nsTArray
<nsRefPtr
<FontFamily
> >& aFamilyArray
)
296 FontFamilyListData
data(aFamilyArray
);
297 mFonts
.Enumerate(FontFamilyListData::AppendFamily
, &data
);
300 static PRBool
SimpleResolverCallback(const nsAString
& aName
, void* aClosure
)
302 nsString
*result
= static_cast<nsString
*>(aClosure
);
303 result
->Assign(aName
);
308 gfxWindowsPlatform::InitBadUnderlineList()
310 nsAutoTArray
<nsString
, 10> blacklist
;
311 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist
);
312 PRUint32 numFonts
= blacklist
.Length();
313 for (PRUint32 i
= 0; i
< numFonts
; i
++) {
315 nsAutoString resolved
;
316 ResolveFontName(blacklist
[i
], SimpleResolverCallback
, &resolved
, aborted
);
317 if (resolved
.IsEmpty())
319 FontFamily
*ff
= FindFontFamily(resolved
);
322 ff
->mIsBadUnderlineFont
= 1;
327 gfxWindowsPlatform::GetStandardFamilyName(const nsAString
& aFontName
, nsAString
& aFamilyName
)
329 aFamilyName
.Truncate();
331 return ResolveFontName(aFontName
, SimpleResolverCallback
, &aFamilyName
, aborted
);
335 ResolveData(gfxPlatform::FontResolverCallback aCallback
,
336 gfxWindowsPlatform
*aCaller
, const nsAString
*aFontName
,
338 mFoundCount(0), mCallback(aCallback
), mCaller(aCaller
),
339 mFontName(aFontName
), mClosure(aClosure
) {}
340 PRUint32 mFoundCount
;
341 gfxPlatform::FontResolverCallback mCallback
;
342 gfxWindowsPlatform
*mCaller
;
343 const nsAString
*mFontName
;
348 gfxWindowsPlatform::ResolveFontName(const nsAString
& aFontName
,
349 FontResolverCallback aCallback
,
353 if (aFontName
.IsEmpty())
354 return NS_ERROR_FAILURE
;
356 nsAutoString
keyName(aFontName
);
357 BuildKeyNameFromFontName(keyName
);
359 nsRefPtr
<FontFamily
> ff
;
360 if (mFonts
.Get(keyName
, &ff
) ||
361 mFontSubstitutes
.Get(keyName
, &ff
) ||
362 mFontAliases
.Get(keyName
, &ff
)) {
363 aAborted
= !(*aCallback
)(ff
->mName
, aClosure
);
364 // XXX If the font has font link, we should add the linked font.
368 if (mNonExistingFonts
.IndexOf(keyName
) >= 0) {
374 logFont
.lfCharSet
= DEFAULT_CHARSET
;
375 logFont
.lfPitchAndFamily
= 0;
376 PRInt32 len
= aFontName
.Length();
377 if (len
>= LF_FACESIZE
)
378 len
= LF_FACESIZE
- 1;
379 memcpy(logFont
.lfFaceName
,
380 nsPromiseFlatString(aFontName
).get(), len
* sizeof(PRUnichar
));
381 logFont
.lfFaceName
[len
] = 0;
383 HDC dc
= ::GetDC(nsnull
);
384 ResolveData
data(aCallback
, this, &keyName
, aClosure
);
385 aAborted
= !EnumFontFamiliesExW(dc
, &logFont
,
386 (FONTENUMPROCW
)gfxWindowsPlatform::FontResolveProc
,
388 if (data
.mFoundCount
== 0)
389 mNonExistingFonts
.AppendString(keyName
);
390 ::ReleaseDC(nsnull
, dc
);
396 gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW
*lpelfe
,
397 const NEWTEXTMETRICEXW
*nmetrics
,
398 DWORD fontType
, LPARAM data
)
400 const LOGFONTW
& logFont
= lpelfe
->elfLogFont
;
401 // Ignore vertical fonts
402 if (logFont
.lfFaceName
[0] == L
'@' || logFont
.lfFaceName
[0] == 0)
405 ResolveData
*rData
= reinterpret_cast<ResolveData
*>(data
);
407 nsAutoString
name(logFont
.lfFaceName
);
409 // Save the alias name to cache
410 nsRefPtr
<FontFamily
> ff
;
411 nsAutoString
keyName(name
);
412 BuildKeyNameFromFontName(keyName
);
413 if (!rData
->mCaller
->mFonts
.Get(keyName
, &ff
)) {
414 // This case only occurs on failing to build
415 // the list of font substitue. In this case, the user should
416 // reboot the Windows. Probably, we don't have the good way for
417 // resolving in this time.
418 NS_WARNING("Cannot find actual font");
422 rData
->mFoundCount
++;
423 rData
->mCaller
->mFontAliases
.Put(*(rData
->mFontName
), ff
);
425 return (rData
->mCallback
)(name
, rData
->mClosure
);
427 // XXX If the font has font link, we should add the linked font.
431 FontSearch(PRUint32 aCh
, gfxWindowsFont
*aFont
) :
432 ch(aCh
), fontToMatch(aFont
), matchRank(-1) {
435 nsRefPtr
<gfxWindowsFont
> fontToMatch
;
437 nsRefPtr
<FontEntry
> bestMatch
;
440 PLDHashOperator PR_CALLBACK
441 gfxWindowsPlatform::FindFontForCharProc(nsStringHashKey::KeyType aKey
,
442 nsRefPtr
<FontFamily
>& aFontFamily
,
445 FontSearch
*data
= (FontSearch
*)userArg
;
447 const PRUint32 ch
= data
->ch
;
449 nsRefPtr
<FontEntry
> fe
= aFontFamily
->FindFontEntry(*data
->fontToMatch
->GetStyle());
451 // skip over non-unicode and bitmap fonts and fonts that don't have
452 // the code point we're looking for
453 if (fe
->IsCrappyFont() || !fe
->mCharacterMap
.test(ch
))
454 return PL_DHASH_NEXT
;
457 // fonts that claim to support the range are more
458 // likely to be "better fonts" than ones that don't... (in theory)
459 if (fe
->SupportsRange(gfxFontUtils::CharRangeBit(ch
)))
462 if (fe
->SupportsLangGroup(data
->fontToMatch
->GetStyle()->langGroup
))
465 if (fe
->mWindowsFamily
== data
->fontToMatch
->GetFontEntry()->mWindowsFamily
)
467 if (fe
->mWindowsPitch
== data
->fontToMatch
->GetFontEntry()->mWindowsFamily
)
471 const PRBool italic
= (data
->fontToMatch
->GetStyle()->style
!= FONT_STYLE_NORMAL
);
472 if (fe
->mItalic
!= italic
)
476 PRInt8 baseWeight
, weightDistance
;
477 data
->fontToMatch
->GetStyle()->ComputeWeightAndOffset(&baseWeight
, &weightDistance
);
478 if (fe
->mWeight
== (baseWeight
* 100) + (weightDistance
* 100))
480 else if (fe
->mWeight
== data
->fontToMatch
->GetFontEntry()->mWeight
)
483 if (rank
> data
->matchRank
||
484 (rank
== data
->matchRank
&& Compare(fe
->GetName(), data
->bestMatch
->GetName()) > 0)) {
485 data
->bestMatch
= fe
;
486 data
->matchRank
= rank
;
489 return PL_DHASH_NEXT
;
492 already_AddRefed
<gfxWindowsFont
>
493 gfxWindowsPlatform::FindFontForChar(PRUint32 aCh
, gfxWindowsFont
*aFont
)
495 // is codepoint with no matching font? return null immediately
496 if (mCodepointsWithNoFonts
.test(aCh
)) {
500 FontSearch
data(aCh
, aFont
);
502 // find fonts that support the character
503 mFonts
.Enumerate(gfxWindowsPlatform::FindFontForCharProc
, &data
);
505 if (data
.bestMatch
) {
506 nsRefPtr
<gfxWindowsFont
> font
=
507 gfxWindowsFont::GetOrMakeFont(data
.bestMatch
, aFont
->GetStyle());
509 return font
.forget();
513 // no match? add to set of non-matching codepoints
514 mCodepointsWithNoFonts
.set(aCh
);
519 gfxWindowsPlatform::CreateFontGroup(const nsAString
&aFamilies
,
520 const gfxFontStyle
*aStyle
)
522 return new gfxWindowsFontGroup(aFamilies
, aStyle
);
526 gfxWindowsPlatform::FindFontFamily(const nsAString
& aName
)
528 nsAutoString
name(aName
);
529 BuildKeyNameFromFontName(name
);
531 nsRefPtr
<FontFamily
> ff
;
532 if (!mFonts
.Get(name
, &ff
) &&
533 !mFontSubstitutes
.Get(name
, &ff
) &&
534 !mFontAliases
.Get(name
, &ff
)) {
541 gfxWindowsPlatform::FindFontEntry(const nsAString
& aName
, const gfxFontStyle
& aFontStyle
)
543 nsRefPtr
<FontFamily
> ff
= FindFontFamily(aName
);
547 return ff
->FindFontEntry(aFontStyle
);
551 gfxWindowsPlatform::GetPlatformCMSOutputProfile()
556 HDC dc
= GetDC(nsnull
);
557 GetICMProfileW(dc
, &size
, (LPWSTR
)&str
);
558 ReleaseDC(nsnull
, dc
);
560 cmsHPROFILE profile
=
561 cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str
).get(), "r");
565 "ICM profile read from %s successfully\n",
566 NS_ConvertUTF16toUTF8(str
).get());
572 gfxWindowsPlatform::GetPrefFontEntries(const nsCString
& aKey
, nsTArray
<nsRefPtr
<FontEntry
> > *array
)
574 return mPrefFonts
.Get(aKey
, array
);
578 gfxWindowsPlatform::SetPrefFontEntries(const nsCString
& aKey
, nsTArray
<nsRefPtr
<FontEntry
> >& array
)
580 mPrefFonts
.Put(aKey
, array
);
584 gfxWindowsPlatform::InitLoader()
586 GetFontFamilyList(mFontFamilies
);
588 mNumFamilies
= mFontFamilies
.Length();
592 gfxWindowsPlatform::RunLoader()
594 PRUint32 i
, endIndex
= ( mStartIndex
+ mIncrement
< mNumFamilies
? mStartIndex
+ mIncrement
: mNumFamilies
);
596 // for each font family, load in various font info
597 for (i
= mStartIndex
; i
< endIndex
; i
++) {
598 // load the cmaps for all variations
599 mFontFamilies
[i
]->FindStyleVariations();
602 mStartIndex
+= mIncrement
;
603 if (mStartIndex
< mNumFamilies
)
609 gfxWindowsPlatform::FinishLoader()
611 mFontFamilies
.Clear();