bug 388706. cache resolved fonts per language group. r=elif
[mozilla-central.git] / gfx / thebes / src / gfxWindowsPlatform.cpp
blobe9e77e65860978671c0ce62123d50da09072b5e7
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
13 * License.
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.
21 * Contributor(s):
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"
48 #include "nsIPref.h"
49 #include "nsServiceManagerUtils.h"
51 #include "nsIWindowsRegKey.h"
53 #include "gfxWindowsFonts.h"
55 #include <string>
57 #include "lcms.h"
59 //#define DEBUG_CMAP_SIZE 1
61 /* Define this if we want to update the unicode range bitsets based
62 * on the actual characters a font supports.
64 * Doing this can result in very large lists of fonts being returned.
65 * Not doing this can let us prioritize fonts that do have the bit set
66 * as they are more likely to provide better glyphs (in theory).
68 //#define UPDATE_RANGES
70 int PR_CALLBACK
71 gfxWindowsPlatform::PrefChangedCallback(const char *aPrefName, void *closure)
73 gfxWindowsPlatform *plat = static_cast<gfxWindowsPlatform *>(closure);
74 plat->mPrefFonts.Clear();
75 return 0;
78 gfxWindowsPlatform::gfxWindowsPlatform()
80 mFonts.Init(200);
81 mFontAliases.Init(20);
82 mFontSubstitutes.Init(50);
83 mPrefFonts.Init(10);
85 UpdateFontList();
87 nsCOMPtr<nsIPref> pref = do_GetService(NS_PREF_CONTRACTID);
88 pref->RegisterCallback("font.", PrefChangedCallback, this);
89 pref->RegisterCallback("font.name-list.", PrefChangedCallback, this);
90 // don't bother unregistering. We'll get shutdown after the pref service
93 gfxWindowsPlatform::~gfxWindowsPlatform()
97 already_AddRefed<gfxASurface>
98 gfxWindowsPlatform::CreateOffscreenSurface(const gfxIntSize& size,
99 gfxASurface::gfxImageFormat imageFormat)
101 gfxASurface *surf = new gfxWindowsSurface(size, imageFormat);
102 NS_IF_ADDREF(surf);
103 return surf;
106 int CALLBACK
107 gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
108 const NEWTEXTMETRICEXW *nmetrics,
109 DWORD fontType, LPARAM data)
111 gfxWindowsPlatform *thisp = reinterpret_cast<gfxWindowsPlatform*>(data);
113 const LOGFONTW& logFont = lpelfe->elfLogFont;
114 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
116 #ifdef DEBUG_pavlov
117 printf("%s %d %d %d\n", NS_ConvertUTF16toUTF8(nsDependentString(logFont.lfFaceName)).get(),
118 logFont.lfCharSet, logFont.lfItalic, logFont.lfWeight);
119 #endif
121 // Ignore vertical fonts
122 if (logFont.lfFaceName[0] == L'@') {
123 return 1;
126 nsString name(logFont.lfFaceName);
127 ToLowerCase(name);
129 nsRefPtr<FontEntry> fe;
130 if (!thisp->mFonts.Get(name, &fe)) {
131 fe = new FontEntry(nsDependentString(logFont.lfFaceName), (PRUint16)fontType);
132 thisp->mFonts.Put(name, fe);
135 // mark the charset bit
136 fe->mCharset[metrics.tmCharSet] = 1;
138 // put the default weight in the weight table
139 fe->mWeightTable.SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
141 // store the default font weight
142 fe->mDefaultWeight = metrics.tmWeight;
144 fe->mFamily = logFont.lfPitchAndFamily & 0xF0;
145 fe->mPitch = logFont.lfPitchAndFamily & 0x0F;
147 if (nmetrics->ntmFontSig.fsUsb[0] == 0x00000000 &&
148 nmetrics->ntmFontSig.fsUsb[1] == 0x00000000 &&
149 nmetrics->ntmFontSig.fsUsb[2] == 0x00000000 &&
150 nmetrics->ntmFontSig.fsUsb[3] == 0x00000000) {
151 // no unicode ranges
152 fe->mUnicodeFont = PR_FALSE;
153 } else {
154 fe->mUnicodeFont = PR_TRUE;
156 // set the unicode ranges
157 PRUint32 x = 0;
158 for (PRUint32 i = 0; i < 4; ++i) {
159 DWORD range = nmetrics->ntmFontSig.fsUsb[i];
160 for (PRUint32 k = 0; k < 32; ++k) {
161 fe->mUnicodeRanges[x++] = (range & (1 << k)) != 0;
166 return 1;
169 static inline PRUint16
170 ReadShortAt(const PRUint8 *aBuf, PRUint32 aIndex)
172 return (aBuf[aIndex] << 8) | aBuf[aIndex + 1];
175 static inline PRUint16
176 ReadShortAt16(const PRUint16 *aBuf, PRUint32 aIndex)
178 return (((aBuf[aIndex]&0xFF) << 8) | ((aBuf[aIndex]&0xFF00) >> 8));
181 static inline PRUint32
182 ReadLongAt(const PRUint8 *aBuf, PRUint32 aIndex)
184 return ((aBuf[aIndex] << 24) | (aBuf[aIndex + 1] << 16) | (aBuf[aIndex + 2] << 8) | (aBuf[aIndex + 3]));
187 static nsresult
188 ReadCMAPTableFormat12(PRUint8 *aBuf, PRInt32 aLength, FontEntry *aFontEntry)
190 enum {
191 OffsetFormat = 0,
192 OffsetReserved = 2,
193 OffsetTableLength = 4,
194 OffsetLanguage = 8,
195 OffsetNumberGroups = 12,
196 OffsetGroups = 16,
198 SizeOfGroup = 12,
200 GroupOffsetStartCode = 0,
201 GroupOffsetEndCode = 4
203 NS_ENSURE_TRUE(aLength >= 16, NS_ERROR_FAILURE);
205 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 12, NS_ERROR_FAILURE);
206 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetReserved) == 0, NS_ERROR_FAILURE);
208 PRUint32 tablelen = ReadLongAt(aBuf, OffsetTableLength);
209 NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_FAILURE);
210 NS_ENSURE_TRUE(tablelen >= 16, NS_ERROR_FAILURE);
212 NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
214 const PRUint32 numGroups = ReadLongAt(aBuf, OffsetNumberGroups);
215 NS_ENSURE_TRUE(tablelen >= 16 + (12 * numGroups), NS_ERROR_FAILURE);
217 const PRUint8 *groups = aBuf + OffsetGroups;
218 for (PRUint32 i = 0; i < numGroups; i++, groups += SizeOfGroup) {
219 const PRUint32 startCharCode = ReadLongAt(groups, GroupOffsetStartCode);
220 const PRUint32 endCharCode = ReadLongAt(groups, GroupOffsetEndCode);
221 aFontEntry->mCharacterMap.SetRange(startCharCode, endCharCode);
222 #ifdef UPDATE_RANGES
223 for (PRUint32 c = startCharCode; c <= endCharCode; ++c) {
224 PRUint16 b = CharRangeBit(c);
225 if (b != NO_RANGE_FOUND)
226 aFontEntry->mUnicodeRanges.set(b, true);
228 #endif
231 return NS_OK;
234 static nsresult
235 ReadCMAPTableFormat4(PRUint8 *aBuf, PRInt32 aLength, FontEntry *aFontEntry)
237 enum {
238 OffsetFormat = 0,
239 OffsetLength = 2,
240 OffsetLanguage = 4,
241 OffsetSegCountX2 = 6
244 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4, NS_ERROR_FAILURE);
245 PRUint16 tablelen = ReadShortAt(aBuf, OffsetLength);
246 NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_FAILURE);
247 NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_FAILURE);
248 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
250 PRUint16 segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
251 NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4), NS_ERROR_FAILURE);
253 const PRUint16 segCount = segCountX2 / 2;
255 const PRUint16 *endCounts = (PRUint16*)(aBuf + 14);
256 const PRUint16 *startCounts = endCounts + 1 /* skip one uint16 for reservedPad */ + segCount;
257 const PRUint16 *idDeltas = startCounts + segCount;
258 const PRUint16 *idRangeOffsets = idDeltas + segCount;
259 for (PRUint16 i = 0; i < segCount; i++) {
260 const PRUint16 endCount = ReadShortAt16(endCounts, i);
261 const PRUint16 startCount = ReadShortAt16(startCounts, i);
262 const PRUint16 idRangeOffset = ReadShortAt16(idRangeOffsets, i);
263 if (idRangeOffset == 0) {
264 aFontEntry->mCharacterMap.SetRange(startCount, endCount);
265 #ifdef UPDATE_RANGES
266 for (PRUint32 c = startCount; c <= endCount; c++) {
267 PRUint16 b = CharRangeBit(c);
268 if (b != NO_RANGE_FOUND)
269 aFontEntry->mUnicodeRanges.set(b, true);
271 #endif
272 } else {
273 const PRUint16 idDelta = ReadShortAt16(idDeltas, i);
274 for (PRUint32 c = startCount; c <= endCount; ++c) {
275 if (c == 0xFFFF)
276 break;
278 const PRUint16 *gdata = (idRangeOffset/2
279 + (c - startCount)
280 + &idRangeOffsets[i]);
282 NS_ENSURE_TRUE((PRUint8*)gdata > aBuf && (PRUint8*)gdata < aBuf + aLength, NS_ERROR_FAILURE);
284 // make sure we have a glyph
285 if (*gdata != 0) {
286 // The glyph index at this point is:
287 // glyph = (ReadShortAt16(idDeltas, i) + *gdata) % 65536;
289 aFontEntry->mCharacterMap.set(c);
290 #ifdef UPDATE_RANGES
291 PRUint16 b = CharRangeBit(c);
292 if (b != NO_RANGE_FOUND)
293 aFontEntry->mUnicodeRanges.set(b, true);
294 #endif
300 return NS_OK;
303 static nsresult
304 ReadCMAP(HDC hdc, FontEntry *aFontEntry)
306 const PRUint32 kCMAP = (('c') | ('m' << 8) | ('a' << 16) | ('p' << 24));
308 DWORD len = GetFontData(hdc, kCMAP, 0, nsnull, 0);
309 NS_ENSURE_TRUE(len != GDI_ERROR && len != 0, NS_ERROR_FAILURE);
311 nsAutoTArray<PRUint8,16384> buffer;
312 if (!buffer.AppendElements(len))
313 return NS_ERROR_OUT_OF_MEMORY;
314 PRUint8 *buf = buffer.Elements();
316 DWORD newLen = GetFontData(hdc, kCMAP, 0, buf, len);
317 NS_ENSURE_TRUE(newLen == len, NS_ERROR_FAILURE);
319 enum {
320 OffsetVersion = 0,
321 OffsetNumTables = 2,
322 SizeOfHeader = 4,
324 TableOffsetPlatformID = 0,
325 TableOffsetEncodingID = 2,
326 TableOffsetOffset = 4,
327 SizeOfTable = 8,
329 SubtableOffsetFormat = 0
331 enum {
332 PlatformIDMicrosoft = 3
334 enum {
335 EncodingIDSymbol = 0,
336 EncodingIDMicrosoft = 1,
337 EncodingIDUCS4 = 10
340 PRUint16 version = ReadShortAt(buf, OffsetVersion);
341 PRUint16 numTables = ReadShortAt(buf, OffsetNumTables);
343 // save the format and offset we want here
344 PRUint32 keepOffset = 0;
345 PRUint32 keepFormat = 0;
347 PRUint8 *table = buf + SizeOfHeader;
348 for (PRUint16 i = 0; i < numTables; ++i, table += SizeOfTable) {
349 const PRUint16 platformID = ReadShortAt(table, TableOffsetPlatformID);
350 if (platformID != PlatformIDMicrosoft)
351 continue;
353 const PRUint16 encodingID = ReadShortAt(table, TableOffsetEncodingID);
354 const PRUint32 offset = ReadLongAt(table, TableOffsetOffset);
356 NS_ASSERTION(offset < newLen, "ugh");
357 const PRUint8 *subtable = buf + offset;
358 const PRUint16 format = ReadShortAt(subtable, SubtableOffsetFormat);
360 if (encodingID == EncodingIDSymbol) {
361 aFontEntry->mUnicodeFont = PR_FALSE;
362 aFontEntry->mSymbolFont = PR_TRUE;
363 keepFormat = format;
364 keepOffset = offset;
365 break;
366 } else if (format == 4 && encodingID == EncodingIDMicrosoft) {
367 keepFormat = format;
368 keepOffset = offset;
369 } else if (format == 12 && encodingID == EncodingIDUCS4) {
370 keepFormat = format;
371 keepOffset = offset;
372 break; // we don't want to try anything else when this format is available.
376 nsresult rv = NS_ERROR_FAILURE;
378 if (keepFormat == 12)
379 rv = ReadCMAPTableFormat12(buf + keepOffset, len - keepOffset, aFontEntry);
380 else if (keepFormat == 4)
381 rv = ReadCMAPTableFormat4(buf + keepOffset, len - keepOffset, aFontEntry);
383 return rv;
386 PLDHashOperator PR_CALLBACK
387 gfxWindowsPlatform::FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
388 nsRefPtr<FontEntry>& aFontEntry,
389 void* userArg)
391 if (aFontEntry->mFontType != TRUETYPE_FONTTYPE) {
392 /* bitmap fonts suck -- just claim they support everything
393 between 0x20 and 0xFF. All the ones on my system do...
394 If we really wanted to test which characters in this
395 range were supported we could just generate a string with
396 each codepoint and do GetGlyphIndicies or similar to determine
397 what is there.
399 for (PRUint16 ch = 0x20; ch <= 0xFF; ch++)
400 aFontEntry->mCharacterMap.set(ch);
401 return PL_DHASH_NEXT;
404 HDC hdc = GetDC(nsnull);
406 LOGFONTW logFont;
407 memset(&logFont, 0, sizeof(LOGFONTW));
408 logFont.lfCharSet = DEFAULT_CHARSET;
409 logFont.lfPitchAndFamily = 0;
410 PRUint32 l = PR_MIN(aFontEntry->mName.Length(), LF_FACESIZE - 1);
411 memcpy(logFont.lfFaceName,
412 nsPromiseFlatString(aFontEntry->mName).get(),
413 l * sizeof(PRUnichar));
414 logFont.lfFaceName[l] = 0;
416 HFONT font = CreateFontIndirectW(&logFont);
418 if (font) {
419 HFONT oldFont = (HFONT)SelectObject(hdc, font);
421 nsresult rv = ReadCMAP(hdc, aFontEntry);
423 if (NS_FAILED(rv)) {
424 aFontEntry->mUnicodeFont = PR_FALSE;
425 //printf("%s failed to get cmap\n", NS_ConvertUTF16toUTF8(aFontEntry->mName).get());
428 SelectObject(hdc, oldFont);
429 DeleteObject(font);
432 ReleaseDC(nsnull, hdc);
434 return PL_DHASH_NEXT;
438 struct FontListData {
439 FontListData(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) :
440 mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {}
441 const nsACString& mLangGroup;
442 const nsACString& mGenericFamily;
443 nsStringArray& mStringArray;
446 PLDHashOperator PR_CALLBACK
447 gfxWindowsPlatform::HashEnumFunc(nsStringHashKey::KeyType aKey,
448 nsRefPtr<FontEntry>& aFontEntry,
449 void* userArg)
451 FontListData *data = (FontListData*)userArg;
453 /* skip symbol fonts */
454 if (aFontEntry->mSymbolFont)
455 return PL_DHASH_NEXT;
457 if (aFontEntry->SupportsLangGroup(data->mLangGroup) &&
458 aFontEntry->MatchesGenericFamily(data->mGenericFamily))
459 data->mStringArray.AppendString(aFontEntry->mName);
461 return PL_DHASH_NEXT;
464 nsresult
465 gfxWindowsPlatform::GetFontList(const nsACString& aLangGroup,
466 const nsACString& aGenericFamily,
467 nsStringArray& aListOfFonts)
469 FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
471 mFonts.Enumerate(gfxWindowsPlatform::HashEnumFunc, &data);
473 aListOfFonts.Sort();
474 aListOfFonts.Compact();
476 return NS_OK;
479 static void
480 RemoveCharsetFromFontSubstitute(nsAString &aName)
482 PRInt32 comma = aName.FindChar(PRUnichar(','));
483 if (comma >= 0)
484 aName.Truncate(comma);
487 static void
488 BuildKeyNameFromFontName(nsAString &aName)
490 if (aName.Length() >= LF_FACESIZE)
491 aName.Truncate(LF_FACESIZE - 1);
492 ToLowerCase(aName);
495 nsresult
496 gfxWindowsPlatform::UpdateFontList()
498 mFonts.Clear();
499 mFontAliases.Clear();
500 mNonExistingFonts.Clear();
501 mFontSubstitutes.Clear();
502 mPrefFonts.Clear();
504 LOGFONTW logFont;
505 logFont.lfCharSet = DEFAULT_CHARSET;
506 logFont.lfFaceName[0] = 0;
507 logFont.lfPitchAndFamily = 0;
509 // Use the screen DC here.. should we use something else for printing?
510 HDC dc = ::GetDC(nsnull);
511 EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontEnumProc, (LPARAM)this, 0);
512 ::ReleaseDC(nsnull, dc);
514 // Update all the fonts cmaps
515 mFonts.Enumerate(gfxWindowsPlatform::FontGetCMapDataProc, nsnull);
517 // Create the list of FontSubstitutes
518 nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
519 if (!regKey)
520 return NS_ERROR_FAILURE;
521 NS_NAMED_LITERAL_STRING(kFontSubstitutesKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
523 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
524 kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
525 if (NS_FAILED(rv))
526 return rv;
528 PRUint32 count;
529 rv = regKey->GetValueCount(&count);
530 if (NS_FAILED(rv) || count == 0)
531 return rv;
532 for (PRUint32 i = 0; i < count; i++) {
533 nsAutoString substituteName;
534 rv = regKey->GetValueName(i, substituteName);
535 if (NS_FAILED(rv) || substituteName.IsEmpty() ||
536 substituteName.CharAt(1) == PRUnichar('@'))
537 continue;
538 PRUint32 valueType;
539 rv = regKey->GetValueType(substituteName, &valueType);
540 if (NS_FAILED(rv) || valueType != nsIWindowsRegKey::TYPE_STRING)
541 continue;
542 nsAutoString actualFontName;
543 rv = regKey->ReadStringValue(substituteName, actualFontName);
544 if (NS_FAILED(rv))
545 continue;
547 RemoveCharsetFromFontSubstitute(substituteName);
548 BuildKeyNameFromFontName(substituteName);
549 RemoveCharsetFromFontSubstitute(actualFontName);
550 BuildKeyNameFromFontName(actualFontName);
551 nsRefPtr<FontEntry> fe;
552 if (!actualFontName.IsEmpty() && mFonts.Get(actualFontName, &fe))
553 mFontSubstitutes.Put(substituteName, fe);
554 else
555 mNonExistingFonts.AppendString(substituteName);
558 return NS_OK;
561 struct ResolveData {
562 ResolveData(gfxPlatform::FontResolverCallback aCallback,
563 gfxWindowsPlatform *aCaller, const nsAString *aFontName,
564 void *aClosure) :
565 mFoundCount(0), mCallback(aCallback), mCaller(aCaller),
566 mFontName(aFontName), mClosure(aClosure) {}
567 PRUint32 mFoundCount;
568 gfxPlatform::FontResolverCallback mCallback;
569 gfxWindowsPlatform *mCaller;
570 const nsAString *mFontName;
571 void *mClosure;
574 nsresult
575 gfxWindowsPlatform::ResolveFontName(const nsAString& aFontName,
576 FontResolverCallback aCallback,
577 void *aClosure,
578 PRBool& aAborted)
580 if (aFontName.IsEmpty())
581 return NS_ERROR_FAILURE;
583 nsAutoString keyName(aFontName);
584 BuildKeyNameFromFontName(keyName);
586 nsRefPtr<FontEntry> fe;
587 if (mFonts.Get(keyName, &fe) ||
588 mFontSubstitutes.Get(keyName, &fe) ||
589 mFontAliases.Get(keyName, &fe)) {
590 aAborted = !(*aCallback)(fe->mName, aClosure);
591 // XXX If the font has font link, we should add the linked font.
592 return NS_OK;
595 if (mNonExistingFonts.IndexOf(keyName) >= 0) {
596 aAborted = PR_FALSE;
597 return NS_OK;
600 LOGFONTW logFont;
601 logFont.lfCharSet = DEFAULT_CHARSET;
602 logFont.lfPitchAndFamily = 0;
603 PRInt32 len = aFontName.Length();
604 if (len >= LF_FACESIZE)
605 len = LF_FACESIZE - 1;
606 memcpy(logFont.lfFaceName,
607 nsPromiseFlatString(aFontName).get(), len * sizeof(PRUnichar));
608 logFont.lfFaceName[len] = 0;
610 HDC dc = ::GetDC(nsnull);
611 ResolveData data(aCallback, this, &keyName, aClosure);
612 aAborted =
613 !EnumFontFamiliesExW(dc, &logFont,
614 (FONTENUMPROCW)gfxWindowsPlatform::FontResolveProc,
615 (LPARAM)&data, 0);
616 if (data.mFoundCount == 0)
617 mNonExistingFonts.AppendString(keyName);
618 ::ReleaseDC(nsnull, dc);
620 return NS_OK;
623 int CALLBACK
624 gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW *lpelfe,
625 const NEWTEXTMETRICEXW *nmetrics,
626 DWORD fontType, LPARAM data)
628 const LOGFONTW& logFont = lpelfe->elfLogFont;
629 // Ignore vertical fonts
630 if (logFont.lfFaceName[0] == L'@' || logFont.lfFaceName[0] == 0)
631 return 1;
633 ResolveData *rData = reinterpret_cast<ResolveData*>(data);
635 nsAutoString name(logFont.lfFaceName);
637 // Save the alias name to cache
638 nsRefPtr<FontEntry> fe;
639 nsAutoString keyName(name);
640 BuildKeyNameFromFontName(keyName);
641 if (!rData->mCaller->mFonts.Get(keyName, &fe)) {
642 // This case only occurs on failing to build
643 // the list of font substitue. In this case, the user should
644 // reboot the Windows. Probably, we don't have the good way for
645 // resolving in this time.
646 NS_WARNING("Cannot find actual font");
647 return 1;
650 rData->mFoundCount++;
651 rData->mCaller->mFontAliases.Put(*(rData->mFontName), fe);
653 return (rData->mCallback)(name, rData->mClosure);
655 // XXX If the font has font link, we should add the linked font.
658 struct FontSearch {
659 FontSearch(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont) :
660 string(aString), length(aLength), fontToMatch(aFont), matchRank(0) {
662 const PRUnichar *string;
663 const PRUint32 length;
664 nsRefPtr<gfxWindowsFont> fontToMatch;
665 PRInt32 matchRank;
666 nsRefPtr<FontEntry> bestMatch;
669 PLDHashOperator PR_CALLBACK
670 gfxWindowsPlatform::FindFontForStringProc(nsStringHashKey::KeyType aKey,
671 nsRefPtr<FontEntry>& aFontEntry,
672 void* userArg)
674 // bitmap fonts suck
675 if (aFontEntry->IsCrappyFont())
676 return PL_DHASH_NEXT;
678 FontSearch *data = (FontSearch*)userArg;
680 PRInt32 rank = 0;
682 for (PRUint32 i = 0; i < data->length; ++i) {
683 PRUint32 ch = data->string[i];
685 if ((i+1 < data->length) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(data->string[i+1])) {
686 i++;
687 ch = SURROGATE_TO_UCS4(ch, data->string[i]);
690 if (aFontEntry->mCharacterMap.test(ch)) {
691 rank += 20;
693 // fonts that claim to support the range are more
694 // likely to be "better fonts" than ones that don't... (in theory)
695 if (aFontEntry->SupportsRange(CharRangeBit(ch)))
696 rank += 1;
700 // if we didn't match any characters don't bother wasting more time.
701 if (rank == 0)
702 return PL_DHASH_NEXT;
705 if (aFontEntry->SupportsLangGroup(data->fontToMatch->GetStyle()->langGroup))
706 rank += 10;
708 if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mFamily)
709 rank += 5;
710 if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mPitch)
711 rank += 5;
713 /* weight */
714 PRInt8 baseWeight, weightDistance;
715 data->fontToMatch->GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
716 PRUint16 targetWeight = (baseWeight * 100) + (weightDistance * 100);
717 if (targetWeight == aFontEntry->mDefaultWeight)
718 rank += 5;
720 if (rank > data->matchRank ||
721 (rank == data->matchRank && Compare(aFontEntry->mName, data->bestMatch->mName) > 0)) {
722 data->bestMatch = aFontEntry;
723 data->matchRank = rank;
726 return PL_DHASH_NEXT;
730 FontEntry *
731 gfxWindowsPlatform::FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont)
733 FontSearch data(aString, aLength, aFont);
735 // find fonts that support the character
736 mFonts.Enumerate(gfxWindowsPlatform::FindFontForStringProc, &data);
738 return data.bestMatch;
741 gfxFontGroup *
742 gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
743 const gfxFontStyle *aStyle)
745 return new gfxWindowsFontGroup(aFamilies, aStyle);
748 FontEntry *
749 gfxWindowsPlatform::FindFontEntry(const nsAString& aName)
751 nsString name(aName);
752 ToLowerCase(name);
754 nsRefPtr<FontEntry> fe;
755 if (!mFonts.Get(name, &fe) &&
756 !mFontSubstitutes.Get(name, &fe) &&
757 !mFontAliases.Get(name, &fe)) {
758 return nsnull;
760 return fe.get();
763 cmsHPROFILE
764 gfxWindowsPlatform::GetPlatformCMSOutputProfile()
766 WCHAR str[1024+1];
767 DWORD size = 1024;
769 HDC dc = GetDC(nsnull);
770 GetICMProfileW(dc, &size, (LPWSTR)&str);
771 ReleaseDC(nsnull, dc);
773 cmsHPROFILE profile =
774 cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str).get(), "r");
775 #ifdef DEBUG_tor
776 if (profile)
777 fprintf(stderr,
778 "ICM profile read from %s successfully\n",
779 NS_ConvertUTF16toUTF8(str).get());
780 #endif
781 return profile;
784 PRBool
785 gfxWindowsPlatform::GetPrefFontEntries(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> > *array)
787 nsCAutoString keyName(aLangGroup);
788 return mPrefFonts.Get(keyName, array);
791 void
792 gfxWindowsPlatform::SetPrefFontEntries(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> >& array)
794 nsCAutoString keyName(aLangGroup);
795 mPrefFonts.Put(keyName, array);