Bug 435739 Poor performance of Firefox 3 with no X RENDER extension
[wine-gecko.git] / gfx / thebes / src / gfxFontconfigUtils.cpp
blob293fa61e08c798873efd8b0f9ddc03e7418fcb3a
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 Japan code.
17 * The Initial Developer of the Original Code is Mozilla Japan.
18 * Portions created by the Initial Developer are Copyright (C) 2007
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Masayuki Nakano <masayuki@d-toybox.com>
23 * Vladimir Vukicevic <vladimir@pobox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "gfxFontconfigUtils.h"
41 #include <fontconfig/fontconfig.h>
43 #include "nsIPrefBranch.h"
44 #include "nsIPrefService.h"
45 #include "nsServiceManagerUtils.h"
47 #include "nsIAtom.h"
48 #include "nsCRT.h"
50 /* static */ gfxFontconfigUtils* gfxFontconfigUtils::sUtils = nsnull;
52 gfxFontconfigUtils::gfxFontconfigUtils()
54 mAliasTable.Init(50);
55 UpdateFontListInternal(PR_TRUE);
58 nsresult
59 gfxFontconfigUtils::GetFontList(const nsACString& aLangGroup,
60 const nsACString& aGenericFamily,
61 nsStringArray& aListOfFonts)
63 aListOfFonts.Clear();
65 nsresult rv = UpdateFontListInternal();
66 if (NS_FAILED(rv))
67 return rv;
69 nsCStringArray tmpFonts;
70 nsCStringArray *fonts = &mFonts;
71 if (!aLangGroup.IsEmpty() || !aGenericFamily.IsEmpty()) {
72 rv = GetFontListInternal(tmpFonts, &aLangGroup);
73 if (NS_FAILED(rv))
74 return rv;
75 fonts = &tmpFonts;
78 for (PRInt32 i = 0; i < fonts->Count(); ++i)
79 aListOfFonts.AppendString(NS_ConvertUTF8toUTF16(*fonts->CStringAt(i)));
81 aListOfFonts.Sort();
83 PRInt32 serif = 0, sansSerif = 0, monospace = 0;
85 // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
86 // "monospace", slightly different from CSS's 5.
87 if (aGenericFamily.IsEmpty())
88 serif = sansSerif = monospace = 1;
89 else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
90 serif = 1;
91 else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
92 sansSerif = 1;
93 else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
94 monospace = 1;
95 else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
96 aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
97 serif = sansSerif = 1;
98 else
99 NS_NOTREACHED("unexpected CSS generic font family");
101 // The first in the list becomes the default in
102 // gFontsDialog.readFontSelection() if the preference-selected font is not
103 // available, so put system configured defaults first.
104 if (monospace)
105 aListOfFonts.InsertStringAt(NS_LITERAL_STRING("monospace"), 0);
106 if (sansSerif)
107 aListOfFonts.InsertStringAt(NS_LITERAL_STRING("sans-serif"), 0);
108 if (serif)
109 aListOfFonts.InsertStringAt(NS_LITERAL_STRING("serif"), 0);
111 return NS_OK;
114 struct MozGtkLangGroup {
115 const char *mozLangGroup;
116 const FcChar8 *Lang;
119 const MozGtkLangGroup MozGtkLangGroups[] = {
120 { "x-western", (const FcChar8 *)"en" },
121 { "x-central-euro", (const FcChar8 *)"pl" },
122 { "x-cyrillic", (const FcChar8 *)"ru" },
123 { "x-baltic", (const FcChar8 *)"lv" },
124 { "x-devanagari", (const FcChar8 *)"hi" },
125 { "x-tamil", (const FcChar8 *)"ta" },
126 { "x-armn", (const FcChar8 *)"hy" },
127 { "x-beng", (const FcChar8 *)"bn" },
128 { "x-cans", (const FcChar8 *)"iu" },
129 { "x-ethi", (const FcChar8 *)"am" },
130 { "x-geor", (const FcChar8 *)"ka" },
131 { "x-gujr", (const FcChar8 *)"gu" },
132 { "x-guru", (const FcChar8 *)"pa" },
133 { "x-khmr", (const FcChar8 *)"km" },
134 { "x-mlym", (const FcChar8 *)"ml" },
135 { "x-orya", (const FcChar8 *)"or" },
136 { "x-telu", (const FcChar8 *)"te" },
137 { "x-knda", (const FcChar8 *)"kn" },
138 { "x-sinh", (const FcChar8 *)"si" },
139 { "x-unicode", 0 },
140 { "x-user-def", 0 }
143 static const MozGtkLangGroup*
144 NS_FindFCLangGroup (nsACString &aLangGroup)
146 for (unsigned int i=0; i < NS_ARRAY_LENGTH(MozGtkLangGroups); ++i) {
147 if (aLangGroup.Equals(MozGtkLangGroups[i].mozLangGroup,
148 nsCaseInsensitiveCStringComparator())) {
149 return &MozGtkLangGroups[i];
153 return nsnull;
156 static void
157 NS_AddLangGroup(FcPattern *aPattern, nsIAtom *aLangGroup)
159 // Find the FC lang group for this lang group
160 nsCAutoString cname;
161 aLangGroup->ToUTF8String(cname);
163 // see if the lang group needs to be translated from mozilla's
164 // internal mapping into fontconfig's
165 const struct MozGtkLangGroup *langGroup;
166 langGroup = NS_FindFCLangGroup(cname);
168 // if there's no lang group, just use the lang group as it was
169 // passed to us
171 // we're casting away the const here for the strings - should be
172 // safe.
173 if (!langGroup)
174 FcPatternAddString(aPattern, FC_LANG, (FcChar8 *)cname.get());
175 else if (langGroup->Lang)
176 FcPatternAddString(aPattern, FC_LANG, (FcChar8 *)langGroup->Lang);
180 nsresult
181 gfxFontconfigUtils::GetFontListInternal(nsCStringArray& aListOfFonts,
182 const nsACString *aLangGroup)
184 FcPattern *pat = NULL;
185 FcObjectSet *os = NULL;
186 FcFontSet *fs = NULL;
187 nsresult rv = NS_ERROR_FAILURE;
189 aListOfFonts.Clear();
191 pat = FcPatternCreate();
192 if (!pat)
193 goto end;
195 os = FcObjectSetBuild(FC_FAMILY, NULL);
196 if (!os)
197 goto end;
199 // take the pattern and add the lang group to it
200 if (aLangGroup && !aLangGroup->IsEmpty()) {
201 nsCOMPtr<nsIAtom> langAtom = do_GetAtom(*aLangGroup);
202 NS_AddLangGroup(pat, langAtom);
205 fs = FcFontList(NULL, pat, os);
206 if (!fs)
207 goto end;
209 for (int i = 0; i < fs->nfont; i++) {
210 char *family;
212 if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
213 (FcChar8 **) &family) != FcResultMatch)
215 continue;
218 // Remove duplicates...
219 nsCAutoString strFamily(family);
220 if (aListOfFonts.IndexOf(strFamily) >= 0)
221 continue;
223 aListOfFonts.AppendCString(strFamily);
226 rv = NS_OK;
228 end:
229 if (NS_FAILED(rv))
230 aListOfFonts.Clear();
232 if (pat)
233 FcPatternDestroy(pat);
234 if (os)
235 FcObjectSetDestroy(os);
236 if (fs)
237 FcFontSetDestroy(fs);
239 return rv;
242 nsresult
243 gfxFontconfigUtils::UpdateFontList()
245 return UpdateFontListInternal(PR_TRUE);
248 nsresult
249 gfxFontconfigUtils::UpdateFontListInternal(PRBool aForce)
251 if (!aForce && FcConfigUptoDate(NULL))
252 return NS_OK;
254 FcInitReinitialize();
256 mFonts.Clear();
257 mAliasForSingleFont.Clear();
258 mAliasForMultiFonts.Clear();
259 mNonExistingFonts.Clear();
261 mAliasTable.Clear();
263 nsresult rv = GetFontListInternal(mFonts);
264 if (NS_FAILED(rv))
265 return rv;
267 // XXX we don't support all alias names.
268 // Because if we don't check whether the given font name is alias name,
269 // fontconfig converts the non existing font to sans-serif.
270 // This is not good if the web page specifies font-family
271 // that has Windows font name in the first.
272 nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
273 if (!prefs)
274 return NS_ERROR_FAILURE;
276 nsCOMPtr<nsIPrefBranch> prefBranch;
277 prefs->GetBranch(0, getter_AddRefs(prefBranch));
278 if (!prefBranch)
279 return NS_ERROR_FAILURE;
281 nsXPIDLCString list;
282 rv = prefBranch->GetCharPref("font.alias-list", getter_Copies(list));
283 if (NS_FAILED(rv))
284 return NS_OK;
286 if (!list.IsEmpty()) {
287 const char kComma = ',';
288 const char *p, *p_end;
289 list.BeginReading(p);
290 list.EndReading(p_end);
291 while (p < p_end) {
292 while (nsCRT::IsAsciiSpace(*p)) {
293 if (++p == p_end)
294 break;
296 if (p == p_end)
297 break;
298 const char *start = p;
299 while (++p != p_end && *p != kComma)
300 /* nothing */ ;
301 nsCAutoString name(Substring(start, p));
302 name.CompressWhitespace(PR_FALSE, PR_TRUE);
303 mAliasForMultiFonts.AppendCString(name);
304 p++;
308 if (mAliasForMultiFonts.Count() == 0)
309 return NS_OK;
311 for (PRInt32 i = 0; i < mAliasForMultiFonts.Count(); i++) {
312 nsRefPtr<gfxFontNameList> fonts = new gfxFontNameList;
313 nsCAutoString fontname(*mAliasForMultiFonts.CStringAt(i));
314 rv = GetResolvedFonts(fontname, fonts);
315 if (NS_FAILED(rv))
316 return rv;
318 nsCAutoString key;
319 ToLowerCase(fontname, key);
320 mAliasTable.Put(key, fonts);
322 return NS_OK;
325 nsresult
326 gfxFontconfigUtils::GetResolvedFonts(const nsACString& aName,
327 gfxFontNameList* aResult)
329 FcPattern *pat = NULL;
330 FcFontSet *fs = NULL;
331 FcResult fresult;
332 aResult->Clear();
333 nsresult rv = NS_ERROR_FAILURE;
335 pat = FcPatternCreate();
336 if (!pat)
337 goto end;
339 FcDefaultSubstitute(pat);
340 FcPatternAddString(pat, FC_FAMILY,
341 (FcChar8 *)nsPromiseFlatCString(aName).get());
342 // Delete the lang param. We need lang independent alias list.
343 FcPatternDel(pat, FC_LANG);
344 FcConfigSubstitute(NULL, pat, FcMatchPattern);
346 fs = FcFontSort(NULL, pat, FcTrue, NULL, &fresult);
347 if (!fs)
348 goto end;
350 rv = NS_OK;
351 for (int i = 0; i < fs->nfont; i++) {
352 char *family;
354 if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
355 (FcChar8 **) &family) != FcResultMatch ||
356 mAliasForMultiFonts.IndexOfIgnoreCase(nsDependentCString(family)) >= 0 ||
357 IsExistingFont(nsDependentCString(family)) == 0)
359 continue;
361 NS_ConvertUTF8toUTF16 actualName(family);
362 if (aResult->Exists(actualName))
363 continue;
364 aResult->AppendElement(actualName);
367 end:
368 if (pat)
369 FcPatternDestroy(pat);
370 if (fs)
371 FcFontSetDestroy(fs);
372 return rv;
375 nsresult
376 gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
378 aFamilyName.Truncate();
380 // The fontconfig has generic family names in the font list.
381 if (aFontName.EqualsLiteral("serif") ||
382 aFontName.EqualsLiteral("sans-serif") ||
383 aFontName.EqualsLiteral("monospace")) {
384 aFamilyName.Assign(aFontName);
385 return NS_OK;
388 NS_ConvertUTF16toUTF8 fontname(aFontName);
390 if (mFonts.IndexOf(fontname) >= 0) {
391 aFamilyName.Assign(aFontName);
392 return NS_OK;
395 if (mNonExistingFonts.IndexOf(fontname) >= 0)
396 return NS_OK;
398 FcPattern *pat = NULL;
399 FcObjectSet *os = NULL;
400 FcFontSet *givenFS = NULL;
401 nsCStringArray candidates;
402 FcFontSet *candidateFS = NULL;
403 nsresult rv = NS_ERROR_FAILURE;
405 pat = FcPatternCreate();
406 if (!pat)
407 goto end;
409 FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
411 os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, NULL);
412 if (!os)
413 goto end;
415 givenFS = FcFontList(NULL, pat, os);
416 if (!givenFS)
417 goto end;
419 // The first value associated with a FC_FAMILY property is the family
420 // returned by GetFontList(), so use this value if appropriate.
422 // See if there is a font face with first family equal to the given family.
423 for (int i = 0; i < givenFS->nfont; ++i) {
424 char *firstFamily;
425 if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
426 (FcChar8 **) &firstFamily) != FcResultMatch)
427 continue;
429 nsDependentCString first(firstFamily);
430 if (candidates.IndexOf(first) < 0) {
431 candidates.AppendCString(first);
433 if (fontname.Equals(first)) {
434 aFamilyName.Assign(aFontName);
435 rv = NS_OK;
436 goto end;
441 // See if any of the first family names represent the same set of font
442 // faces as the given family.
443 for (PRInt32 j = 0; j < candidates.Count(); ++j) {
444 FcPatternDel(pat, FC_FAMILY);
445 FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j]->get());
447 candidateFS = FcFontList(NULL, pat, os);
448 if (!candidateFS)
449 goto end;
451 if (candidateFS->nfont != givenFS->nfont)
452 continue;
454 PRBool equal = PR_TRUE;
455 for (int i = 0; i < givenFS->nfont; ++i) {
456 if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
457 equal = PR_FALSE;
458 break;
461 if (equal) {
462 AppendUTF8toUTF16(*candidates[j], aFamilyName);
463 rv = NS_OK;
464 goto end;
468 // No match found; return empty string.
469 rv = NS_OK;
471 end:
472 if (pat)
473 FcPatternDestroy(pat);
474 if (os)
475 FcObjectSetDestroy(os);
476 if (givenFS)
477 FcFontSetDestroy(givenFS);
478 if (candidateFS)
479 FcFontSetDestroy(candidateFS);
481 return rv;
484 nsresult
485 gfxFontconfigUtils::ResolveFontName(const nsAString& aFontName,
486 gfxPlatform::FontResolverCallback aCallback,
487 void *aClosure,
488 PRBool& aAborted)
490 aAborted = PR_FALSE;
492 nsresult rv = UpdateFontListInternal();
493 if (NS_FAILED(rv))
494 return rv;
496 NS_ConvertUTF16toUTF8 fontname(aFontName);
497 if (mAliasForMultiFonts.IndexOfIgnoreCase(fontname) >= 0) {
498 nsCAutoString key;
499 ToLowerCase(fontname, key);
500 nsRefPtr<gfxFontNameList> fonts;
501 if (!mAliasTable.Get(key, &fonts))
502 NS_ERROR("The mAliasTable was broken!");
503 for (PRUint32 i = 0; i < fonts->Length(); i++) {
504 aAborted = !(*aCallback)(fonts->ElementAt(i), aClosure);
505 if (aAborted)
506 break;
508 } else {
509 PRInt32 result = IsExistingFont(fontname);
510 if (result < 0)
511 return NS_ERROR_FAILURE;
513 if (result > 0)
514 aAborted = !(*aCallback)(aFontName, aClosure);
517 return NS_OK;
520 PRInt32
521 gfxFontconfigUtils::IsExistingFont(const nsACString &aFontName)
523 // Very many sites may specify the font-family only for Windows and Mac.
524 // We should check negative cache at first.
525 if (mNonExistingFonts.IndexOf(aFontName) >= 0)
526 return 0;
527 if (mAliasForSingleFont.IndexOf(aFontName) >= 0)
528 return 1;
529 if (mFonts.IndexOf(aFontName) >= 0)
530 return 1;
532 // XXX Sometimes, the font has two or more names (e.g., "Sazanami Gothic"
533 // has Japanese localized name). The another name doesn't including the
534 // cache. Therefore, we need to check the name.
535 // But we don't need to resolve the name. Because both names are not same
536 // behavior. E.g., the default settings of "Sazanami" on Fedora Core 5,
537 // the non-localized name uses Anti-alias, but the localized name uses it.
538 // So, we should check just whether the font is existing, don't resolve
539 // to regular name.
541 FcPattern *pat = NULL;
542 FcObjectSet *os = NULL;
543 FcFontSet *fs = NULL;
544 PRInt32 result = -1;
546 pat = FcPatternCreate();
547 if (!pat)
548 goto end;
550 FcPatternAddString(pat, FC_FAMILY,
551 (FcChar8 *)nsPromiseFlatCString(aFontName).get());
553 os = FcObjectSetBuild(FC_FAMILY, NULL);
554 if (!os)
555 goto end;
557 fs = FcFontList(NULL, pat, os);
558 if (!fs)
559 goto end;
561 // There can be more than one matching set of family names: see bug 393819.
562 if (fs->nfont > 0) {
563 mAliasForSingleFont.AppendCString(aFontName);
564 result = 1;
565 } else {
566 mNonExistingFonts.AppendCString(aFontName);
567 result = 0;
570 end:
571 if (pat)
572 FcPatternDestroy(pat);
573 if (os)
574 FcObjectSetDestroy(os);
575 if (fs)
576 FcFontSetDestroy(fs);
577 return result;
580 PRBool
581 gfxFontNameList::Exists(nsAString& aName) {
582 for (PRUint32 i = 0; i < Length(); i++) {
583 if (aName.Equals(ElementAt(i)))
584 return PR_TRUE;
586 return PR_FALSE;