123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599 |
- /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * 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/ArrayUtils.h"
- #include "mozilla/Base64.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/dom/ContentChild.h"
- #include "gfxAndroidPlatform.h"
- #include "mozilla/Omnijar.h"
- #include "mozilla/UniquePtr.h"
- #include "mozilla/UniquePtrExtensions.h"
- #include "nsIInputStream.h"
- #define gfxToolkitPlatform gfxAndroidPlatform
- #include "nsXULAppAPI.h"
- #include <dirent.h>
- #include <android/log.h>
- #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
- #include "ft2build.h"
- #include FT_FREETYPE_H
- #include FT_TRUETYPE_TAGS_H
- #include FT_TRUETYPE_TABLES_H
- #include "cairo-ft.h"
- #include "gfxFT2FontList.h"
- #include "gfxFT2Fonts.h"
- #include "gfxUserFontSet.h"
- #include "gfxFontUtils.h"
- #include "nsServiceManagerUtils.h"
- #include "nsIObserverService.h"
- #include "nsTArray.h"
- #include "nsUnicharUtils.h"
- #include "nsCRT.h"
- #include "nsDirectoryServiceUtils.h"
- #include "nsDirectoryServiceDefs.h"
- #include "nsAppDirectoryServiceDefs.h"
- #include "nsISimpleEnumerator.h"
- #include "nsIMemory.h"
- #include "gfxFontConstants.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/scache/StartupCache.h"
- #include <fcntl.h>
- #include <sys/mman.h>
- #include <sys/stat.h>
- using namespace mozilla;
- static LazyLogModule sFontInfoLog("fontInfoLog");
- #undef LOG
- #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
- #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
- static cairo_user_data_key_t sFTUserFontDataKey;
- static __inline void
- BuildKeyNameFromFontName(nsAString &aName)
- {
- ToLowerCase(aName);
- }
- // Helper to access the FT_Face for a given FT2FontEntry,
- // creating a temporary face if the entry does not have one yet.
- // This allows us to read font names, tables, etc if necessary
- // without permanently instantiating a freetype face and consuming
- // memory long-term.
- // This may fail (resulting in a null FT_Face), e.g. if it fails to
- // allocate memory to uncompress a font from omnijar.
- class AutoFTFace {
- public:
- AutoFTFace(FT2FontEntry* aFontEntry)
- : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
- {
- if (aFontEntry->mFTFace) {
- mFace = aFontEntry->mFTFace;
- return;
- }
- NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
- "can't use AutoFTFace for fonts without a filename");
- FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary();
- // A relative path (no initial "/") means this is a resource in
- // omnijar, not an installed font on the device.
- // The NS_ASSERTIONs here should never fail, as the resource must have
- // been read successfully during font-list initialization or we'd never
- // have created the font entry. The only legitimate runtime failure
- // here would be memory allocation, in which case mFace remains null.
- if (aFontEntry->mFilename[0] != '/') {
- RefPtr<nsZipArchive> reader =
- Omnijar::GetReader(Omnijar::Type::GRE);
- nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
- NS_ASSERTION(item, "failed to find zip entry");
- uint32_t bufSize = item->RealSize();
- mFontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
- if (mFontDataBuf) {
- nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
- cursor.Copy(&bufSize);
- NS_ASSERTION(bufSize == item->RealSize(),
- "error reading bundled font");
- if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
- aFontEntry->mFTFontIndex,
- &mFace)) {
- NS_WARNING("failed to create freetype face");
- }
- }
- } else {
- if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
- aFontEntry->mFTFontIndex, &mFace)) {
- NS_WARNING("failed to create freetype face");
- }
- }
- if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
- NS_WARNING("failed to select Unicode charmap");
- }
- mOwnsFace = true;
- }
- ~AutoFTFace() {
- if (mFace && mOwnsFace) {
- FT_Done_Face(mFace);
- if (mFontDataBuf) {
- free(mFontDataBuf);
- }
- }
- }
- operator FT_Face() { return mFace; }
- // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
- // we do -not- free the mFontDataBuf (if used); that also becomes the
- // responsibility of the new owner of the face.
- FT_Face forget() {
- NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
- mOwnsFace = false;
- return mFace;
- }
- const uint8_t* FontData() const { return mFontDataBuf; }
- private:
- FT_Face mFace;
- uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
- // or null for fonts instantiated from a file.
- // If non-null, this must survive as long as the
- // FT_Face.
- bool mOwnsFace;
- };
- /*
- * FT2FontEntry
- * gfxFontEntry subclass corresponding to a specific face that can be
- * rendered by freetype. This is associated with a face index in a
- * file (normally a .ttf/.otf file holding a single face, but in principle
- * there could be .ttc files with multiple faces).
- * The FT2FontEntry can create the necessary FT_Face on demand, and can
- * then create a Cairo font_face and scaled_font for drawing.
- */
- cairo_scaled_font_t *
- FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
- {
- cairo_font_face_t *cairoFace = CairoFontFace();
- if (!cairoFace) {
- return nullptr;
- }
- cairo_scaled_font_t *scaledFont = nullptr;
- cairo_matrix_t sizeMatrix;
- cairo_matrix_t identityMatrix;
- // XXX deal with adjusted size
- cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
- cairo_matrix_init_identity(&identityMatrix);
- // synthetic oblique by skewing via the font matrix
- bool needsOblique = IsUpright() &&
- aStyle->style != NS_FONT_STYLE_NORMAL &&
- aStyle->allowSyntheticStyle;
- if (needsOblique) {
- cairo_matrix_t style;
- cairo_matrix_init(&style,
- 1, //xx
- 0, //yx
- -1 * OBLIQUE_SKEW_FACTOR, //xy
- 1, //yy
- 0, //x0
- 0); //y0
- cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
- }
- cairo_font_options_t *fontOptions = cairo_font_options_create();
- if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
- cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
- }
- scaledFont = cairo_scaled_font_create(cairoFace,
- &sizeMatrix,
- &identityMatrix, fontOptions);
- cairo_font_options_destroy(fontOptions);
- NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
- "Failed to make scaled font");
- return scaledFont;
- }
- FT2FontEntry::~FT2FontEntry()
- {
- // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
- mFTFace = nullptr;
- #ifndef ANDROID
- if (mFontFace) {
- cairo_font_face_destroy(mFontFace);
- mFontFace = nullptr;
- }
- #endif
- }
- gfxFont*
- FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
- {
- cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
- if (!scaledFont) {
- return nullptr;
- }
- gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
- cairo_scaled_font_destroy(scaledFont);
- return font;
- }
- /* static */
- FT2FontEntry*
- FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
- uint16_t aWeight,
- int16_t aStretch,
- uint8_t aStyle,
- const uint8_t* aFontData,
- uint32_t aLength)
- {
- // Ownership of aFontData is passed in here; the fontEntry must
- // retain it as long as the FT_Face needs it, and ensure it is
- // eventually deleted.
- FT_Face face;
- FT_Error error =
- FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
- aFontData, aLength, 0, &face);
- if (error != FT_Err_Ok) {
- free((void*)aFontData);
- return nullptr;
- }
- if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
- FT_Done_Face(face);
- free((void*)aFontData);
- return nullptr;
- }
- // Create our FT2FontEntry, which inherits the name of the userfont entry
- // as it's not guaranteed that the face has valid names (bug 737315)
- FT2FontEntry* fe =
- FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
- aFontData);
- if (fe) {
- fe->mStyle = aStyle;
- fe->mWeight = aWeight;
- fe->mStretch = aStretch;
- fe->mIsDataUserFont = true;
- }
- return fe;
- }
- class FTUserFontData {
- public:
- FTUserFontData(FT_Face aFace, const uint8_t* aData)
- : mFace(aFace), mFontData(aData)
- {
- }
- ~FTUserFontData()
- {
- FT_Done_Face(mFace);
- if (mFontData) {
- free((void*)mFontData);
- }
- }
- const uint8_t *FontData() const { return mFontData; }
- private:
- FT_Face mFace;
- const uint8_t *mFontData;
- };
- static void
- FTFontDestroyFunc(void *data)
- {
- FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
- delete userFontData;
- }
- /* static */
- FT2FontEntry*
- FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
- {
- FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
- fe->mFilename = aFLE.filepath();
- fe->mFTFontIndex = aFLE.index();
- fe->mWeight = aFLE.weight();
- fe->mStretch = aFLE.stretch();
- fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
- return fe;
- }
- // Helpers to extract font entry properties from an FT_Face
- static bool
- FTFaceIsItalic(FT_Face aFace)
- {
- return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
- }
- static uint16_t
- FTFaceGetWeight(FT_Face aFace)
- {
- TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
- uint16_t os2weight = 0;
- if (os2 && os2->version != 0xffff) {
- // Technically, only 100 to 900 are valid, but some fonts
- // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
- // it set to 6 instead of 600. We try to be nice and handle that
- // as well.
- if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
- os2weight = os2->usWeightClass;
- } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
- os2weight = os2->usWeightClass * 100;
- }
- }
- uint16_t result;
- if (os2weight != 0) {
- result = os2weight;
- } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
- result = 700;
- } else {
- result = 400;
- }
- NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
- return result;
- }
- // Used to create the font entry for installed faces on the device,
- // when iterating over the fonts directories.
- // We use the FT_Face to retrieve the details needed for the font entry,
- // but unless we have been passed font data (i.e. for a user font),
- // we do *not* save a reference to it, nor create a cairo face,
- // as we don't want to keep a freetype face for every installed font
- // permanently in memory.
- /* static */
- FT2FontEntry*
- FT2FontEntry::CreateFontEntry(FT_Face aFace,
- const char* aFilename, uint8_t aIndex,
- const nsAString& aName,
- const uint8_t* aFontData)
- {
- FT2FontEntry *fe = new FT2FontEntry(aName);
- fe->mStyle = (FTFaceIsItalic(aFace) ?
- NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
- fe->mWeight = FTFaceGetWeight(aFace);
- fe->mFilename = aFilename;
- fe->mFTFontIndex = aIndex;
- if (aFontData) {
- fe->mFTFace = aFace;
- int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
- FT_LOAD_DEFAULT :
- (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
- fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
- FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
- cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
- userFontData, FTFontDestroyFunc);
- }
- return fe;
- }
- // construct font entry name for an installed font from names in the FT_Face,
- // and then create our FT2FontEntry
- static FT2FontEntry*
- CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
- {
- if (!aFace->family_name) {
- return nullptr;
- }
- nsAutoString fontName;
- AppendUTF8toUTF16(aFace->family_name, fontName);
- if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
- fontName.Append(' ');
- AppendUTF8toUTF16(aFace->style_name, fontName);
- }
- return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
- }
- FT2FontEntry*
- gfxFT2Font::GetFontEntry()
- {
- return static_cast<FT2FontEntry*> (mFontEntry.get());
- }
- cairo_font_face_t *
- FT2FontEntry::CairoFontFace()
- {
- if (!mFontFace) {
- AutoFTFace face(this);
- if (!face) {
- return nullptr;
- }
- mFTFace = face.forget();
- int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
- FT_LOAD_DEFAULT :
- (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
- mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
- FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
- cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
- userFontData, FTFontDestroyFunc);
- }
- return mFontFace;
- }
- // Copied/modified from similar code in gfxMacPlatformFontList.mm:
- // Complex scripts will not render correctly unless Graphite or OT
- // layout tables are present.
- // For OpenType, we also check that the GSUB table supports the relevant
- // script tag, to avoid using things like Arial Unicode MS for Lao (it has
- // the characters, but lacks OpenType support).
- // TODO: consider whether we should move this to gfxFontEntry and do similar
- // cmap-masking on all platforms to avoid using fonts that won't shape
- // properly.
- nsresult
- FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
- {
- if (mCharacterMap) {
- return NS_OK;
- }
- RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
- AutoTArray<uint8_t, 16384> buffer;
- nsresult rv = CopyFontTable(TTAG_cmap, buffer);
-
- if (NS_SUCCEEDED(rv)) {
- bool unicodeFont;
- bool symbolFont;
- rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
- *charmap, mUVSOffset,
- unicodeFont, symbolFont);
- }
- if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
- // We assume a Graphite font knows what it's doing,
- // and provides whatever shaping is needed for the
- // characters it supports, so only check/clear the
- // complex-script ranges for non-Graphite fonts
- // for layout support, check for the presence of opentype layout tables
- bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
- for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
- sr->rangeStart; sr++) {
- // check to see if the cmap includes complex script codepoints
- if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
- // We check for GSUB here, as GPOS alone would not be ok.
- if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
- continue;
- }
- charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
- }
- }
- }
- mHasCmapTable = NS_SUCCEEDED(rv);
- if (mHasCmapTable) {
- gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
- mCharacterMap = pfl->FindCharMap(charmap);
- } else {
- // if error occurred, initialize to null cmap
- mCharacterMap = new gfxCharacterMap();
- }
- return rv;
- }
- nsresult
- FT2FontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
- {
- AutoFTFace face(this);
- if (!face) {
- return NS_ERROR_FAILURE;
- }
- FT_Error status;
- FT_ULong len = 0;
- status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
- if (status != FT_Err_Ok || len == 0) {
- return NS_ERROR_FAILURE;
- }
- if (!aBuffer.SetLength(len, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- uint8_t *buf = aBuffer.Elements();
- status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
- NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
- return NS_OK;
- }
- hb_blob_t*
- FT2FontEntry::GetFontTable(uint32_t aTableTag)
- {
- if (mFontFace) {
- // if there's a cairo font face, we may be able to return a blob
- // that just wraps a range of the attached user font data
- FTUserFontData *userFontData = static_cast<FTUserFontData*>(
- cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
- if (userFontData && userFontData->FontData()) {
- return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
- aTableTag);
- }
- }
- // otherwise, use the default method (which in turn will call our
- // implementation of CopyFontTable)
- return gfxFontEntry::GetFontTable(aTableTag);
- }
- void
- FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
- {
- gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
- aSizes->mFontListSize +=
- mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
- }
- void
- FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
- {
- aSizes->mFontListSize += aMallocSizeOf(this);
- AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
- }
- /*
- * FT2FontFamily
- * A standard gfxFontFamily; just adds a method used to support sending
- * the font list from chrome to content via IPC.
- */
- void
- FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
- Visibility aVisibility)
- {
- for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
- const FT2FontEntry *fe =
- static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
- if (!fe) {
- continue;
- }
-
- aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
- fe->mFilename,
- fe->Weight(), fe->Stretch(),
- fe->mStyle,
- fe->mFTFontIndex,
- aVisibility == kHidden));
- }
- }
- /*
- * Startup cache support for the font list:
- * We store the list of families and faces, with their style attributes and the
- * corresponding font files, in the startup cache.
- * This allows us to recreate the gfxFT2FontList collection of families and
- * faces without instantiating Freetype faces for each font file (in order to
- * find their attributes), leading to significantly quicker startup.
- */
- #define CACHE_KEY "font.cached-list"
- class FontNameCache {
- public:
- // Creates the object but does NOT load the cached data from the startup
- // cache; call Init() after creation to do that.
- FontNameCache()
- : mMap(&mOps, sizeof(FNCMapEntry), 0)
- , mWriteNeeded(false)
- {
- // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
- // it to |mMap|'s constructor. A more normal approach here would be to
- // have a static |sOps| member. Unfortunately, this mysteriously but
- // consistently makes Fennec start-up slower, so we take this
- // unorthodox approach instead. It's safe because PLDHashTable's
- // constructor doesn't dereference the pointer; it just makes a copy of
- // it.
- mOps = (PLDHashTableOps) {
- StringHash,
- HashMatchEntry,
- MoveEntry,
- PLDHashTable::ClearEntryStub,
- nullptr
- };
- MOZ_ASSERT(XRE_IsParentProcess(),
- "FontNameCache should only be used in chrome process");
- mCache = mozilla::scache::StartupCache::GetSingleton();
- }
- ~FontNameCache()
- {
- if (!mWriteNeeded || !mCache) {
- return;
- }
- nsAutoCString buf;
- for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
- auto entry = static_cast<FNCMapEntry*>(iter.Get());
- if (!entry->mFileExists) {
- // skip writing entries for files that are no longer present
- continue;
- }
- buf.Append(entry->mFilename);
- buf.Append(';');
- buf.Append(entry->mFaces);
- buf.Append(';');
- buf.AppendInt(entry->mTimestamp);
- buf.Append(';');
- buf.AppendInt(entry->mFilesize);
- buf.Append(';');
- }
- mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
- }
- // This may be called more than once (if we re-load the font list).
- void Init()
- {
- if (!mCache) {
- return;
- }
- uint32_t size;
- UniquePtr<char[]> buf;
- if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
- return;
- }
- LOG(("got: %s from the cache", nsDependentCString(buf.get(), size).get()));
- mMap.Clear();
- mWriteNeeded = false;
- const char* beginning = buf.get();
- const char* end = strchr(beginning, ';');
- while (end) {
- nsCString filename(beginning, end - beginning);
- beginning = end + 1;
- if (!(end = strchr(beginning, ';'))) {
- break;
- }
- nsCString faceList(beginning, end - beginning);
- beginning = end + 1;
- if (!(end = strchr(beginning, ';'))) {
- break;
- }
- uint32_t timestamp = strtoul(beginning, nullptr, 10);
- beginning = end + 1;
- if (!(end = strchr(beginning, ';'))) {
- break;
- }
- uint32_t filesize = strtoul(beginning, nullptr, 10);
- auto mapEntry =
- static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible));
- if (mapEntry) {
- mapEntry->mFilename.Assign(filename);
- mapEntry->mTimestamp = timestamp;
- mapEntry->mFilesize = filesize;
- mapEntry->mFaces.Assign(faceList);
- // entries from the startupcache are marked "non-existing"
- // until we have confirmed that the file still exists
- mapEntry->mFileExists = false;
- }
- beginning = end + 1;
- end = strchr(beginning, ';');
- }
- }
- void
- GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
- uint32_t *aTimestamp, uint32_t *aFilesize)
- {
- auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
- if (entry) {
- *aTimestamp = entry->mTimestamp;
- *aFilesize = entry->mFilesize;
- aFaceList.Assign(entry->mFaces);
- // this entry does correspond to an existing file
- // (although it might not be up-to-date, in which case
- // it will get overwritten via CacheFileInfo)
- entry->mFileExists = true;
- }
- }
- void
- CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
- uint32_t aTimestamp, uint32_t aFilesize)
- {
- auto entry =
- static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
- if (entry) {
- entry->mFilename.Assign(aFileName);
- entry->mTimestamp = aTimestamp;
- entry->mFilesize = aFilesize;
- entry->mFaces.Assign(aFaceList);
- entry->mFileExists = true;
- }
- mWriteNeeded = true;
- }
- private:
- mozilla::scache::StartupCache* mCache;
- PLDHashTable mMap;
- bool mWriteNeeded;
- PLDHashTableOps mOps;
- typedef struct : public PLDHashEntryHdr {
- public:
- nsCString mFilename;
- uint32_t mTimestamp;
- uint32_t mFilesize;
- nsCString mFaces;
- bool mFileExists;
- } FNCMapEntry;
- static PLDHashNumber StringHash(const void *key)
- {
- return HashString(reinterpret_cast<const char*>(key));
- }
- static bool HashMatchEntry(const PLDHashEntryHdr *aHdr, const void *key)
- {
- const FNCMapEntry* entry =
- static_cast<const FNCMapEntry*>(aHdr);
- return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
- }
- static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
- PLDHashEntryHdr *aTo)
- {
- FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
- const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
- to->mFilename.Assign(from->mFilename);
- to->mTimestamp = from->mTimestamp;
- to->mFilesize = from->mFilesize;
- to->mFaces.Assign(from->mFaces);
- to->mFileExists = from->mFileExists;
- }
- };
- /***************************************************************
- *
- * gfxFT2FontList
- *
- */
- // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
- // scanning the system's Fonts directory for OpenType and TrueType files.
- #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
- class WillShutdownObserver : public nsIObserver
- {
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
- explicit WillShutdownObserver(gfxFT2FontList* aFontList)
- : mFontList(aFontList)
- { }
- void Remove()
- {
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (obs) {
- obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
- }
- mFontList = nullptr;
- }
- protected:
- virtual ~WillShutdownObserver()
- { }
- gfxFT2FontList *mFontList;
- };
- NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
- NS_IMETHODIMP
- WillShutdownObserver::Observe(nsISupports *aSubject,
- const char *aTopic,
- const char16_t *aData)
- {
- if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
- mFontList->WillShutdown();
- } else {
- NS_NOTREACHED("unexpected notification topic");
- }
- return NS_OK;
- }
- gfxFT2FontList::gfxFT2FontList()
- : mJarModifiedTime(0)
- {
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (obs) {
- mObserver = new WillShutdownObserver(this);
- obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
- }
- }
- gfxFT2FontList::~gfxFT2FontList()
- {
- if (mObserver) {
- mObserver->Remove();
- }
- }
- void
- gfxFT2FontList::AppendFacesFromCachedFaceList(
- const nsCString& aFileName,
- const nsCString& aFaceList,
- StandardFile aStdFile,
- FT2FontFamily::Visibility aVisibility)
- {
- const char *beginning = aFaceList.get();
- const char *end = strchr(beginning, ',');
- while (end) {
- NS_ConvertUTF8toUTF16 familyName(beginning, end - beginning);
- ToLowerCase(familyName);
- beginning = end + 1;
- if (!(end = strchr(beginning, ','))) {
- break;
- }
- NS_ConvertUTF8toUTF16 faceName(beginning, end - beginning);
- beginning = end + 1;
- if (!(end = strchr(beginning, ','))) {
- break;
- }
- uint32_t index = strtoul(beginning, nullptr, 10);
- beginning = end + 1;
- if (!(end = strchr(beginning, ','))) {
- break;
- }
- bool italic = (*beginning != '0');
- beginning = end + 1;
- if (!(end = strchr(beginning, ','))) {
- break;
- }
- uint32_t weight = strtoul(beginning, nullptr, 10);
- beginning = end + 1;
- if (!(end = strchr(beginning, ','))) {
- break;
- }
- int32_t stretch = strtol(beginning, nullptr, 10);
- FontListEntry fle(familyName, faceName, aFileName,
- weight, stretch, italic, index,
- aVisibility == FT2FontFamily::kHidden);
- AppendFaceFromFontListEntry(fle, aStdFile);
- beginning = end + 1;
- end = strchr(beginning, ',');
- }
- }
- static void
- AppendToFaceList(nsCString& aFaceList,
- nsAString& aFamilyName, FT2FontEntry* aFontEntry)
- {
- aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
- aFaceList.Append(',');
- aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
- aFaceList.Append(',');
- aFaceList.AppendInt(aFontEntry->mFTFontIndex);
- aFaceList.Append(',');
- aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
- aFaceList.Append(',');
- aFaceList.AppendInt(aFontEntry->Weight());
- aFaceList.Append(',');
- aFaceList.AppendInt(aFontEntry->Stretch());
- aFaceList.Append(',');
- }
- void
- FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
- {
- // note if the family is in the "bad underline" blacklist
- if (aFamily->IsBadUnderlineFamily()) {
- mIsBadUnderlineFont = true;
- }
- // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
- // because of unwanted on-by-default "ae" ligature.
- // (See also AppendFaceFromFontListEntry.)
- if (aFamily->Name().EqualsLiteral("roboto")) {
- mIgnoreGSUB = true;
- }
- // bug 706888 - set the IgnoreGSUB flag on the broken version of
- // Droid Sans Arabic from certain phones, as identified by the
- // font checksum in the 'head' table
- else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
- AutoFTFace face(this);
- if (face) {
- const TT_Header *head = static_cast<const TT_Header*>
- (FT_Get_Sfnt_Table(face, ft_sfnt_head));
- if (head && head->CheckSum_Adjust == 0xe445242) {
- mIgnoreGSUB = true;
- }
- }
- }
- }
- void
- gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
- FontNameCache *aCache,
- StandardFile aStdFile,
- FT2FontFamily::Visibility aVisibility)
- {
- nsCString cachedFaceList;
- uint32_t filesize = 0, timestamp = 0;
- if (aCache) {
- aCache->GetInfoForFile(aFileName, cachedFaceList, ×tamp, &filesize);
- }
- struct stat s;
- int statRetval = stat(aFileName.get(), &s);
- if (!cachedFaceList.IsEmpty() && 0 == statRetval &&
- s.st_mtime == timestamp && s.st_size == filesize)
- {
- LOG(("using cached font info for %s", aFileName.get()));
- AppendFacesFromCachedFaceList(aFileName, cachedFaceList, aStdFile,
- aVisibility);
- return;
- }
- FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
- FT_Face dummy;
- if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
- LOG(("reading font info via FreeType for %s", aFileName.get()));
- nsCString newFaceList;
- timestamp = s.st_mtime;
- filesize = s.st_size;
- for (FT_Long i = 0; i < dummy->num_faces; i++) {
- FT_Face face;
- if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
- continue;
- }
- AddFaceToList(aFileName, i, aStdFile, aVisibility, face, newFaceList);
- FT_Done_Face(face);
- }
- FT_Done_Face(dummy);
- if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
- aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
- }
- }
- }
- void
- gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
- {
- bool jarChanged = false;
- mozilla::scache::StartupCache* cache =
- mozilla::scache::StartupCache::GetSingleton();
- UniquePtr<char[]> cachedModifiedTimeBuf;
- uint32_t longSize;
- if (cache &&
- NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
- &cachedModifiedTimeBuf,
- &longSize)) &&
- longSize == sizeof(int64_t))
- {
- nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
- jarFile->GetLastModifiedTime(&mJarModifiedTime);
- if (mJarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
- jarChanged = true;
- }
- }
- static const char* sJarSearchPaths[] = {
- "res/fonts/*.ttf$",
- };
- RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
- for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
- nsZipFind* find;
- if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
- const char* path;
- uint16_t len;
- while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
- nsCString entryName(path, len);
- AppendFacesFromOmnijarEntry(reader, entryName, aCache,
- jarChanged);
- }
- delete find;
- }
- }
- }
- // Given the freetype face corresponding to an entryName and face index,
- // add the face to the available font list and to the faceList string
- void
- gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
- StandardFile aStdFile,
- FT2FontFamily::Visibility aVisibility,
- FT_Face aFace,
- nsCString& aFaceList)
- {
- if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
- // ignore faces that don't support a Unicode charmap
- return;
- }
- // build the font entry name and create an FT2FontEntry,
- // but do -not- keep a reference to the FT_Face
- RefPtr<FT2FontEntry> fe =
- CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
- auto& fontFamilies =
- (aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
- mFontFamilies;
- if (fe) {
- NS_ConvertUTF8toUTF16 name(aFace->family_name);
- BuildKeyNameFromFontName(name);
- RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
- if (!family) {
- family = new FT2FontFamily(name);
- fontFamilies.Put(name, family);
- if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
- family->SetSkipSpaceFeatureCheck(true);
- }
- if (mBadUnderlineFamilyNames.Contains(name)) {
- family->SetBadUnderlineFamily();
- }
- }
- fe->mStandardFace = (aStdFile == kStandard);
- family->AddFontEntry(fe);
- fe->CheckForBrokenFont(family);
- AppendToFaceList(aFaceList, name, fe);
- if (LOG_ENABLED()) {
- LOG(("(fontinit) added (%s) to family (%s)"
- " with style: %s weight: %d stretch: %d",
- NS_ConvertUTF16toUTF8(fe->Name()).get(),
- NS_ConvertUTF16toUTF8(family->Name()).get(),
- fe->IsItalic() ? "italic" : "normal",
- fe->Weight(), fe->Stretch()));
- }
- }
- }
- void
- gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
- const nsCString& aEntryName,
- FontNameCache *aCache,
- bool aJarChanged)
- {
- nsCString faceList;
- if (aCache && !aJarChanged) {
- uint32_t filesize, timestamp;
- aCache->GetInfoForFile(aEntryName, faceList, ×tamp, &filesize);
- if (faceList.Length() > 0) {
- AppendFacesFromCachedFaceList(aEntryName, faceList);
- return;
- }
- }
- nsZipItem *item = aArchive->GetItem(aEntryName.get());
- NS_ASSERTION(item, "failed to find zip entry");
- uint32_t bufSize = item->RealSize();
- // We use fallible allocation here; if there's not enough RAM, we'll simply
- // ignore the bundled fonts and fall back to the device's installed fonts.
- auto buf = MakeUniqueFallible<uint8_t[]>(bufSize);
- if (!buf) {
- return;
- }
- nsZipCursor cursor(item, aArchive, buf.get(), bufSize);
- uint8_t* data = cursor.Copy(&bufSize);
- NS_ASSERTION(data && bufSize == item->RealSize(),
- "error reading bundled font");
- if (!data) {
- return;
- }
- FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
- FT_Face dummy;
- if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, 0, &dummy)) {
- return;
- }
- for (FT_Long i = 0; i < dummy->num_faces; i++) {
- FT_Face face;
- if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, i, &face)) {
- continue;
- }
- AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
- face, faceList);
- FT_Done_Face(face);
- }
- FT_Done_Face(dummy);
- if (aCache && !faceList.IsEmpty()) {
- aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
- }
- }
- // Called on each family after all fonts are added to the list;
- // this will sort faces to give priority to "standard" font files
- // if aUserArg is non-null (i.e. we're using it as a boolean flag)
- static void
- FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
- RefPtr<gfxFontFamily>& aFamily,
- bool aSortFaces)
- {
- gfxFontFamily *family = aFamily.get();
- family->SetHasStyles(true);
- if (aSortFaces) {
- family->SortAvailableFonts();
- }
- family->CheckForSimpleFamily();
- }
- void
- gfxFT2FontList::FindFonts()
- {
- gfxFontCache *fc = gfxFontCache::GetCache();
- if (fc)
- fc->AgeAllGenerations();
- ClearLangGroupPrefFonts();
- mCodepointsWithNoFonts.reset();
- mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
- mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
- if (!XRE_IsParentProcess()) {
- // Content process: ask the Chrome process to give us the list
- InfallibleTArray<FontListEntry> fonts;
- mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
- for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
- // We don't need to identify "standard" font files here,
- // as the faces are already sorted.
- AppendFaceFromFontListEntry(fonts[i], kUnknown);
- }
- // Passing null for userdata tells Finalize that it does not need
- // to sort faces (because they were already sorted by chrome,
- // so we just maintain the existing order)
- for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- nsStringHashKey::KeyType key = iter.Key();
- RefPtr<gfxFontFamily>& family = iter.Data();
- FinalizeFamilyMemberList(key, family, /* aSortFaces */ false);
- }
- for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- nsStringHashKey::KeyType key = iter.Key();
- RefPtr<gfxFontFamily>& family = iter.Data();
- FinalizeFamilyMemberList(key, family, /* aSortFaces */ false );
- }
- LOG(("got font list from chrome process: %d faces in %d families "
- "and %d in hidden families",
- fonts.Length(), mFontFamilies.Count(),
- mHiddenFontFamilies.Count()));
- return;
- }
- // Chrome process: get the cached list (if any)
- if (!mFontNameCache) {
- mFontNameCache = MakeUnique<FontNameCache>();
- }
- mFontNameCache->Init();
- // ANDROID_ROOT is the root of the android system, typically /system;
- // font files are in /$ANDROID_ROOT/fonts/
- nsCString root;
- char *androidRoot = PR_GetEnv("ANDROID_ROOT");
- if (androidRoot) {
- root = androidRoot;
- } else {
- root = NS_LITERAL_CSTRING("/system");
- }
- root.AppendLiteral("/fonts");
- FindFontsInDir(root, mFontNameCache.get(), FT2FontFamily::kVisible);
- if (mFontFamilies.Count() == 0) {
- // if we can't find/read the font directory, we are doomed!
- NS_RUNTIMEABORT("Could not read the system fonts directory");
- }
- // Look for fonts stored in omnijar, unless we're on a low-memory
- // device where we don't want to spend the RAM to decompress them.
- // (Prefs may disable this, or force-enable it even with low memory.)
- bool lowmem;
- nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
- if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
- Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
- Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
- FindFontsInOmnijar(mFontNameCache.get());
- }
- // Look for downloaded fonts in a profile-agnostic "fonts" directory.
- nsCOMPtr<nsIProperties> dirSvc =
- do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
- if (dirSvc) {
- nsCOMPtr<nsIFile> appDir;
- nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
- NS_GET_IID(nsIFile), getter_AddRefs(appDir));
- if (NS_SUCCEEDED(rv)) {
- appDir->AppendNative(NS_LITERAL_CSTRING("fonts"));
- nsCString localPath;
- if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
- FindFontsInDir(localPath, mFontNameCache.get(),
- FT2FontFamily::kVisible);
- }
- }
- }
- // look for locally-added fonts in a "fonts" subdir of the profile
- nsCOMPtr<nsIFile> localDir;
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
- getter_AddRefs(localDir));
- if (NS_SUCCEEDED(rv) &&
- NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
- nsCString localPath;
- rv = localDir->GetNativePath(localPath);
- if (NS_SUCCEEDED(rv)) {
- FindFontsInDir(localPath, mFontNameCache.get(),
- FT2FontFamily::kVisible);
- }
- }
- // Finalize the families by sorting faces into standard order
- // and marking "simple" families.
- // Passing non-null userData here says that we want faces to be sorted.
- for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- nsStringHashKey::KeyType key = iter.Key();
- RefPtr<gfxFontFamily>& family = iter.Data();
- FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
- }
- for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- nsStringHashKey::KeyType key = iter.Key();
- RefPtr<gfxFontFamily>& family = iter.Data();
- FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
- }
- }
- void
- gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
- FontNameCache *aFNC,
- FT2FontFamily::Visibility aVisibility)
- {
- static const char* sStandardFonts[] = {
- "DroidSans.ttf",
- "DroidSans-Bold.ttf",
- "DroidSerif-Regular.ttf",
- "DroidSerif-Bold.ttf",
- "DroidSerif-Italic.ttf",
- "DroidSerif-BoldItalic.ttf",
- "DroidSansMono.ttf",
- "DroidSansArabic.ttf",
- "DroidSansHebrew.ttf",
- "DroidSansThai.ttf",
- "MTLmr3m.ttf",
- "MTLc3m.ttf",
- "NanumGothic.ttf",
- "DroidSansJapanese.ttf",
- "DroidSansFallback.ttf"
- };
- DIR *d = opendir(aDir.get());
- if (!d) {
- return;
- }
- struct dirent *ent = nullptr;
- while ((ent = readdir(d)) != nullptr) {
- const char *ext = strrchr(ent->d_name, '.');
- if (!ext) {
- continue;
- }
- if (strcasecmp(ext, ".ttf") == 0 ||
- strcasecmp(ext, ".otf") == 0 ||
- strcasecmp(ext, ".woff") == 0 ||
- strcasecmp(ext, ".ttc") == 0) {
- bool isStdFont = false;
- for (unsigned int i = 0;
- i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
- isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
- }
- nsCString s(aDir);
- s.Append('/');
- s.Append(ent->d_name);
- // Add the face(s) from this file to our font list;
- // note that if we have cached info for this file in fnc,
- // and the file is unchanged, we won't actually need to read it.
- // If the file is new/changed, this will update the FontNameCache.
- AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown,
- aVisibility);
- }
- }
- closedir(d);
- }
- void
- gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
- StandardFile aStdFile)
- {
- FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
- if (fe) {
- auto& fontFamilies =
- aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
- fe->mStandardFace = (aStdFile == kStandard);
- nsAutoString name(aFLE.familyName());
- RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
- if (!family) {
- family = new FT2FontFamily(name);
- fontFamilies.Put(name, family);
- if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
- family->SetSkipSpaceFeatureCheck(true);
- }
- if (mBadUnderlineFamilyNames.Contains(name)) {
- family->SetBadUnderlineFamily();
- }
- }
- family->AddFontEntry(fe);
- fe->CheckForBrokenFont(family);
- }
- }
- void
- gfxFT2FontList::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
- {
- for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- auto family = static_cast<FT2FontFamily*>(iter.Data().get());
- family->AddFacesToFontList(retValue, FT2FontFamily::kVisible);
- }
- for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- auto family = static_cast<FT2FontFamily*>(iter.Data().get());
- family->AddFacesToFontList(retValue, FT2FontFamily::kHidden);
- }
- }
- static void
- LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
- {
- AutoTArray<nsString, 5> skiplist;
- gfxFontUtils::GetPrefsFontList(
- "font.whitelist.skip_default_features_space_check",
- skiplist);
- uint32_t numFonts = skiplist.Length();
- for (uint32_t i = 0; i < numFonts; i++) {
- ToLowerCase(skiplist[i]);
- aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
- }
- }
- void
- PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
- RefPtr<gfxFontFamily>& aFamily)
- {
- gfxFontFamily *family = aFamily.get();
- auto& faces = family->GetFontList();
- size_t count = faces.Length();
- for (size_t i = 0; i < count; ++i) {
- FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
- if (fe->mFTFontIndex != 0) {
- NS_NOTREACHED("don't try to preload a multi-face font");
- continue;
- }
- // XXX Should we move the i/o here off the main thread?
- // Map the font data in fe->mFilename, so we can calculate its CRC32.
- int fd = open(fe->mFilename.get(), O_RDONLY);
- if (fd < 0) {
- continue;
- }
- struct stat buf;
- if (fstat(fd, &buf) != 0 || buf.st_size < 12) {
- close(fd);
- continue;
- }
- char* data = static_cast<char*>(
- mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
- close(fd);
- if (data == MAP_FAILED) {
- continue;
- }
- // Calculate CRC32
- uint32_t crc = crc32(0, nullptr, 0);
- crc = crc32(crc, (Bytef*)data, buf.st_size);
- munmap(data, buf.st_size);
- #if 0
- ALOG("\n**** Preloading family [%s] face [%s] CRC32 [0x%08x]",
- NS_ConvertUTF16toUTF8(family->Name()).get(),
- fe->mFilename.get(),
- crc);
- #endif
- fe->mUserFontData = MakeUnique<gfxUserFontData>();
- fe->mUserFontData->mRealName = fe->Name();
- fe->mUserFontData->mCRC32 = crc;
- fe->mUserFontData->mLength = buf.st_size;
- // Stash it persistently in the user-font cache.
- gfxUserFontSet::UserFontCache::CacheFont(
- fe, gfxUserFontSet::UserFontCache::kPersistent);
- }
- }
- nsresult
- gfxFT2FontList::InitFontListForPlatform()
- {
- // reset hidden font list
- mHiddenFontFamilies.Clear();
- LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
- FindFonts();
- for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- nsStringHashKey::KeyType key = iter.Key();
- RefPtr<gfxFontFamily>& family = iter.Data();
- PreloadAsUserFontFaces(key, family);
- }
- return NS_OK;
- }
- // called for each family name, based on the assumption that the
- // first part of the full name is the family name
- gfxFontEntry*
- gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
- uint16_t aWeight,
- int16_t aStretch,
- uint8_t aStyle)
- {
- // walk over list of names
- FT2FontEntry* fontEntry = nullptr;
- nsString fullName(aFontName);
- // Note that we only check mFontFamilies here, not mHiddenFontFamilies;
- // hence @font-face { src:local(...) } will not find hidden fonts.
- for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- // Check family name, based on the assumption that the
- // first part of the full name is the family name
- RefPtr<gfxFontFamily>& fontFamily = iter.Data();
- // does the family name match up to the length of the family name?
- const nsString& family = fontFamily->Name();
- nsString fullNameFamily;
- fullName.Left(fullNameFamily, family.Length());
- // if so, iterate over faces in this family to see if there is a match
- if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
- nsTArray<RefPtr<gfxFontEntry> >& fontList = fontFamily->GetFontList();
- int index, len = fontList.Length();
- for (index = 0; index < len; index++) {
- gfxFontEntry* fe = fontList[index];
- if (!fe) {
- continue;
- }
- if (fe->Name().Equals(fullName,
- nsCaseInsensitiveStringComparator())) {
- fontEntry = static_cast<FT2FontEntry*>(fe);
- goto searchDone;
- }
- }
- }
- }
- searchDone:
- if (!fontEntry) {
- return nullptr;
- }
- // Clone the font entry so that we can then set its style descriptors
- // from the userfont entry rather than the actual font.
- // Ensure existence of mFTFace in the original entry
- fontEntry->CairoFontFace();
- if (!fontEntry->mFTFace) {
- return nullptr;
- }
- FT2FontEntry* fe =
- FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
- fontEntry->mFilename.get(),
- fontEntry->mFTFontIndex,
- fontEntry->Name(), nullptr);
- if (fe) {
- fe->mStyle = aStyle;
- fe->mWeight = aWeight;
- fe->mStretch = aStretch;
- fe->mIsLocalUserFont = true;
- }
- return fe;
- }
- gfxFontFamily*
- gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
- {
- gfxFontFamily *ff = nullptr;
- /* TODO: what about Qt or other platforms that may use this? */
- return ff;
- }
- gfxFontEntry*
- gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
- uint16_t aWeight,
- int16_t aStretch,
- uint8_t aStyle,
- const uint8_t* aFontData,
- uint32_t aLength)
- {
- // The FT2 font needs the font data to persist, so we do NOT free it here
- // but instead pass ownership to the font entry.
- // Deallocation will happen later, when the font face is destroyed.
- return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
- aStyle, aFontData, aLength);
- }
- void
- gfxFT2FontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
- {
- for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- RefPtr<gfxFontFamily>& family = iter.Data();
- aFamilyArray.AppendElement(family);
- }
- for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
- RefPtr<gfxFontFamily>& family = iter.Data();
- aFamilyArray.AppendElement(family);
- }
- }
- void
- gfxFT2FontList::WillShutdown()
- {
- mozilla::scache::StartupCache* cache =
- mozilla::scache::StartupCache::GetSingleton();
- if (cache && mJarModifiedTime > 0) {
- cache->PutBuffer(JAR_LAST_MODIFED_TIME,
- (char*)&mJarModifiedTime, sizeof(mJarModifiedTime));
- }
- mFontNameCache = nullptr;
- }
|