gfx/thebes/gfxFontconfigUtils.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/thebes/gfxFontconfigUtils.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1080 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/ArrayUtils.h"
    1.10 +
    1.11 +#include "gfxFontconfigUtils.h"
    1.12 +#include "gfxFont.h"
    1.13 +#include "nsGkAtoms.h"
    1.14 +
    1.15 +#include <locale.h>
    1.16 +#include <fontconfig/fontconfig.h>
    1.17 +
    1.18 +#include "nsServiceManagerUtils.h"
    1.19 +#include "nsILanguageAtomService.h"
    1.20 +#include "nsTArray.h"
    1.21 +#include "mozilla/Preferences.h"
    1.22 +
    1.23 +#include "nsIAtom.h"
    1.24 +#include "nsCRT.h"
    1.25 +#include "gfxFontConstants.h"
    1.26 +#include "mozilla/gfx/2D.h"
    1.27 +
    1.28 +using namespace mozilla;
    1.29 +
    1.30 +/* static */ gfxFontconfigUtils* gfxFontconfigUtils::sUtils = nullptr;
    1.31 +static nsILanguageAtomService* gLangService = nullptr;
    1.32 +
    1.33 +/* static */ void
    1.34 +gfxFontconfigUtils::Shutdown() {
    1.35 +    if (sUtils) {
    1.36 +        delete sUtils;
    1.37 +        sUtils = nullptr;
    1.38 +    }
    1.39 +    NS_IF_RELEASE(gLangService);
    1.40 +}
    1.41 +
    1.42 +/* static */ uint8_t
    1.43 +gfxFontconfigUtils::FcSlantToThebesStyle(int aFcSlant)
    1.44 +{
    1.45 +    switch (aFcSlant) {
    1.46 +        case FC_SLANT_ITALIC:
    1.47 +            return NS_FONT_STYLE_ITALIC;
    1.48 +        case FC_SLANT_OBLIQUE:
    1.49 +            return NS_FONT_STYLE_OBLIQUE;
    1.50 +        default:
    1.51 +            return NS_FONT_STYLE_NORMAL;
    1.52 +    }
    1.53 +}
    1.54 +
    1.55 +/* static */ uint8_t
    1.56 +gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
    1.57 +{
    1.58 +    int slant;
    1.59 +    if (FcPatternGetInteger(aPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
    1.60 +        return NS_FONT_STYLE_NORMAL;
    1.61 +    }
    1.62 +
    1.63 +    return FcSlantToThebesStyle(slant);
    1.64 +}
    1.65 +
    1.66 +/* static */ int
    1.67 +gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
    1.68 +{
    1.69 +    if (aFontStyle.style == NS_FONT_STYLE_ITALIC)
    1.70 +        return FC_SLANT_ITALIC;
    1.71 +    if (aFontStyle.style == NS_FONT_STYLE_OBLIQUE)
    1.72 +        return FC_SLANT_OBLIQUE;
    1.73 +
    1.74 +    return FC_SLANT_ROMAN;
    1.75 +}
    1.76 +
    1.77 +// OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
    1.78 +#ifndef FC_WEIGHT_THIN 
    1.79 +#define FC_WEIGHT_THIN              0 // 2.1.93
    1.80 +#define FC_WEIGHT_EXTRALIGHT        40 // 2.1.93
    1.81 +#define FC_WEIGHT_REGULAR           80 // 2.1.93
    1.82 +#define FC_WEIGHT_EXTRABOLD         205 // 2.1.93
    1.83 +#endif
    1.84 +// book was introduced in fontconfig-2.2.90 (and so fontconfig-2.3.0 in 2005)
    1.85 +#ifndef FC_WEIGHT_BOOK
    1.86 +#define FC_WEIGHT_BOOK              75
    1.87 +#endif
    1.88 +// extra black was introduced in fontconfig-2.4.91 (2007)
    1.89 +#ifndef FC_WEIGHT_EXTRABLACK
    1.90 +#define FC_WEIGHT_EXTRABLACK        215
    1.91 +#endif
    1.92 +
    1.93 +/* static */ uint16_t
    1.94 +gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
    1.95 +{
    1.96 +    int weight;
    1.97 +    if (FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
    1.98 +        return NS_FONT_WEIGHT_NORMAL;
    1.99 +
   1.100 +    if (weight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2)
   1.101 +        return 100;
   1.102 +    if (weight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2)
   1.103 +        return 200;
   1.104 +    if (weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2)
   1.105 +        return 300;
   1.106 +    if (weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
   1.107 +        // This includes FC_WEIGHT_BOOK
   1.108 +        return 400;
   1.109 +    if (weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
   1.110 +        return 500;
   1.111 +    if (weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
   1.112 +        return 600;
   1.113 +    if (weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2)
   1.114 +        return 700;
   1.115 +    if (weight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2)
   1.116 +        return 800;
   1.117 +    if (weight <= FC_WEIGHT_BLACK)
   1.118 +        return 900;
   1.119 +
   1.120 +    // including FC_WEIGHT_EXTRABLACK
   1.121 +    return 901;
   1.122 +}
   1.123 +
   1.124 +/* static */ int
   1.125 +gfxFontconfigUtils::FcWeightForBaseWeight(int8_t aBaseWeight)
   1.126 +{
   1.127 +    NS_PRECONDITION(aBaseWeight >= 0 && aBaseWeight <= 10,
   1.128 +                    "base weight out of range");
   1.129 +
   1.130 +    switch (aBaseWeight) {
   1.131 +        case 2:
   1.132 +            return FC_WEIGHT_EXTRALIGHT;
   1.133 +        case 3:
   1.134 +            return FC_WEIGHT_LIGHT;
   1.135 +        case 4:
   1.136 +            return FC_WEIGHT_REGULAR;
   1.137 +        case 5:
   1.138 +            return FC_WEIGHT_MEDIUM;
   1.139 +        case 6:
   1.140 +            return FC_WEIGHT_DEMIBOLD;
   1.141 +        case 7:
   1.142 +            return FC_WEIGHT_BOLD;
   1.143 +        case 8:
   1.144 +            return FC_WEIGHT_EXTRABOLD;
   1.145 +        case 9:
   1.146 +            return FC_WEIGHT_BLACK;
   1.147 +    }
   1.148 +
   1.149 +    // extremes
   1.150 +    return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
   1.151 +}
   1.152 +
   1.153 +/* static */ int16_t
   1.154 +gfxFontconfigUtils::GetThebesStretch(FcPattern *aPattern)
   1.155 +{
   1.156 +    int width;
   1.157 +    if (FcPatternGetInteger(aPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
   1.158 +        return NS_FONT_STRETCH_NORMAL;
   1.159 +    }
   1.160 +
   1.161 +    if (width <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
   1.162 +        return NS_FONT_STRETCH_ULTRA_CONDENSED;
   1.163 +    }
   1.164 +    if (width <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
   1.165 +        return NS_FONT_STRETCH_EXTRA_CONDENSED;
   1.166 +    }
   1.167 +    if (width <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
   1.168 +        return NS_FONT_STRETCH_CONDENSED;
   1.169 +    }
   1.170 +    if (width <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
   1.171 +        return NS_FONT_STRETCH_SEMI_CONDENSED;
   1.172 +    }
   1.173 +    if (width <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
   1.174 +        return NS_FONT_STRETCH_NORMAL;
   1.175 +    }
   1.176 +    if (width <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
   1.177 +        return NS_FONT_STRETCH_SEMI_EXPANDED;
   1.178 +    }
   1.179 +    if (width <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
   1.180 +        return NS_FONT_STRETCH_EXPANDED;
   1.181 +    }
   1.182 +    if (width <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
   1.183 +        return NS_FONT_STRETCH_EXTRA_EXPANDED;
   1.184 +    }
   1.185 +    return NS_FONT_STRETCH_ULTRA_EXPANDED;
   1.186 +}
   1.187 +
   1.188 +/* static */ int
   1.189 +gfxFontconfigUtils::FcWidthForThebesStretch(int16_t aStretch)
   1.190 +{
   1.191 +    switch (aStretch) {
   1.192 +        default: // this will catch "normal" (0) as well as out-of-range values
   1.193 +            return FC_WIDTH_NORMAL;
   1.194 +        case NS_FONT_STRETCH_ULTRA_CONDENSED:
   1.195 +            return FC_WIDTH_ULTRACONDENSED;
   1.196 +        case NS_FONT_STRETCH_EXTRA_CONDENSED:
   1.197 +            return FC_WIDTH_EXTRACONDENSED;
   1.198 +        case NS_FONT_STRETCH_CONDENSED:
   1.199 +            return FC_WIDTH_CONDENSED;
   1.200 +        case NS_FONT_STRETCH_SEMI_CONDENSED:
   1.201 +            return FC_WIDTH_SEMICONDENSED;
   1.202 +        case NS_FONT_STRETCH_SEMI_EXPANDED:
   1.203 +            return FC_WIDTH_SEMIEXPANDED;
   1.204 +        case NS_FONT_STRETCH_EXPANDED:
   1.205 +            return FC_WIDTH_EXPANDED;
   1.206 +        case NS_FONT_STRETCH_EXTRA_EXPANDED:
   1.207 +            return FC_WIDTH_EXTRAEXPANDED;
   1.208 +        case NS_FONT_STRETCH_ULTRA_EXPANDED:
   1.209 +            return FC_WIDTH_ULTRAEXPANDED;
   1.210 +    }
   1.211 +}
   1.212 +
   1.213 +// This makes a guess at an FC_WEIGHT corresponding to a base weight and
   1.214 +// offset (without any knowledge of which weights are available).
   1.215 +
   1.216 +/* static */ int
   1.217 +GuessFcWeight(const gfxFontStyle& aFontStyle)
   1.218 +{
   1.219 +    /*
   1.220 +     * weights come in two parts crammed into one
   1.221 +     * integer -- the "base" weight is weight / 100,
   1.222 +     * the rest of the value is the "offset" from that
   1.223 +     * weight -- the number of steps to move to adjust
   1.224 +     * the weight in the list of supported font weights,
   1.225 +     * this value can be negative or positive.
   1.226 +     */
   1.227 +    int8_t weight = aFontStyle.ComputeWeight();
   1.228 +
   1.229 +    // ComputeWeight trimmed the range of weights for us
   1.230 +    NS_ASSERTION(weight >= 0 && weight <= 10,
   1.231 +                 "base weight out of range");
   1.232 +
   1.233 +    return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
   1.234 +}
   1.235 +
   1.236 +static void
   1.237 +AddString(FcPattern *aPattern, const char *object, const char *aString)
   1.238 +{
   1.239 +    FcPatternAddString(aPattern, object,
   1.240 +                       gfxFontconfigUtils::ToFcChar8(aString));
   1.241 +}
   1.242 +
   1.243 +static void
   1.244 +AddWeakString(FcPattern *aPattern, const char *object, const char *aString)
   1.245 +{
   1.246 +    FcValue value;
   1.247 +    value.type = FcTypeString;
   1.248 +    value.u.s = gfxFontconfigUtils::ToFcChar8(aString);
   1.249 +
   1.250 +    FcPatternAddWeak(aPattern, object, value, FcTrue);
   1.251 +}
   1.252 +
   1.253 +static void
   1.254 +AddLangGroup(FcPattern *aPattern, nsIAtom *aLangGroup)
   1.255 +{
   1.256 +    // Translate from mozilla's internal mapping into fontconfig's
   1.257 +    nsAutoCString lang;
   1.258 +    gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
   1.259 +
   1.260 +    if (!lang.IsEmpty()) {
   1.261 +        AddString(aPattern, FC_LANG, lang.get());
   1.262 +    }
   1.263 +}
   1.264 +
   1.265 +nsReturnRef<FcPattern>
   1.266 +gfxFontconfigUtils::NewPattern(const nsTArray<nsString>& aFamilies,
   1.267 +                               const gfxFontStyle& aFontStyle,
   1.268 +                               const char *aLang)
   1.269 +{
   1.270 +    static const char* sFontconfigGenerics[] =
   1.271 +        { "sans-serif", "serif", "monospace", "fantasy", "cursive" };
   1.272 +
   1.273 +    nsAutoRef<FcPattern> pattern(FcPatternCreate());
   1.274 +    if (!pattern)
   1.275 +        return nsReturnRef<FcPattern>();
   1.276 +
   1.277 +    FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
   1.278 +    FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
   1.279 +    FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
   1.280 +    FcPatternAddInteger(pattern, FC_WIDTH, FcWidthForThebesStretch(aFontStyle.stretch));
   1.281 +
   1.282 +    if (aLang) {
   1.283 +        AddString(pattern, FC_LANG, aLang);
   1.284 +    }
   1.285 +
   1.286 +    bool useWeakBinding = false;
   1.287 +    for (uint32_t i = 0; i < aFamilies.Length(); ++i) {
   1.288 +        NS_ConvertUTF16toUTF8 family(aFamilies[i]);
   1.289 +        if (!useWeakBinding) {
   1.290 +            AddString(pattern, FC_FAMILY, family.get());
   1.291 +
   1.292 +            // fontconfig generic families are typically implemented with weak
   1.293 +            // aliases (so that the preferred font depends on language).
   1.294 +            // However, this would give them lower priority than subsequent
   1.295 +            // non-generic families in the list.  To ensure that subsequent
   1.296 +            // families do not have a higher priority, they are given weak
   1.297 +            // bindings.
   1.298 +            for (uint32_t g = 0;
   1.299 +                 g < ArrayLength(sFontconfigGenerics);
   1.300 +                 ++g) {
   1.301 +                if (0 == FcStrCmpIgnoreCase(ToFcChar8(sFontconfigGenerics[g]),
   1.302 +                                            ToFcChar8(family.get()))) {
   1.303 +                    useWeakBinding = true;
   1.304 +                    break;
   1.305 +                }
   1.306 +            }
   1.307 +        } else {
   1.308 +            AddWeakString(pattern, FC_FAMILY, family.get());
   1.309 +        }
   1.310 +    }
   1.311 +
   1.312 +    return pattern.out();
   1.313 +}
   1.314 +
   1.315 +gfxFontconfigUtils::gfxFontconfigUtils()
   1.316 +    : mFontsByFamily(50)
   1.317 +    , mFontsByFullname(50)
   1.318 +    , mLangSupportTable(50)
   1.319 +    , mLastConfig(nullptr)
   1.320 +{
   1.321 +    UpdateFontListInternal();
   1.322 +}
   1.323 +
   1.324 +nsresult
   1.325 +gfxFontconfigUtils::GetFontList(nsIAtom *aLangGroup,
   1.326 +                                const nsACString& aGenericFamily,
   1.327 +                                nsTArray<nsString>& aListOfFonts)
   1.328 +{
   1.329 +    aListOfFonts.Clear();
   1.330 +
   1.331 +    nsTArray<nsCString> fonts;
   1.332 +    nsresult rv = GetFontListInternal(fonts, aLangGroup);
   1.333 +    if (NS_FAILED(rv))
   1.334 +        return rv;
   1.335 +
   1.336 +    for (uint32_t i = 0; i < fonts.Length(); ++i) {
   1.337 +        aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(fonts[i]));
   1.338 +    }
   1.339 +
   1.340 +    aListOfFonts.Sort();
   1.341 +
   1.342 +    int32_t serif = 0, sansSerif = 0, monospace = 0;
   1.343 +
   1.344 +    // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
   1.345 +    // "monospace", slightly different from CSS's 5.
   1.346 +    if (aGenericFamily.IsEmpty())
   1.347 +        serif = sansSerif = monospace = 1;
   1.348 +    else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
   1.349 +        serif = 1;
   1.350 +    else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
   1.351 +        sansSerif = 1;
   1.352 +    else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
   1.353 +        monospace = 1;
   1.354 +    else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
   1.355 +             aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
   1.356 +        serif = sansSerif = 1;
   1.357 +    else
   1.358 +        NS_NOTREACHED("unexpected CSS generic font family");
   1.359 +
   1.360 +    // The first in the list becomes the default in
   1.361 +    // gFontsDialog.readFontSelection() if the preference-selected font is not
   1.362 +    // available, so put system configured defaults first.
   1.363 +    if (monospace)
   1.364 +        aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
   1.365 +    if (sansSerif)
   1.366 +        aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
   1.367 +    if (serif)
   1.368 +        aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
   1.369 +
   1.370 +    return NS_OK;
   1.371 +}
   1.372 +
   1.373 +struct MozLangGroupData {
   1.374 +    nsIAtom* const& mozLangGroup;
   1.375 +    const char *defaultLang;
   1.376 +};
   1.377 +
   1.378 +const MozLangGroupData MozLangGroups[] = {
   1.379 +    { nsGkAtoms::x_western,      "en" },
   1.380 +    { nsGkAtoms::x_central_euro, "pl" },
   1.381 +    { nsGkAtoms::x_cyrillic,     "ru" },
   1.382 +    { nsGkAtoms::x_baltic,       "lv" },
   1.383 +    { nsGkAtoms::x_devanagari,   "hi" },
   1.384 +    { nsGkAtoms::x_tamil,        "ta" },
   1.385 +    { nsGkAtoms::x_armn,         "hy" },
   1.386 +    { nsGkAtoms::x_beng,         "bn" },
   1.387 +    { nsGkAtoms::x_cans,         "iu" },
   1.388 +    { nsGkAtoms::x_ethi,         "am" },
   1.389 +    { nsGkAtoms::x_geor,         "ka" },
   1.390 +    { nsGkAtoms::x_gujr,         "gu" },
   1.391 +    { nsGkAtoms::x_guru,         "pa" },
   1.392 +    { nsGkAtoms::x_khmr,         "km" },
   1.393 +    { nsGkAtoms::x_knda,         "kn" },
   1.394 +    { nsGkAtoms::x_mlym,         "ml" },
   1.395 +    { nsGkAtoms::x_orya,         "or" },
   1.396 +    { nsGkAtoms::x_sinh,         "si" },
   1.397 +    { nsGkAtoms::x_telu,         "te" },
   1.398 +    { nsGkAtoms::x_tibt,         "bo" },
   1.399 +    { nsGkAtoms::Unicode,        0    },
   1.400 +};
   1.401 +
   1.402 +static bool
   1.403 +TryLangForGroup(const nsACString& aOSLang, nsIAtom *aLangGroup,
   1.404 +                nsACString *aFcLang)
   1.405 +{
   1.406 +    // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
   1.407 +    // aOSLang is in the form "language[_territory][.codeset][@modifier]".
   1.408 +    // fontconfig takes languages in the form "language-territory".
   1.409 +    // nsILanguageAtomService takes languages in the form language-subtag,
   1.410 +    // where subtag may be a territory.  fontconfig and nsILanguageAtomService
   1.411 +    // handle case-conversion for us.
   1.412 +    const char *pos, *end;
   1.413 +    aOSLang.BeginReading(pos);
   1.414 +    aOSLang.EndReading(end);
   1.415 +    aFcLang->Truncate();
   1.416 +    while (pos < end) {
   1.417 +        switch (*pos) {
   1.418 +            case '.':
   1.419 +            case '@':
   1.420 +                end = pos;
   1.421 +                break;
   1.422 +            case '_':
   1.423 +                aFcLang->Append('-');
   1.424 +                break;
   1.425 +            default:
   1.426 +                aFcLang->Append(*pos);
   1.427 +        }
   1.428 +        ++pos;
   1.429 +    }
   1.430 +
   1.431 +    nsIAtom *atom =
   1.432 +        gLangService->LookupLanguage(*aFcLang);
   1.433 +
   1.434 +    return atom == aLangGroup;
   1.435 +}
   1.436 +
   1.437 +/* static */ void
   1.438 +gfxFontconfigUtils::GetSampleLangForGroup(nsIAtom *aLangGroup,
   1.439 +                                          nsACString *aFcLang)
   1.440 +{
   1.441 +    NS_PRECONDITION(aFcLang != nullptr, "aFcLang must not be NULL");
   1.442 +
   1.443 +    const MozLangGroupData *langGroup = nullptr;
   1.444 +
   1.445 +    for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
   1.446 +        if (aLangGroup == MozLangGroups[i].mozLangGroup) {
   1.447 +            langGroup = &MozLangGroups[i];
   1.448 +            break;
   1.449 +        }
   1.450 +    }
   1.451 +
   1.452 +    if (!langGroup) {
   1.453 +        // Not a special mozilla language group.
   1.454 +        // Use aLangGroup as a language code.
   1.455 +        aLangGroup->ToUTF8String(*aFcLang);
   1.456 +        return;
   1.457 +    }
   1.458 +
   1.459 +    // Check the environment for the users preferred language that corresponds
   1.460 +    // to this langGroup.
   1.461 +    if (!gLangService) {
   1.462 +        CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
   1.463 +    }
   1.464 +
   1.465 +    if (gLangService) {
   1.466 +        const char *languages = getenv("LANGUAGE");
   1.467 +        if (languages) {
   1.468 +            const char separator = ':';
   1.469 +
   1.470 +            for (const char *pos = languages; true; ++pos) {
   1.471 +                if (*pos == '\0' || *pos == separator) {
   1.472 +                    if (languages < pos &&
   1.473 +                        TryLangForGroup(Substring(languages, pos),
   1.474 +                                        aLangGroup, aFcLang))
   1.475 +                        return;
   1.476 +
   1.477 +                    if (*pos == '\0')
   1.478 +                        break;
   1.479 +
   1.480 +                    languages = pos + 1;
   1.481 +                }
   1.482 +            }
   1.483 +        }
   1.484 +        const char *ctype = setlocale(LC_CTYPE, nullptr);
   1.485 +        if (ctype &&
   1.486 +            TryLangForGroup(nsDependentCString(ctype), aLangGroup, aFcLang))
   1.487 +            return;
   1.488 +    }
   1.489 +
   1.490 +    if (langGroup->defaultLang) {
   1.491 +        aFcLang->Assign(langGroup->defaultLang);
   1.492 +    } else {
   1.493 +        aFcLang->Truncate();
   1.494 +    }
   1.495 +}
   1.496 +
   1.497 +nsresult
   1.498 +gfxFontconfigUtils::GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
   1.499 +                                        nsIAtom *aLangGroup)
   1.500 +{
   1.501 +    FcPattern *pat = nullptr;
   1.502 +    FcObjectSet *os = nullptr;
   1.503 +    FcFontSet *fs = nullptr;
   1.504 +    nsresult rv = NS_ERROR_FAILURE;
   1.505 +
   1.506 +    aListOfFonts.Clear();
   1.507 +
   1.508 +    pat = FcPatternCreate();
   1.509 +    if (!pat)
   1.510 +        goto end;
   1.511 +
   1.512 +    os = FcObjectSetBuild(FC_FAMILY, nullptr);
   1.513 +    if (!os)
   1.514 +        goto end;
   1.515 +
   1.516 +    // take the pattern and add the lang group to it
   1.517 +    if (aLangGroup) {
   1.518 +        AddLangGroup(pat, aLangGroup);
   1.519 +    }
   1.520 +
   1.521 +    fs = FcFontList(nullptr, pat, os);
   1.522 +    if (!fs)
   1.523 +        goto end;
   1.524 +
   1.525 +    for (int i = 0; i < fs->nfont; i++) {
   1.526 +        char *family;
   1.527 +
   1.528 +        if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
   1.529 +                               (FcChar8 **) &family) != FcResultMatch)
   1.530 +        {
   1.531 +            continue;
   1.532 +        }
   1.533 +
   1.534 +        // Remove duplicates...
   1.535 +        nsAutoCString strFamily(family);
   1.536 +        if (aListOfFonts.Contains(strFamily))
   1.537 +            continue;
   1.538 +
   1.539 +        aListOfFonts.AppendElement(strFamily);
   1.540 +    }
   1.541 +
   1.542 +    rv = NS_OK;
   1.543 +
   1.544 +  end:
   1.545 +    if (NS_FAILED(rv))
   1.546 +        aListOfFonts.Clear();
   1.547 +
   1.548 +    if (pat)
   1.549 +        FcPatternDestroy(pat);
   1.550 +    if (os)
   1.551 +        FcObjectSetDestroy(os);
   1.552 +    if (fs)
   1.553 +        FcFontSetDestroy(fs);
   1.554 +
   1.555 +    return rv;
   1.556 +}
   1.557 +
   1.558 +nsresult
   1.559 +gfxFontconfigUtils::UpdateFontList()
   1.560 +{
   1.561 +    return UpdateFontListInternal(true);
   1.562 +}
   1.563 +
   1.564 +nsresult
   1.565 +gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
   1.566 +{
   1.567 +    if (!aForce) {
   1.568 +        // This checks periodically according to fontconfig's configured
   1.569 +        // <rescan> interval.
   1.570 +        FcInitBringUptoDate();
   1.571 +    } else if (!FcConfigUptoDate(nullptr)) { // check now with aForce
   1.572 +        mLastConfig = nullptr;
   1.573 +        FcInitReinitialize();
   1.574 +    }
   1.575 +
   1.576 +    // FcInitReinitialize() (used by FcInitBringUptoDate) creates a new config
   1.577 +    // before destroying the old config, so the only way that we'd miss an
   1.578 +    // update is if fontconfig did more than one update and the memory for the
   1.579 +    // most recent config happened to be at the same location as the original
   1.580 +    // config.
   1.581 +    FcConfig *currentConfig = FcConfigGetCurrent();
   1.582 +    if (currentConfig == mLastConfig)
   1.583 +        return NS_OK;
   1.584 +
   1.585 +    // This FcFontSet is owned by fontconfig
   1.586 +    FcFontSet *fontSet = FcConfigGetFonts(currentConfig, FcSetSystem);
   1.587 +
   1.588 +    mFontsByFamily.Clear();
   1.589 +    mFontsByFullname.Clear();
   1.590 +    mLangSupportTable.Clear();
   1.591 +    mAliasForMultiFonts.Clear();
   1.592 +
   1.593 +    // Record the existing font families
   1.594 +    for (int f = 0; f < fontSet->nfont; ++f) {
   1.595 +        FcPattern *font = fontSet->fonts[f];
   1.596 +
   1.597 +        FcChar8 *family;
   1.598 +        for (int v = 0;
   1.599 +             FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
   1.600 +             ++v) {
   1.601 +            FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
   1.602 +            if (entry) {
   1.603 +                bool added = entry->AddFont(font);
   1.604 +
   1.605 +                if (!entry->mKey) {
   1.606 +                    // The reference to the font pattern keeps the pointer to
   1.607 +                    // string for the key valid.  If adding the font failed
   1.608 +                    // then the entry must be removed.
   1.609 +                    if (added) {
   1.610 +                        entry->mKey = family;
   1.611 +                    } else {
   1.612 +                        mFontsByFamily.RawRemoveEntry(entry);
   1.613 +                    }
   1.614 +                }
   1.615 +            }
   1.616 +        }
   1.617 +    }
   1.618 +
   1.619 +    // XXX we don't support all alias names.
   1.620 +    // Because if we don't check whether the given font name is alias name,
   1.621 +    // fontconfig converts the non existing font to sans-serif.
   1.622 +    // This is not good if the web page specifies font-family
   1.623 +    // that has Windows font name in the first.
   1.624 +    NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
   1.625 +    nsAdoptingCString list = Preferences::GetCString("font.alias-list");
   1.626 +
   1.627 +    if (!list.IsEmpty()) {
   1.628 +        const char kComma = ',';
   1.629 +        const char *p, *p_end;
   1.630 +        list.BeginReading(p);
   1.631 +        list.EndReading(p_end);
   1.632 +        while (p < p_end) {
   1.633 +            while (nsCRT::IsAsciiSpace(*p)) {
   1.634 +                if (++p == p_end)
   1.635 +                    break;
   1.636 +            }
   1.637 +            if (p == p_end)
   1.638 +                break;
   1.639 +            const char *start = p;
   1.640 +            while (++p != p_end && *p != kComma)
   1.641 +                /* nothing */ ;
   1.642 +            nsAutoCString name(Substring(start, p));
   1.643 +            name.CompressWhitespace(false, true);
   1.644 +            mAliasForMultiFonts.AppendElement(name);
   1.645 +            p++;
   1.646 +        }
   1.647 +    }
   1.648 +
   1.649 +    mLastConfig = currentConfig;
   1.650 +    return NS_OK;
   1.651 +}
   1.652 +
   1.653 +nsresult
   1.654 +gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
   1.655 +{
   1.656 +    aFamilyName.Truncate();
   1.657 +
   1.658 +    // The fontconfig has generic family names in the font list.
   1.659 +    if (aFontName.EqualsLiteral("serif") ||
   1.660 +        aFontName.EqualsLiteral("sans-serif") ||
   1.661 +        aFontName.EqualsLiteral("monospace")) {
   1.662 +        aFamilyName.Assign(aFontName);
   1.663 +        return NS_OK;
   1.664 +    }
   1.665 +
   1.666 +    nsresult rv = UpdateFontListInternal();
   1.667 +    if (NS_FAILED(rv))
   1.668 +        return rv;
   1.669 +
   1.670 +    NS_ConvertUTF16toUTF8 fontname(aFontName);
   1.671 +
   1.672 +    // return empty string if no such family exists
   1.673 +    if (!IsExistingFamily(fontname))
   1.674 +        return NS_OK;
   1.675 +
   1.676 +    FcPattern *pat = nullptr;
   1.677 +    FcObjectSet *os = nullptr;
   1.678 +    FcFontSet *givenFS = nullptr;
   1.679 +    nsTArray<nsCString> candidates;
   1.680 +    FcFontSet *candidateFS = nullptr;
   1.681 +    rv = NS_ERROR_FAILURE;
   1.682 +
   1.683 +    pat = FcPatternCreate();
   1.684 +    if (!pat)
   1.685 +        goto end;
   1.686 +
   1.687 +    FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
   1.688 +
   1.689 +    os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, nullptr);
   1.690 +    if (!os)
   1.691 +        goto end;
   1.692 +
   1.693 +    givenFS = FcFontList(nullptr, pat, os);
   1.694 +    if (!givenFS)
   1.695 +        goto end;
   1.696 +
   1.697 +    // The first value associated with a FC_FAMILY property is the family
   1.698 +    // returned by GetFontList(), so use this value if appropriate.
   1.699 +
   1.700 +    // See if there is a font face with first family equal to the given family.
   1.701 +    for (int i = 0; i < givenFS->nfont; ++i) {
   1.702 +        char *firstFamily;
   1.703 +        if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
   1.704 +                               (FcChar8 **) &firstFamily) != FcResultMatch)
   1.705 +            continue;
   1.706 +
   1.707 +        nsDependentCString first(firstFamily);
   1.708 +        if (!candidates.Contains(first)) {
   1.709 +            candidates.AppendElement(first);
   1.710 +
   1.711 +            if (fontname.Equals(first)) {
   1.712 +                aFamilyName.Assign(aFontName);
   1.713 +                rv = NS_OK;
   1.714 +                goto end;
   1.715 +            }
   1.716 +        }
   1.717 +    }
   1.718 +
   1.719 +    // See if any of the first family names represent the same set of font
   1.720 +    // faces as the given family.
   1.721 +    for (uint32_t j = 0; j < candidates.Length(); ++j) {
   1.722 +        FcPatternDel(pat, FC_FAMILY);
   1.723 +        FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
   1.724 +
   1.725 +        candidateFS = FcFontList(nullptr, pat, os);
   1.726 +        if (!candidateFS)
   1.727 +            goto end;
   1.728 +
   1.729 +        if (candidateFS->nfont != givenFS->nfont)
   1.730 +            continue;
   1.731 +
   1.732 +        bool equal = true;
   1.733 +        for (int i = 0; i < givenFS->nfont; ++i) {
   1.734 +            if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
   1.735 +                equal = false;
   1.736 +                break;
   1.737 +            }
   1.738 +        }
   1.739 +        if (equal) {
   1.740 +            AppendUTF8toUTF16(candidates[j], aFamilyName);
   1.741 +            rv = NS_OK;
   1.742 +            goto end;
   1.743 +        }
   1.744 +    }
   1.745 +
   1.746 +    // No match found; return empty string.
   1.747 +    rv = NS_OK;
   1.748 +
   1.749 +  end:
   1.750 +    if (pat)
   1.751 +        FcPatternDestroy(pat);
   1.752 +    if (os)
   1.753 +        FcObjectSetDestroy(os);
   1.754 +    if (givenFS)
   1.755 +        FcFontSetDestroy(givenFS);
   1.756 +    if (candidateFS)
   1.757 +        FcFontSetDestroy(candidateFS);
   1.758 +
   1.759 +    return rv;
   1.760 +}
   1.761 +
   1.762 +nsresult
   1.763 +gfxFontconfigUtils::ResolveFontName(const nsAString& aFontName,
   1.764 +                                    gfxPlatform::FontResolverCallback aCallback,
   1.765 +                                    void *aClosure,
   1.766 +                                    bool& aAborted)
   1.767 +{
   1.768 +    aAborted = false;
   1.769 +
   1.770 +    nsresult rv = UpdateFontListInternal();
   1.771 +    if (NS_FAILED(rv))
   1.772 +        return rv;
   1.773 +
   1.774 +    NS_ConvertUTF16toUTF8 fontname(aFontName);
   1.775 +    // Sometimes, the font has two or more names (e.g., "Sazanami Gothic" has
   1.776 +    // Japanese localized name).  We should not resolve to a single name
   1.777 +    // because different names sometimes have different behavior. e.g., with
   1.778 +    // the default settings of "Sazanami" on Fedora Core 5, the non-localized
   1.779 +    // name uses anti-alias, but the localized name uses it.  So, we should
   1.780 +    // check just whether the font is existing, without resolving to regular
   1.781 +    // name.
   1.782 +    //
   1.783 +    // The family names in mAliasForMultiFonts are names understood by
   1.784 +    // fontconfig.  The actual font to which they resolve depends on the
   1.785 +    // entire match pattern.  That info is not available here, but there
   1.786 +    // will be a font so leave the resolving to the gfxFontGroup.
   1.787 +    if (IsExistingFamily(fontname) ||
   1.788 +        mAliasForMultiFonts.Contains(fontname, gfxIgnoreCaseCStringComparator()))
   1.789 +        aAborted = !(*aCallback)(aFontName, aClosure);
   1.790 +
   1.791 +    return NS_OK;
   1.792 +}
   1.793 +
   1.794 +bool
   1.795 +gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
   1.796 +{
   1.797 +    return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName)) != nullptr;
   1.798 +}
   1.799 +
   1.800 +const nsTArray< nsCountedRef<FcPattern> >&
   1.801 +gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
   1.802 +{
   1.803 +    FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
   1.804 +
   1.805 +    if (!entry)
   1.806 +        return mEmptyPatternArray;
   1.807 +
   1.808 +    return entry->GetFonts();
   1.809 +}
   1.810 +
   1.811 +// Fontconfig only provides a fullname property for fonts in formats with SFNT
   1.812 +// wrappers.  For other font formats (including PCF and PS Type 1), a fullname
   1.813 +// must be generated from the family and style properties.  Only the first
   1.814 +// family and style is checked, but that should be OK, as I don't expect
   1.815 +// non-SFNT fonts to have multiple families or styles.
   1.816 +bool
   1.817 +gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(FcPattern *aFont,
   1.818 +                                                  nsACString *aFullname)
   1.819 +{
   1.820 +    FcChar8 *family;
   1.821 +    if (FcPatternGetString(aFont, FC_FAMILY, 0, &family) != FcResultMatch)
   1.822 +        return false;
   1.823 +
   1.824 +    aFullname->Truncate();
   1.825 +    aFullname->Append(ToCString(family));
   1.826 +
   1.827 +    FcChar8 *style;
   1.828 +    if (FcPatternGetString(aFont, FC_STYLE, 0, &style) == FcResultMatch &&
   1.829 +        strcmp(ToCString(style), "Regular") != 0) {
   1.830 +        aFullname->Append(' ');
   1.831 +        aFullname->Append(ToCString(style));
   1.832 +    }
   1.833 +
   1.834 +    return true;
   1.835 +}
   1.836 +
   1.837 +bool
   1.838 +gfxFontconfigUtils::FontsByFullnameEntry::KeyEquals(KeyTypePointer aKey) const
   1.839 +{
   1.840 +    const FcChar8 *key = mKey;
   1.841 +    // If mKey is nullptr, key comes from the style and family of the first
   1.842 +    // font.
   1.843 +    nsAutoCString fullname;
   1.844 +    if (!key) {
   1.845 +        NS_ASSERTION(mFonts.Length(), "No font in FontsByFullnameEntry!");
   1.846 +        GetFullnameFromFamilyAndStyle(mFonts[0], &fullname);
   1.847 +
   1.848 +        key = ToFcChar8(fullname);
   1.849 +    }
   1.850 +
   1.851 +    return FcStrCmpIgnoreCase(aKey, key) == 0;
   1.852 +}
   1.853 +
   1.854 +void
   1.855 +gfxFontconfigUtils::AddFullnameEntries()
   1.856 +{
   1.857 +    // This FcFontSet is owned by fontconfig
   1.858 +    FcFontSet *fontSet = FcConfigGetFonts(nullptr, FcSetSystem);
   1.859 +
   1.860 +    // Record the existing font families
   1.861 +    for (int f = 0; f < fontSet->nfont; ++f) {
   1.862 +        FcPattern *font = fontSet->fonts[f];
   1.863 +
   1.864 +        int v = 0;
   1.865 +        FcChar8 *fullname;
   1.866 +        while (FcPatternGetString(font,
   1.867 +                                  FC_FULLNAME, v, &fullname) == FcResultMatch) {
   1.868 +            FontsByFullnameEntry *entry = mFontsByFullname.PutEntry(fullname);
   1.869 +            if (entry) {
   1.870 +                // entry always has space for one font, so the first AddFont
   1.871 +                // will always succeed, and so the entry will always have a
   1.872 +                // font from which to obtain the key.
   1.873 +                bool added = entry->AddFont(font);
   1.874 +                // The key may be nullptr either if this is the first font, or
   1.875 +                // if the first font does not have a fullname property, and so
   1.876 +                // the key is obtained from the font.  Set the key in both
   1.877 +                // cases.  The check that AddFont succeeded is required for
   1.878 +                // the second case.
   1.879 +                if (!entry->mKey && added) {
   1.880 +                    entry->mKey = fullname;
   1.881 +                }
   1.882 +            }
   1.883 +
   1.884 +            ++v;
   1.885 +        }
   1.886 +
   1.887 +        // Fontconfig does not provide a fullname property for all fonts.
   1.888 +        if (v == 0) {
   1.889 +            nsAutoCString name;
   1.890 +            if (!GetFullnameFromFamilyAndStyle(font, &name))
   1.891 +                continue;
   1.892 +
   1.893 +            FontsByFullnameEntry *entry =
   1.894 +                mFontsByFullname.PutEntry(ToFcChar8(name));
   1.895 +            if (entry) {
   1.896 +                entry->AddFont(font);
   1.897 +                // Either entry->mKey has been set for a previous font or it
   1.898 +                // remains nullptr to indicate that the key is obtained from
   1.899 +                // the first font.
   1.900 +            }
   1.901 +        }
   1.902 +    }
   1.903 +}
   1.904 +
   1.905 +const nsTArray< nsCountedRef<FcPattern> >&
   1.906 +gfxFontconfigUtils::GetFontsForFullname(const FcChar8 *aFullname)
   1.907 +{
   1.908 +    if (mFontsByFullname.Count() == 0) {
   1.909 +        AddFullnameEntries();
   1.910 +    }
   1.911 +
   1.912 +    FontsByFullnameEntry *entry = mFontsByFullname.GetEntry(aFullname);
   1.913 +
   1.914 +    if (!entry)
   1.915 +        return mEmptyPatternArray;
   1.916 +
   1.917 +    return entry->GetFonts();
   1.918 +}
   1.919 +
   1.920 +static FcLangResult
   1.921 +CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
   1.922 +    FcLangResult result = FcLangDifferentLang;
   1.923 +    for (uint32_t i = 0; ; ++i) {
   1.924 +        FcChar8 a = FcToLower(aLangA[i]);
   1.925 +        FcChar8 b = FcToLower(aLangB[i]);
   1.926 +
   1.927 +        if (a != b) {
   1.928 +            if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
   1.929 +                return FcLangDifferentCountry;
   1.930 +
   1.931 +            return result;
   1.932 +        }
   1.933 +        if (a == '\0')
   1.934 +            return FcLangEqual;
   1.935 +
   1.936 +        if (a == '-') {
   1.937 +            result = FcLangDifferentCountry;
   1.938 +        }
   1.939 +    }
   1.940 +}
   1.941 +
   1.942 +/* static */
   1.943 +FcLangResult
   1.944 +gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
   1.945 +{
   1.946 +    // When fontconfig builds a pattern for a system font, it will set a
   1.947 +    // single LangSet property value for the font.  That value may be removed
   1.948 +    // and additional string values may be added through FcConfigSubsitute
   1.949 +    // with FcMatchScan.  Values that are neither LangSet nor string are
   1.950 +    // considered errors in fontconfig sort and match functions.
   1.951 +    //
   1.952 +    // If no string nor LangSet value is found, then either the font is a
   1.953 +    // system font and the LangSet has been removed through FcConfigSubsitute,
   1.954 +    // or the font is a web font and its language support is unknown.
   1.955 +    // Returning FcLangDifferentLang for these fonts ensures that this font
   1.956 +    // will not be assumed to satisfy the language, and so language will be
   1.957 +    // prioritized in sorting fallback fonts.
   1.958 +    FcValue value;
   1.959 +    FcLangResult best = FcLangDifferentLang;
   1.960 +    for (int v = 0;
   1.961 +         FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch;
   1.962 +         ++v) {
   1.963 +
   1.964 +        FcLangResult support;
   1.965 +        switch (value.type) {
   1.966 +            case FcTypeLangSet:
   1.967 +                support = FcLangSetHasLang(value.u.l, aLang);
   1.968 +                break;
   1.969 +            case FcTypeString:
   1.970 +                support = CompareLangString(value.u.s, aLang);
   1.971 +                break;
   1.972 +            default:
   1.973 +                // error. continue to see if there is a useful value.
   1.974 +                continue;
   1.975 +        }
   1.976 +
   1.977 +        if (support < best) { // lower is better
   1.978 +            if (support == FcLangEqual)
   1.979 +                return support;
   1.980 +            best = support;
   1.981 +        }        
   1.982 +    }
   1.983 +
   1.984 +    return best;
   1.985 +}
   1.986 +
   1.987 +gfxFontconfigUtils::LangSupportEntry *
   1.988 +gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts)
   1.989 +{
   1.990 +    // Currently any unrecognized languages from documents will be converted
   1.991 +    // to x-unicode by nsILanguageAtomService, so there is a limit on the
   1.992 +    // langugages that will be added here.  Reconsider when/if document
   1.993 +    // languages are passed to this routine.
   1.994 +
   1.995 +    LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
   1.996 +    if (!entry)
   1.997 +        return nullptr;
   1.998 +
   1.999 +    FcLangResult best = FcLangDifferentLang;
  1.1000 +
  1.1001 +    if (!entry->IsKeyInitialized()) {
  1.1002 +        entry->InitKey(aLang);
  1.1003 +    } else {
  1.1004 +        // mSupport is already initialized.
  1.1005 +        if (!aWithFonts)
  1.1006 +            return entry;
  1.1007 +
  1.1008 +        best = entry->mSupport;
  1.1009 +        // If there is support for this language, an empty font list indicates
  1.1010 +        // that the list hasn't been initialized yet.
  1.1011 +        if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
  1.1012 +            return entry;
  1.1013 +    }
  1.1014 +
  1.1015 +    // This FcFontSet is owned by fontconfig
  1.1016 +    FcFontSet *fontSet = FcConfigGetFonts(nullptr, FcSetSystem);
  1.1017 +
  1.1018 +    nsAutoTArray<FcPattern*,100> fonts;
  1.1019 +
  1.1020 +    for (int f = 0; f < fontSet->nfont; ++f) {
  1.1021 +        FcPattern *font = fontSet->fonts[f];
  1.1022 +
  1.1023 +        FcLangResult support = GetLangSupport(font, aLang);
  1.1024 +
  1.1025 +        if (support < best) { // lower is better
  1.1026 +            best = support;
  1.1027 +            if (aWithFonts) {
  1.1028 +                fonts.Clear();
  1.1029 +            } else if (best == FcLangEqual) {
  1.1030 +                break;
  1.1031 +            }
  1.1032 +        }
  1.1033 +
  1.1034 +        // The font list in the LangSupportEntry is expected to be used only
  1.1035 +        // when no default fonts support the language.  There would be a large
  1.1036 +        // number of fonts in entries for languages using Latin script but
  1.1037 +        // these do not need to be created because default fonts already
  1.1038 +        // support these languages.
  1.1039 +        if (aWithFonts && support != FcLangDifferentLang && support == best) {
  1.1040 +            fonts.AppendElement(font);
  1.1041 +        }
  1.1042 +    }
  1.1043 +
  1.1044 +    entry->mSupport = best;
  1.1045 +    if (aWithFonts) {
  1.1046 +        if (fonts.Length() != 0) {
  1.1047 +            entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
  1.1048 +        } else if (best != FcLangDifferentLang) {
  1.1049 +            // Previously there was a font that supported this language at the
  1.1050 +            // level of entry->mSupport, but it has now disappeared.  At least
  1.1051 +            // entry->mSupport needs to be recalculated, but this is an
  1.1052 +            // indication that the set of installed fonts has changed, so
  1.1053 +            // update all caches.
  1.1054 +            mLastConfig = nullptr; // invalidates caches
  1.1055 +            UpdateFontListInternal(true);
  1.1056 +            return GetLangSupportEntry(aLang, aWithFonts);
  1.1057 +        }
  1.1058 +    }
  1.1059 +
  1.1060 +    return entry;
  1.1061 +}
  1.1062 +
  1.1063 +FcLangResult
  1.1064 +gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
  1.1065 +{
  1.1066 +    UpdateFontListInternal();
  1.1067 +
  1.1068 +    LangSupportEntry *entry = GetLangSupportEntry(aLang, false);
  1.1069 +    if (!entry)
  1.1070 +        return FcLangEqual;
  1.1071 +
  1.1072 +    return entry->mSupport;
  1.1073 +}
  1.1074 +
  1.1075 +const nsTArray< nsCountedRef<FcPattern> >&
  1.1076 +gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
  1.1077 +{
  1.1078 +    LangSupportEntry *entry = GetLangSupportEntry(aLang, true);
  1.1079 +    if (!entry)
  1.1080 +        return mEmptyPatternArray;
  1.1081 +
  1.1082 +    return entry->mFonts;
  1.1083 +}

mercurial