Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/thebes/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 104 kB image not shown  

Quelle  gfxFcPlatformFontList.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/Logging.h"

#include "gfxFcPlatformFontList.h"
#include "gfxFont.h"
#include "gfxFontConstants.h"
#include "gfxFT2Utils.h"
#include "gfxPlatform.h"
#include "nsPresContext.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/Preferences.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/TimeStamp.h"
#include "nsGkAtoms.h"
#include "nsIConsoleService.h"
#include "nsIGfxInfo.h"
#include "mozilla/Components.h"
#include "nsString.h"
#include "nsStringFwd.h"
#include "nsUnicodeProperties.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsXULAppAPI.h"
#include "SharedFontList-impl.h"
#include "StandardFonts-linux.inc"
#include "mozilla/intl/Locale.h"

#include "mozilla/gfx/HelpersCairo.h"

#include <cairo-ft.h>
#include <fontconfig/fcfreetype.h>
#include <fontconfig/fontconfig.h>
#include <harfbuzz/hb.h>
#include <dlfcn.h>
#include <unistd.h>

#ifdef MOZ_WIDGET_GTK
#  include <gdk/gdk.h>
#  include <gtk/gtk.h>
#  include "gfxPlatformGtk.h"
#  include "mozilla/WidgetUtilsGtk.h"
#endif

#ifdef MOZ_X11
#  include "mozilla/X11Util.h"
#endif

#if defined(MOZ_SANDBOX) && defined(XP_LINUX)
#  include "mozilla/SandboxBrokerPolicyFactory.h"
#  include "mozilla/SandboxSettings.h"
#endif

#include FT_MULTIPLE_MASTERS_H

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
using namespace mozilla::intl;

#ifndef FC_POSTSCRIPT_NAME
#  define FC_POSTSCRIPT_NAME "postscriptname" /* String */
#endif
#ifndef FC_VARIABLE
#  define FC_VARIABLE "variable" /* Bool */
#endif
#ifndef FC_NAMED_INSTANCE
#  define FC_NAMED_INSTANCE "namedinstance" /* Bool */
#endif

#define PRINTING_FC_PROPERTY "gfx.printing"

#define LOG_FONTLIST(args) \
  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
#define LOG_FONTLIST_ENABLED() \
  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
#define LOG_CMAPDATA_ENABLED() \
  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)

static const FcChar8* ToFcChar8Ptr(const char* aStr) {
  return reinterpret_cast<const FcChar8*>(aStr);
}

static const char* ToCharPtr(const FcChar8* aStr) {
  return reinterpret_cast<const char*>(aStr);
}

// canonical name ==> first en name or first name if no en name
// This is the required logic for fullname lookups as per CSS3 Fonts spec.
static uint32_t FindCanonicalNameIndex(FcPattern* aFont,
                                       const char* aLangField) {
  uint32_t n = 0, en = 0;
  FcChar8* lang;
  while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
    // look for 'en' or variants, en-US, en-JP etc.
    uint32_t len = strlen(ToCharPtr(lang));
    bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
    if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
      en = n;
      break;
    }
    n++;
  }
  return en;
}

static void GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName,
                         nsACString& aPostscriptName, nsACString& aFullname) {
  // get the Postscript name
  FcChar8* psname;
  if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) ==
      FcResultMatch) {
    aPostscriptName = ToCharPtr(psname);
  }

  // get the canonical fullname (i.e. en name or first name)
  uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
  FcChar8* fullname;
  if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
    aFullname = ToCharPtr(fullname);
  }

  // if have fullname, done
  if (!aFullname.IsEmpty()) {
    return;
  }

  // otherwise, set the fullname to family + style name [en] and use that
  aFullname = aFamilyName;

  // figure out the en style name
  en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
  nsAutoCString style;
  FcChar8* stylename = nullptr;
  FcPatternGetString(aFont, FC_STYLE, en, &stylename);
  if (stylename) {
    style = ToCharPtr(stylename);
  }

  if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
    aFullname.Append(' ');
    aFullname.Append(style);
  }
}

static FontWeight MapFcWeight(int aFcWeight) {
  if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
    return FontWeight::FromInt(100);
  }
  if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
    return FontWeight::FromInt(200);
  }
  if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
    return FontWeight::FromInt(300);
  }
  if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
    // This includes FC_WEIGHT_BOOK
    return FontWeight::FromInt(400);
  }
  if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
    return FontWeight::FromInt(500);
  }
  if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
    return FontWeight::FromInt(600);
  }
  if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
    return FontWeight::FromInt(700);
  }
  if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
    return FontWeight::FromInt(800);
  }
  if (aFcWeight <= FC_WEIGHT_BLACK) {
    return FontWeight::FromInt(900);
  }

  // including FC_WEIGHT_EXTRABLACK
  return FontWeight::FromInt(901);
}

// TODO(emilio, jfkthame): I think this can now be more fine-grained.
static FontStretch MapFcWidth(int aFcWidth) {
  if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
    return FontStretch::ULTRA_CONDENSED;
  }
  if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
    return FontStretch::EXTRA_CONDENSED;
  }
  if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
    return FontStretch::CONDENSED;
  }
  if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
    return FontStretch::SEMI_CONDENSED;
  }
  if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
    return FontStretch::NORMAL;
  }
  if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
    return FontStretch::SEMI_EXPANDED;
  }
  if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
    return FontStretch::EXPANDED;
  }
  if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
    return FontStretch::EXTRA_EXPANDED;
  }
  return FontStretch::ULTRA_EXPANDED;
}

static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
                              StretchRange* aStretch,
                              SlantStyleRange* aSlantStyle,
                              uint16_t* aSize = nullptr) {
  // weight
  int weight;
  if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
      FcResultMatch) {
    weight = FC_WEIGHT_REGULAR;
  }
  *aWeight = WeightRange(MapFcWeight(weight));

  // width
  int width;
  if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
    width = FC_WIDTH_NORMAL;
  }
  *aStretch = StretchRange(MapFcWidth(width));

  // italic
  int slant;
  if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
    slant = FC_SLANT_ROMAN;
  }
  if (slant == FC_SLANT_OBLIQUE) {
    *aSlantStyle = SlantStyleRange(FontSlantStyle::OBLIQUE);
  } else if (slant > 0) {
    *aSlantStyle = SlantStyleRange(FontSlantStyle::ITALIC);
  }

  if (aSize) {
    // pixel size, or zero if scalable
    FcBool scalable;
    if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
            FcResultMatch &&
        scalable) {
      *aSize = 0;
    } else {
      double size;
      if (FcPatternGetDouble(aFontPattern, FC_PIXEL_SIZE, 0, &size) ==
          FcResultMatch) {
        *aSize = uint16_t(NS_round(size));
      } else {
        *aSize = 0;
      }
    }
  }
}

void gfxFontconfigFontEntry::GetUserFontFeatures(FcPattern* aPattern) {
  int fontFeaturesNum = 0;
  char* s;
  hb_feature_t tmpFeature;
  while (FcResultMatch == FcPatternGetString(aPattern, "fontfeatures",
                                             fontFeaturesNum, (FcChar8**)&s)) {
    bool ret = hb_feature_from_string(s, -1, &tmpFeature);
    if (ret) {
      mFeatureSettings.AppendElement(
          (gfxFontFeature){tmpFeature.tag, tmpFeature.value});
    }
    fontFeaturesNum++;
  }
}

gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
                                               FcPattern* aFontPattern,
                                               bool aIgnoreFcCharmap)
    : gfxFT2FontEntryBase(aFaceName),
      mFontPattern(aFontPattern),
      mFTFaceInitialized(false),
      mIgnoreFcCharmap(aIgnoreFcCharmap) {
  GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
  GetUserFontFeatures(mFontPattern);
}

gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
  MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
  return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
}

static already_AddRefed<FcPattern> CreatePatternForFace(FT_Face aFace) {
  // Use fontconfig to fill out the pattern from the FTFace.
  // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
  // least). The dummy file passed here is removed below.
  //
  // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
  // is passed as the "blanks" argument, which provides that unexpectedly
  // blank glyphs are elided.  Here, however, we pass nullptr for
  // "blanks", effectively assuming that, if the font has a blank glyph,
  // then the author intends any associated character to be rendered
  // blank.
  RefPtr<FcPattern> pattern =
      dont_AddRef(FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr));
  // given that we have a FT_Face, not really sure this is possible...
  if (!pattern) {
    pattern = dont_AddRef(FcPatternCreate());
  }
  FcPatternDel(pattern, FC_FILE);
  FcPatternDel(pattern, FC_INDEX);

  // Make a new pattern and store the face in it so that cairo uses
  // that when creating a cairo font face.
  FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);

  return pattern.forget();
}

static already_AddRefed<SharedFTFace> CreateFaceForPattern(
    FcPattern* aPattern) {
  FcChar8* filename;
  if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
    return nullptr;
  }
  int index;
  if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
    index = 0;  // default to 0 if not found in pattern
  }
  return Factory::NewSharedFTFace(nullptr, ToCharPtr(filename), index);
}

gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
                                               WeightRange aWeight,
                                               StretchRange aStretch,
                                               SlantStyleRange aStyle,
                                               RefPtr<SharedFTFace>&& aFace)
    : gfxFT2FontEntryBase(aFaceName),
      mFontPattern(CreatePatternForFace(aFace->GetFace())),
      mFTFace(aFace.forget().take()),
      mFTFaceInitialized(true),
      mIgnoreFcCharmap(true) {
  mWeightRange = aWeight;
  mStyleRange = aStyle;
  mStretchRange = aStretch;
  mIsDataUserFont = true;
}

gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
                                               FcPattern* aFontPattern,
                                               WeightRange aWeight,
                                               StretchRange aStretch,
                                               SlantStyleRange aStyle)
    : gfxFT2FontEntryBase(aFaceName),
      mFontPattern(aFontPattern),
      mFTFaceInitialized(false) {
  mWeightRange = aWeight;
  mStyleRange = aStyle;
  mStretchRange = aStretch;
  mIsLocalUserFont = true;

  // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
  // via src:local()...
  // If the local font happens to come from the application fontset,
  // we want to set it to true so that color/svg fonts will work even
  // if the default glyphs are blank; but if the local font is a non-
  // sfnt face (e.g. legacy type 1) then we need to set it to false
  // because our cmap-reading code will fail and we depend on FT+Fc to
  // determine the coverage.
  // We set the flag here, but may flip it the first time TestCharacterMap
  // is called, at which point we'll look to see whether a 'cmap' is
  // actually present in the font.
  mIgnoreFcCharmap = true;

  GetUserFontFeatures(mFontPattern);
}

typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
static GetVarFunc sGetVar;
static DoneVarFunc sDoneVar;
static bool sInitializedVarFuncs = false;

static void InitializeVarFuncs() {
  if (sInitializedVarFuncs) {
    return;
  }
  sInitializedVarFuncs = true;
#if MOZ_TREE_FREETYPE
  sGetVar = &FT_Get_MM_Var;
  sDoneVar = &FT_Done_MM_Var;
#else
  sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
  sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
#endif
}

gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
  if (mMMVar) {
    // Prior to freetype 2.9, there was no specific function to free the
    // FT_MM_Var record, and the docs just said to use free().
    // InitializeVarFuncs must have been called in order for mMMVar to be
    // non-null here, so we don't need to do it again.
    if (sDoneVar) {
      auto ftFace = GetFTFace();
      MOZ_ASSERT(ftFace, "How did mMMVar get set without a face?");
      (*sDoneVar)(ftFace->GetFace()->glyph->library, mMMVar);
    } else {
      free(mMMVar);
    }
  }
  if (mFTFaceInitialized) {
    auto face = mFTFace.exchange(nullptr);
    NS_IF_RELEASE(face);
  }
}

nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
  // attempt this once, if errors occur leave a blank cmap
  if (mCharacterMap) {
    return NS_OK;
  }

  RefPtr<gfxCharacterMap> charmap;
  nsresult rv;

  uint32_t uvsOffset = 0;
  if (aFontInfoData &&
      (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
    rv = NS_OK;
  } else {
    uint32_t kCMAP = TRUETYPE_TAG('c''m''a''p');
    charmap = new gfxCharacterMap();
    AutoTable cmapTable(this, kCMAP);

    if (cmapTable) {
      uint32_t cmapLen;
      const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
          hb_blob_get_data(cmapTable, &cmapLen));
      rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
    } else {
      rv = NS_ERROR_NOT_AVAILABLE;
    }
  }
  mUVSOffset.exchange(uvsOffset);

  bool setCharMap = true;
  if (NS_SUCCEEDED(rv)) {
    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
    fontlist::FontList* sharedFontList = pfl->SharedFontList();
    if (!IsUserFont() && mShmemFace) {
      mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
      if (TrySetShmemCharacterMap()) {
        setCharMap = false;
      }
    } else {
      charmap = pfl->FindCharMap(charmap);
    }
    mHasCmapTable = true;
  } else {
    // if error occurred, initialize to null cmap
    charmap = new gfxCharacterMap();
    mHasCmapTable = false;
  }
  if (setCharMap) {
    if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
      charmap.get()->AddRef();
    }
  }

  LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
                mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
                charmap->mHash, mCharacterMap == charmap ? " new" : ""));
  if (LOG_CMAPDATA_ENABLED()) {
    char prefix[256];
    SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
    charmap->Dump(prefix, eGfxLog_cmapdata);
  }

  return rv;
}

static bool HasChar(FcPattern* aFont, FcChar32 aCh) {
  FcCharSet* charset = nullptr;
  FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
  return charset && FcCharSetHasChar(charset, aCh);
}

bool gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) {
  // For user fonts, or for fonts bundled with the app (which might include
  // color/svg glyphs where the default glyphs may be blank, and thus confuse
  // fontconfig/freetype's char map checking), we instead check the cmap
  // directly for character coverage.
  if (mIgnoreFcCharmap) {
    // If it does not actually have a cmap, switch our strategy to use
    // fontconfig's charmap after all (except for data fonts, which must
    // always have a cmap to have passed OTS validation).
    if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c''m''a''p'))) {
      mIgnoreFcCharmap = false;
      // ...and continue with HasChar() below.
    } else {
      return gfxFontEntry::TestCharacterMap(aCh);
    }
  }
  // otherwise (for system fonts), use the charmap in the pattern
  return HasChar(mFontPattern, aCh);
}

bool gfxFontconfigFontEntry::HasFontTable(uint32_t aTableTag) {
  if (FTUserFontData* ufd = GetUserFontData()) {
    if (ufd->FontData()) {
      return !!gfxFontUtils::FindTableDirEntry(ufd->FontData(), aTableTag);
    }
  }
  return gfxFT2FontEntryBase::FaceHasTable(GetFTFace(), aTableTag);
}

hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
  // for data fonts, read directly from the font data
  if (FTUserFontData* ufd = GetUserFontData()) {
    if (ufd->FontData()) {
      return gfxFontUtils::GetTableFromFontData(ufd->FontData(), aTableTag);
    }
  }

  return gfxFontEntry::GetFontTable(aTableTag);
}

double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
  using FontSizeAdjust = gfxFont::FontSizeAdjust;
  if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::ExHeight ||
      FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::CapHeight) {
    // try to compute aspect from OS/2 metrics if available
    AutoTable os2Table(this, TRUETYPE_TAG('O''S''/''2'));
    if (os2Table) {
      uint16_t upem = UnitsPerEm();
      if (upem != kInvalidUPEM) {
        uint32_t len;
        const auto* os2 =
            reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
        if (uint16_t(os2->version) >= 2) {
          // XXX(jfkthame) Other implementations don't have the check for
          // values <= 0.1em; should we drop that here? Just require it to be
          // a positive number?
          if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
              FontSizeAdjust::Tag::ExHeight) {
            if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
                int16_t(os2->sxHeight) > 0.1 * upem) {
              return double(int16_t(os2->sxHeight)) / upem;
            }
          }
          if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
              FontSizeAdjust::Tag::CapHeight) {
            if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
                int16_t(os2->sCapHeight) > 0.1 * upem) {
              return double(int16_t(os2->sCapHeight)) / upem;
            }
          }
        }
      }
    }
  }

  // create a font to calculate the requested aspect
  gfxFontStyle s;
  s.size = 256.0;  // pick large size to reduce hinting artifacts
  RefPtr<gfxFont> font = FindOrMakeFont(&s);
  if (font) {
    const gfxFont::Metrics& metrics =
        font->GetMetrics(nsFontMetrics::eHorizontal);
    if (metrics.emHeight == 0) {
      return 0;
    }
    switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
      case FontSizeAdjust::Tag::ExHeight:
        return metrics.xHeight / metrics.emHeight;
      case FontSizeAdjust::Tag::CapHeight:
        return metrics.capHeight / metrics.emHeight;
      case FontSizeAdjust::Tag::ChWidth:
        return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
                                     : 0.5;
      case FontSizeAdjust::Tag::IcWidth:
      case FontSizeAdjust::Tag::IcHeight: {
        bool vertical = FontSizeAdjust::Tag(aSizeAdjustBasis) ==
                        FontSizeAdjust::Tag::IcHeight;
        gfxFloat advance =
            font->GetCharAdvance(gfxFont::kWaterIdeograph, vertical);
        return advance > 0 ? advance / metrics.emHeight : 1.0;
      }
      default:
        break;
    }
  }

  MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
  return 0.5;
}

static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
                               unsigned int* aOutSynthFlags) {
  int loadFlags = FT_LOAD_DEFAULT;
  unsigned int synthFlags = 0;

  // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed

  FcBool printing;
  if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
      FcResultMatch) {
    printing = FcFalse;
  }

  // Font options are set explicitly here to improve cairo's caching
  // behavior and to record the relevant parts of the pattern so that
  // the pattern can be released.
  //
  // Most font_options have already been set as defaults on the FcPattern
  // with cairo_ft_font_options_substitute(), then user and system
  // fontconfig configurations were applied.  The resulting font_options
  // have been recorded on the face during
  // cairo_ft_font_face_create_for_pattern().
  //
  // None of the settings here cause this scaled_font to behave any
  // differently from how it would behave if it were created from the same
  // face with default font_options.
  //
  // We set options explicitly so that the same scaled_font will be found in
  // the cairo_scaled_font_map when cairo loads glyphs from a context with
  // the same font_face, font_matrix, ctm, and surface font_options.
  //
  // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
  // font_options on the cairo_ft_font_face, and doesn't consider default
  // option values to not match any explicit values.
  //
  // Even after cairo_set_scaled_font is used to set font_options for the
  // cairo context, when cairo looks for a scaled_font for the context, it
  // will look for a font with some option values from the target surface if
  // any values are left default on the context font_options.  If this
  // scaled_font is created with default font_options, cairo will not find
  // it.
  //
  // The one option not recorded in the pattern is hint_metrics, which will
  // affect glyph metrics.  The default behaves as CAIRO_HINT_METRICS_ON.
  // We should be considering the font_options of the surface on which this
  // font will be used, but currently we don't have different gfxFonts for
  // different surface font_options, so we'll create a font suitable for the
  // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.

  // The remaining options have been recorded on the pattern and the face.
  // _cairo_ft_options_merge has some logic to decide which options from the
  // scaled_font or from the cairo_ft_font_face take priority in the way the
  // font behaves.
  //
  // In the majority of cases, _cairo_ft_options_merge uses the options from
  // the cairo_ft_font_face, so sometimes it is not so important which
  // values are set here so long as they are not defaults, but we'll set
  // them to the exact values that we expect from the font, to be consistent
  // and to protect against changes in cairo.
  //
  // In some cases, _cairo_ft_options_merge uses some options from the
  // scaled_font's font_options rather than options on the
  // cairo_ft_font_face (from fontconfig).
  // https://bugs.freedesktop.org/show_bug.cgi?id=11838
  //
  // Surface font options were set on the pattern in
  // cairo_ft_font_options_substitute.  If fontconfig has changed the
  // hint_style then that is what the user (or distribution) wants, so we
  // use the setting from the FcPattern.
  //
  // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
  FcBool hinting = FcFalse;
  if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
    hinting = FcTrue;
  }

  int fc_hintstyle = FC_HINT_NONE;
  if (!printing && hinting &&
      FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &fc_hintstyle) !=
          FcResultMatch) {
    fc_hintstyle = FC_HINT_FULL;
  }
  switch (fc_hintstyle) {
    case FC_HINT_NONE:
      loadFlags = FT_LOAD_NO_HINTING;
      break;
    case FC_HINT_SLIGHT:
      loadFlags = FT_LOAD_TARGET_LIGHT;
      break;
  }

  FcBool fc_antialias;
  if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &fc_antialias) !=
      FcResultMatch) {
    fc_antialias = FcTrue;
  }
  if (!fc_antialias) {
    if (fc_hintstyle != FC_HINT_NONE) {
      loadFlags = FT_LOAD_TARGET_MONO;
    }
    loadFlags |= FT_LOAD_MONOCHROME;
  } else if (fc_hintstyle == FC_HINT_FULL) {
    int fc_rgba;
    if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) {
      fc_rgba = FC_RGBA_UNKNOWN;
    }
    switch (fc_rgba) {
      case FC_RGBA_RGB:
      case FC_RGBA_BGR:
        loadFlags = FT_LOAD_TARGET_LCD;
        break;
      case FC_RGBA_VRGB:
      case FC_RGBA_VBGR:
        loadFlags = FT_LOAD_TARGET_LCD_V;
        break;
    }
  }

  if (!FcPatternAllowsBitmaps(aPattern, fc_antialias != FcFalse,
                              fc_hintstyle != FC_HINT_NONE)) {
    loadFlags |= FT_LOAD_NO_BITMAP;
  }

  FcBool autohint;
  if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
      autohint) {
    loadFlags |= FT_LOAD_FORCE_AUTOHINT;
  }

  FcBool embolden;
  if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
      embolden) {
    synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
  }

  *aOutLoadFlags = loadFlags;
  *aOutSynthFlags = synthFlags;
}

#ifdef MOZ_X11
static bool GetXftInt(Display* aDisplay, const char* aName, int* aResult) {
  if (!aDisplay) {
    return false;
  }
  char* value = XGetDefault(aDisplay, "Xft", aName);
  if (!value) {
    return false;
  }
  if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
    return true;
  }
  char* end;
  *aResult = strtol(value, &end, 0);
  if (end != value) {
    return true;
  }
  return false;
}
#endif

static void PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) {
  FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);

  // This gets cairo_font_options_t for the Screen.  We should have
  // different font options for printing (no hinting) but we are not told
  // what we are measuring for.
  //
  // If cairo adds support for lcd_filter, gdk will not provide the default
  // setting for that option.  We could get the default setting by creating
  // an xlib surface once, recording its font_options, and then merging the
  // gdk options.
  //
  // Using an xlib surface would also be an option to get Screen font
  // options for non-GTK X11 toolkits, but less efficient than using GDK to
  // pick up dynamic changes.
  if (aIsPrinterFont) {
    cairo_font_options_t* options = cairo_font_options_create();
    cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
    cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
    cairo_ft_font_options_substitute(options, aPattern);
    cairo_font_options_destroy(options);
    FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
#ifdef MOZ_WIDGET_GTK
  } else {
    gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
        aPattern);
#endif  // MOZ_WIDGET_GTK
  }

  FcDefaultSubstitute(aPattern);
}

void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
  if (aIndex > 0) {
    ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
        std::move(mUnscaledFonts[aIndex]);
    for (size_t i = aIndex; i > 0; i--) {
      mUnscaledFonts[i] = std::move(mUnscaledFonts[i - 1]);
    }
    mUnscaledFonts[0] = std::move(front);
  }
}

already_AddRefed<UnscaledFontFontconfig>
gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
                                                  uint32_t aIndex) {
  for (size_t i = 0; i < kNumEntries; i++) {
    RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
    if (entry && entry->GetFile() == aFile && entry->GetIndex() == aIndex) {
      MoveToFront(i);
      return entry.forget();
    }
  }
  return nullptr;
}

static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
                                    const gfxFontStyle& aStyle) {
  return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
                 StyleFontSizeAdjust::Tag::None
             ? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
             : aStyle.size * aEntry->mSizeAdjust;
}

static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,
                             const gfxFontStyle& aStyle) {
  double requestedSize = SizeForStyle(aEntry, aStyle);
  double bestDist = -1.0;
  double bestSize = requestedSize;
  double size;
  int v = 0;
  while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
         FcResultMatch) {
    ++v;
    double dist = fabs(size - requestedSize);
    if (bestDist < 0.0 || dist < bestDist) {
      bestDist = dist;
      bestSize = size;
    }
  }
  // If the font has bitmaps but wants to be scaled, then let it scale.
  if (bestSize >= 0.0) {
    FcBool scalable;
    if (FcPatternGetBool(aEntry->GetPattern(), FC_SCALABLE, 0, &scalable) ==
            FcResultMatch &&
        scalable) {
      return requestedSize;
    }
  }
  return bestSize;
}

gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
    const gfxFontStyle* aFontStyle) {
  RefPtr<FcPattern> pattern = dont_AddRef(FcPatternCreate());
  if (!pattern) {
    NS_WARNING("Failed to create Fontconfig pattern for font instance");
    return nullptr;
  }

  double size = ChooseFontSize(this, *aFontStyle);
  FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);

  RefPtr<SharedFTFace> face = GetFTFace();
  if (!face) {
    NS_WARNING("Failed to get FreeType face for pattern");
    return nullptr;
  }
  if (HasVariations()) {
    // For variation fonts, we create a new FT_Face here so that
    // variation coordinates from the style can be applied without
    // affecting other font instances created from the same entry
    // (font resource).
    // For user fonts: create a new FT_Face from the font data, and then make
    // a pattern from that.
    // For system fonts: create a new FT_Face and store it in a copy of the
    // original mFontPattern.
    RefPtr<SharedFTFace> varFace = face->GetData()
                                       ? face->GetData()->CloneFace()
                                       : CreateFaceForPattern(mFontPattern);
    if (varFace) {
      AutoTArray<gfxFontVariation, 8> settings;
      GetVariationsForStyle(settings, *aFontStyle);
      gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, varFace->GetFace());
      face = std::move(varFace);
    }
  }

  PreparePattern(pattern, aFontStyle->printerFont);
  RefPtr<FcPattern> renderPattern =
      dont_AddRef(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
  if (!renderPattern) {
    NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
    return nullptr;
  }

  if (aFontStyle->NeedsSyntheticBold(this)) {
    FcPatternAddBool(renderPattern, FC_EMBOLDEN, FcTrue);
  }

  // will synthetic oblique be applied using a transform?
  if (IsUpright() && !aFontStyle->style.IsNormal() &&
      aFontStyle->allowSyntheticStyle) {
    // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
    FcPatternDel(renderPattern, FC_EMBEDDED_BITMAP);
    FcPatternAddBool(renderPattern, FC_EMBEDDED_BITMAP, FcFalse);
  }

  int loadFlags;
  unsigned int synthFlags;
  PrepareFontOptions(renderPattern, &loadFlags, &synthFlags);

  std::string file;
  int index = 0;
  if (!face->GetData()) {
    const FcChar8* fcFile;
    if (FcPatternGetString(renderPattern, FC_FILE, 0,
                           const_cast<FcChar8**>(&fcFile)) != FcResultMatch ||
        FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) !=
            FcResultMatch) {
      NS_WARNING("No file in Fontconfig pattern for font instance");
      return nullptr;
    }
    file = ToCharPtr(fcFile);
  }

  RefPtr<UnscaledFontFontconfig> unscaledFont;
  {
    AutoReadLock lock(mLock);
    unscaledFont = mUnscaledFontCache.Lookup(file, index);
  }

  if (!unscaledFont) {
    AutoWriteLock lock(mLock);
    // Here, we use the original mFTFace, not a potential clone with variation
    // settings applied.
    auto ftFace = GetFTFace();
    unscaledFont = ftFace->GetData() ? new UnscaledFontFontconfig(ftFace)
                                     : new UnscaledFontFontconfig(
                                           std::move(file), index, ftFace);
    mUnscaledFontCache.Add(unscaledFont);
  }

  gfxFont* newFont = new gfxFontconfigFont(
      unscaledFont, std::move(face), renderPattern, size, this, aFontStyle,
      loadFlags, (synthFlags & CAIRO_FT_SYNTHESIZE_BOLD) != 0);

  return newFont;
}

SharedFTFace* gfxFontconfigFontEntry::GetFTFace() {
  if (!mFTFaceInitialized) {
    RefPtr<SharedFTFace> face = CreateFaceForPattern(mFontPattern);
    if (face) {
      if (mFTFace.compareExchange(nullptr, face.get())) {
        Unused << face.forget();  // The reference is now owned by mFTFace.
        mFTFaceInitialized = true;
      } else {
        // We lost a race to set mFTFace! Just discard our new face.
      }
    }
  }
  return mFTFace;
}

FTUserFontData* gfxFontconfigFontEntry::GetUserFontData() {
  auto face = GetFTFace();
  if (face && face->GetData()) {
    return static_cast<FTUserFontData*>(face->GetData());
  }
  return nullptr;
}

bool gfxFontconfigFontEntry::HasVariations() {
  // If the answer is already cached, just return it.
  switch (mHasVariations) {
    case HasVariationsState::No:
      return false;
    case HasVariationsState::Yes:
      return true;
    case HasVariationsState::Uninitialized:
      break;
  }

  // Figure out whether we have variations, and record in mHasVariations.
  // (It doesn't matter if we race with another thread to set this; the result
  // will be the same.)

  if (!gfxPlatform::HasVariationFontSupport()) {
    mHasVariations = HasVariationsState::No;
    return false;
  }

  // For installed fonts, query the fontconfig pattern rather than paying
  // the cost of loading a FT_Face that we otherwise might never need.
  if (!IsUserFont() || IsLocalUserFont()) {
    FcBool variable;
    if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, &variable) ==
         FcResultMatch) &&
        variable) {
      mHasVariations = HasVariationsState::Yes;
      return true;
    }
  } else {
    if (auto ftFace = GetFTFace()) {
      if (ftFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
        mHasVariations = HasVariationsState::Yes;
        return true;
      }
    }
  }

  mHasVariations = HasVariationsState::No;
  return false;
}

FT_MM_Var* gfxFontconfigFontEntry::GetMMVar() {
  {
    AutoReadLock lock(mLock);
    if (mMMVarInitialized) {
      return mMMVar;
    }
  }

  AutoWriteLock lock(mLock);

  mMMVarInitialized = true;
  InitializeVarFuncs();
  if (!sGetVar) {
    return nullptr;
  }
  auto ftFace = GetFTFace();
  if (!ftFace) {
    return nullptr;
  }
  if (FT_Err_Ok != (*sGetVar)(ftFace->GetFace(), &mMMVar)) {
    mMMVar = nullptr;
  }
  return mMMVar;
}

void gfxFontconfigFontEntry::GetVariationAxes(
    nsTArray<gfxFontVariationAxis>& aAxes) {
  if (!HasVariations()) {
    return;
  }
  gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
}

void gfxFontconfigFontEntry::GetVariationInstances(
    nsTArray<gfxFontVariationInstance>& aInstances) {
  if (!HasVariations()) {
    return;
  }
  gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
}

nsresult gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
                                               nsTArray<uint8_t>& aBuffer) {
  NS_ASSERTION(!mIsDataUserFont,
               "data fonts should be reading tables directly from memory");
  return gfxFT2FontEntryBase::CopyFaceTable(GetFTFace(), aTableTag, aBuffer);
}

void gfxFontconfigFontFamily::FindStyleVariationsLocked(
    FontInfoData* aFontInfoData) {
  if (mHasStyles) {
    return;
  }

  // add font entries for each of the faces
  uint32_t numFonts = mFontPatterns.Length();
  NS_ASSERTION(numFonts, "font family containing no faces!!");
  uint32_t numRegularFaces = 0;
  for (uint32_t i = 0; i < numFonts; i++) {
    FcPattern* face = mFontPatterns[i];

    // figure out the psname/fullname and choose which to use as the facename
    nsAutoCString psname, fullname;
    GetFaceNames(face, mName, psname, fullname);
    const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname;

    gfxFontconfigFontEntry* fontEntry =
        new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);

    if (gfxPlatform::HasVariationFontSupport()) {
      fontEntry->SetupVariationRanges();
    }

    AddFontEntryLocked(fontEntry);

    if (fontEntry->IsNormalStyle()) {
      numRegularFaces++;
    }

    if (LOG_FONTLIST_ENABLED()) {
      nsAutoCString weightString;
      fontEntry->Weight().ToString(weightString);
      nsAutoCString stretchString;
      fontEntry->Stretch().ToString(stretchString);
      nsAutoCString styleString;
      fontEntry->SlantStyle().ToString(styleString);
      LOG_FONTLIST(
          ("(fontlist) added (%s) to family (%s)"
           " with style: %s weight: %s stretch: %s"
           " psname: %s fullname: %s",
           fontEntry->Name().get(), Name().get(), styleString.get(),
           weightString.get(), stretchString.get(), psname.get(),
           fullname.get()));
    }
  }

  // somewhat arbitrary, but define a family with two or more regular
  // faces as a family for which intra-family fallback should be used
  if (numRegularFaces > 1) {
    mCheckForFallbackFaces = true;
  }
  mFaceNamesInitialized = true;
  mFontPatterns.Clear();
  SetHasStyles(true);

  CheckForSimpleFamily();
}

void gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern,
                                             bool aSingleName) {
  NS_ASSERTION(
      !mHasStyles,
      "font patterns must not be added to already enumerated families");

  FcBool outline;
  if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) !=
          FcResultMatch ||
      !outline) {
    mHasNonScalableFaces = true;

    FcBool scalable;
    if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
            FcResultMatch &&
        scalable) {
      mForceScalable = true;
    }
  }

  if (aSingleName) {
    mFontPatterns.InsertElementAt(mUniqueNameFaceCount++, aFontPattern);
  } else {
    mFontPatterns.AppendElement(aFontPattern);
  }
}

static const double kRejectDistance = 10000.0;

// Calculate a distance score representing the size disparity between the
// requested style's size and the font entry's size.
static double SizeDistance(gfxFontconfigFontEntry* aEntry,
                           const gfxFontStyle& aStyle, bool aForceScalable) {
  double requestedSize = SizeForStyle(aEntry, aStyle);
  double bestDist = -1.0;
  double size;
  int v = 0;
  while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
         FcResultMatch) {
    ++v;
    double dist = fabs(size - requestedSize);
    if (bestDist < 0.0 || dist < bestDist) {
      bestDist = dist;
    }
  }
  if (bestDist < 0.0) {
    // No size means scalable
    return -1.0;
  } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
    // fontconfig prefers a matching family or lang to pixelsize of bitmap
    // fonts. CSS suggests a tolerance of 20% on pixelsize.
    return bestDist;
  } else {
    // Reject any non-scalable fonts that are not within tolerance.
    return kRejectDistance;
  }
}

void gfxFontconfigFontFamily::FindAllFontsForStyle(
    const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
    bool aIgnoreSizeTolerance) {
  gfxFontFamily::FindAllFontsForStyle(aFontStyle, aFontEntryList,
                                      aIgnoreSizeTolerance);

  if (!mHasNonScalableFaces) {
    return;
  }

  // Iterate over the the available fonts while compacting any groups
  // of unscalable fonts with matching styles into a single entry
  // corresponding to the closest available size. If the closest
  // available size is rejected for being outside tolerance, then the
  // entire group will be skipped.
  size_t skipped = 0;
  gfxFontconfigFontEntry* bestEntry = nullptr;
  double bestDist = -1.0;
  for (size_t i = 0; i < aFontEntryList.Length(); i++) {
    gfxFontconfigFontEntry* entry =
        static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
    double dist =
        SizeDistance(entry, aFontStyle, mForceScalable || aIgnoreSizeTolerance);
    // If the entry is scalable or has a style that does not match
    // the group of unscalable fonts, then start a new group.
    if (dist < 0.0 || !bestEntry || bestEntry->Stretch() != entry->Stretch() ||
        bestEntry->Weight() != entry->Weight() ||
        bestEntry->SlantStyle() != entry->SlantStyle()) {
      // If the best entry in this group is still outside the tolerance,
      // then skip the entire group.
      if (bestDist >= kRejectDistance) {
        skipped++;
      }
      // Remove any compacted entries from the previous group.
      if (skipped) {
        i -= skipped;
        aFontEntryList.RemoveElementsAt(i, skipped);
        skipped = 0;
      }
      // Mark the start of the new group.
      bestEntry = entry;
      bestDist = dist;
    } else {
      // If this entry more closely matches the requested size than the
      // current best in the group, then take this entry instead.
      if (dist < bestDist) {
        aFontEntryList[i - 1 - skipped] = entry;
        bestEntry = entry;
        bestDist = dist;
      }
      skipped++;
    }
  }
  // If the best entry in this group is still outside the tolerance,
  // then skip the entire group.
  if (bestDist >= kRejectDistance) {
    skipped++;
  }
  // Remove any compacted entries from the current group.
  if (skipped) {
    aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
  }
}

static bool PatternHasLang(const FcPattern* aPattern, const FcChar8* aLang) {
  FcLangSet* langset;

  if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
    return false;
  }

  if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
    return true;
  }
  return false;
}

bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom* aLangGroup) const {
  if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
    return true;
  }

  nsAutoCString fcLang;
  gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
  pfl->GetSampleLangForGroup(aLangGroup, fcLang);
  if (fcLang.IsEmpty()) {
    return true;
  }

  // Before FindStyleVariations has been called, mFontPatterns will contain
  // the font patterns.  Afterward, it'll be empty, but mAvailableFonts
  // will contain the font entries, each of which holds a reference to its
  // pattern.  We only check the first pattern in each list, because support
  // for langs is considered to be consistent across all faces in a family.
  AutoReadLock lock(mLock);
  FcPattern* fontPattern;
  if (mFontPatterns.Length()) {
    fontPattern = mFontPatterns[0];
  } else if (mAvailableFonts.Length()) {
    fontPattern = static_cast<gfxFontconfigFontEntry*>(mAvailableFonts[0].get())
                      ->GetPattern();
  } else {
    return true;
  }

  // is lang included in the underlying pattern?
  return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
}

/* virtual */
gfxFontconfigFontFamily::~gfxFontconfigFontFamily() {
  // Should not be dropped by stylo
  MOZ_ASSERT(NS_IsMainThread());
}

template <typename Func>
void gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) {
  AutoReadLock lock(mLock);
  if (HasStyles()) {
    for (auto& fe : mAvailableFonts) {
      if (!fe) {
        continue;
      }
      auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
      aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
    }
  } else {
    for (auto& pat : mFontPatterns) {
      aAddPatternFunc(pat, mContainsAppFonts);
    }
  }
}

gfxFontconfigFont::gfxFontconfigFont(
    const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
    RefPtr<SharedFTFace>&& aFTFace, FcPattern* aPattern, gfxFloat aAdjustedSize,
    gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, int aLoadFlags,
    bool aEmbolden)
    : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle,
                     aLoadFlags, aEmbolden),
      mPattern(aPattern) {
  mAdjustedSize = aAdjustedSize;
  InitMetrics();
}

gfxFontconfigFont::~gfxFontconfigFont() = default;

already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
    const TextRunDrawParams& aRunParams) {
  if (ScaledFont* scaledFont = mAzureScaledFont) {
    return do_AddRef(scaledFont);
  }

  RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFontconfigFont(
      GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
  if (!newScaledFont) {
    return nullptr;
  }

  InitializeScaledFont(newScaledFont);

  if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
    Unused << newScaledFont.forget();
  }
  ScaledFont* scaledFont = mAzureScaledFont;
  return do_AddRef(scaledFont);
}

bool gfxFontconfigFont::ShouldHintMetrics() const {
  return !GetStyle()->printerFont;
}

gfxFcPlatformFontList::gfxFcPlatformFontList()
    : mLocalNames(64),
      mGenericMappings(32),
      mFcSubstituteCache(64),
      mLastConfig(nullptr),
      mAlwaysUseFontconfigGenerics(true) {
  CheckFamilyList(kBaseFonts_Ubuntu_22_04);
  CheckFamilyList(kLangFonts_Ubuntu_22_04);
  CheckFamilyList(kBaseFonts_Ubuntu_20_04);
  CheckFamilyList(kLangFonts_Ubuntu_20_04);
  CheckFamilyList(kBaseFonts_Fedora_39);
  CheckFamilyList(kBaseFonts_Fedora_38);
  mLastConfig = FcConfigGetCurrent();
  if (XRE_IsParentProcess()) {
    // if the rescan interval is set, start the timer
    int rescanInterval = FcConfigGetRescanInterval(nullptr);
    if (rescanInterval) {
      NS_NewTimerWithFuncCallback(
          getter_AddRefs(mCheckFontUpdatesTimer), CheckFontUpdates, this,
          (rescanInterval + 1) * 1000, nsITimer::TYPE_REPEATING_SLACK,
          "gfxFcPlatformFontList::gfxFcPlatformFontList");
      if (!mCheckFontUpdatesTimer) {
        NS_WARNING("Failure to create font updates timer");
      }
    }
  }

#ifdef MOZ_BUNDLED_FONTS
  mBundledFontsInitialized = false;
#endif
}

gfxFcPlatformFontList::~gfxFcPlatformFontList() {
  AutoLock lock(mLock);

  if (mCheckFontUpdatesTimer) {
    mCheckFontUpdatesTimer->Cancel();
    mCheckFontUpdatesTimer = nullptr;
  }
#ifdef MOZ_WIDGET_GTK
  ClearSystemFontOptions();
#endif
}

void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
                                               const SandboxPolicy* aPolicy,
                                               bool aAppFonts) {
  // This iterates over the fonts in a font set and adds in gfxFontFamily
  // objects for each family. Individual gfxFontEntry objects for each face
  // are not created here; the patterns are just stored in the family. When
  // a family is actually used, it will be populated with gfxFontEntry
  // records and the patterns moved to those.

  if (NS_WARN_IF(!aFontSet)) {
    return;
  }

  FcChar8* lastFamilyName = (FcChar8*)"";
  RefPtr<gfxFontconfigFontFamily> fontFamily;
  nsAutoCString familyName;
  for (int f = 0; f < aFontSet->nfont; f++) {
    FcPattern* pattern = aFontSet->fonts[f];

    // Skip any fonts that aren't readable for us (e.g. due to restrictive
    // file ownership/permissions).
    FcChar8* path;
    if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
      continue;
    }
    if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
      continue;
    }

#if defined(MOZ_SANDBOX) && defined(XP_LINUX)
    // Skip any fonts that will be blocked by the content-process sandbox
    // policy.
    if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
                     SandboxBroker::Perms::MAY_READ)) {
      continue;
    }
#endif

    AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
                         aAppFonts);
  }
}

// Check whether a pattern refers to a non-variable font, or a specific named
// instance of a variable font. Only such patterns are eligible for src:local()
// name lookups, as local() is defined to reference a specific face.
static bool IsNonVariableOrNamedInstance(FcPattern* aPattern) {
  FcBool value;
  if (FcPatternGetBool(aPattern, FC_VARIABLE, 0, &value) == FcResultMatch &&
      value) {
    // It's a variable font resource; check if this is a named instance.
    if (FcPatternGetBool(aPattern, FC_NAMED_INSTANCE, 0, &value) ==
            FcResultMatch &&
        value) {
      // Yes, named instance: ok to use.
      return true;
    }
    // It's a variable font; we don't want it.
    return false;
  }
  // Non-variable: no problem.
  return true;
}

void gfxFcPlatformFontList::AddPatternToFontList(
    FcPattern* aFont, FcChar8*& aLastFamilyName, nsACString& aFamilyName,
    RefPtr<gfxFontconfigFontFamily>& aFontFamily, bool aAppFonts) {
  // get canonical name
  uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
  FcChar8* canonical = nullptr;
  FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
  if (!canonical) {
    return;
  }

  // same as the last one? no need to add a new family, skip
  if (FcStrCmp(canonical, aLastFamilyName) != 0) {
    aLastFamilyName = canonical;

    // add new family if one doesn't already exist
    aFamilyName.Truncate();
    aFamilyName = ToCharPtr(canonical);
    nsAutoCString keyName(aFamilyName);
    ToLowerCase(keyName);

    aFontFamily = static_cast<gfxFontconfigFontFamily*>(
        mFontFamilies
            .LookupOrInsertWith(keyName,
                                [&] {
                                  FontVisibility visibility =
                                      aAppFonts
                                          ? FontVisibility::Base
                                          : GetVisibilityForFamily(keyName);
                                  return MakeRefPtr<gfxFontconfigFontFamily>(
                                      aFamilyName, visibility);
                                })
            .get());
    // Record if the family contains fonts from the app font set
    // (in which case we won't rely on fontconfig's charmap, due to
    // bug 1276594).
    if (aAppFonts) {
      aFontFamily->SetFamilyContainsAppFonts(true);
    }
  }

  // Add pointers to other localized family names. Most fonts
  // only have a single name, so the first call to GetString
  // will usually not match
  FcChar8* otherName;
  int n = (cIndex == 0 ? 1 : 0);
  AutoTArray<nsCString, 4> otherFamilyNames;
  while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == FcResultMatch) {
    otherFamilyNames.AppendElement(nsCString(ToCharPtr(otherName)));
    n++;
    if (n == int(cIndex)) {
      n++;  // skip over canonical name
    }
  }
  if (!otherFamilyNames.IsEmpty()) {
    AddOtherFamilyNames(aFontFamily, otherFamilyNames);
  }

  const bool singleName = n == 1;

  MOZ_ASSERT(aFontFamily, "font must belong to a font family");
  aFontFamily->AddFontPattern(aFont, singleName);

  if (IsNonVariableOrNamedInstance(aFont)) {
    // map the psname, fullname ==> font family for local font lookups
    nsAutoCString psname, fullname;
    GetFaceNames(aFont, aFamilyName, psname, fullname);
    if (!psname.IsEmpty()) {
      ToLowerCase(psname);
      mLocalNames.InsertOrUpdate(psname, RefPtr{aFont});
    }
    if (!fullname.IsEmpty()) {
      ToLowerCase(fullname);
      mLocalNames.WithEntryHandle(fullname, [&](auto&& entry) {
        if (entry && !singleName) {
          return;
        }
        entry.InsertOrUpdate(RefPtr{aFont});
      });
    }
  }
}

nsresult gfxFcPlatformFontList::InitFontListForPlatform() {
#ifdef MOZ_BUNDLED_FONTS
  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
    ActivateBundledFonts();
  }
#endif

  mLocalNames.Clear();
  mFcSubstituteCache.Clear();

  ClearSystemFontOptions();

  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
  mOtherFamilyNamesInitialized = true;

  mLastConfig = FcConfigGetCurrent();

  if (XRE_IsContentProcess()) {
    // Content process: use the font list passed from the chrome process,
    // because we can't rely on fontconfig in the presence of sandboxing;
    // it may report fonts that we can't actually access.

    FcChar8* lastFamilyName = (FcChar8*)"";
    RefPtr<gfxFontconfigFontFamily> fontFamily;
    nsAutoCString familyName;

    // Get font list that was passed during XPCOM startup
    // or in an UpdateFontList message.
    auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();

#ifdef MOZ_WIDGET_GTK
    UpdateSystemFontOptionsFromIpc(fontList.options());
#endif

    // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
    // we need to escape any leading space in the charset element,
    // otherwise FcNameParse will fail. :(
    //
    // The bug was introduced on 2013-05-24 by
    //   https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
    //   "Bug 64906 - FcNameParse() should ignore leading whitespace in
    //   parameters"
    // because ignoring a leading space in the encoded value of charset
    // causes erroneous decoding of the whole element.
    // This first shipped in version 2.10.94, and was eventually fixed as
    // a side-effect of switching to the "human-readable" representation of
    // charsets on 2014-07-03 in
    //   https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
    //   "Change charset parse/unparse format to be human readable"
    // (with a followup fix next day) which means a leading space is no
    // longer significant. This fix landed after 2.11.1 had been shipped,
    // so the first version tag without the bug is 2.11.91.
    int fcVersion = FcGetVersion();
    bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;

    for (FontPatternListEntry& fpe : fontList.entries()) {
      nsCString& patternStr = fpe.pattern();
      if (fcCharsetParseBug) {
        int32_t index = patternStr.Find(":charset= ");
        if (index != kNotFound) {
          // insert backslash after the =, before the space
          patternStr.Insert('\\', index + 9);
        }
      }
      FcPattern* pattern = FcNameParse((const FcChar8*)patternStr.get());
      AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
                           fpe.appFontFamily());
      FcPatternDestroy(pattern);
    }

    LOG_FONTLIST(
        ("got font list from chrome process: "
         "%u faces in %u families",
         (unsigned)fontList.entries().Length(), mFontFamilies.Count()));

    fontList.entries().Clear();
    return NS_OK;
  }

  UpdateSystemFontOptions();

  UniquePtr<SandboxPolicy> policy;

#if defined(MOZ_SANDBOX) && defined(XP_LINUX)
  // If read sandboxing is enabled, create a temporary SandboxPolicy to
  // check font paths; use a fake PID to avoid picking up any PID-specific
  // rules by accident.
  SandboxBrokerPolicyFactory policyFactory;
  if (GetEffectiveContentSandboxLevel() > 2 &&
      !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
    policy = policyFactory.GetContentPolicy(-1, false);
  }
#endif

#ifdef MOZ_BUNDLED_FONTS
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1745715:
  // It's important to do this *before* processing the standard system fonts,
  // so that if a family is present in both font sets, we'll treat it as app-
  // bundled and therefore always visible.
  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
    FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
    AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
  }
#endif

  // iterate over available fonts
  FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
  AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);

  return NS_OK;
}

void gfxFcPlatformFontList::ReadSystemFontList(dom::SystemFontList* retValue) {
  AutoLock lock(mLock);

  // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
  // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
  // an older version, we manually append it to the unparsed pattern.
#ifdef MOZ_WIDGET_GTK
  SystemFontOptionsToIpc(retValue->options());
#endif

  if (FcGetVersion() < 20900) {
    for (const auto& entry : mFontFamilies) {
      auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
      family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
        char* s = (char*)FcNameUnparse(aPat);
        nsDependentCString patternStr(s);
        char* file = nullptr;
        if (FcResultMatch ==
            FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&file)) {
          patternStr.Append(":file=");
          patternStr.Append(file);
        }
        retValue->entries().AppendElement(
            FontPatternListEntry(patternStr, aAppFonts));
        free(s);
      });
    }
  } else {
    for (const auto& entry : mFontFamilies) {
      auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
      family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
        char* s = (char*)FcNameUnparse(aPat);
        nsDependentCString patternStr(s);
        retValue->entries().AppendElement(
            FontPatternListEntry(patternStr, aAppFonts));
        free(s);
      });
    }
  }
}

using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
static Device sFontVisibilityDevice = Device::Unassigned;

void AssignFontVisibilityDevice() {
  if (sFontVisibilityDevice == Device::Unassigned) {
    nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    NS_ENSURE_SUCCESS_VOID(
        gfxInfo->GetFontVisibilityDetermination(&sFontVisibilityDevice));
  }
}

// Per family array of faces.
class FacesData {
  using FaceInitArray = AutoTArray<fontlist::Face::InitData, 8>;

  FaceInitArray mFaces;

  // Number of faces that have a single name. Faces that have multiple names are
  // sorted last.
  uint32_t mUniqueNameFaceCount = 0;

 public:
  void Add(fontlist::Face::InitData&& aData, bool aSingleName) {
    if (aSingleName) {
      mFaces.InsertElementAt(mUniqueNameFaceCount++, std::move(aData));
    } else {
      mFaces.AppendElement(std::move(aData));
    }
  }

  const FaceInitArray& Get() const { return mFaces; }
};

void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
  mLocalNames.Clear();
  mFcSubstituteCache.Clear();

  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
  mOtherFamilyNamesInitialized = true;

  mLastConfig = FcConfigGetCurrent();

  if (!XRE_IsParentProcess()) {
#ifdef MOZ_WIDGET_GTK
    auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
    UpdateSystemFontOptionsFromIpc(fontList.options());
#endif
    // Content processes will access the shared-memory data created by the
    // parent, so they do not need to query fontconfig for the available
    // fonts themselves.
    return;
  }

#ifdef MOZ_WIDGET_GTK
  UpdateSystemFontOptions();
#endif

#ifdef MOZ_BUNDLED_FONTS
  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
    auto timerId = glean::fontlist::bundledfonts_activate.Start();
    ActivateBundledFonts();
    glean::fontlist::bundledfonts_activate.StopAndAccumulate(
        std::move(timerId));
  }
#endif

  UniquePtr<SandboxPolicy> policy;

#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
  // If read sandboxing is enabled, create a temporary SandboxPolicy to
  // check font paths; use a fake PID to avoid picking up any PID-specific
  // rules by accident.
  SandboxBrokerPolicyFactory policyFactory;
  if (GetEffectiveContentSandboxLevel() > 2 &&
      !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
    policy = policyFactory.GetContentPolicy(-1, false);
  }
#endif

  nsTArray<fontlist::Family::InitData> families;

  nsClassHashtable<nsCStringHashKey, FacesData> faces;

  // Do we need to work around the fontconfig FcNameParse/FcNameUnparse bug
  // (present in versions between 2.10.94 and 2.11.1 inclusive)? See comment
  // in InitFontListForPlatform for details.
  int fcVersion = FcGetVersion();
  bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;

  // Returns true if the font was added with FontVisibility::Base.
  // This enables us to count how many known Base fonts are present.
  auto addPattern = [this, fcCharsetParseBug, &families, &faces](
                        FcPattern* aPattern, FcChar8*& aLastFamilyName,
                        nsCString& aFamilyName, bool aAppFont) -> bool {
    // get canonical name
    uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
    FcChar8* canonical = nullptr;
    FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
    if (!canonical) {
      return false;
    }

    nsAutoCString keyName;
    keyName = ToCharPtr(canonical);
    ToLowerCase(keyName);

    aLastFamilyName = canonical;
    aFamilyName = ToCharPtr(canonical);

    const FontVisibility visibility =
        aAppFont ? FontVisibility::Base : GetVisibilityForFamily(keyName);

    // Same canonical family name as the last one? Definitely no need to add a
    // new family record.
    auto* faceList =
        faces
            .LookupOrInsertWith(
                keyName,
                [&] {
                  families.AppendElement(fontlist::Family::InitData(
                      keyName, aFamilyName, fontlist::Family::kNoIndex,
                      visibility,
                      /*bundled*/ aAppFont, /*badUnderline*/ false));
                  return MakeUnique<FacesData>();
                })
            .get();

    char* s = (char*)FcNameUnparse(aPattern);
    nsAutoCString descriptor(s);
    free(s);

    if (fcCharsetParseBug) {
      // Escape any leading space in charset to work around FcNameParse bug.
      int32_t index = descriptor.Find(":charset= ");
      if (index != kNotFound) {
        // insert backslash after the =, before the space
        descriptor.Insert('\\', index + 9);
      }
    }

    WeightRange weight(FontWeight::NORMAL);
    StretchRange stretch(FontStretch::NORMAL);
    SlantStyleRange style(FontSlantStyle::NORMAL);
    uint16_t size;
    GetFontProperties(aPattern, &weight, &stretch, &style, &size);

    auto initData = fontlist::Face::InitData{descriptor, 0,       size, false,
                                             weight,     stretch, style};

    // Add entries for any other localized family names. (Most fonts only have
    // a single family name, so the first call to GetString will usually fail).
    // These get the same visibility level as we looked up for the first name.
    FcChar8* otherName;
    int n = (cIndex == 0 ? 1 : 0);
    while (FcPatternGetString(aPattern, FC_FAMILY, n, &otherName) ==
           FcResultMatch) {
      nsAutoCString otherFamilyName(ToCharPtr(otherName));
      keyName = otherFamilyName;
      ToLowerCase(keyName);

      faces
          .LookupOrInsertWith(
              keyName,
              [&] {
                families.AppendElement(fontlist::Family::InitData(
                    keyName, otherFamilyName, fontlist::Family::kNoIndex,
                    visibility,
                    /*bundled*/ aAppFont, /*badUnderline*/ false));

                return MakeUnique<FacesData>();
              })
          .get()
          ->Add(fontlist::Face::InitData(initData), /* singleName = */ false);

      n++;
      if (n == int(cIndex)) {
        n++;  // skip over canonical name
      }
    }

    const bool singleName = n == 1;
    faceList->Add(std::move(initData), singleName);

    if (IsNonVariableOrNamedInstance(aPattern)) {
      // map the psname, fullname ==> font family for local font lookups
      nsAutoCString psname, fullname;
      GetFaceNames(aPattern, aFamilyName, psname, fullname);
      MOZ_PUSH_IGNORE_THREAD_SAFETY
      if (!psname.IsEmpty()) {
        ToLowerCase(psname);
        MaybeAddToLocalNameTable(
            psname, fontlist::LocalFaceRec::InitData(keyName, descriptor));
      }
      if (!fullname.IsEmpty()) {
        ToLowerCase(fullname);
        if (fullname != psname) {
          // We only consider overriding an existing entry if this is the only
          // way to name this family. This prevents dubious aliases from
          // clobbering the local name table.
          if (singleName || !mLocalNameTable.Contains(fullname)) {
            MaybeAddToLocalNameTable(fullname, fontlist::LocalFaceRec::InitData(
                                                   keyName, descriptor));
          }
        }
      }
      MOZ_POP_THREAD_SAFETY
    }

    return visibility == FontVisibility::Base;
  };

  // Returns the number of families with FontVisibility::Base that were found.
  auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
                                          SandboxPolicy* aPolicy,
                                          bool aAppFonts) -> size_t {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=95 G=94

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.