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"
20 #include <android/log.h>
21 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
24 #include FT_FREETYPE_H
25 #include FT_TRUETYPE_TAGS_H
26 #include FT_TRUETYPE_TABLES_H
27 #include FT_MULTIPLE_MASTERS_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"
39 #include "nsUnicharUtils.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"
55 using namespace mozilla
;
56 using namespace mozilla::gfx
;
58 static LazyLogModule
sFontInfoLog("fontInfoLog");
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
;
67 BuildKeyNameFromFontName(nsAString
&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
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.
81 explicit AutoFTFace(FT2FontEntry
* aFontEntry
)
82 : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
84 if (aFontEntry
->mFTFace
) {
85 mFace
= aFontEntry
->mFTFace
;
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
));
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
);
114 NS_WARNING("failed to create freetype face");
118 mFace
= Factory::NewFTFace(nullptr, aFontEntry
->mFilename
.get(), aFontEntry
->mFTFontIndex
);
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");
130 if (mFace
&& mOwnsFace
) {
131 Factory::ReleaseFTFace(mFace
);
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.
144 NS_ASSERTION(mOwnsFace
, "can't forget() when we didn't own the face");
149 const uint8_t* FontData() const { return mFontDataBuf
; }
150 uint32_t DataLength() const { return mDataLength
; }
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
158 uint32_t mDataLength
; // Size of mFontDataBuf, if present.
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
);
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
,
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");
206 FT2FontEntry::~FT2FontEntry()
208 // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
213 cairo_font_face_destroy(mFontFace
);
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
;
233 FT2FontEntry::CreateFontInstance(const gfxFontStyle
*aFontStyle
, bool aNeedsBold
)
235 cairo_scaled_font_t
*scaledFont
= CreateScaledFont(aFontStyle
);
240 RefPtr
<UnscaledFontFreeType
> unscaledFont(mUnscaledFont
);
243 mFilename
.IsEmpty() ?
244 new UnscaledFontFreeType(mFTFace
) :
245 new UnscaledFontFreeType(mFilename
.BeginReading(),
247 mUnscaledFont
= unscaledFont
;
250 gfxFont
*font
= new gfxFT2Font(unscaledFont
, scaledFont
, this,
251 aFontStyle
, aNeedsBold
);
252 cairo_scaled_font_destroy(scaledFont
);
258 FT2FontEntry::CreateFontEntry(const nsAString
& aFontName
,
262 const uint8_t* aFontData
,
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);
270 free((void*)aFontData
);
273 if (FT_Err_Ok
!= FT_Select_Charmap(face
, FT_ENCODING_UNICODE
)) {
274 Factory::ReleaseFTFace(face
);
275 free((void*)aFontData
);
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)
281 FT2FontEntry::CreateFontEntry(face
, nullptr, 0, aFontName
,
285 fe
->mWeight
= aWeight
;
286 fe
->mStretch
= aStretch
;
287 fe
->mIsDataUserFont
= true;
292 class FTUserFontData
{
294 FTUserFontData(FT_Face aFace
, const uint8_t* aData
, uint32_t aLength
)
295 : mFace(aFace
), mFontData(aData
), mLength(aLength
)
301 Factory::ReleaseFTFace(mFace
);
303 free((void*)mFontData
);
307 const uint8_t *FontData() const { return mFontData
; }
308 uint32_t Length() const { return mLength
; }
312 const uint8_t *mFontData
;
317 FTFontDestroyFunc(void *data
)
319 FTUserFontData
*userFontData
= static_cast<FTUserFontData
*>(data
);
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
);
336 // Helpers to extract font entry properties from an FT_Face
338 FTFaceIsItalic(FT_Face aFace
)
340 return !!(aFace
->style_flags
& FT_STYLE_FLAG_ITALIC
);
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
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;
361 if (os2weight
!= 0) {
363 } else if (aFace
->style_flags
& FT_STYLE_FLAG_BOLD
) {
369 NS_ASSERTION(result
>= 100 && result
<= 900, "Invalid weight in font!");
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.
383 FT2FontEntry::CreateFontEntry(FT_Face aFace
,
384 const char* aFilename
, uint8_t aIndex
,
385 const nsAString
& aName
,
386 const uint8_t* aFontData
,
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
;
398 int flags
= gfxPlatform::GetPlatform()->FontHintingEnabled() ?
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
);
410 // construct font entry name for an installed font from names in the FT_Face,
411 // and then create our FT2FontEntry
413 CreateNamedFontEntry(FT_Face aFace
, const char* aFilename
, uint8_t aIndex
)
415 if (!aFace
->family_name
) {
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
);
428 gfxFT2Font::GetFontEntry()
430 return static_cast<FT2FontEntry
*> (mFontEntry
.get());
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.
439 AutoFTFace
face(this);
443 int flags
= gfxPlatform::GetPlatform()->FontHintingEnabled() ?
445 (FT_LOAD_NO_AUTOHINT
| FT_LOAD_NO_HINTING
);
446 mFontFace
= cairo_ft_font_face_create_for_ft_face(face
, flags
,
448 auto userFontData
= new FTUserFontData(face
, face
.FontData(),
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() ?
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
;
468 if (mVariationSettings
.IsEmpty()) {
469 settings
= &aStyle
->variationSettings
;
471 gfxFontUtils::MergeVariations(mVariationSettings
,
472 aStyle
->variationSettings
,
474 settings
= &mergedSettings
;
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.
484 if (!mFilename
.IsEmpty()) {
485 ftFace
= Factory::NewFTFace(nullptr, mFilename
.get(), mFTFontIndex
);
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
,
498 // Set up user data to properly release the FT_Face when the cairo face
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
);
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
526 FT2FontEntry::ReadCMAP(FontInfoData
*aFontInfoData
)
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
)) {
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);
576 mHasCmapTable
= NS_SUCCEEDED(rv
);
578 gfxPlatformFontList
*pfl
= gfxPlatformFontList::PlatformFontList();
579 mCharacterMap
= pfl
->FindCharMap(charmap
);
581 // if error occurred, initialize to null cmap
582 mCharacterMap
= new gfxCharacterMap();
588 FT2FontEntry::CopyFontTable(uint32_t aTableTag
, nsTArray
<uint8_t>& aBuffer
)
590 AutoFTFace
face(this);
592 return NS_ERROR_FAILURE
;
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
);
613 FT2FontEntry::GetFontTable(uint32_t aTableTag
)
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(),
626 // otherwise, use the default method (which in turn will call our
627 // implementation of CopyFontTable)
628 return gfxFontEntry::GetFontTable(aTableTag
);
632 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
633 FontListSizes
* aSizes
) const
635 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
636 aSizes
->mFontListSize
+=
637 mFilename
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
641 FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
642 FontListSizes
* aSizes
) const
644 aSizes
->mFontListSize
+= aMallocSizeOf(this);
645 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
650 * A standard gfxFontFamily; just adds a method used to support sending
651 * the font list from chrome to content via IPC.
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());
664 aFontList
->AppendElement(FontListEntry(Name(), fe
->Name(),
666 fe
->Weight(), fe
->Stretch(),
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
{
685 // Creates the object but does NOT load the cached data from the startup
686 // cache; call Init() after creation to do that.
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
698 mOps
= (PLDHashTableOps
) {
702 PLDHashTable::ClearEntryStub
,
706 MOZ_ASSERT(XRE_IsParentProcess(),
707 "FontNameCache should only be used in chrome process");
708 mCache
= mozilla::scache::StartupCache::GetSingleton();
713 if (!mWriteNeeded
|| !mCache
) {
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
724 buf
.Append(entry
->mFilename
);
726 buf
.Append(entry
->mFaces
);
728 buf
.AppendInt(entry
->mTimestamp
);
730 buf
.AppendInt(entry
->mFilesize
);
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).
746 UniquePtr
<char[]> buf
;
747 if (NS_FAILED(mCache
->GetBuffer(CACHE_KEY
, &buf
, &size
))) {
751 LOG(("got: %s from the cache", nsDependentCString(buf
.get(), size
).get()));
754 mWriteNeeded
= false;
756 const char* beginning
= buf
.get();
757 const char* end
= strchr(beginning
, ';');
759 nsCString
filename(beginning
, end
- beginning
);
761 if (!(end
= strchr(beginning
, ';'))) {
764 nsCString
faceList(beginning
, end
- beginning
);
766 if (!(end
= strchr(beginning
, ';'))) {
769 uint32_t timestamp
= strtoul(beginning
, nullptr, 10);
771 if (!(end
= strchr(beginning
, ';'))) {
774 uint32_t filesize
= strtoul(beginning
, nullptr, 10);
777 static_cast<FNCMapEntry
*>(mMap
.Add(filename
.get(), fallible
));
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;
789 end
= strchr(beginning
, ';');
794 GetInfoForFile(const nsCString
& aFileName
, nsCString
& aFaceList
,
795 uint32_t *aTimestamp
, uint32_t *aFilesize
)
797 auto entry
= static_cast<FNCMapEntry
*>(mMap
.Search(aFileName
.get()));
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;
810 CacheFileInfo(const nsCString
& aFileName
, const nsCString
& aFaceList
,
811 uint32_t aTimestamp
, uint32_t aFilesize
)
814 static_cast<FNCMapEntry
*>(mMap
.Add(aFileName
.get(), fallible
));
816 entry
->mFilename
.Assign(aFileName
);
817 entry
->mTimestamp
= aTimestamp
;
818 entry
->mFilesize
= aFilesize
;
819 entry
->mFaces
.Assign(aFaceList
);
820 entry
->mFileExists
= true;
826 mozilla::scache::StartupCache
* mCache
;
830 PLDHashTableOps mOps
;
832 typedef struct : public PLDHashEntryHdr
{
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 /***************************************************************
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
883 explicit WillShutdownObserver(gfxFT2FontList
* aFontList
)
884 : mFontList(aFontList
)
888 virtual ~WillShutdownObserver()
891 gfxFT2FontList
*mFontList
;
894 NS_IMPL_ISUPPORTS(WillShutdownObserver
, nsIObserver
)
897 WillShutdownObserver::Observe(nsISupports
*aSubject
,
899 const char16_t
*aData
)
901 if (!nsCRT::strcmp(aTopic
, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
)) {
902 mFontList
->WillShutdown();
904 NS_NOTREACHED("unexpected notification topic");
909 gfxFT2FontList::gfxFT2FontList()
910 : mJarModifiedTime(0)
912 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
914 mObserver
= new WillShutdownObserver(this);
915 obs
->AddObserver(mObserver
, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
, false);
919 gfxFT2FontList::~gfxFT2FontList()
922 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
924 obs
->RemoveObserver(mObserver
, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
);
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
, ',');
939 NS_ConvertUTF8toUTF16
familyName(beginning
, end
- beginning
);
940 ToLowerCase(familyName
);
942 if (!(end
= strchr(beginning
, ','))) {
945 NS_ConvertUTF8toUTF16
faceName(beginning
, end
- beginning
);
947 if (!(end
= strchr(beginning
, ','))) {
950 uint32_t index
= strtoul(beginning
, nullptr, 10);
952 if (!(end
= strchr(beginning
, ','))) {
955 bool italic
= (*beginning
!= '0');
957 if (!(end
= strchr(beginning
, ','))) {
960 uint32_t weight
= strtoul(beginning
, nullptr, 10);
962 if (!(end
= strchr(beginning
, ','))) {
965 int32_t stretch
= strtol(beginning
, nullptr, 10);
967 FontListEntry
fle(familyName
, faceName
, aFileName
,
968 weight
, stretch
, italic
, index
);
969 AppendFaceFromFontListEntry(fle
, aStdFile
);
972 end
= strchr(beginning
, ',');
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(',');
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")) {
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);
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) {
1025 gfxFT2FontList::AppendFacesFromFontFile(const nsCString
& aFileName
,
1026 FontNameCache
*aCache
,
1027 StandardFile aStdFile
)
1029 nsCString cachedFaceList
;
1030 uint32_t filesize
= 0, timestamp
= 0;
1032 aCache
->GetInfoForFile(aFileName
, cachedFaceList
, ×tamp
, &filesize
);
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
);
1045 FT_Face dummy
= Factory::NewFTFace(nullptr, aFileName
.get(), -1);
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
);
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
);
1067 gfxFT2FontList::FindFontsInOmnijar(FontNameCache
*aCache
)
1069 bool jarChanged
= false;
1071 mozilla::scache::StartupCache
* cache
=
1072 mozilla::scache::StartupCache::GetSingleton();
1073 UniquePtr
<char[]> cachedModifiedTimeBuf
;
1076 NS_SUCCEEDED(cache
->GetBuffer(JAR_LAST_MODIFED_TIME
,
1077 &cachedModifiedTimeBuf
,
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()) {
1088 static const char* sJarSearchPaths
[] = {
1091 RefPtr
<nsZipArchive
> reader
= Omnijar::GetReader(Omnijar::Type::GRE
);
1092 for (unsigned i
= 0; i
< ArrayLength(sJarSearchPaths
); i
++) {
1094 if (NS_SUCCEEDED(reader
->FindInit(sJarSearchPaths
[i
], &find
))) {
1097 while (NS_SUCCEEDED(find
->FindNext(&path
, &len
))) {
1098 nsCString
entryName(path
, len
);
1099 AppendFacesFromOmnijarEntry(reader
, entryName
, aCache
,
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
1110 gfxFT2FontList::AddFaceToList(const nsCString
& aEntryName
, uint32_t aIndex
,
1111 StandardFile aStdFile
,
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
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
);
1126 NS_ConvertUTF8toUTF16
name(aFace
->family_name
);
1127 BuildKeyNameFromFontName(name
);
1128 RefPtr
<gfxFontFamily
> family
= mFontFamilies
.GetWeak(name
);
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()));
1157 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive
* aArchive
,
1158 const nsCString
& aEntryName
,
1159 FontNameCache
*aCache
,
1163 if (aCache
&& !aJarChanged
) {
1164 uint32_t filesize
, timestamp
;
1165 aCache
->GetInfoForFile(aEntryName
, faceList
, ×tamp
, &filesize
);
1166 if (faceList
.Length() > 0) {
1167 AppendFacesFromCachedFaceList(aEntryName
, faceList
);
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
);
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");
1191 FT_Face dummy
= Factory::NewFTFaceFromData(nullptr, buf
.get(), bufSize
, 0);
1196 for (FT_Long i
= 0; i
< dummy
->num_faces
; i
++) {
1197 FT_Face face
= Factory::NewFTFaceFromData(nullptr, buf
.get(), bufSize
, i
);
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)
1216 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey
,
1217 RefPtr
<gfxFontFamily
>& aFamily
,
1220 gfxFontFamily
*family
= aFamily
.get();
1222 family
->SetHasStyles(true);
1225 family
->SortAvailableFonts();
1227 family
->CheckForSimpleFamily();
1231 gfxFT2FontList::FindFonts()
1233 gfxFontCache
*fc
= gfxFontCache::GetCache();
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 %"
1262 fonts
.Length(), mFontFamilies
.Count()));
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/
1275 char *androidRoot
= PR_GetEnv("ANDROID_ROOT");
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.)
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
);
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);
1341 gfxFT2FontList::FindFontsInDir(const nsCString
& aDir
,
1342 FontNameCache
*aFNC
)
1344 static const char* sStandardFonts
[] = {
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",
1358 "DroidSansJapanese.ttf",
1359 "DroidSansFallback.ttf"
1362 DIR *d
= opendir(aDir
.get());
1367 struct dirent
*ent
= nullptr;
1368 while ((ent
= readdir(d
)) != nullptr) {
1369 const char *ext
= strrchr(ent
->d_name
, '.');
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;
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
);
1399 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry
& aFLE
,
1400 StandardFile aStdFile
)
1402 FT2FontEntry
* fe
= FT2FontEntry::CreateFontEntry(aFLE
);
1404 fe
->mStandardFace
= (aStdFile
== kStandard
);
1405 nsAutoString
name(aFLE
.familyName());
1406 RefPtr
<gfxFontFamily
> family
= mFontFamilies
.GetWeak(name
);
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
);
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
);
1433 LoadSkipSpaceLookupCheck(nsTHashtable
<nsStringHashKey
>& aSkipSpaceLookupCheck
)
1435 AutoTArray
<nsString
, 5> skiplist
;
1436 gfxFontUtils::GetPrefsFontList(
1437 "font.whitelist.skip_default_features_space_check",
1439 uint32_t numFonts
= skiplist
.Length();
1440 for (uint32_t i
= 0; i
< numFonts
; i
++) {
1441 ToLowerCase(skiplist
[i
]);
1442 aSkipSpaceLookupCheck
.PutEntry(skiplist
[i
]);
1447 gfxFT2FontList::InitFontListForPlatform()
1449 LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies
);
1456 // called for each family name, based on the assumption that the
1457 // first part of the full name is the family name
1460 gfxFT2FontList::LookupLocalFont(const nsAString
& aFontName
,
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
];
1489 if (fe
->Name().Equals(fullName
,
1490 nsCaseInsensitiveStringComparator())) {
1491 fontEntry
= static_cast<FT2FontEntry
*>(fe
);
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
) {
1513 FT2FontEntry::CreateFontEntry(fontEntry
->mFTFace
,
1514 fontEntry
->mFilename
.get(),
1515 fontEntry
->mFTFontIndex
,
1516 fontEntry
->Name(), nullptr);
1518 fe
->mStyle
= aStyle
;
1519 fe
->mWeight
= aWeight
;
1520 fe
->mStretch
= aStretch
;
1521 fe
->mIsLocalUserFont
= true;
1528 gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle
* aStyle
)
1530 gfxFontFamily
*ff
= nullptr;
1531 #if defined(MOZ_WIDGET_ANDROID)
1532 ff
= FindFamily(NS_LITERAL_STRING("Roboto"));
1534 ff
= FindFamily(NS_LITERAL_STRING("Droid Sans"));
1537 /* TODO: what about Qt or other platforms that may use this? */
1542 gfxFT2FontList::MakePlatformFont(const nsAString
& aFontName
,
1546 const uint8_t* aFontData
,
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
);
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
);
1566 gfxFT2FontList::CreateFontFamily(const nsAString
& aName
) const
1568 return new FT2FontFamily(aName
);
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;