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 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.
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"
50 /* static */ gfxFontconfigUtils
* gfxFontconfigUtils::sUtils
= nsnull
;
52 gfxFontconfigUtils::gfxFontconfigUtils()
55 UpdateFontListInternal(PR_TRUE
);
59 gfxFontconfigUtils::GetFontList(const nsACString
& aLangGroup
,
60 const nsACString
& aGenericFamily
,
61 nsStringArray
& aListOfFonts
)
65 nsresult rv
= UpdateFontListInternal();
69 nsCStringArray tmpFonts
;
70 nsCStringArray
*fonts
= &mFonts
;
71 if (!aLangGroup
.IsEmpty() || !aGenericFamily
.IsEmpty()) {
72 rv
= GetFontListInternal(tmpFonts
, &aLangGroup
);
78 for (PRInt32 i
= 0; i
< fonts
->Count(); ++i
)
79 aListOfFonts
.AppendString(NS_ConvertUTF8toUTF16(*fonts
->CStringAt(i
)));
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"))
91 else if (aGenericFamily
.LowerCaseEqualsLiteral("sans-serif"))
93 else if (aGenericFamily
.LowerCaseEqualsLiteral("monospace"))
95 else if (aGenericFamily
.LowerCaseEqualsLiteral("cursive") ||
96 aGenericFamily
.LowerCaseEqualsLiteral("fantasy"))
97 serif
= sansSerif
= 1;
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.
105 aListOfFonts
.InsertStringAt(NS_LITERAL_STRING("monospace"), 0);
107 aListOfFonts
.InsertStringAt(NS_LITERAL_STRING("sans-serif"), 0);
109 aListOfFonts
.InsertStringAt(NS_LITERAL_STRING("serif"), 0);
114 struct MozGtkLangGroup
{
115 const char *mozLangGroup
;
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" },
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
];
157 NS_AddLangGroup(FcPattern
*aPattern
, nsIAtom
*aLangGroup
)
159 // Find the FC lang group for this lang group
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
171 // we're casting away the const here for the strings - should be
174 FcPatternAddString(aPattern
, FC_LANG
, (FcChar8
*)cname
.get());
175 else if (langGroup
->Lang
)
176 FcPatternAddString(aPattern
, FC_LANG
, (FcChar8
*)langGroup
->Lang
);
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();
195 os
= FcObjectSetBuild(FC_FAMILY
, NULL
);
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
);
209 for (int i
= 0; i
< fs
->nfont
; i
++) {
212 if (FcPatternGetString(fs
->fonts
[i
], FC_FAMILY
, 0,
213 (FcChar8
**) &family
) != FcResultMatch
)
218 // Remove duplicates...
219 nsCAutoString
strFamily(family
);
220 if (aListOfFonts
.IndexOf(strFamily
) >= 0)
223 aListOfFonts
.AppendCString(strFamily
);
230 aListOfFonts
.Clear();
233 FcPatternDestroy(pat
);
235 FcObjectSetDestroy(os
);
237 FcFontSetDestroy(fs
);
243 gfxFontconfigUtils::UpdateFontList()
245 return UpdateFontListInternal(PR_TRUE
);
249 gfxFontconfigUtils::UpdateFontListInternal(PRBool aForce
)
251 if (!aForce
&& FcConfigUptoDate(NULL
))
254 FcInitReinitialize();
257 mAliasForSingleFont
.Clear();
258 mAliasForMultiFonts
.Clear();
259 mNonExistingFonts
.Clear();
263 nsresult rv
= GetFontListInternal(mFonts
);
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
);
274 return NS_ERROR_FAILURE
;
276 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
277 prefs
->GetBranch(0, getter_AddRefs(prefBranch
));
279 return NS_ERROR_FAILURE
;
282 rv
= prefBranch
->GetCharPref("font.alias-list", getter_Copies(list
));
286 if (!list
.IsEmpty()) {
287 const char kComma
= ',';
288 const char *p
, *p_end
;
289 list
.BeginReading(p
);
290 list
.EndReading(p_end
);
292 while (nsCRT::IsAsciiSpace(*p
)) {
298 const char *start
= p
;
299 while (++p
!= p_end
&& *p
!= kComma
)
301 nsCAutoString
name(Substring(start
, p
));
302 name
.CompressWhitespace(PR_FALSE
, PR_TRUE
);
303 mAliasForMultiFonts
.AppendCString(name
);
308 if (mAliasForMultiFonts
.Count() == 0)
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
);
319 ToLowerCase(fontname
, key
);
320 mAliasTable
.Put(key
, fonts
);
326 gfxFontconfigUtils::GetResolvedFonts(const nsACString
& aName
,
327 gfxFontNameList
* aResult
)
329 FcPattern
*pat
= NULL
;
330 FcFontSet
*fs
= NULL
;
333 nsresult rv
= NS_ERROR_FAILURE
;
335 pat
= FcPatternCreate();
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
);
351 for (int i
= 0; i
< fs
->nfont
; i
++) {
354 if (FcPatternGetString(fs
->fonts
[i
], FC_FAMILY
, 0,
355 (FcChar8
**) &family
) != FcResultMatch
||
356 mAliasForMultiFonts
.IndexOfIgnoreCase(nsDependentCString(family
)) >= 0 ||
357 IsExistingFont(nsDependentCString(family
)) == 0)
361 NS_ConvertUTF8toUTF16
actualName(family
);
362 if (aResult
->Exists(actualName
))
364 aResult
->AppendElement(actualName
);
369 FcPatternDestroy(pat
);
371 FcFontSetDestroy(fs
);
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
);
388 NS_ConvertUTF16toUTF8
fontname(aFontName
);
390 if (mFonts
.IndexOf(fontname
) >= 0) {
391 aFamilyName
.Assign(aFontName
);
395 if (mNonExistingFonts
.IndexOf(fontname
) >= 0)
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();
409 FcPatternAddString(pat
, FC_FAMILY
, (FcChar8
*)fontname
.get());
411 os
= FcObjectSetBuild(FC_FAMILY
, FC_FILE
, FC_INDEX
, NULL
);
415 givenFS
= FcFontList(NULL
, pat
, os
);
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
) {
425 if (FcPatternGetString(givenFS
->fonts
[i
], FC_FAMILY
, 0,
426 (FcChar8
**) &firstFamily
) != FcResultMatch
)
429 nsDependentCString
first(firstFamily
);
430 if (candidates
.IndexOf(first
) < 0) {
431 candidates
.AppendCString(first
);
433 if (fontname
.Equals(first
)) {
434 aFamilyName
.Assign(aFontName
);
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
);
451 if (candidateFS
->nfont
!= givenFS
->nfont
)
454 PRBool equal
= PR_TRUE
;
455 for (int i
= 0; i
< givenFS
->nfont
; ++i
) {
456 if (!FcPatternEqual(candidateFS
->fonts
[i
], givenFS
->fonts
[i
])) {
462 AppendUTF8toUTF16(*candidates
[j
], aFamilyName
);
468 // No match found; return empty string.
473 FcPatternDestroy(pat
);
475 FcObjectSetDestroy(os
);
477 FcFontSetDestroy(givenFS
);
479 FcFontSetDestroy(candidateFS
);
485 gfxFontconfigUtils::ResolveFontName(const nsAString
& aFontName
,
486 gfxPlatform::FontResolverCallback aCallback
,
492 nsresult rv
= UpdateFontListInternal();
496 NS_ConvertUTF16toUTF8
fontname(aFontName
);
497 if (mAliasForMultiFonts
.IndexOfIgnoreCase(fontname
) >= 0) {
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
);
509 PRInt32 result
= IsExistingFont(fontname
);
511 return NS_ERROR_FAILURE
;
514 aAborted
= !(*aCallback
)(aFontName
, aClosure
);
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)
527 if (mAliasForSingleFont
.IndexOf(aFontName
) >= 0)
529 if (mFonts
.IndexOf(aFontName
) >= 0)
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
541 FcPattern
*pat
= NULL
;
542 FcObjectSet
*os
= NULL
;
543 FcFontSet
*fs
= NULL
;
546 pat
= FcPatternCreate();
550 FcPatternAddString(pat
, FC_FAMILY
,
551 (FcChar8
*)nsPromiseFlatCString(aFontName
).get());
553 os
= FcObjectSetBuild(FC_FAMILY
, NULL
);
557 fs
= FcFontList(NULL
, pat
, os
);
561 // There can be more than one matching set of family names: see bug 393819.
563 mAliasForSingleFont
.AppendCString(aFontName
);
566 mNonExistingFonts
.AppendCString(aFontName
);
572 FcPatternDestroy(pat
);
574 FcObjectSetDestroy(os
);
576 FcFontSetDestroy(fs
);
581 gfxFontNameList::Exists(nsAString
& aName
) {
582 for (PRUint32 i
= 0; i
< Length(); i
++) {
583 if (aName
.Equals(ElementAt(i
)))