gfx/thebes/gfxFontconfigUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     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/ArrayUtils.h"
     8 #include "gfxFontconfigUtils.h"
     9 #include "gfxFont.h"
    10 #include "nsGkAtoms.h"
    12 #include <locale.h>
    13 #include <fontconfig/fontconfig.h>
    15 #include "nsServiceManagerUtils.h"
    16 #include "nsILanguageAtomService.h"
    17 #include "nsTArray.h"
    18 #include "mozilla/Preferences.h"
    20 #include "nsIAtom.h"
    21 #include "nsCRT.h"
    22 #include "gfxFontConstants.h"
    23 #include "mozilla/gfx/2D.h"
    25 using namespace mozilla;
    27 /* static */ gfxFontconfigUtils* gfxFontconfigUtils::sUtils = nullptr;
    28 static nsILanguageAtomService* gLangService = nullptr;
    30 /* static */ void
    31 gfxFontconfigUtils::Shutdown() {
    32     if (sUtils) {
    33         delete sUtils;
    34         sUtils = nullptr;
    35     }
    36     NS_IF_RELEASE(gLangService);
    37 }
    39 /* static */ uint8_t
    40 gfxFontconfigUtils::FcSlantToThebesStyle(int aFcSlant)
    41 {
    42     switch (aFcSlant) {
    43         case FC_SLANT_ITALIC:
    44             return NS_FONT_STYLE_ITALIC;
    45         case FC_SLANT_OBLIQUE:
    46             return NS_FONT_STYLE_OBLIQUE;
    47         default:
    48             return NS_FONT_STYLE_NORMAL;
    49     }
    50 }
    52 /* static */ uint8_t
    53 gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
    54 {
    55     int slant;
    56     if (FcPatternGetInteger(aPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
    57         return NS_FONT_STYLE_NORMAL;
    58     }
    60     return FcSlantToThebesStyle(slant);
    61 }
    63 /* static */ int
    64 gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
    65 {
    66     if (aFontStyle.style == NS_FONT_STYLE_ITALIC)
    67         return FC_SLANT_ITALIC;
    68     if (aFontStyle.style == NS_FONT_STYLE_OBLIQUE)
    69         return FC_SLANT_OBLIQUE;
    71     return FC_SLANT_ROMAN;
    72 }
    74 // OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
    75 #ifndef FC_WEIGHT_THIN 
    76 #define FC_WEIGHT_THIN              0 // 2.1.93
    77 #define FC_WEIGHT_EXTRALIGHT        40 // 2.1.93
    78 #define FC_WEIGHT_REGULAR           80 // 2.1.93
    79 #define FC_WEIGHT_EXTRABOLD         205 // 2.1.93
    80 #endif
    81 // book was introduced in fontconfig-2.2.90 (and so fontconfig-2.3.0 in 2005)
    82 #ifndef FC_WEIGHT_BOOK
    83 #define FC_WEIGHT_BOOK              75
    84 #endif
    85 // extra black was introduced in fontconfig-2.4.91 (2007)
    86 #ifndef FC_WEIGHT_EXTRABLACK
    87 #define FC_WEIGHT_EXTRABLACK        215
    88 #endif
    90 /* static */ uint16_t
    91 gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
    92 {
    93     int weight;
    94     if (FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
    95         return NS_FONT_WEIGHT_NORMAL;
    97     if (weight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2)
    98         return 100;
    99     if (weight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2)
   100         return 200;
   101     if (weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2)
   102         return 300;
   103     if (weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
   104         // This includes FC_WEIGHT_BOOK
   105         return 400;
   106     if (weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
   107         return 500;
   108     if (weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
   109         return 600;
   110     if (weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2)
   111         return 700;
   112     if (weight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2)
   113         return 800;
   114     if (weight <= FC_WEIGHT_BLACK)
   115         return 900;
   117     // including FC_WEIGHT_EXTRABLACK
   118     return 901;
   119 }
   121 /* static */ int
   122 gfxFontconfigUtils::FcWeightForBaseWeight(int8_t aBaseWeight)
   123 {
   124     NS_PRECONDITION(aBaseWeight >= 0 && aBaseWeight <= 10,
   125                     "base weight out of range");
   127     switch (aBaseWeight) {
   128         case 2:
   129             return FC_WEIGHT_EXTRALIGHT;
   130         case 3:
   131             return FC_WEIGHT_LIGHT;
   132         case 4:
   133             return FC_WEIGHT_REGULAR;
   134         case 5:
   135             return FC_WEIGHT_MEDIUM;
   136         case 6:
   137             return FC_WEIGHT_DEMIBOLD;
   138         case 7:
   139             return FC_WEIGHT_BOLD;
   140         case 8:
   141             return FC_WEIGHT_EXTRABOLD;
   142         case 9:
   143             return FC_WEIGHT_BLACK;
   144     }
   146     // extremes
   147     return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
   148 }
   150 /* static */ int16_t
   151 gfxFontconfigUtils::GetThebesStretch(FcPattern *aPattern)
   152 {
   153     int width;
   154     if (FcPatternGetInteger(aPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
   155         return NS_FONT_STRETCH_NORMAL;
   156     }
   158     if (width <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
   159         return NS_FONT_STRETCH_ULTRA_CONDENSED;
   160     }
   161     if (width <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
   162         return NS_FONT_STRETCH_EXTRA_CONDENSED;
   163     }
   164     if (width <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
   165         return NS_FONT_STRETCH_CONDENSED;
   166     }
   167     if (width <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
   168         return NS_FONT_STRETCH_SEMI_CONDENSED;
   169     }
   170     if (width <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
   171         return NS_FONT_STRETCH_NORMAL;
   172     }
   173     if (width <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
   174         return NS_FONT_STRETCH_SEMI_EXPANDED;
   175     }
   176     if (width <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
   177         return NS_FONT_STRETCH_EXPANDED;
   178     }
   179     if (width <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
   180         return NS_FONT_STRETCH_EXTRA_EXPANDED;
   181     }
   182     return NS_FONT_STRETCH_ULTRA_EXPANDED;
   183 }
   185 /* static */ int
   186 gfxFontconfigUtils::FcWidthForThebesStretch(int16_t aStretch)
   187 {
   188     switch (aStretch) {
   189         default: // this will catch "normal" (0) as well as out-of-range values
   190             return FC_WIDTH_NORMAL;
   191         case NS_FONT_STRETCH_ULTRA_CONDENSED:
   192             return FC_WIDTH_ULTRACONDENSED;
   193         case NS_FONT_STRETCH_EXTRA_CONDENSED:
   194             return FC_WIDTH_EXTRACONDENSED;
   195         case NS_FONT_STRETCH_CONDENSED:
   196             return FC_WIDTH_CONDENSED;
   197         case NS_FONT_STRETCH_SEMI_CONDENSED:
   198             return FC_WIDTH_SEMICONDENSED;
   199         case NS_FONT_STRETCH_SEMI_EXPANDED:
   200             return FC_WIDTH_SEMIEXPANDED;
   201         case NS_FONT_STRETCH_EXPANDED:
   202             return FC_WIDTH_EXPANDED;
   203         case NS_FONT_STRETCH_EXTRA_EXPANDED:
   204             return FC_WIDTH_EXTRAEXPANDED;
   205         case NS_FONT_STRETCH_ULTRA_EXPANDED:
   206             return FC_WIDTH_ULTRAEXPANDED;
   207     }
   208 }
   210 // This makes a guess at an FC_WEIGHT corresponding to a base weight and
   211 // offset (without any knowledge of which weights are available).
   213 /* static */ int
   214 GuessFcWeight(const gfxFontStyle& aFontStyle)
   215 {
   216     /*
   217      * weights come in two parts crammed into one
   218      * integer -- the "base" weight is weight / 100,
   219      * the rest of the value is the "offset" from that
   220      * weight -- the number of steps to move to adjust
   221      * the weight in the list of supported font weights,
   222      * this value can be negative or positive.
   223      */
   224     int8_t weight = aFontStyle.ComputeWeight();
   226     // ComputeWeight trimmed the range of weights for us
   227     NS_ASSERTION(weight >= 0 && weight <= 10,
   228                  "base weight out of range");
   230     return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
   231 }
   233 static void
   234 AddString(FcPattern *aPattern, const char *object, const char *aString)
   235 {
   236     FcPatternAddString(aPattern, object,
   237                        gfxFontconfigUtils::ToFcChar8(aString));
   238 }
   240 static void
   241 AddWeakString(FcPattern *aPattern, const char *object, const char *aString)
   242 {
   243     FcValue value;
   244     value.type = FcTypeString;
   245     value.u.s = gfxFontconfigUtils::ToFcChar8(aString);
   247     FcPatternAddWeak(aPattern, object, value, FcTrue);
   248 }
   250 static void
   251 AddLangGroup(FcPattern *aPattern, nsIAtom *aLangGroup)
   252 {
   253     // Translate from mozilla's internal mapping into fontconfig's
   254     nsAutoCString lang;
   255     gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
   257     if (!lang.IsEmpty()) {
   258         AddString(aPattern, FC_LANG, lang.get());
   259     }
   260 }
   262 nsReturnRef<FcPattern>
   263 gfxFontconfigUtils::NewPattern(const nsTArray<nsString>& aFamilies,
   264                                const gfxFontStyle& aFontStyle,
   265                                const char *aLang)
   266 {
   267     static const char* sFontconfigGenerics[] =
   268         { "sans-serif", "serif", "monospace", "fantasy", "cursive" };
   270     nsAutoRef<FcPattern> pattern(FcPatternCreate());
   271     if (!pattern)
   272         return nsReturnRef<FcPattern>();
   274     FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
   275     FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
   276     FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
   277     FcPatternAddInteger(pattern, FC_WIDTH, FcWidthForThebesStretch(aFontStyle.stretch));
   279     if (aLang) {
   280         AddString(pattern, FC_LANG, aLang);
   281     }
   283     bool useWeakBinding = false;
   284     for (uint32_t i = 0; i < aFamilies.Length(); ++i) {
   285         NS_ConvertUTF16toUTF8 family(aFamilies[i]);
   286         if (!useWeakBinding) {
   287             AddString(pattern, FC_FAMILY, family.get());
   289             // fontconfig generic families are typically implemented with weak
   290             // aliases (so that the preferred font depends on language).
   291             // However, this would give them lower priority than subsequent
   292             // non-generic families in the list.  To ensure that subsequent
   293             // families do not have a higher priority, they are given weak
   294             // bindings.
   295             for (uint32_t g = 0;
   296                  g < ArrayLength(sFontconfigGenerics);
   297                  ++g) {
   298                 if (0 == FcStrCmpIgnoreCase(ToFcChar8(sFontconfigGenerics[g]),
   299                                             ToFcChar8(family.get()))) {
   300                     useWeakBinding = true;
   301                     break;
   302                 }
   303             }
   304         } else {
   305             AddWeakString(pattern, FC_FAMILY, family.get());
   306         }
   307     }
   309     return pattern.out();
   310 }
   312 gfxFontconfigUtils::gfxFontconfigUtils()
   313     : mFontsByFamily(50)
   314     , mFontsByFullname(50)
   315     , mLangSupportTable(50)
   316     , mLastConfig(nullptr)
   317 {
   318     UpdateFontListInternal();
   319 }
   321 nsresult
   322 gfxFontconfigUtils::GetFontList(nsIAtom *aLangGroup,
   323                                 const nsACString& aGenericFamily,
   324                                 nsTArray<nsString>& aListOfFonts)
   325 {
   326     aListOfFonts.Clear();
   328     nsTArray<nsCString> fonts;
   329     nsresult rv = GetFontListInternal(fonts, aLangGroup);
   330     if (NS_FAILED(rv))
   331         return rv;
   333     for (uint32_t i = 0; i < fonts.Length(); ++i) {
   334         aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(fonts[i]));
   335     }
   337     aListOfFonts.Sort();
   339     int32_t serif = 0, sansSerif = 0, monospace = 0;
   341     // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
   342     // "monospace", slightly different from CSS's 5.
   343     if (aGenericFamily.IsEmpty())
   344         serif = sansSerif = monospace = 1;
   345     else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
   346         serif = 1;
   347     else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
   348         sansSerif = 1;
   349     else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
   350         monospace = 1;
   351     else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
   352              aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
   353         serif = sansSerif = 1;
   354     else
   355         NS_NOTREACHED("unexpected CSS generic font family");
   357     // The first in the list becomes the default in
   358     // gFontsDialog.readFontSelection() if the preference-selected font is not
   359     // available, so put system configured defaults first.
   360     if (monospace)
   361         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
   362     if (sansSerif)
   363         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
   364     if (serif)
   365         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
   367     return NS_OK;
   368 }
   370 struct MozLangGroupData {
   371     nsIAtom* const& mozLangGroup;
   372     const char *defaultLang;
   373 };
   375 const MozLangGroupData MozLangGroups[] = {
   376     { nsGkAtoms::x_western,      "en" },
   377     { nsGkAtoms::x_central_euro, "pl" },
   378     { nsGkAtoms::x_cyrillic,     "ru" },
   379     { nsGkAtoms::x_baltic,       "lv" },
   380     { nsGkAtoms::x_devanagari,   "hi" },
   381     { nsGkAtoms::x_tamil,        "ta" },
   382     { nsGkAtoms::x_armn,         "hy" },
   383     { nsGkAtoms::x_beng,         "bn" },
   384     { nsGkAtoms::x_cans,         "iu" },
   385     { nsGkAtoms::x_ethi,         "am" },
   386     { nsGkAtoms::x_geor,         "ka" },
   387     { nsGkAtoms::x_gujr,         "gu" },
   388     { nsGkAtoms::x_guru,         "pa" },
   389     { nsGkAtoms::x_khmr,         "km" },
   390     { nsGkAtoms::x_knda,         "kn" },
   391     { nsGkAtoms::x_mlym,         "ml" },
   392     { nsGkAtoms::x_orya,         "or" },
   393     { nsGkAtoms::x_sinh,         "si" },
   394     { nsGkAtoms::x_telu,         "te" },
   395     { nsGkAtoms::x_tibt,         "bo" },
   396     { nsGkAtoms::Unicode,        0    },
   397 };
   399 static bool
   400 TryLangForGroup(const nsACString& aOSLang, nsIAtom *aLangGroup,
   401                 nsACString *aFcLang)
   402 {
   403     // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
   404     // aOSLang is in the form "language[_territory][.codeset][@modifier]".
   405     // fontconfig takes languages in the form "language-territory".
   406     // nsILanguageAtomService takes languages in the form language-subtag,
   407     // where subtag may be a territory.  fontconfig and nsILanguageAtomService
   408     // handle case-conversion for us.
   409     const char *pos, *end;
   410     aOSLang.BeginReading(pos);
   411     aOSLang.EndReading(end);
   412     aFcLang->Truncate();
   413     while (pos < end) {
   414         switch (*pos) {
   415             case '.':
   416             case '@':
   417                 end = pos;
   418                 break;
   419             case '_':
   420                 aFcLang->Append('-');
   421                 break;
   422             default:
   423                 aFcLang->Append(*pos);
   424         }
   425         ++pos;
   426     }
   428     nsIAtom *atom =
   429         gLangService->LookupLanguage(*aFcLang);
   431     return atom == aLangGroup;
   432 }
   434 /* static */ void
   435 gfxFontconfigUtils::GetSampleLangForGroup(nsIAtom *aLangGroup,
   436                                           nsACString *aFcLang)
   437 {
   438     NS_PRECONDITION(aFcLang != nullptr, "aFcLang must not be NULL");
   440     const MozLangGroupData *langGroup = nullptr;
   442     for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
   443         if (aLangGroup == MozLangGroups[i].mozLangGroup) {
   444             langGroup = &MozLangGroups[i];
   445             break;
   446         }
   447     }
   449     if (!langGroup) {
   450         // Not a special mozilla language group.
   451         // Use aLangGroup as a language code.
   452         aLangGroup->ToUTF8String(*aFcLang);
   453         return;
   454     }
   456     // Check the environment for the users preferred language that corresponds
   457     // to this langGroup.
   458     if (!gLangService) {
   459         CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
   460     }
   462     if (gLangService) {
   463         const char *languages = getenv("LANGUAGE");
   464         if (languages) {
   465             const char separator = ':';
   467             for (const char *pos = languages; true; ++pos) {
   468                 if (*pos == '\0' || *pos == separator) {
   469                     if (languages < pos &&
   470                         TryLangForGroup(Substring(languages, pos),
   471                                         aLangGroup, aFcLang))
   472                         return;
   474                     if (*pos == '\0')
   475                         break;
   477                     languages = pos + 1;
   478                 }
   479             }
   480         }
   481         const char *ctype = setlocale(LC_CTYPE, nullptr);
   482         if (ctype &&
   483             TryLangForGroup(nsDependentCString(ctype), aLangGroup, aFcLang))
   484             return;
   485     }
   487     if (langGroup->defaultLang) {
   488         aFcLang->Assign(langGroup->defaultLang);
   489     } else {
   490         aFcLang->Truncate();
   491     }
   492 }
   494 nsresult
   495 gfxFontconfigUtils::GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
   496                                         nsIAtom *aLangGroup)
   497 {
   498     FcPattern *pat = nullptr;
   499     FcObjectSet *os = nullptr;
   500     FcFontSet *fs = nullptr;
   501     nsresult rv = NS_ERROR_FAILURE;
   503     aListOfFonts.Clear();
   505     pat = FcPatternCreate();
   506     if (!pat)
   507         goto end;
   509     os = FcObjectSetBuild(FC_FAMILY, nullptr);
   510     if (!os)
   511         goto end;
   513     // take the pattern and add the lang group to it
   514     if (aLangGroup) {
   515         AddLangGroup(pat, aLangGroup);
   516     }
   518     fs = FcFontList(nullptr, pat, os);
   519     if (!fs)
   520         goto end;
   522     for (int i = 0; i < fs->nfont; i++) {
   523         char *family;
   525         if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
   526                                (FcChar8 **) &family) != FcResultMatch)
   527         {
   528             continue;
   529         }
   531         // Remove duplicates...
   532         nsAutoCString strFamily(family);
   533         if (aListOfFonts.Contains(strFamily))
   534             continue;
   536         aListOfFonts.AppendElement(strFamily);
   537     }
   539     rv = NS_OK;
   541   end:
   542     if (NS_FAILED(rv))
   543         aListOfFonts.Clear();
   545     if (pat)
   546         FcPatternDestroy(pat);
   547     if (os)
   548         FcObjectSetDestroy(os);
   549     if (fs)
   550         FcFontSetDestroy(fs);
   552     return rv;
   553 }
   555 nsresult
   556 gfxFontconfigUtils::UpdateFontList()
   557 {
   558     return UpdateFontListInternal(true);
   559 }
   561 nsresult
   562 gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
   563 {
   564     if (!aForce) {
   565         // This checks periodically according to fontconfig's configured
   566         // <rescan> interval.
   567         FcInitBringUptoDate();
   568     } else if (!FcConfigUptoDate(nullptr)) { // check now with aForce
   569         mLastConfig = nullptr;
   570         FcInitReinitialize();
   571     }
   573     // FcInitReinitialize() (used by FcInitBringUptoDate) creates a new config
   574     // before destroying the old config, so the only way that we'd miss an
   575     // update is if fontconfig did more than one update and the memory for the
   576     // most recent config happened to be at the same location as the original
   577     // config.
   578     FcConfig *currentConfig = FcConfigGetCurrent();
   579     if (currentConfig == mLastConfig)
   580         return NS_OK;
   582     // This FcFontSet is owned by fontconfig
   583     FcFontSet *fontSet = FcConfigGetFonts(currentConfig, FcSetSystem);
   585     mFontsByFamily.Clear();
   586     mFontsByFullname.Clear();
   587     mLangSupportTable.Clear();
   588     mAliasForMultiFonts.Clear();
   590     // Record the existing font families
   591     for (int f = 0; f < fontSet->nfont; ++f) {
   592         FcPattern *font = fontSet->fonts[f];
   594         FcChar8 *family;
   595         for (int v = 0;
   596              FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
   597              ++v) {
   598             FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
   599             if (entry) {
   600                 bool added = entry->AddFont(font);
   602                 if (!entry->mKey) {
   603                     // The reference to the font pattern keeps the pointer to
   604                     // string for the key valid.  If adding the font failed
   605                     // then the entry must be removed.
   606                     if (added) {
   607                         entry->mKey = family;
   608                     } else {
   609                         mFontsByFamily.RawRemoveEntry(entry);
   610                     }
   611                 }
   612             }
   613         }
   614     }
   616     // XXX we don't support all alias names.
   617     // Because if we don't check whether the given font name is alias name,
   618     // fontconfig converts the non existing font to sans-serif.
   619     // This is not good if the web page specifies font-family
   620     // that has Windows font name in the first.
   621     NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
   622     nsAdoptingCString list = Preferences::GetCString("font.alias-list");
   624     if (!list.IsEmpty()) {
   625         const char kComma = ',';
   626         const char *p, *p_end;
   627         list.BeginReading(p);
   628         list.EndReading(p_end);
   629         while (p < p_end) {
   630             while (nsCRT::IsAsciiSpace(*p)) {
   631                 if (++p == p_end)
   632                     break;
   633             }
   634             if (p == p_end)
   635                 break;
   636             const char *start = p;
   637             while (++p != p_end && *p != kComma)
   638                 /* nothing */ ;
   639             nsAutoCString name(Substring(start, p));
   640             name.CompressWhitespace(false, true);
   641             mAliasForMultiFonts.AppendElement(name);
   642             p++;
   643         }
   644     }
   646     mLastConfig = currentConfig;
   647     return NS_OK;
   648 }
   650 nsresult
   651 gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
   652 {
   653     aFamilyName.Truncate();
   655     // The fontconfig has generic family names in the font list.
   656     if (aFontName.EqualsLiteral("serif") ||
   657         aFontName.EqualsLiteral("sans-serif") ||
   658         aFontName.EqualsLiteral("monospace")) {
   659         aFamilyName.Assign(aFontName);
   660         return NS_OK;
   661     }
   663     nsresult rv = UpdateFontListInternal();
   664     if (NS_FAILED(rv))
   665         return rv;
   667     NS_ConvertUTF16toUTF8 fontname(aFontName);
   669     // return empty string if no such family exists
   670     if (!IsExistingFamily(fontname))
   671         return NS_OK;
   673     FcPattern *pat = nullptr;
   674     FcObjectSet *os = nullptr;
   675     FcFontSet *givenFS = nullptr;
   676     nsTArray<nsCString> candidates;
   677     FcFontSet *candidateFS = nullptr;
   678     rv = NS_ERROR_FAILURE;
   680     pat = FcPatternCreate();
   681     if (!pat)
   682         goto end;
   684     FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
   686     os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, nullptr);
   687     if (!os)
   688         goto end;
   690     givenFS = FcFontList(nullptr, pat, os);
   691     if (!givenFS)
   692         goto end;
   694     // The first value associated with a FC_FAMILY property is the family
   695     // returned by GetFontList(), so use this value if appropriate.
   697     // See if there is a font face with first family equal to the given family.
   698     for (int i = 0; i < givenFS->nfont; ++i) {
   699         char *firstFamily;
   700         if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
   701                                (FcChar8 **) &firstFamily) != FcResultMatch)
   702             continue;
   704         nsDependentCString first(firstFamily);
   705         if (!candidates.Contains(first)) {
   706             candidates.AppendElement(first);
   708             if (fontname.Equals(first)) {
   709                 aFamilyName.Assign(aFontName);
   710                 rv = NS_OK;
   711                 goto end;
   712             }
   713         }
   714     }
   716     // See if any of the first family names represent the same set of font
   717     // faces as the given family.
   718     for (uint32_t j = 0; j < candidates.Length(); ++j) {
   719         FcPatternDel(pat, FC_FAMILY);
   720         FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
   722         candidateFS = FcFontList(nullptr, pat, os);
   723         if (!candidateFS)
   724             goto end;
   726         if (candidateFS->nfont != givenFS->nfont)
   727             continue;
   729         bool equal = true;
   730         for (int i = 0; i < givenFS->nfont; ++i) {
   731             if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
   732                 equal = false;
   733                 break;
   734             }
   735         }
   736         if (equal) {
   737             AppendUTF8toUTF16(candidates[j], aFamilyName);
   738             rv = NS_OK;
   739             goto end;
   740         }
   741     }
   743     // No match found; return empty string.
   744     rv = NS_OK;
   746   end:
   747     if (pat)
   748         FcPatternDestroy(pat);
   749     if (os)
   750         FcObjectSetDestroy(os);
   751     if (givenFS)
   752         FcFontSetDestroy(givenFS);
   753     if (candidateFS)
   754         FcFontSetDestroy(candidateFS);
   756     return rv;
   757 }
   759 nsresult
   760 gfxFontconfigUtils::ResolveFontName(const nsAString& aFontName,
   761                                     gfxPlatform::FontResolverCallback aCallback,
   762                                     void *aClosure,
   763                                     bool& aAborted)
   764 {
   765     aAborted = false;
   767     nsresult rv = UpdateFontListInternal();
   768     if (NS_FAILED(rv))
   769         return rv;
   771     NS_ConvertUTF16toUTF8 fontname(aFontName);
   772     // Sometimes, the font has two or more names (e.g., "Sazanami Gothic" has
   773     // Japanese localized name).  We should not resolve to a single name
   774     // because different names sometimes have different behavior. e.g., with
   775     // the default settings of "Sazanami" on Fedora Core 5, the non-localized
   776     // name uses anti-alias, but the localized name uses it.  So, we should
   777     // check just whether the font is existing, without resolving to regular
   778     // name.
   779     //
   780     // The family names in mAliasForMultiFonts are names understood by
   781     // fontconfig.  The actual font to which they resolve depends on the
   782     // entire match pattern.  That info is not available here, but there
   783     // will be a font so leave the resolving to the gfxFontGroup.
   784     if (IsExistingFamily(fontname) ||
   785         mAliasForMultiFonts.Contains(fontname, gfxIgnoreCaseCStringComparator()))
   786         aAborted = !(*aCallback)(aFontName, aClosure);
   788     return NS_OK;
   789 }
   791 bool
   792 gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
   793 {
   794     return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName)) != nullptr;
   795 }
   797 const nsTArray< nsCountedRef<FcPattern> >&
   798 gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
   799 {
   800     FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
   802     if (!entry)
   803         return mEmptyPatternArray;
   805     return entry->GetFonts();
   806 }
   808 // Fontconfig only provides a fullname property for fonts in formats with SFNT
   809 // wrappers.  For other font formats (including PCF and PS Type 1), a fullname
   810 // must be generated from the family and style properties.  Only the first
   811 // family and style is checked, but that should be OK, as I don't expect
   812 // non-SFNT fonts to have multiple families or styles.
   813 bool
   814 gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(FcPattern *aFont,
   815                                                   nsACString *aFullname)
   816 {
   817     FcChar8 *family;
   818     if (FcPatternGetString(aFont, FC_FAMILY, 0, &family) != FcResultMatch)
   819         return false;
   821     aFullname->Truncate();
   822     aFullname->Append(ToCString(family));
   824     FcChar8 *style;
   825     if (FcPatternGetString(aFont, FC_STYLE, 0, &style) == FcResultMatch &&
   826         strcmp(ToCString(style), "Regular") != 0) {
   827         aFullname->Append(' ');
   828         aFullname->Append(ToCString(style));
   829     }
   831     return true;
   832 }
   834 bool
   835 gfxFontconfigUtils::FontsByFullnameEntry::KeyEquals(KeyTypePointer aKey) const
   836 {
   837     const FcChar8 *key = mKey;
   838     // If mKey is nullptr, key comes from the style and family of the first
   839     // font.
   840     nsAutoCString fullname;
   841     if (!key) {
   842         NS_ASSERTION(mFonts.Length(), "No font in FontsByFullnameEntry!");
   843         GetFullnameFromFamilyAndStyle(mFonts[0], &fullname);
   845         key = ToFcChar8(fullname);
   846     }
   848     return FcStrCmpIgnoreCase(aKey, key) == 0;
   849 }
   851 void
   852 gfxFontconfigUtils::AddFullnameEntries()
   853 {
   854     // This FcFontSet is owned by fontconfig
   855     FcFontSet *fontSet = FcConfigGetFonts(nullptr, FcSetSystem);
   857     // Record the existing font families
   858     for (int f = 0; f < fontSet->nfont; ++f) {
   859         FcPattern *font = fontSet->fonts[f];
   861         int v = 0;
   862         FcChar8 *fullname;
   863         while (FcPatternGetString(font,
   864                                   FC_FULLNAME, v, &fullname) == FcResultMatch) {
   865             FontsByFullnameEntry *entry = mFontsByFullname.PutEntry(fullname);
   866             if (entry) {
   867                 // entry always has space for one font, so the first AddFont
   868                 // will always succeed, and so the entry will always have a
   869                 // font from which to obtain the key.
   870                 bool added = entry->AddFont(font);
   871                 // The key may be nullptr either if this is the first font, or
   872                 // if the first font does not have a fullname property, and so
   873                 // the key is obtained from the font.  Set the key in both
   874                 // cases.  The check that AddFont succeeded is required for
   875                 // the second case.
   876                 if (!entry->mKey && added) {
   877                     entry->mKey = fullname;
   878                 }
   879             }
   881             ++v;
   882         }
   884         // Fontconfig does not provide a fullname property for all fonts.
   885         if (v == 0) {
   886             nsAutoCString name;
   887             if (!GetFullnameFromFamilyAndStyle(font, &name))
   888                 continue;
   890             FontsByFullnameEntry *entry =
   891                 mFontsByFullname.PutEntry(ToFcChar8(name));
   892             if (entry) {
   893                 entry->AddFont(font);
   894                 // Either entry->mKey has been set for a previous font or it
   895                 // remains nullptr to indicate that the key is obtained from
   896                 // the first font.
   897             }
   898         }
   899     }
   900 }
   902 const nsTArray< nsCountedRef<FcPattern> >&
   903 gfxFontconfigUtils::GetFontsForFullname(const FcChar8 *aFullname)
   904 {
   905     if (mFontsByFullname.Count() == 0) {
   906         AddFullnameEntries();
   907     }
   909     FontsByFullnameEntry *entry = mFontsByFullname.GetEntry(aFullname);
   911     if (!entry)
   912         return mEmptyPatternArray;
   914     return entry->GetFonts();
   915 }
   917 static FcLangResult
   918 CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
   919     FcLangResult result = FcLangDifferentLang;
   920     for (uint32_t i = 0; ; ++i) {
   921         FcChar8 a = FcToLower(aLangA[i]);
   922         FcChar8 b = FcToLower(aLangB[i]);
   924         if (a != b) {
   925             if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
   926                 return FcLangDifferentCountry;
   928             return result;
   929         }
   930         if (a == '\0')
   931             return FcLangEqual;
   933         if (a == '-') {
   934             result = FcLangDifferentCountry;
   935         }
   936     }
   937 }
   939 /* static */
   940 FcLangResult
   941 gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
   942 {
   943     // When fontconfig builds a pattern for a system font, it will set a
   944     // single LangSet property value for the font.  That value may be removed
   945     // and additional string values may be added through FcConfigSubsitute
   946     // with FcMatchScan.  Values that are neither LangSet nor string are
   947     // considered errors in fontconfig sort and match functions.
   948     //
   949     // If no string nor LangSet value is found, then either the font is a
   950     // system font and the LangSet has been removed through FcConfigSubsitute,
   951     // or the font is a web font and its language support is unknown.
   952     // Returning FcLangDifferentLang for these fonts ensures that this font
   953     // will not be assumed to satisfy the language, and so language will be
   954     // prioritized in sorting fallback fonts.
   955     FcValue value;
   956     FcLangResult best = FcLangDifferentLang;
   957     for (int v = 0;
   958          FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch;
   959          ++v) {
   961         FcLangResult support;
   962         switch (value.type) {
   963             case FcTypeLangSet:
   964                 support = FcLangSetHasLang(value.u.l, aLang);
   965                 break;
   966             case FcTypeString:
   967                 support = CompareLangString(value.u.s, aLang);
   968                 break;
   969             default:
   970                 // error. continue to see if there is a useful value.
   971                 continue;
   972         }
   974         if (support < best) { // lower is better
   975             if (support == FcLangEqual)
   976                 return support;
   977             best = support;
   978         }        
   979     }
   981     return best;
   982 }
   984 gfxFontconfigUtils::LangSupportEntry *
   985 gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts)
   986 {
   987     // Currently any unrecognized languages from documents will be converted
   988     // to x-unicode by nsILanguageAtomService, so there is a limit on the
   989     // langugages that will be added here.  Reconsider when/if document
   990     // languages are passed to this routine.
   992     LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
   993     if (!entry)
   994         return nullptr;
   996     FcLangResult best = FcLangDifferentLang;
   998     if (!entry->IsKeyInitialized()) {
   999         entry->InitKey(aLang);
  1000     } else {
  1001         // mSupport is already initialized.
  1002         if (!aWithFonts)
  1003             return entry;
  1005         best = entry->mSupport;
  1006         // If there is support for this language, an empty font list indicates
  1007         // that the list hasn't been initialized yet.
  1008         if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
  1009             return entry;
  1012     // This FcFontSet is owned by fontconfig
  1013     FcFontSet *fontSet = FcConfigGetFonts(nullptr, FcSetSystem);
  1015     nsAutoTArray<FcPattern*,100> fonts;
  1017     for (int f = 0; f < fontSet->nfont; ++f) {
  1018         FcPattern *font = fontSet->fonts[f];
  1020         FcLangResult support = GetLangSupport(font, aLang);
  1022         if (support < best) { // lower is better
  1023             best = support;
  1024             if (aWithFonts) {
  1025                 fonts.Clear();
  1026             } else if (best == FcLangEqual) {
  1027                 break;
  1031         // The font list in the LangSupportEntry is expected to be used only
  1032         // when no default fonts support the language.  There would be a large
  1033         // number of fonts in entries for languages using Latin script but
  1034         // these do not need to be created because default fonts already
  1035         // support these languages.
  1036         if (aWithFonts && support != FcLangDifferentLang && support == best) {
  1037             fonts.AppendElement(font);
  1041     entry->mSupport = best;
  1042     if (aWithFonts) {
  1043         if (fonts.Length() != 0) {
  1044             entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
  1045         } else if (best != FcLangDifferentLang) {
  1046             // Previously there was a font that supported this language at the
  1047             // level of entry->mSupport, but it has now disappeared.  At least
  1048             // entry->mSupport needs to be recalculated, but this is an
  1049             // indication that the set of installed fonts has changed, so
  1050             // update all caches.
  1051             mLastConfig = nullptr; // invalidates caches
  1052             UpdateFontListInternal(true);
  1053             return GetLangSupportEntry(aLang, aWithFonts);
  1057     return entry;
  1060 FcLangResult
  1061 gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
  1063     UpdateFontListInternal();
  1065     LangSupportEntry *entry = GetLangSupportEntry(aLang, false);
  1066     if (!entry)
  1067         return FcLangEqual;
  1069     return entry->mSupport;
  1072 const nsTArray< nsCountedRef<FcPattern> >&
  1073 gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
  1075     LangSupportEntry *entry = GetLangSupportEntry(aLang, true);
  1076     if (!entry)
  1077         return mEmptyPatternArray;
  1079     return entry->mFonts;

mercurial