Bug 1444940 [wpt PR 9917] - Writable streams: test changes to abort() under error...
[gecko.git] / gfx / thebes / gfxFT2FontList.cpp
blobfbf9e2b102c551d500fe13fefaafb7da0906c717
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/Base64.h"
8 #include "mozilla/MemoryReporting.h"
10 #include "mozilla/dom/ContentChild.h"
11 #include "gfxAndroidPlatform.h"
12 #include "mozilla/Omnijar.h"
13 #include "mozilla/UniquePtr.h"
14 #include "mozilla/UniquePtrExtensions.h"
15 #include "nsIInputStream.h"
16 #include "nsReadableUtils.h"
18 #include "nsXULAppAPI.h"
19 #include <dirent.h>
20 #include <android/log.h>
21 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
23 #include "ft2build.h"
24 #include FT_FREETYPE_H
25 #include FT_TRUETYPE_TAGS_H
26 #include FT_TRUETYPE_TABLES_H
27 #include FT_MULTIPLE_MASTERS_H
28 #include "cairo-ft.h"
30 #include "gfxFT2FontList.h"
31 #include "gfxFT2Fonts.h"
32 #include "gfxFT2Utils.h"
33 #include "gfxUserFontSet.h"
34 #include "gfxFontUtils.h"
36 #include "nsServiceManagerUtils.h"
37 #include "nsIObserverService.h"
38 #include "nsTArray.h"
39 #include "nsUnicharUtils.h"
40 #include "nsCRT.h"
42 #include "nsDirectoryServiceUtils.h"
43 #include "nsDirectoryServiceDefs.h"
44 #include "nsAppDirectoryServiceDefs.h"
45 #include "nsISimpleEnumerator.h"
46 #include "nsIMemory.h"
47 #include "gfxFontConstants.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/scache/StartupCache.h"
51 #include <fcntl.h>
52 #include <sys/mman.h>
53 #include <sys/stat.h>
55 using namespace mozilla;
56 using namespace mozilla::gfx;
58 static LazyLogModule sFontInfoLog("fontInfoLog");
60 #undef LOG
61 #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
62 #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
64 static cairo_user_data_key_t sFTUserFontDataKey;
66 static __inline void
67 BuildKeyNameFromFontName(nsAString &aName)
69 ToLowerCase(aName);
72 // Helper to access the FT_Face for a given FT2FontEntry,
73 // creating a temporary face if the entry does not have one yet.
74 // This allows us to read font names, tables, etc if necessary
75 // without permanently instantiating a freetype face and consuming
76 // memory long-term.
77 // This may fail (resulting in a null FT_Face), e.g. if it fails to
78 // allocate memory to uncompress a font from omnijar.
79 class AutoFTFace {
80 public:
81 explicit AutoFTFace(FT2FontEntry* aFontEntry)
82 : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
84 if (aFontEntry->mFTFace) {
85 mFace = aFontEntry->mFTFace;
86 return;
89 NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
90 "can't use AutoFTFace for fonts without a filename");
92 // A relative path (no initial "/") means this is a resource in
93 // omnijar, not an installed font on the device.
94 // The NS_ASSERTIONs here should never fail, as the resource must have
95 // been read successfully during font-list initialization or we'd never
96 // have created the font entry. The only legitimate runtime failure
97 // here would be memory allocation, in which case mFace remains null.
98 if (aFontEntry->mFilename[0] != '/') {
99 RefPtr<nsZipArchive> reader =
100 Omnijar::GetReader(Omnijar::Type::GRE);
101 nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
102 NS_ASSERTION(item, "failed to find zip entry");
104 uint32_t bufSize = item->RealSize();
105 mFontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
106 if (mFontDataBuf) {
107 nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
108 cursor.Copy(&bufSize);
109 NS_ASSERTION(bufSize == item->RealSize(),
110 "error reading bundled font");
111 mDataLength = bufSize;
112 mFace = Factory::NewFTFaceFromData(nullptr, mFontDataBuf, bufSize, aFontEntry->mFTFontIndex);
113 if (!mFace) {
114 NS_WARNING("failed to create freetype face");
117 } else {
118 mFace = Factory::NewFTFace(nullptr, aFontEntry->mFilename.get(), aFontEntry->mFTFontIndex);
119 if (!mFace) {
120 NS_WARNING("failed to create freetype face");
123 if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
124 NS_WARNING("failed to select Unicode charmap");
126 mOwnsFace = true;
129 ~AutoFTFace() {
130 if (mFace && mOwnsFace) {
131 Factory::ReleaseFTFace(mFace);
132 if (mFontDataBuf) {
133 free(mFontDataBuf);
138 operator FT_Face() { return mFace; }
140 // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
141 // we do -not- free the mFontDataBuf (if used); that also becomes the
142 // responsibility of the new owner of the face.
143 FT_Face forget() {
144 NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
145 mOwnsFace = false;
146 return mFace;
149 const uint8_t* FontData() const { return mFontDataBuf; }
150 uint32_t DataLength() const { return mDataLength; }
152 private:
153 FT_Face mFace;
154 uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
155 // or null for fonts instantiated from a file.
156 // If non-null, this must survive as long as the
157 // FT_Face.
158 uint32_t mDataLength; // Size of mFontDataBuf, if present.
159 bool mOwnsFace;
163 * FT2FontEntry
164 * gfxFontEntry subclass corresponding to a specific face that can be
165 * rendered by freetype. This is associated with a face index in a
166 * file (normally a .ttf/.otf file holding a single face, but in principle
167 * there could be .ttc files with multiple faces).
168 * The FT2FontEntry can create the necessary FT_Face on demand, and can
169 * then create a Cairo font_face and scaled_font for drawing.
172 cairo_scaled_font_t *
173 FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
175 cairo_font_face_t *cairoFace = CairoFontFace(aStyle);
176 if (!cairoFace) {
177 return nullptr;
180 cairo_scaled_font_t *scaledFont = nullptr;
182 cairo_matrix_t sizeMatrix;
183 cairo_matrix_t identityMatrix;
185 // XXX deal with adjusted size
186 cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
187 cairo_matrix_init_identity(&identityMatrix);
189 cairo_font_options_t *fontOptions = cairo_font_options_create();
191 if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
192 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
195 scaledFont = cairo_scaled_font_create(cairoFace,
196 &sizeMatrix,
197 &identityMatrix, fontOptions);
198 cairo_font_options_destroy(fontOptions);
200 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
201 "Failed to make scaled font");
203 return scaledFont;
206 FT2FontEntry::~FT2FontEntry()
208 // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
209 mFTFace = nullptr;
211 #ifndef ANDROID
212 if (mFontFace) {
213 cairo_font_face_destroy(mFontFace);
214 mFontFace = nullptr;
216 #endif
219 gfxFontEntry*
220 FT2FontEntry::Clone() const
222 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
223 FT2FontEntry* fe = new FT2FontEntry(Name());
224 fe->mFilename = mFilename;
225 fe->mFTFontIndex = mFTFontIndex;
226 fe->mWeight = mWeight;
227 fe->mStretch = mStretch;
228 fe->mStyle = mStyle;
229 return fe;
232 gfxFont*
233 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
235 cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
236 if (!scaledFont) {
237 return nullptr;
240 RefPtr<UnscaledFontFreeType> unscaledFont(mUnscaledFont);
241 if (!unscaledFont) {
242 unscaledFont =
243 mFilename.IsEmpty() ?
244 new UnscaledFontFreeType(mFTFace) :
245 new UnscaledFontFreeType(mFilename.BeginReading(),
246 mFTFontIndex);
247 mUnscaledFont = unscaledFont;
250 gfxFont *font = new gfxFT2Font(unscaledFont, scaledFont, this,
251 aFontStyle, aNeedsBold);
252 cairo_scaled_font_destroy(scaledFont);
253 return font;
256 /* static */
257 FT2FontEntry*
258 FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
259 uint16_t aWeight,
260 int16_t aStretch,
261 uint8_t aStyle,
262 const uint8_t* aFontData,
263 uint32_t aLength)
265 // Ownership of aFontData is passed in here; the fontEntry must
266 // retain it as long as the FT_Face needs it, and ensure it is
267 // eventually deleted.
268 FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
269 if (!face) {
270 free((void*)aFontData);
271 return nullptr;
273 if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
274 Factory::ReleaseFTFace(face);
275 free((void*)aFontData);
276 return nullptr;
278 // Create our FT2FontEntry, which inherits the name of the userfont entry
279 // as it's not guaranteed that the face has valid names (bug 737315)
280 FT2FontEntry* fe =
281 FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
282 aFontData, aLength);
283 if (fe) {
284 fe->mStyle = aStyle;
285 fe->mWeight = aWeight;
286 fe->mStretch = aStretch;
287 fe->mIsDataUserFont = true;
289 return fe;
292 class FTUserFontData {
293 public:
294 FTUserFontData(FT_Face aFace, const uint8_t* aData, uint32_t aLength)
295 : mFace(aFace), mFontData(aData), mLength(aLength)
299 ~FTUserFontData()
301 Factory::ReleaseFTFace(mFace);
302 if (mFontData) {
303 free((void*)mFontData);
307 const uint8_t *FontData() const { return mFontData; }
308 uint32_t Length() const { return mLength; }
310 private:
311 FT_Face mFace;
312 const uint8_t *mFontData;
313 uint32_t mLength;
316 static void
317 FTFontDestroyFunc(void *data)
319 FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
320 delete userFontData;
323 /* static */
324 FT2FontEntry*
325 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
327 FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
328 fe->mFilename = aFLE.filepath();
329 fe->mFTFontIndex = aFLE.index();
330 fe->mWeight = aFLE.weight();
331 fe->mStretch = aFLE.stretch();
332 fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
333 return fe;
336 // Helpers to extract font entry properties from an FT_Face
337 static bool
338 FTFaceIsItalic(FT_Face aFace)
340 return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
343 static uint16_t
344 FTFaceGetWeight(FT_Face aFace)
346 TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
347 uint16_t os2weight = 0;
348 if (os2 && os2->version != 0xffff) {
349 // Technically, only 100 to 900 are valid, but some fonts
350 // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
351 // it set to 6 instead of 600. We try to be nice and handle that
352 // as well.
353 if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
354 os2weight = os2->usWeightClass;
355 } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
356 os2weight = os2->usWeightClass * 100;
360 uint16_t result;
361 if (os2weight != 0) {
362 result = os2weight;
363 } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
364 result = 700;
365 } else {
366 result = 400;
369 NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
371 return result;
374 // Used to create the font entry for installed faces on the device,
375 // when iterating over the fonts directories.
376 // We use the FT_Face to retrieve the details needed for the font entry,
377 // but unless we have been passed font data (i.e. for a user font),
378 // we do *not* save a reference to it, nor create a cairo face,
379 // as we don't want to keep a freetype face for every installed font
380 // permanently in memory.
381 /* static */
382 FT2FontEntry*
383 FT2FontEntry::CreateFontEntry(FT_Face aFace,
384 const char* aFilename, uint8_t aIndex,
385 const nsAString& aName,
386 const uint8_t* aFontData,
387 uint32_t aLength)
389 FT2FontEntry *fe = new FT2FontEntry(aName);
390 fe->mStyle = (FTFaceIsItalic(aFace) ?
391 NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
392 fe->mWeight = FTFaceGetWeight(aFace);
393 fe->mFilename = aFilename;
394 fe->mFTFontIndex = aIndex;
396 if (aFontData) {
397 fe->mFTFace = aFace;
398 int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
399 FT_LOAD_DEFAULT :
400 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
401 fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags, nullptr, 0);
402 FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData, aLength);
403 cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
404 userFontData, FTFontDestroyFunc);
407 return fe;
410 // construct font entry name for an installed font from names in the FT_Face,
411 // and then create our FT2FontEntry
412 static FT2FontEntry*
413 CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
415 if (!aFace->family_name) {
416 return nullptr;
418 nsAutoString fontName;
419 AppendUTF8toUTF16(aFace->family_name, fontName);
420 if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
421 fontName.Append(' ');
422 AppendUTF8toUTF16(aFace->style_name, fontName);
424 return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
427 FT2FontEntry*
428 gfxFT2Font::GetFontEntry()
430 return static_cast<FT2FontEntry*> (mFontEntry.get());
433 cairo_font_face_t *
434 FT2FontEntry::CairoFontFace(const gfxFontStyle* aStyle)
436 // Create our basic (no-variations) mFontFace if not already present;
437 // this also ensures we have mFTFace available.
438 if (!mFontFace) {
439 AutoFTFace face(this);
440 if (!face) {
441 return nullptr;
443 int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
444 FT_LOAD_DEFAULT :
445 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
446 mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags,
447 nullptr, 0);
448 auto userFontData = new FTUserFontData(face, face.FontData(),
449 face.DataLength());
450 cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
451 userFontData, FTFontDestroyFunc);
452 mFTFace = face.forget();
455 // If variations are present, we will not use our cached mFontFace
456 // but always create a new cairo_font_face_t because its FT_Face will
457 // have custom variation coordinates applied.
458 if ((!mVariationSettings.IsEmpty() ||
459 (aStyle && !aStyle->variationSettings.IsEmpty())) &&
460 (mFTFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
461 int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
462 FT_LOAD_DEFAULT :
463 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
464 // Resolve variations from entry (descriptor) and style (property)
465 const nsTArray<gfxFontVariation>* settings;
466 AutoTArray<gfxFontVariation,8> mergedSettings;
467 if (aStyle) {
468 if (mVariationSettings.IsEmpty()) {
469 settings = &aStyle->variationSettings;
470 } else {
471 gfxFontUtils::MergeVariations(mVariationSettings,
472 aStyle->variationSettings,
473 &mergedSettings);
474 settings = &mergedSettings;
476 } else {
477 settings = &mVariationSettings;
479 AutoTArray<FT_Fixed,8> coords;
480 gfxFT2FontBase::SetupVarCoords(mFTFace, *settings, &coords);
481 // Create a separate FT_Face because we need to apply custom
482 // variation settings to it.
483 FT_Face ftFace;
484 if (!mFilename.IsEmpty()) {
485 ftFace = Factory::NewFTFace(nullptr, mFilename.get(), mFTFontIndex);
486 } else {
487 auto ufd = reinterpret_cast<FTUserFontData*>(
488 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
489 ftFace = Factory::NewFTFaceFromData(nullptr, ufd->FontData(),
490 ufd->Length(), mFTFontIndex);
492 // The variation coordinates will actually be applied to ftFace by
493 // gfxFT2FontBase::InitMetrics, so we don't need to do it here.
494 cairo_font_face_t* cairoFace =
495 cairo_ft_font_face_create_for_ft_face(ftFace, flags,
496 coords.Elements(),
497 coords.Length());
498 // Set up user data to properly release the FT_Face when the cairo face
499 // is deleted.
500 static cairo_user_data_key_t sDestroyFaceKey;
501 if (cairo_font_face_set_user_data(cairoFace, &sDestroyFaceKey, ftFace,
502 (cairo_destroy_func_t)&Factory::ReleaseFTFace)) {
503 // set_user_data failed! discard, and fall back to default face
504 cairo_font_face_destroy(cairoFace);
505 FT_Done_Face(ftFace);
506 } else {
507 return cairoFace;
511 return mFontFace;
514 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
515 // Complex scripts will not render correctly unless Graphite or OT
516 // layout tables are present.
517 // For OpenType, we also check that the GSUB table supports the relevant
518 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
519 // the characters, but lacks OpenType support).
521 // TODO: consider whether we should move this to gfxFontEntry and do similar
522 // cmap-masking on all platforms to avoid using fonts that won't shape
523 // properly.
525 nsresult
526 FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
528 if (mCharacterMap) {
529 return NS_OK;
532 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
534 AutoTArray<uint8_t, 16384> buffer;
535 nsresult rv = CopyFontTable(TTAG_cmap, buffer);
537 if (NS_SUCCEEDED(rv)) {
538 rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
539 *charmap, mUVSOffset);
542 if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) {
543 // For downloadable fonts, trust the author and don't
544 // try to munge the cmap based on script shaping support.
546 // We also assume a Graphite font knows what it's doing,
547 // and provides whatever shaping is needed for the
548 // characters it supports, so only check/clear the
549 // complex-script ranges for non-Graphite fonts
551 // for layout support, check for the presence of opentype layout tables
552 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
554 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
555 sr->rangeStart; sr++) {
556 // check to see if the cmap includes complex script codepoints
557 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
558 // We check for GSUB here, as GPOS alone would not be ok.
559 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
560 continue;
562 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
567 #ifdef MOZ_WIDGET_ANDROID
568 // Hack for the SamsungDevanagari font, bug 1012365:
569 // pretend the font supports U+0972.
570 if (!charmap->test(0x0972) &&
571 charmap->test(0x0905) && charmap->test(0x0945)) {
572 charmap->set(0x0972);
574 #endif
576 mHasCmapTable = NS_SUCCEEDED(rv);
577 if (mHasCmapTable) {
578 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
579 mCharacterMap = pfl->FindCharMap(charmap);
580 } else {
581 // if error occurred, initialize to null cmap
582 mCharacterMap = new gfxCharacterMap();
584 return rv;
587 nsresult
588 FT2FontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
590 AutoFTFace face(this);
591 if (!face) {
592 return NS_ERROR_FAILURE;
595 FT_Error status;
596 FT_ULong len = 0;
597 status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
598 if (status != FT_Err_Ok || len == 0) {
599 return NS_ERROR_FAILURE;
602 if (!aBuffer.SetLength(len, fallible)) {
603 return NS_ERROR_OUT_OF_MEMORY;
605 uint8_t *buf = aBuffer.Elements();
606 status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
607 NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
609 return NS_OK;
612 hb_blob_t*
613 FT2FontEntry::GetFontTable(uint32_t aTableTag)
615 if (mFontFace) {
616 // if there's a cairo font face, we may be able to return a blob
617 // that just wraps a range of the attached user font data
618 FTUserFontData *userFontData = static_cast<FTUserFontData*>(
619 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
620 if (userFontData && userFontData->FontData()) {
621 return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
622 aTableTag);
626 // otherwise, use the default method (which in turn will call our
627 // implementation of CopyFontTable)
628 return gfxFontEntry::GetFontTable(aTableTag);
631 void
632 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
633 FontListSizes* aSizes) const
635 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
636 aSizes->mFontListSize +=
637 mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
640 void
641 FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
642 FontListSizes* aSizes) const
644 aSizes->mFontListSize += aMallocSizeOf(this);
645 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
649 * FT2FontFamily
650 * A standard gfxFontFamily; just adds a method used to support sending
651 * the font list from chrome to content via IPC.
654 void
655 FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
657 for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
658 const FT2FontEntry *fe =
659 static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
660 if (!fe) {
661 continue;
664 aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
665 fe->mFilename,
666 fe->Weight(), fe->Stretch(),
667 fe->mStyle,
668 fe->mFTFontIndex));
673 * Startup cache support for the font list:
674 * We store the list of families and faces, with their style attributes and the
675 * corresponding font files, in the startup cache.
676 * This allows us to recreate the gfxFT2FontList collection of families and
677 * faces without instantiating Freetype faces for each font file (in order to
678 * find their attributes), leading to significantly quicker startup.
681 #define CACHE_KEY "font.cached-list"
683 class FontNameCache {
684 public:
685 // Creates the object but does NOT load the cached data from the startup
686 // cache; call Init() after creation to do that.
687 FontNameCache()
688 : mMap(&mOps, sizeof(FNCMapEntry), 0)
689 , mWriteNeeded(false)
691 // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
692 // it to |mMap|'s constructor. A more normal approach here would be to
693 // have a static |sOps| member. Unfortunately, this mysteriously but
694 // consistently makes Fennec start-up slower, so we take this
695 // unorthodox approach instead. It's safe because PLDHashTable's
696 // constructor doesn't dereference the pointer; it just makes a copy of
697 // it.
698 mOps = (PLDHashTableOps) {
699 StringHash,
700 HashMatchEntry,
701 MoveEntry,
702 PLDHashTable::ClearEntryStub,
703 nullptr
706 MOZ_ASSERT(XRE_IsParentProcess(),
707 "FontNameCache should only be used in chrome process");
708 mCache = mozilla::scache::StartupCache::GetSingleton();
711 ~FontNameCache()
713 if (!mWriteNeeded || !mCache) {
714 return;
717 nsAutoCString buf;
718 for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
719 auto entry = static_cast<FNCMapEntry*>(iter.Get());
720 if (!entry->mFileExists) {
721 // skip writing entries for files that are no longer present
722 continue;
724 buf.Append(entry->mFilename);
725 buf.Append(';');
726 buf.Append(entry->mFaces);
727 buf.Append(';');
728 buf.AppendInt(entry->mTimestamp);
729 buf.Append(';');
730 buf.AppendInt(entry->mFilesize);
731 buf.Append(';');
734 mCache->PutBuffer(CACHE_KEY,
735 UniquePtr<char[]>(ToNewCString(buf)), buf.Length() + 1);
738 // This may be called more than once (if we re-load the font list).
739 void Init()
741 if (!mCache) {
742 return;
745 uint32_t size;
746 UniquePtr<char[]> buf;
747 if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
748 return;
751 LOG(("got: %s from the cache", nsDependentCString(buf.get(), size).get()));
753 mMap.Clear();
754 mWriteNeeded = false;
756 const char* beginning = buf.get();
757 const char* end = strchr(beginning, ';');
758 while (end) {
759 nsCString filename(beginning, end - beginning);
760 beginning = end + 1;
761 if (!(end = strchr(beginning, ';'))) {
762 break;
764 nsCString faceList(beginning, end - beginning);
765 beginning = end + 1;
766 if (!(end = strchr(beginning, ';'))) {
767 break;
769 uint32_t timestamp = strtoul(beginning, nullptr, 10);
770 beginning = end + 1;
771 if (!(end = strchr(beginning, ';'))) {
772 break;
774 uint32_t filesize = strtoul(beginning, nullptr, 10);
776 auto mapEntry =
777 static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible));
778 if (mapEntry) {
779 mapEntry->mFilename.Assign(filename);
780 mapEntry->mTimestamp = timestamp;
781 mapEntry->mFilesize = filesize;
782 mapEntry->mFaces.Assign(faceList);
783 // entries from the startupcache are marked "non-existing"
784 // until we have confirmed that the file still exists
785 mapEntry->mFileExists = false;
788 beginning = end + 1;
789 end = strchr(beginning, ';');
793 void
794 GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
795 uint32_t *aTimestamp, uint32_t *aFilesize)
797 auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
798 if (entry) {
799 *aTimestamp = entry->mTimestamp;
800 *aFilesize = entry->mFilesize;
801 aFaceList.Assign(entry->mFaces);
802 // this entry does correspond to an existing file
803 // (although it might not be up-to-date, in which case
804 // it will get overwritten via CacheFileInfo)
805 entry->mFileExists = true;
809 void
810 CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
811 uint32_t aTimestamp, uint32_t aFilesize)
813 auto entry =
814 static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
815 if (entry) {
816 entry->mFilename.Assign(aFileName);
817 entry->mTimestamp = aTimestamp;
818 entry->mFilesize = aFilesize;
819 entry->mFaces.Assign(aFaceList);
820 entry->mFileExists = true;
822 mWriteNeeded = true;
825 private:
826 mozilla::scache::StartupCache* mCache;
827 PLDHashTable mMap;
828 bool mWriteNeeded;
830 PLDHashTableOps mOps;
832 typedef struct : public PLDHashEntryHdr {
833 public:
834 nsCString mFilename;
835 uint32_t mTimestamp;
836 uint32_t mFilesize;
837 nsCString mFaces;
838 bool mFileExists;
839 } FNCMapEntry;
841 static PLDHashNumber StringHash(const void *key)
843 return HashString(reinterpret_cast<const char*>(key));
846 static bool HashMatchEntry(const PLDHashEntryHdr *aHdr, const void *key)
848 const FNCMapEntry* entry =
849 static_cast<const FNCMapEntry*>(aHdr);
850 return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
853 static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
854 PLDHashEntryHdr *aTo)
856 FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
857 const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
858 to->mFilename.Assign(from->mFilename);
859 to->mTimestamp = from->mTimestamp;
860 to->mFilesize = from->mFilesize;
861 to->mFaces.Assign(from->mFaces);
862 to->mFileExists = from->mFileExists;
866 /***************************************************************
868 * gfxFT2FontList
872 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
873 // scanning the system's Fonts directory for OpenType and TrueType files.
875 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
877 class WillShutdownObserver : public nsIObserver
879 public:
880 NS_DECL_ISUPPORTS
881 NS_DECL_NSIOBSERVER
883 explicit WillShutdownObserver(gfxFT2FontList* aFontList)
884 : mFontList(aFontList)
887 protected:
888 virtual ~WillShutdownObserver()
891 gfxFT2FontList *mFontList;
894 NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
896 NS_IMETHODIMP
897 WillShutdownObserver::Observe(nsISupports *aSubject,
898 const char *aTopic,
899 const char16_t *aData)
901 if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
902 mFontList->WillShutdown();
903 } else {
904 NS_NOTREACHED("unexpected notification topic");
906 return NS_OK;
909 gfxFT2FontList::gfxFT2FontList()
910 : mJarModifiedTime(0)
912 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
913 if (obs) {
914 mObserver = new WillShutdownObserver(this);
915 obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
919 gfxFT2FontList::~gfxFT2FontList()
921 if (mObserver) {
922 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
923 if (obs) {
924 obs->RemoveObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
926 mObserver = nullptr;
930 void
931 gfxFT2FontList::AppendFacesFromCachedFaceList(
932 const nsCString& aFileName,
933 const nsCString& aFaceList,
934 StandardFile aStdFile)
936 const char *beginning = aFaceList.get();
937 const char *end = strchr(beginning, ',');
938 while (end) {
939 NS_ConvertUTF8toUTF16 familyName(beginning, end - beginning);
940 ToLowerCase(familyName);
941 beginning = end + 1;
942 if (!(end = strchr(beginning, ','))) {
943 break;
945 NS_ConvertUTF8toUTF16 faceName(beginning, end - beginning);
946 beginning = end + 1;
947 if (!(end = strchr(beginning, ','))) {
948 break;
950 uint32_t index = strtoul(beginning, nullptr, 10);
951 beginning = end + 1;
952 if (!(end = strchr(beginning, ','))) {
953 break;
955 bool italic = (*beginning != '0');
956 beginning = end + 1;
957 if (!(end = strchr(beginning, ','))) {
958 break;
960 uint32_t weight = strtoul(beginning, nullptr, 10);
961 beginning = end + 1;
962 if (!(end = strchr(beginning, ','))) {
963 break;
965 int32_t stretch = strtol(beginning, nullptr, 10);
967 FontListEntry fle(familyName, faceName, aFileName,
968 weight, stretch, italic, index);
969 AppendFaceFromFontListEntry(fle, aStdFile);
971 beginning = end + 1;
972 end = strchr(beginning, ',');
976 static void
977 AppendToFaceList(nsCString& aFaceList,
978 nsAString& aFamilyName, FT2FontEntry* aFontEntry)
980 aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
981 aFaceList.Append(',');
982 aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
983 aFaceList.Append(',');
984 aFaceList.AppendInt(aFontEntry->mFTFontIndex);
985 aFaceList.Append(',');
986 aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
987 aFaceList.Append(',');
988 aFaceList.AppendInt(aFontEntry->Weight());
989 aFaceList.Append(',');
990 aFaceList.AppendInt(aFontEntry->Stretch());
991 aFaceList.Append(',');
994 void
995 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
997 // note if the family is in the "bad underline" blacklist
998 if (aFamily->IsBadUnderlineFamily()) {
999 mIsBadUnderlineFont = true;
1002 // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
1003 // because of unwanted on-by-default "ae" ligature.
1004 // (See also AppendFaceFromFontListEntry.)
1005 if (aFamily->Name().EqualsLiteral("roboto")) {
1006 mIgnoreGSUB = true;
1009 // bug 706888 - set the IgnoreGSUB flag on the broken version of
1010 // Droid Sans Arabic from certain phones, as identified by the
1011 // font checksum in the 'head' table
1012 else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
1013 AutoFTFace face(this);
1014 if (face) {
1015 const TT_Header *head = static_cast<const TT_Header*>
1016 (FT_Get_Sfnt_Table(face, ft_sfnt_head));
1017 if (head && head->CheckSum_Adjust == 0xe445242) {
1018 mIgnoreGSUB = true;
1024 void
1025 gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
1026 FontNameCache *aCache,
1027 StandardFile aStdFile)
1029 nsCString cachedFaceList;
1030 uint32_t filesize = 0, timestamp = 0;
1031 if (aCache) {
1032 aCache->GetInfoForFile(aFileName, cachedFaceList, &timestamp, &filesize);
1035 struct stat s;
1036 int statRetval = stat(aFileName.get(), &s);
1037 if (!cachedFaceList.IsEmpty() && 0 == statRetval &&
1038 uint32_t(s.st_mtime) == timestamp && s.st_size == filesize)
1040 LOG(("using cached font info for %s", aFileName.get()));
1041 AppendFacesFromCachedFaceList(aFileName, cachedFaceList, aStdFile);
1042 return;
1045 FT_Face dummy = Factory::NewFTFace(nullptr, aFileName.get(), -1);
1046 if (dummy) {
1047 LOG(("reading font info via FreeType for %s", aFileName.get()));
1048 nsCString newFaceList;
1049 timestamp = s.st_mtime;
1050 filesize = s.st_size;
1051 for (FT_Long i = 0; i < dummy->num_faces; i++) {
1052 FT_Face face = Factory::NewFTFace(nullptr, aFileName.get(), i);
1053 if (!face) {
1054 continue;
1056 AddFaceToList(aFileName, i, aStdFile, face, newFaceList);
1057 Factory::ReleaseFTFace(face);
1059 Factory::ReleaseFTFace(dummy);
1060 if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
1061 aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
1066 void
1067 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
1069 bool jarChanged = false;
1071 mozilla::scache::StartupCache* cache =
1072 mozilla::scache::StartupCache::GetSingleton();
1073 UniquePtr<char[]> cachedModifiedTimeBuf;
1074 uint32_t longSize;
1075 if (cache &&
1076 NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
1077 &cachedModifiedTimeBuf,
1078 &longSize)) &&
1079 longSize == sizeof(int64_t))
1081 nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
1082 jarFile->GetLastModifiedTime(&mJarModifiedTime);
1083 if (mJarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
1084 jarChanged = true;
1088 static const char* sJarSearchPaths[] = {
1089 "res/fonts/*.ttf$",
1091 RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
1092 for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
1093 nsZipFind* find;
1094 if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
1095 const char* path;
1096 uint16_t len;
1097 while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
1098 nsCString entryName(path, len);
1099 AppendFacesFromOmnijarEntry(reader, entryName, aCache,
1100 jarChanged);
1102 delete find;
1107 // Given the freetype face corresponding to an entryName and face index,
1108 // add the face to the available font list and to the faceList string
1109 void
1110 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
1111 StandardFile aStdFile,
1112 FT_Face aFace,
1113 nsCString& aFaceList)
1115 if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
1116 // ignore faces that don't support a Unicode charmap
1117 return;
1120 // build the font entry name and create an FT2FontEntry,
1121 // but do -not- keep a reference to the FT_Face
1122 RefPtr<FT2FontEntry> fe =
1123 CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
1125 if (fe) {
1126 NS_ConvertUTF8toUTF16 name(aFace->family_name);
1127 BuildKeyNameFromFontName(name);
1128 RefPtr<gfxFontFamily> family = mFontFamilies.GetWeak(name);
1129 if (!family) {
1130 family = new FT2FontFamily(name);
1131 mFontFamilies.Put(name, family);
1132 if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
1133 family->SetSkipSpaceFeatureCheck(true);
1135 if (mBadUnderlineFamilyNames.Contains(name)) {
1136 family->SetBadUnderlineFamily();
1139 fe->mStandardFace = (aStdFile == kStandard);
1140 family->AddFontEntry(fe);
1142 fe->CheckForBrokenFont(family);
1144 AppendToFaceList(aFaceList, name, fe);
1145 if (LOG_ENABLED()) {
1146 LOG(("(fontinit) added (%s) to family (%s)"
1147 " with style: %s weight: %d stretch: %d",
1148 NS_ConvertUTF16toUTF8(fe->Name()).get(),
1149 NS_ConvertUTF16toUTF8(family->Name()).get(),
1150 fe->IsItalic() ? "italic" : "normal",
1151 fe->Weight(), fe->Stretch()));
1156 void
1157 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
1158 const nsCString& aEntryName,
1159 FontNameCache *aCache,
1160 bool aJarChanged)
1162 nsCString faceList;
1163 if (aCache && !aJarChanged) {
1164 uint32_t filesize, timestamp;
1165 aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
1166 if (faceList.Length() > 0) {
1167 AppendFacesFromCachedFaceList(aEntryName, faceList);
1168 return;
1172 nsZipItem *item = aArchive->GetItem(aEntryName.get());
1173 NS_ASSERTION(item, "failed to find zip entry");
1175 uint32_t bufSize = item->RealSize();
1176 // We use fallible allocation here; if there's not enough RAM, we'll simply
1177 // ignore the bundled fonts and fall back to the device's installed fonts.
1178 auto buf = MakeUniqueFallible<uint8_t[]>(bufSize);
1179 if (!buf) {
1180 return;
1183 nsZipCursor cursor(item, aArchive, buf.get(), bufSize);
1184 uint8_t* data = cursor.Copy(&bufSize);
1185 NS_ASSERTION(data && bufSize == item->RealSize(),
1186 "error reading bundled font");
1187 if (!data) {
1188 return;
1191 FT_Face dummy = Factory::NewFTFaceFromData(nullptr, buf.get(), bufSize, 0);
1192 if (!dummy) {
1193 return;
1196 for (FT_Long i = 0; i < dummy->num_faces; i++) {
1197 FT_Face face = Factory::NewFTFaceFromData(nullptr, buf.get(), bufSize, i);
1198 if (!face) {
1199 continue;
1201 AddFaceToList(aEntryName, i, kStandard, face, faceList);
1202 Factory::ReleaseFTFace(face);
1205 Factory::ReleaseFTFace(dummy);
1207 if (aCache && !faceList.IsEmpty()) {
1208 aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
1212 // Called on each family after all fonts are added to the list;
1213 // this will sort faces to give priority to "standard" font files
1214 // if aUserArg is non-null (i.e. we're using it as a boolean flag)
1215 static void
1216 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
1217 RefPtr<gfxFontFamily>& aFamily,
1218 bool aSortFaces)
1220 gfxFontFamily *family = aFamily.get();
1222 family->SetHasStyles(true);
1224 if (aSortFaces) {
1225 family->SortAvailableFonts();
1227 family->CheckForSimpleFamily();
1230 void
1231 gfxFT2FontList::FindFonts()
1233 gfxFontCache *fc = gfxFontCache::GetCache();
1234 if (fc)
1235 fc->AgeAllGenerations();
1236 ClearLangGroupPrefFonts();
1237 mCodepointsWithNoFonts.reset();
1239 mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
1240 mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
1242 if (!XRE_IsParentProcess()) {
1243 // Content process: ask the Chrome process to give us the list
1244 InfallibleTArray<FontListEntry> fonts;
1245 mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
1246 for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
1247 // We don't need to identify "standard" font files here,
1248 // as the faces are already sorted.
1249 AppendFaceFromFontListEntry(fonts[i], kUnknown);
1251 // Passing null for userdata tells Finalize that it does not need
1252 // to sort faces (because they were already sorted by chrome,
1253 // so we just maintain the existing order)
1254 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1255 nsStringHashKey::KeyType key = iter.Key();
1256 RefPtr<gfxFontFamily>& family = iter.Data();
1257 FinalizeFamilyMemberList(key, family, /* aSortFaces */ false);
1260 LOG(("got font list from chrome process: %" PRIdPTR " faces in %"
1261 PRIu32 " families",
1262 fonts.Length(), mFontFamilies.Count()));
1263 return;
1266 // Chrome process: get the cached list (if any)
1267 if (!mFontNameCache) {
1268 mFontNameCache = MakeUnique<FontNameCache>();
1270 mFontNameCache->Init();
1272 // ANDROID_ROOT is the root of the android system, typically /system;
1273 // font files are in /$ANDROID_ROOT/fonts/
1274 nsCString root;
1275 char *androidRoot = PR_GetEnv("ANDROID_ROOT");
1276 if (androidRoot) {
1277 root = androidRoot;
1278 } else {
1279 root = NS_LITERAL_CSTRING("/system");
1281 root.AppendLiteral("/fonts");
1283 FindFontsInDir(root, mFontNameCache.get());
1285 if (mFontFamilies.Count() == 0) {
1286 // if we can't find/read the font directory, we are doomed!
1287 MOZ_CRASH("Could not read the system fonts directory");
1290 // Look for fonts stored in omnijar, unless we're on a low-memory
1291 // device where we don't want to spend the RAM to decompress them.
1292 // (Prefs may disable this, or force-enable it even with low memory.)
1293 bool lowmem;
1294 nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
1295 if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
1296 Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
1297 Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
1298 FindFontsInOmnijar(mFontNameCache.get());
1301 // Look for downloaded fonts in a profile-agnostic "fonts" directory.
1302 nsCOMPtr<nsIProperties> dirSvc =
1303 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1304 if (dirSvc) {
1305 nsCOMPtr<nsIFile> appDir;
1306 nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
1307 NS_GET_IID(nsIFile), getter_AddRefs(appDir));
1308 if (NS_SUCCEEDED(rv)) {
1309 appDir->AppendNative(NS_LITERAL_CSTRING("fonts"));
1310 nsCString localPath;
1311 if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
1312 FindFontsInDir(localPath, mFontNameCache.get());
1317 // look for locally-added fonts in a "fonts" subdir of the profile
1318 nsCOMPtr<nsIFile> localDir;
1319 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1320 getter_AddRefs(localDir));
1321 if (NS_SUCCEEDED(rv) &&
1322 NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1323 nsCString localPath;
1324 rv = localDir->GetNativePath(localPath);
1325 if (NS_SUCCEEDED(rv)) {
1326 FindFontsInDir(localPath, mFontNameCache.get());
1330 // Finalize the families by sorting faces into standard order
1331 // and marking "simple" families.
1332 // Passing non-null userData here says that we want faces to be sorted.
1333 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1334 nsStringHashKey::KeyType key = iter.Key();
1335 RefPtr<gfxFontFamily>& family = iter.Data();
1336 FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
1340 void
1341 gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
1342 FontNameCache *aFNC)
1344 static const char* sStandardFonts[] = {
1345 "DroidSans.ttf",
1346 "DroidSans-Bold.ttf",
1347 "DroidSerif-Regular.ttf",
1348 "DroidSerif-Bold.ttf",
1349 "DroidSerif-Italic.ttf",
1350 "DroidSerif-BoldItalic.ttf",
1351 "DroidSansMono.ttf",
1352 "DroidSansArabic.ttf",
1353 "DroidSansHebrew.ttf",
1354 "DroidSansThai.ttf",
1355 "MTLmr3m.ttf",
1356 "MTLc3m.ttf",
1357 "NanumGothic.ttf",
1358 "DroidSansJapanese.ttf",
1359 "DroidSansFallback.ttf"
1362 DIR *d = opendir(aDir.get());
1363 if (!d) {
1364 return;
1367 struct dirent *ent = nullptr;
1368 while ((ent = readdir(d)) != nullptr) {
1369 const char *ext = strrchr(ent->d_name, '.');
1370 if (!ext) {
1371 continue;
1373 if (strcasecmp(ext, ".ttf") == 0 ||
1374 strcasecmp(ext, ".otf") == 0 ||
1375 strcasecmp(ext, ".woff") == 0 ||
1376 strcasecmp(ext, ".ttc") == 0) {
1377 bool isStdFont = false;
1378 for (unsigned int i = 0;
1379 i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
1380 isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
1383 nsCString s(aDir);
1384 s.Append('/');
1385 s.Append(ent->d_name);
1387 // Add the face(s) from this file to our font list;
1388 // note that if we have cached info for this file in fnc,
1389 // and the file is unchanged, we won't actually need to read it.
1390 // If the file is new/changed, this will update the FontNameCache.
1391 AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown);
1395 closedir(d);
1398 void
1399 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
1400 StandardFile aStdFile)
1402 FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
1403 if (fe) {
1404 fe->mStandardFace = (aStdFile == kStandard);
1405 nsAutoString name(aFLE.familyName());
1406 RefPtr<gfxFontFamily> family = mFontFamilies.GetWeak(name);
1407 if (!family) {
1408 family = new FT2FontFamily(name);
1409 mFontFamilies.Put(name, family);
1410 if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
1411 family->SetSkipSpaceFeatureCheck(true);
1413 if (mBadUnderlineFamilyNames.Contains(name)) {
1414 family->SetBadUnderlineFamily();
1417 family->AddFontEntry(fe);
1419 fe->CheckForBrokenFont(family);
1423 void
1424 gfxFT2FontList::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
1426 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1427 auto family = static_cast<FT2FontFamily*>(iter.Data().get());
1428 family->AddFacesToFontList(retValue);
1432 static void
1433 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
1435 AutoTArray<nsString, 5> skiplist;
1436 gfxFontUtils::GetPrefsFontList(
1437 "font.whitelist.skip_default_features_space_check",
1438 skiplist);
1439 uint32_t numFonts = skiplist.Length();
1440 for (uint32_t i = 0; i < numFonts; i++) {
1441 ToLowerCase(skiplist[i]);
1442 aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
1446 nsresult
1447 gfxFT2FontList::InitFontListForPlatform()
1449 LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
1451 FindFonts();
1453 return NS_OK;
1456 // called for each family name, based on the assumption that the
1457 // first part of the full name is the family name
1459 gfxFontEntry*
1460 gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
1461 uint16_t aWeight,
1462 int16_t aStretch,
1463 uint8_t aStyle)
1465 // walk over list of names
1466 FT2FontEntry* fontEntry = nullptr;
1467 nsString fullName(aFontName);
1469 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1470 // Check family name, based on the assumption that the
1471 // first part of the full name is the family name
1472 RefPtr<gfxFontFamily>& fontFamily = iter.Data();
1474 // does the family name match up to the length of the family name?
1475 const nsString& family = fontFamily->Name();
1476 nsString fullNameFamily;
1478 fullName.Left(fullNameFamily, family.Length());
1480 // if so, iterate over faces in this family to see if there is a match
1481 if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
1482 nsTArray<RefPtr<gfxFontEntry> >& fontList = fontFamily->GetFontList();
1483 int index, len = fontList.Length();
1484 for (index = 0; index < len; index++) {
1485 gfxFontEntry* fe = fontList[index];
1486 if (!fe) {
1487 continue;
1489 if (fe->Name().Equals(fullName,
1490 nsCaseInsensitiveStringComparator())) {
1491 fontEntry = static_cast<FT2FontEntry*>(fe);
1492 goto searchDone;
1498 searchDone:
1499 if (!fontEntry) {
1500 return nullptr;
1503 // Clone the font entry so that we can then set its style descriptors
1504 // from the userfont entry rather than the actual font.
1506 // Ensure existence of mFTFace in the original entry
1507 fontEntry->CairoFontFace();
1508 if (!fontEntry->mFTFace) {
1509 return nullptr;
1512 FT2FontEntry* fe =
1513 FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
1514 fontEntry->mFilename.get(),
1515 fontEntry->mFTFontIndex,
1516 fontEntry->Name(), nullptr);
1517 if (fe) {
1518 fe->mStyle = aStyle;
1519 fe->mWeight = aWeight;
1520 fe->mStretch = aStretch;
1521 fe->mIsLocalUserFont = true;
1524 return fe;
1527 gfxFontFamily*
1528 gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
1530 gfxFontFamily *ff = nullptr;
1531 #if defined(MOZ_WIDGET_ANDROID)
1532 ff = FindFamily(NS_LITERAL_STRING("Roboto"));
1533 if (!ff) {
1534 ff = FindFamily(NS_LITERAL_STRING("Droid Sans"));
1536 #endif
1537 /* TODO: what about Qt or other platforms that may use this? */
1538 return ff;
1541 gfxFontEntry*
1542 gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
1543 uint16_t aWeight,
1544 int16_t aStretch,
1545 uint8_t aStyle,
1546 const uint8_t* aFontData,
1547 uint32_t aLength)
1549 // The FT2 font needs the font data to persist, so we do NOT free it here
1550 // but instead pass ownership to the font entry.
1551 // Deallocation will happen later, when the font face is destroyed.
1552 return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
1553 aStyle, aFontData, aLength);
1556 void
1557 gfxFT2FontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
1559 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1560 RefPtr<gfxFontFamily>& family = iter.Data();
1561 aFamilyArray.AppendElement(family);
1565 gfxFontFamily*
1566 gfxFT2FontList::CreateFontFamily(const nsAString& aName) const
1568 return new FT2FontFamily(aName);
1571 void
1572 gfxFT2FontList::WillShutdown()
1574 mozilla::scache::StartupCache* cache =
1575 mozilla::scache::StartupCache::GetSingleton();
1576 if (cache && mJarModifiedTime > 0) {
1577 const size_t bufSize = sizeof(mJarModifiedTime);
1578 auto buf = MakeUnique<char[]>(bufSize);
1579 memcpy(buf.get(), &mJarModifiedTime, bufSize);
1581 cache->PutBuffer(JAR_LAST_MODIFED_TIME,
1582 Move(buf), bufSize);
1584 mFontNameCache = nullptr;