1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxFT2FontList.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1544 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/ArrayUtils.h" 1.10 +#include "mozilla/MemoryReporting.h" 1.11 + 1.12 +#if (MOZ_WIDGET_GTK == 2) 1.13 +#include "gfxPlatformGtk.h" 1.14 +#define gfxToolkitPlatform gfxPlatformGtk 1.15 +#elif defined(MOZ_WIDGET_QT) 1.16 +#include <qfontinfo.h> 1.17 +#include "gfxQtPlatform.h" 1.18 +#define gfxToolkitPlatform gfxQtPlatform 1.19 +#elif defined(XP_WIN) 1.20 +#include "gfxWindowsPlatform.h" 1.21 +#define gfxToolkitPlatform gfxWindowsPlatform 1.22 +#elif defined(ANDROID) 1.23 +#include "mozilla/dom/ContentChild.h" 1.24 +#include "gfxAndroidPlatform.h" 1.25 +#include "mozilla/Omnijar.h" 1.26 +#include "nsIInputStream.h" 1.27 +#include "nsNetUtil.h" 1.28 +#define gfxToolkitPlatform gfxAndroidPlatform 1.29 +#endif 1.30 + 1.31 +#ifdef ANDROID 1.32 +#include "nsXULAppAPI.h" 1.33 +#include <dirent.h> 1.34 +#include <android/log.h> 1.35 +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args) 1.36 +#endif 1.37 + 1.38 +#include "ft2build.h" 1.39 +#include FT_FREETYPE_H 1.40 +#include FT_TRUETYPE_TAGS_H 1.41 +#include FT_TRUETYPE_TABLES_H 1.42 +#include "cairo-ft.h" 1.43 + 1.44 +#include "gfxFT2FontList.h" 1.45 +#include "gfxFT2Fonts.h" 1.46 +#include "gfxUserFontSet.h" 1.47 +#include "gfxFontUtils.h" 1.48 + 1.49 +#include "nsServiceManagerUtils.h" 1.50 +#include "nsTArray.h" 1.51 +#include "nsUnicharUtils.h" 1.52 + 1.53 +#include "nsDirectoryServiceUtils.h" 1.54 +#include "nsDirectoryServiceDefs.h" 1.55 +#include "nsAppDirectoryServiceDefs.h" 1.56 +#include "nsISimpleEnumerator.h" 1.57 +#include "nsIMemory.h" 1.58 +#include "gfxFontConstants.h" 1.59 + 1.60 +#include "mozilla/Preferences.h" 1.61 +#include "mozilla/scache/StartupCache.h" 1.62 +#include <sys/stat.h> 1.63 + 1.64 +#ifdef XP_WIN 1.65 +#include "nsIWindowsRegKey.h" 1.66 +#include <windows.h> 1.67 +#endif 1.68 + 1.69 +using namespace mozilla; 1.70 + 1.71 +#ifdef PR_LOGGING 1.72 +static PRLogModuleInfo * 1.73 +GetFontInfoLog() 1.74 +{ 1.75 + static PRLogModuleInfo *sLog; 1.76 + if (!sLog) 1.77 + sLog = PR_NewLogModule("fontInfoLog"); 1.78 + return sLog; 1.79 +} 1.80 +#endif /* PR_LOGGING */ 1.81 + 1.82 +#undef LOG 1.83 +#define LOG(args) PR_LOG(GetFontInfoLog(), PR_LOG_DEBUG, args) 1.84 +#define LOG_ENABLED() PR_LOG_TEST(GetFontInfoLog(), PR_LOG_DEBUG) 1.85 + 1.86 +static cairo_user_data_key_t sFTUserFontDataKey; 1.87 + 1.88 +static __inline void 1.89 +BuildKeyNameFromFontName(nsAString &aName) 1.90 +{ 1.91 +#ifdef XP_WIN 1.92 + if (aName.Length() >= LF_FACESIZE) 1.93 + aName.Truncate(LF_FACESIZE - 1); 1.94 +#endif 1.95 + ToLowerCase(aName); 1.96 +} 1.97 + 1.98 +// Helper to access the FT_Face for a given FT2FontEntry, 1.99 +// creating a temporary face if the entry does not have one yet. 1.100 +// This allows us to read font names, tables, etc if necessary 1.101 +// without permanently instantiating a freetype face and consuming 1.102 +// memory long-term. 1.103 +// This may fail (resulting in a null FT_Face), e.g. if it fails to 1.104 +// allocate memory to uncompress a font from omnijar. 1.105 +class AutoFTFace { 1.106 +public: 1.107 + AutoFTFace(FT2FontEntry* aFontEntry) 1.108 + : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false) 1.109 + { 1.110 + if (aFontEntry->mFTFace) { 1.111 + mFace = aFontEntry->mFTFace; 1.112 + return; 1.113 + } 1.114 + 1.115 + NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(), 1.116 + "can't use AutoFTFace for fonts without a filename"); 1.117 + FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary(); 1.118 + 1.119 + // A relative path (no initial "/") means this is a resource in 1.120 + // omnijar, not an installed font on the device. 1.121 + // The NS_ASSERTIONs here should never fail, as the resource must have 1.122 + // been read successfully during font-list initialization or we'd never 1.123 + // have created the font entry. The only legitimate runtime failure 1.124 + // here would be memory allocation, in which case mFace remains null. 1.125 + if (aFontEntry->mFilename[0] != '/') { 1.126 + nsRefPtr<nsZipArchive> reader = 1.127 + Omnijar::GetReader(Omnijar::Type::GRE); 1.128 + nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get()); 1.129 + NS_ASSERTION(item, "failed to find zip entry"); 1.130 + 1.131 + uint32_t bufSize = item->RealSize(); 1.132 + mFontDataBuf = static_cast<uint8_t*>(moz_malloc(bufSize)); 1.133 + if (mFontDataBuf) { 1.134 + nsZipCursor cursor(item, reader, mFontDataBuf, bufSize); 1.135 + cursor.Copy(&bufSize); 1.136 + NS_ASSERTION(bufSize == item->RealSize(), 1.137 + "error reading bundled font"); 1.138 + 1.139 + if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize, 1.140 + aFontEntry->mFTFontIndex, 1.141 + &mFace)) { 1.142 + NS_WARNING("failed to create freetype face"); 1.143 + } 1.144 + } 1.145 + } else { 1.146 + if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(), 1.147 + aFontEntry->mFTFontIndex, &mFace)) { 1.148 + NS_WARNING("failed to create freetype face"); 1.149 + } 1.150 + } 1.151 + if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) { 1.152 + NS_WARNING("failed to select Unicode charmap"); 1.153 + } 1.154 + mOwnsFace = true; 1.155 + } 1.156 + 1.157 + ~AutoFTFace() { 1.158 + if (mFace && mOwnsFace) { 1.159 + FT_Done_Face(mFace); 1.160 + if (mFontDataBuf) { 1.161 + moz_free(mFontDataBuf); 1.162 + } 1.163 + } 1.164 + } 1.165 + 1.166 + operator FT_Face() { return mFace; } 1.167 + 1.168 + // If we 'forget' the FT_Face (used when ownership is handed over to Cairo), 1.169 + // we do -not- free the mFontDataBuf (if used); that also becomes the 1.170 + // responsibility of the new owner of the face. 1.171 + FT_Face forget() { 1.172 + NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face"); 1.173 + mOwnsFace = false; 1.174 + return mFace; 1.175 + } 1.176 + 1.177 + const uint8_t* FontData() const { return mFontDataBuf; } 1.178 + 1.179 +private: 1.180 + FT_Face mFace; 1.181 + uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR), 1.182 + // or null for fonts instantiated from a file. 1.183 + // If non-null, this must survive as long as the 1.184 + // FT_Face. 1.185 + bool mOwnsFace; 1.186 +}; 1.187 + 1.188 +/* 1.189 + * FT2FontEntry 1.190 + * gfxFontEntry subclass corresponding to a specific face that can be 1.191 + * rendered by freetype. This is associated with a face index in a 1.192 + * file (normally a .ttf/.otf file holding a single face, but in principle 1.193 + * there could be .ttc files with multiple faces). 1.194 + * The FT2FontEntry can create the necessary FT_Face on demand, and can 1.195 + * then create a Cairo font_face and scaled_font for drawing. 1.196 + */ 1.197 + 1.198 +cairo_scaled_font_t * 1.199 +FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle) 1.200 +{ 1.201 + cairo_font_face_t *cairoFace = CairoFontFace(); 1.202 + if (!cairoFace) { 1.203 + return nullptr; 1.204 + } 1.205 + 1.206 + cairo_scaled_font_t *scaledFont = nullptr; 1.207 + 1.208 + cairo_matrix_t sizeMatrix; 1.209 + cairo_matrix_t identityMatrix; 1.210 + 1.211 + // XXX deal with adjusted size 1.212 + cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size); 1.213 + cairo_matrix_init_identity(&identityMatrix); 1.214 + 1.215 + // synthetic oblique by skewing via the font matrix 1.216 + bool needsOblique = !IsItalic() && 1.217 + (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)); 1.218 + 1.219 + if (needsOblique) { 1.220 + const double kSkewFactor = 0.25; 1.221 + 1.222 + cairo_matrix_t style; 1.223 + cairo_matrix_init(&style, 1.224 + 1, //xx 1.225 + 0, //yx 1.226 + -1 * kSkewFactor, //xy 1.227 + 1, //yy 1.228 + 0, //x0 1.229 + 0); //y0 1.230 + cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); 1.231 + } 1.232 + 1.233 + cairo_font_options_t *fontOptions = cairo_font_options_create(); 1.234 + 1.235 + if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) { 1.236 + cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF); 1.237 + } 1.238 + 1.239 + scaledFont = cairo_scaled_font_create(cairoFace, 1.240 + &sizeMatrix, 1.241 + &identityMatrix, fontOptions); 1.242 + cairo_font_options_destroy(fontOptions); 1.243 + 1.244 + NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS, 1.245 + "Failed to make scaled font"); 1.246 + 1.247 + return scaledFont; 1.248 +} 1.249 + 1.250 +FT2FontEntry::~FT2FontEntry() 1.251 +{ 1.252 + // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo. 1.253 + mFTFace = nullptr; 1.254 + 1.255 +#ifndef ANDROID 1.256 + if (mFontFace) { 1.257 + cairo_font_face_destroy(mFontFace); 1.258 + mFontFace = nullptr; 1.259 + } 1.260 +#endif 1.261 +} 1.262 + 1.263 +gfxFont* 1.264 +FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) 1.265 +{ 1.266 + cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle); 1.267 + if (!scaledFont) { 1.268 + return nullptr; 1.269 + } 1.270 + gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold); 1.271 + cairo_scaled_font_destroy(scaledFont); 1.272 + return font; 1.273 +} 1.274 + 1.275 +/* static */ 1.276 +FT2FontEntry* 1.277 +FT2FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, 1.278 + const uint8_t *aFontData, 1.279 + uint32_t aLength) 1.280 +{ 1.281 + // Ownership of aFontData is passed in here; the fontEntry must 1.282 + // retain it as long as the FT_Face needs it, and ensure it is 1.283 + // eventually deleted. 1.284 + FT_Face face; 1.285 + FT_Error error = 1.286 + FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), 1.287 + aFontData, aLength, 0, &face); 1.288 + if (error != FT_Err_Ok) { 1.289 + NS_Free((void*)aFontData); 1.290 + return nullptr; 1.291 + } 1.292 + if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { 1.293 + FT_Done_Face(face); 1.294 + NS_Free((void*)aFontData); 1.295 + return nullptr; 1.296 + } 1.297 + // Create our FT2FontEntry, which inherits the name of the proxy 1.298 + // as it's not guaranteed that the face has valid names (bug 737315) 1.299 + FT2FontEntry* fe = 1.300 + FT2FontEntry::CreateFontEntry(face, nullptr, 0, aProxyEntry.Name(), 1.301 + aFontData); 1.302 + if (fe) { 1.303 + fe->mItalic = aProxyEntry.mItalic; 1.304 + fe->mWeight = aProxyEntry.mWeight; 1.305 + fe->mStretch = aProxyEntry.mStretch; 1.306 + fe->mIsUserFont = true; 1.307 + } 1.308 + return fe; 1.309 +} 1.310 + 1.311 +class FTUserFontData { 1.312 +public: 1.313 + FTUserFontData(FT_Face aFace, const uint8_t* aData) 1.314 + : mFace(aFace), mFontData(aData) 1.315 + { 1.316 + } 1.317 + 1.318 + ~FTUserFontData() 1.319 + { 1.320 + FT_Done_Face(mFace); 1.321 + if (mFontData) { 1.322 + NS_Free((void*)mFontData); 1.323 + } 1.324 + } 1.325 + 1.326 + const uint8_t *FontData() const { return mFontData; } 1.327 + 1.328 +private: 1.329 + FT_Face mFace; 1.330 + const uint8_t *mFontData; 1.331 +}; 1.332 + 1.333 +static void 1.334 +FTFontDestroyFunc(void *data) 1.335 +{ 1.336 + FTUserFontData *userFontData = static_cast<FTUserFontData*>(data); 1.337 + delete userFontData; 1.338 +} 1.339 + 1.340 +/* static */ 1.341 +FT2FontEntry* 1.342 +FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE) 1.343 +{ 1.344 + FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName()); 1.345 + fe->mFilename = aFLE.filepath(); 1.346 + fe->mFTFontIndex = aFLE.index(); 1.347 + fe->mWeight = aFLE.weight(); 1.348 + fe->mStretch = aFLE.stretch(); 1.349 + fe->mItalic = aFLE.italic(); 1.350 + return fe; 1.351 +} 1.352 + 1.353 +// Helpers to extract font entry properties from an FT_Face 1.354 +static bool 1.355 +FTFaceIsItalic(FT_Face aFace) 1.356 +{ 1.357 + return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC); 1.358 +} 1.359 + 1.360 +static uint16_t 1.361 +FTFaceGetWeight(FT_Face aFace) 1.362 +{ 1.363 + TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2)); 1.364 + uint16_t os2weight = 0; 1.365 + if (os2 && os2->version != 0xffff) { 1.366 + // Technically, only 100 to 900 are valid, but some fonts 1.367 + // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has 1.368 + // it set to 6 instead of 600. We try to be nice and handle that 1.369 + // as well. 1.370 + if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) { 1.371 + os2weight = os2->usWeightClass; 1.372 + } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) { 1.373 + os2weight = os2->usWeightClass * 100; 1.374 + } 1.375 + } 1.376 + 1.377 + uint16_t result; 1.378 + if (os2weight != 0) { 1.379 + result = os2weight; 1.380 + } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) { 1.381 + result = 700; 1.382 + } else { 1.383 + result = 400; 1.384 + } 1.385 + 1.386 + NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!"); 1.387 + 1.388 + return result; 1.389 +} 1.390 + 1.391 +// Used to create the font entry for installed faces on the device, 1.392 +// when iterating over the fonts directories. 1.393 +// We use the FT_Face to retrieve the details needed for the font entry, 1.394 +// but unless we have been passed font data (i.e. for a user font), 1.395 +// we do *not* save a reference to it, nor create a cairo face, 1.396 +// as we don't want to keep a freetype face for every installed font 1.397 +// permanently in memory. 1.398 +/* static */ 1.399 +FT2FontEntry* 1.400 +FT2FontEntry::CreateFontEntry(FT_Face aFace, 1.401 + const char* aFilename, uint8_t aIndex, 1.402 + const nsAString& aName, 1.403 + const uint8_t *aFontData) 1.404 +{ 1.405 + FT2FontEntry *fe = new FT2FontEntry(aName); 1.406 + fe->mItalic = FTFaceIsItalic(aFace); 1.407 + fe->mWeight = FTFaceGetWeight(aFace); 1.408 + fe->mFilename = aFilename; 1.409 + fe->mFTFontIndex = aIndex; 1.410 + 1.411 + if (aFontData) { 1.412 + fe->mFTFace = aFace; 1.413 + int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ? 1.414 + FT_LOAD_DEFAULT : 1.415 + (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING); 1.416 + fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags); 1.417 + FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData); 1.418 + cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey, 1.419 + userFontData, FTFontDestroyFunc); 1.420 + } 1.421 + 1.422 + return fe; 1.423 +} 1.424 + 1.425 +// construct font entry name for an installed font from names in the FT_Face, 1.426 +// and then create our FT2FontEntry 1.427 +static FT2FontEntry* 1.428 +CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex) 1.429 +{ 1.430 + if (!aFace->family_name) { 1.431 + return nullptr; 1.432 + } 1.433 + nsAutoString fontName; 1.434 + AppendUTF8toUTF16(aFace->family_name, fontName); 1.435 + if (aFace->style_name && strcmp("Regular", aFace->style_name)) { 1.436 + fontName.AppendLiteral(" "); 1.437 + AppendUTF8toUTF16(aFace->style_name, fontName); 1.438 + } 1.439 + return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName); 1.440 +} 1.441 + 1.442 +FT2FontEntry* 1.443 +gfxFT2Font::GetFontEntry() 1.444 +{ 1.445 + return static_cast<FT2FontEntry*> (mFontEntry.get()); 1.446 +} 1.447 + 1.448 +cairo_font_face_t * 1.449 +FT2FontEntry::CairoFontFace() 1.450 +{ 1.451 + if (!mFontFace) { 1.452 + AutoFTFace face(this); 1.453 + if (!face) { 1.454 + return nullptr; 1.455 + } 1.456 + mFTFace = face.forget(); 1.457 + int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ? 1.458 + FT_LOAD_DEFAULT : 1.459 + (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING); 1.460 + mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags); 1.461 + FTUserFontData *userFontData = new FTUserFontData(face, face.FontData()); 1.462 + cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey, 1.463 + userFontData, FTFontDestroyFunc); 1.464 + } 1.465 + return mFontFace; 1.466 +} 1.467 + 1.468 +// Copied/modified from similar code in gfxMacPlatformFontList.mm: 1.469 +// Complex scripts will not render correctly unless Graphite or OT 1.470 +// layout tables are present. 1.471 +// For OpenType, we also check that the GSUB table supports the relevant 1.472 +// script tag, to avoid using things like Arial Unicode MS for Lao (it has 1.473 +// the characters, but lacks OpenType support). 1.474 + 1.475 +// TODO: consider whether we should move this to gfxFontEntry and do similar 1.476 +// cmap-masking on all platforms to avoid using fonts that won't shape 1.477 +// properly. 1.478 + 1.479 +nsresult 1.480 +FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData) 1.481 +{ 1.482 + if (mCharacterMap) { 1.483 + return NS_OK; 1.484 + } 1.485 + 1.486 + nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); 1.487 + 1.488 + AutoFallibleTArray<uint8_t,16384> buffer; 1.489 + nsresult rv = CopyFontTable(TTAG_cmap, buffer); 1.490 + 1.491 + if (NS_SUCCEEDED(rv)) { 1.492 + bool unicodeFont; 1.493 + bool symbolFont; 1.494 + rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(), 1.495 + *charmap, mUVSOffset, 1.496 + unicodeFont, symbolFont); 1.497 + } 1.498 + 1.499 + if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) { 1.500 + // We assume a Graphite font knows what it's doing, 1.501 + // and provides whatever shaping is needed for the 1.502 + // characters it supports, so only check/clear the 1.503 + // complex-script ranges for non-Graphite fonts 1.504 + 1.505 + // for layout support, check for the presence of opentype layout tables 1.506 + bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B')); 1.507 + 1.508 + for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; 1.509 + sr->rangeStart; sr++) { 1.510 + // check to see if the cmap includes complex script codepoints 1.511 + if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { 1.512 + // We check for GSUB here, as GPOS alone would not be ok. 1.513 + if (hasGSUB && SupportsScriptInGSUB(sr->tags)) { 1.514 + continue; 1.515 + } 1.516 + charmap->ClearRange(sr->rangeStart, sr->rangeEnd); 1.517 + } 1.518 + } 1.519 + } 1.520 + 1.521 +#ifdef MOZ_WIDGET_ANDROID 1.522 + // Hack for the SamsungDevanagari font, bug 1012365: 1.523 + // pretend the font supports U+0972. 1.524 + if (!charmap->test(0x0972) && 1.525 + charmap->test(0x0905) && charmap->test(0x0945)) { 1.526 + charmap->set(0x0972); 1.527 + } 1.528 +#endif 1.529 + 1.530 + mHasCmapTable = NS_SUCCEEDED(rv); 1.531 + if (mHasCmapTable) { 1.532 + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); 1.533 + mCharacterMap = pfl->FindCharMap(charmap); 1.534 + } else { 1.535 + // if error occurred, initialize to null cmap 1.536 + mCharacterMap = new gfxCharacterMap(); 1.537 + } 1.538 + return rv; 1.539 +} 1.540 + 1.541 +nsresult 1.542 +FT2FontEntry::CopyFontTable(uint32_t aTableTag, 1.543 + FallibleTArray<uint8_t>& aBuffer) 1.544 +{ 1.545 + AutoFTFace face(this); 1.546 + if (!face) { 1.547 + return NS_ERROR_FAILURE; 1.548 + } 1.549 + 1.550 + FT_Error status; 1.551 + FT_ULong len = 0; 1.552 + status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len); 1.553 + if (status != FT_Err_Ok || len == 0) { 1.554 + return NS_ERROR_FAILURE; 1.555 + } 1.556 + 1.557 + if (!aBuffer.SetLength(len)) { 1.558 + return NS_ERROR_OUT_OF_MEMORY; 1.559 + } 1.560 + uint8_t *buf = aBuffer.Elements(); 1.561 + status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len); 1.562 + NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE); 1.563 + 1.564 + return NS_OK; 1.565 +} 1.566 + 1.567 +hb_blob_t* 1.568 +FT2FontEntry::GetFontTable(uint32_t aTableTag) 1.569 +{ 1.570 + if (mFontFace) { 1.571 + // if there's a cairo font face, we may be able to return a blob 1.572 + // that just wraps a range of the attached user font data 1.573 + FTUserFontData *userFontData = static_cast<FTUserFontData*>( 1.574 + cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey)); 1.575 + if (userFontData && userFontData->FontData()) { 1.576 + return GetTableFromFontData(userFontData->FontData(), aTableTag); 1.577 + } 1.578 + } 1.579 + 1.580 + // otherwise, use the default method (which in turn will call our 1.581 + // implementation of CopyFontTable) 1.582 + return gfxFontEntry::GetFontTable(aTableTag); 1.583 +} 1.584 + 1.585 +void 1.586 +FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 1.587 + FontListSizes* aSizes) const 1.588 +{ 1.589 + gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.590 + aSizes->mFontListSize += 1.591 + mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 1.592 +} 1.593 + 1.594 +void 1.595 +FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 1.596 + FontListSizes* aSizes) const 1.597 +{ 1.598 + aSizes->mFontListSize += aMallocSizeOf(this); 1.599 + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.600 +} 1.601 + 1.602 +/* 1.603 + * FT2FontFamily 1.604 + * A standard gfxFontFamily; just adds a method used to support sending 1.605 + * the font list from chrome to content via IPC. 1.606 + */ 1.607 + 1.608 +void 1.609 +FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList) 1.610 +{ 1.611 + for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) { 1.612 + const FT2FontEntry *fe = 1.613 + static_cast<const FT2FontEntry*>(mAvailableFonts[i].get()); 1.614 + if (!fe) { 1.615 + continue; 1.616 + } 1.617 + 1.618 + aFontList->AppendElement(FontListEntry(Name(), fe->Name(), 1.619 + fe->mFilename, 1.620 + fe->Weight(), fe->Stretch(), 1.621 + fe->IsItalic(), 1.622 + fe->mFTFontIndex)); 1.623 + } 1.624 +} 1.625 + 1.626 +/* 1.627 + * Startup cache support for the font list: 1.628 + * We store the list of families and faces, with their style attributes and the 1.629 + * corresponding font files, in the startup cache. 1.630 + * This allows us to recreate the gfxFT2FontList collection of families and 1.631 + * faces without instantiating Freetype faces for each font file (in order to 1.632 + * find their attributes), leading to significantly quicker startup. 1.633 + */ 1.634 + 1.635 +#define CACHE_KEY "font.cached-list" 1.636 + 1.637 +class FontNameCache { 1.638 +public: 1.639 + FontNameCache() 1.640 + : mWriteNeeded(false) 1.641 + { 1.642 + mOps = (PLDHashTableOps) { 1.643 + PL_DHashAllocTable, 1.644 + PL_DHashFreeTable, 1.645 + StringHash, 1.646 + HashMatchEntry, 1.647 + MoveEntry, 1.648 + PL_DHashClearEntryStub, 1.649 + PL_DHashFinalizeStub, 1.650 + nullptr 1.651 + }; 1.652 + 1.653 + PL_DHashTableInit(&mMap, &mOps, nullptr, sizeof(FNCMapEntry), 0); 1.654 + 1.655 + NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default, 1.656 + "StartupCacheFontNameCache should only be used in chrome process"); 1.657 + mCache = mozilla::scache::StartupCache::GetSingleton(); 1.658 + 1.659 + Init(); 1.660 + } 1.661 + 1.662 + ~FontNameCache() 1.663 + { 1.664 + if (!mMap.ops) { 1.665 + return; 1.666 + } 1.667 + if (!mWriteNeeded || !mCache) { 1.668 + PL_DHashTableFinish(&mMap); 1.669 + return; 1.670 + } 1.671 + 1.672 + nsAutoCString buf; 1.673 + PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf); 1.674 + PL_DHashTableFinish(&mMap); 1.675 + mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1); 1.676 + } 1.677 + 1.678 + void Init() 1.679 + { 1.680 + if (!mMap.ops || !mCache) { 1.681 + return; 1.682 + } 1.683 + uint32_t size; 1.684 + char* buf; 1.685 + if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) { 1.686 + return; 1.687 + } 1.688 + 1.689 + LOG(("got: %s from the cache", nsDependentCString(buf, size).get())); 1.690 + 1.691 + const char* beginning = buf; 1.692 + const char* end = strchr(beginning, ';'); 1.693 + while (end) { 1.694 + nsCString filename(beginning, end - beginning); 1.695 + beginning = end + 1; 1.696 + if (!(end = strchr(beginning, ';'))) { 1.697 + break; 1.698 + } 1.699 + nsCString faceList(beginning, end - beginning); 1.700 + beginning = end + 1; 1.701 + if (!(end = strchr(beginning, ';'))) { 1.702 + break; 1.703 + } 1.704 + uint32_t timestamp = strtoul(beginning, nullptr, 10); 1.705 + beginning = end + 1; 1.706 + if (!(end = strchr(beginning, ';'))) { 1.707 + break; 1.708 + } 1.709 + uint32_t filesize = strtoul(beginning, nullptr, 10); 1.710 + 1.711 + FNCMapEntry* mapEntry = 1.712 + static_cast<FNCMapEntry*> 1.713 + (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD)); 1.714 + if (mapEntry) { 1.715 + mapEntry->mFilename.Assign(filename); 1.716 + mapEntry->mTimestamp = timestamp; 1.717 + mapEntry->mFilesize = filesize; 1.718 + mapEntry->mFaces.Assign(faceList); 1.719 + // entries from the startupcache are marked "non-existing" 1.720 + // until we have confirmed that the file still exists 1.721 + mapEntry->mFileExists = false; 1.722 + } 1.723 + 1.724 + beginning = end + 1; 1.725 + end = strchr(beginning, ';'); 1.726 + } 1.727 + 1.728 + // Should we use free() or delete[] here? See bug 684700. 1.729 + free(buf); 1.730 + } 1.731 + 1.732 + virtual void 1.733 + GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList, 1.734 + uint32_t *aTimestamp, uint32_t *aFilesize) 1.735 + { 1.736 + if (!mMap.ops) { 1.737 + return; 1.738 + } 1.739 + PLDHashEntryHdr *hdr = 1.740 + PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP); 1.741 + if (!hdr) { 1.742 + return; 1.743 + } 1.744 + FNCMapEntry* entry = static_cast<FNCMapEntry*>(hdr); 1.745 + if (entry && entry->mFilesize) { 1.746 + *aTimestamp = entry->mTimestamp; 1.747 + *aFilesize = entry->mFilesize; 1.748 + aFaceList.Assign(entry->mFaces); 1.749 + // this entry does correspond to an existing file 1.750 + // (although it might not be up-to-date, in which case 1.751 + // it will get overwritten via CacheFileInfo) 1.752 + entry->mFileExists = true; 1.753 + } 1.754 + } 1.755 + 1.756 + virtual void 1.757 + CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList, 1.758 + uint32_t aTimestamp, uint32_t aFilesize) 1.759 + { 1.760 + if (!mMap.ops) { 1.761 + return; 1.762 + } 1.763 + FNCMapEntry* entry = 1.764 + static_cast<FNCMapEntry*> 1.765 + (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD)); 1.766 + if (entry) { 1.767 + entry->mFilename.Assign(aFileName); 1.768 + entry->mTimestamp = aTimestamp; 1.769 + entry->mFilesize = aFilesize; 1.770 + entry->mFaces.Assign(aFaceList); 1.771 + entry->mFileExists = true; 1.772 + } 1.773 + mWriteNeeded = true; 1.774 + } 1.775 + 1.776 +private: 1.777 + mozilla::scache::StartupCache* mCache; 1.778 + PLDHashTable mMap; 1.779 + bool mWriteNeeded; 1.780 + 1.781 + PLDHashTableOps mOps; 1.782 + 1.783 + static PLDHashOperator WriteOutMap(PLDHashTable *aTable, 1.784 + PLDHashEntryHdr *aHdr, 1.785 + uint32_t aNumber, void *aData) 1.786 + { 1.787 + FNCMapEntry* entry = static_cast<FNCMapEntry*>(aHdr); 1.788 + if (!entry->mFileExists) { 1.789 + // skip writing entries for files that are no longer present 1.790 + return PL_DHASH_NEXT; 1.791 + } 1.792 + 1.793 + nsAutoCString* buf = reinterpret_cast<nsAutoCString*>(aData); 1.794 + buf->Append(entry->mFilename); 1.795 + buf->Append(';'); 1.796 + buf->Append(entry->mFaces); 1.797 + buf->Append(';'); 1.798 + buf->AppendInt(entry->mTimestamp); 1.799 + buf->Append(';'); 1.800 + buf->AppendInt(entry->mFilesize); 1.801 + buf->Append(';'); 1.802 + return PL_DHASH_NEXT; 1.803 + } 1.804 + 1.805 + typedef struct : public PLDHashEntryHdr { 1.806 + public: 1.807 + nsCString mFilename; 1.808 + uint32_t mTimestamp; 1.809 + uint32_t mFilesize; 1.810 + nsCString mFaces; 1.811 + bool mFileExists; 1.812 + } FNCMapEntry; 1.813 + 1.814 + static PLDHashNumber StringHash(PLDHashTable *table, const void *key) 1.815 + { 1.816 + return HashString(reinterpret_cast<const char*>(key)); 1.817 + } 1.818 + 1.819 + static bool HashMatchEntry(PLDHashTable *table, 1.820 + const PLDHashEntryHdr *aHdr, const void *key) 1.821 + { 1.822 + const FNCMapEntry* entry = 1.823 + static_cast<const FNCMapEntry*>(aHdr); 1.824 + return entry->mFilename.Equals(reinterpret_cast<const char*>(key)); 1.825 + } 1.826 + 1.827 + static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom, 1.828 + PLDHashEntryHdr *aTo) 1.829 + { 1.830 + FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo); 1.831 + const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom); 1.832 + to->mFilename.Assign(from->mFilename); 1.833 + to->mTimestamp = from->mTimestamp; 1.834 + to->mFilesize = from->mFilesize; 1.835 + to->mFaces.Assign(from->mFaces); 1.836 + to->mFileExists = from->mFileExists; 1.837 + } 1.838 +}; 1.839 + 1.840 +/*************************************************************** 1.841 + * 1.842 + * gfxFT2FontList 1.843 + * 1.844 + */ 1.845 + 1.846 +// For Mobile, we use gfxFT2Fonts, and we build the font list by directly 1.847 +// scanning the system's Fonts directory for OpenType and TrueType files. 1.848 + 1.849 +gfxFT2FontList::gfxFT2FontList() 1.850 +{ 1.851 +} 1.852 + 1.853 +void 1.854 +gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName, 1.855 + bool aStdFile, 1.856 + const nsCString& aFaceList) 1.857 +{ 1.858 + const char *beginning = aFaceList.get(); 1.859 + const char *end = strchr(beginning, ','); 1.860 + while (end) { 1.861 + nsString familyName = 1.862 + NS_ConvertUTF8toUTF16(beginning, end - beginning); 1.863 + ToLowerCase(familyName); 1.864 + beginning = end + 1; 1.865 + if (!(end = strchr(beginning, ','))) { 1.866 + break; 1.867 + } 1.868 + nsString faceName = 1.869 + NS_ConvertUTF8toUTF16(beginning, end - beginning); 1.870 + beginning = end + 1; 1.871 + if (!(end = strchr(beginning, ','))) { 1.872 + break; 1.873 + } 1.874 + uint32_t index = strtoul(beginning, nullptr, 10); 1.875 + beginning = end + 1; 1.876 + if (!(end = strchr(beginning, ','))) { 1.877 + break; 1.878 + } 1.879 + bool italic = (*beginning != '0'); 1.880 + beginning = end + 1; 1.881 + if (!(end = strchr(beginning, ','))) { 1.882 + break; 1.883 + } 1.884 + uint32_t weight = strtoul(beginning, nullptr, 10); 1.885 + beginning = end + 1; 1.886 + if (!(end = strchr(beginning, ','))) { 1.887 + break; 1.888 + } 1.889 + int32_t stretch = strtol(beginning, nullptr, 10); 1.890 + 1.891 + FontListEntry fle(familyName, faceName, aFileName, 1.892 + weight, stretch, italic, index); 1.893 + AppendFaceFromFontListEntry(fle, aStdFile); 1.894 + 1.895 + beginning = end + 1; 1.896 + end = strchr(beginning, ','); 1.897 + } 1.898 +} 1.899 + 1.900 +static void 1.901 +AppendToFaceList(nsCString& aFaceList, 1.902 + nsAString& aFamilyName, FT2FontEntry* aFontEntry) 1.903 +{ 1.904 + aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName)); 1.905 + aFaceList.Append(','); 1.906 + aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name())); 1.907 + aFaceList.Append(','); 1.908 + aFaceList.AppendInt(aFontEntry->mFTFontIndex); 1.909 + aFaceList.Append(','); 1.910 + aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0'); 1.911 + aFaceList.Append(','); 1.912 + aFaceList.AppendInt(aFontEntry->Weight()); 1.913 + aFaceList.Append(','); 1.914 + aFaceList.AppendInt(aFontEntry->Stretch()); 1.915 + aFaceList.Append(','); 1.916 +} 1.917 + 1.918 +void 1.919 +FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily) 1.920 +{ 1.921 + // note if the family is in the "bad underline" blacklist 1.922 + if (aFamily->IsBadUnderlineFamily()) { 1.923 + mIsBadUnderlineFont = true; 1.924 + } 1.925 + 1.926 + // bug 721719 - set the IgnoreGSUB flag on entries for Roboto 1.927 + // because of unwanted on-by-default "ae" ligature. 1.928 + // (See also AppendFaceFromFontListEntry.) 1.929 + if (aFamily->Name().EqualsLiteral("roboto")) { 1.930 + mIgnoreGSUB = true; 1.931 + } 1.932 + 1.933 + // bug 706888 - set the IgnoreGSUB flag on the broken version of 1.934 + // Droid Sans Arabic from certain phones, as identified by the 1.935 + // font checksum in the 'head' table 1.936 + else if (aFamily->Name().EqualsLiteral("droid sans arabic")) { 1.937 + AutoFTFace face(this); 1.938 + if (face) { 1.939 + const TT_Header *head = static_cast<const TT_Header*> 1.940 + (FT_Get_Sfnt_Table(face, ft_sfnt_head)); 1.941 + if (head && head->CheckSum_Adjust == 0xe445242) { 1.942 + mIgnoreGSUB = true; 1.943 + } 1.944 + } 1.945 + } 1.946 +} 1.947 + 1.948 +void 1.949 +gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName, 1.950 + bool aStdFile, 1.951 + FontNameCache *aCache) 1.952 +{ 1.953 + nsCString faceList; 1.954 + uint32_t filesize = 0, timestamp = 0; 1.955 + if (aCache) { 1.956 + aCache->GetInfoForFile(aFileName, faceList, ×tamp, &filesize); 1.957 + } 1.958 + 1.959 + struct stat s; 1.960 + int statRetval = stat(aFileName.get(), &s); 1.961 + if (!faceList.IsEmpty() && 0 == statRetval && 1.962 + s.st_mtime == timestamp && s.st_size == filesize) 1.963 + { 1.964 + LOG(("using cached font info for %s", aFileName.get())); 1.965 + AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList); 1.966 + return; 1.967 + } 1.968 + 1.969 +#ifdef XP_WIN 1.970 + FT_Library ftLibrary = gfxWindowsPlatform::GetPlatform()->GetFTLibrary(); 1.971 +#elif defined(ANDROID) 1.972 + FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary(); 1.973 +#endif 1.974 + FT_Face dummy; 1.975 + if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) { 1.976 + LOG(("reading font info via FreeType for %s", aFileName.get())); 1.977 + nsCString faceList; 1.978 + timestamp = s.st_mtime; 1.979 + filesize = s.st_size; 1.980 + for (FT_Long i = 0; i < dummy->num_faces; i++) { 1.981 + FT_Face face; 1.982 + if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) { 1.983 + continue; 1.984 + } 1.985 + AddFaceToList(aFileName, i, aStdFile, face, faceList); 1.986 + FT_Done_Face(face); 1.987 + } 1.988 + FT_Done_Face(dummy); 1.989 + if (aCache && 0 == statRetval && !faceList.IsEmpty()) { 1.990 + aCache->CacheFileInfo(aFileName, faceList, timestamp, filesize); 1.991 + } 1.992 + } 1.993 +} 1.994 + 1.995 +#define JAR_LAST_MODIFED_TIME "jar-last-modified-time" 1.996 + 1.997 +void 1.998 +gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache) 1.999 +{ 1.1000 + bool jarChanged = false; 1.1001 + 1.1002 + mozilla::scache::StartupCache* cache = 1.1003 + mozilla::scache::StartupCache::GetSingleton(); 1.1004 + char *cachedModifiedTimeBuf; 1.1005 + uint32_t longSize; 1.1006 + int64_t jarModifiedTime; 1.1007 + if (cache && 1.1008 + NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, 1.1009 + &cachedModifiedTimeBuf, 1.1010 + &longSize)) && 1.1011 + longSize == sizeof(int64_t)) 1.1012 + { 1.1013 + nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE); 1.1014 + jarFile->GetLastModifiedTime(&jarModifiedTime); 1.1015 + if (jarModifiedTime > *(int64_t*)cachedModifiedTimeBuf) { 1.1016 + jarChanged = true; 1.1017 + } 1.1018 + } 1.1019 + 1.1020 + static const char* sJarSearchPaths[] = { 1.1021 + "res/fonts/*.ttf$", 1.1022 + }; 1.1023 + nsRefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE); 1.1024 + for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) { 1.1025 + nsZipFind* find; 1.1026 + if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) { 1.1027 + const char* path; 1.1028 + uint16_t len; 1.1029 + while (NS_SUCCEEDED(find->FindNext(&path, &len))) { 1.1030 + nsCString entryName(path, len); 1.1031 + AppendFacesFromOmnijarEntry(reader, entryName, aCache, 1.1032 + jarChanged); 1.1033 + } 1.1034 + delete find; 1.1035 + } 1.1036 + } 1.1037 + 1.1038 + if (cache) { 1.1039 + cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime, 1.1040 + sizeof(jarModifiedTime)); 1.1041 + } 1.1042 +} 1.1043 + 1.1044 +// Given the freetype face corresponding to an entryName and face index, 1.1045 +// add the face to the available font list and to the faceList string 1.1046 +void 1.1047 +gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex, 1.1048 + bool aStdFile, FT_Face aFace, 1.1049 + nsCString& aFaceList) 1.1050 +{ 1.1051 + if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) { 1.1052 + // ignore faces that don't support a Unicode charmap 1.1053 + return; 1.1054 + } 1.1055 + 1.1056 + // build the font entry name and create an FT2FontEntry, 1.1057 + // but do -not- keep a reference to the FT_Face 1.1058 + FT2FontEntry* fe = 1.1059 + CreateNamedFontEntry(aFace, aEntryName.get(), aIndex); 1.1060 + 1.1061 + if (fe) { 1.1062 + NS_ConvertUTF8toUTF16 name(aFace->family_name); 1.1063 + BuildKeyNameFromFontName(name); 1.1064 + gfxFontFamily *family = mFontFamilies.GetWeak(name); 1.1065 + if (!family) { 1.1066 + family = new FT2FontFamily(name); 1.1067 + mFontFamilies.Put(name, family); 1.1068 + if (mSkipSpaceLookupCheckFamilies.Contains(name)) { 1.1069 + family->SetSkipSpaceFeatureCheck(true); 1.1070 + } 1.1071 + if (mBadUnderlineFamilyNames.Contains(name)) { 1.1072 + family->SetBadUnderlineFamily(); 1.1073 + } 1.1074 + } 1.1075 + fe->mStandardFace = aStdFile; 1.1076 + family->AddFontEntry(fe); 1.1077 + 1.1078 + fe->CheckForBrokenFont(family); 1.1079 + 1.1080 + AppendToFaceList(aFaceList, name, fe); 1.1081 +#ifdef PR_LOGGING 1.1082 + if (LOG_ENABLED()) { 1.1083 + LOG(("(fontinit) added (%s) to family (%s)" 1.1084 + " with style: %s weight: %d stretch: %d", 1.1085 + NS_ConvertUTF16toUTF8(fe->Name()).get(), 1.1086 + NS_ConvertUTF16toUTF8(family->Name()).get(), 1.1087 + fe->IsItalic() ? "italic" : "normal", 1.1088 + fe->Weight(), fe->Stretch())); 1.1089 + } 1.1090 +#endif 1.1091 + } 1.1092 +} 1.1093 + 1.1094 +void 1.1095 +gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive, 1.1096 + const nsCString& aEntryName, 1.1097 + FontNameCache *aCache, 1.1098 + bool aJarChanged) 1.1099 +{ 1.1100 + nsCString faceList; 1.1101 + if (aCache && !aJarChanged) { 1.1102 + uint32_t filesize, timestamp; 1.1103 + aCache->GetInfoForFile(aEntryName, faceList, ×tamp, &filesize); 1.1104 + if (faceList.Length() > 0) { 1.1105 + AppendFacesFromCachedFaceList(aEntryName, true, faceList); 1.1106 + return; 1.1107 + } 1.1108 + } 1.1109 + 1.1110 + nsZipItem *item = aArchive->GetItem(aEntryName.get()); 1.1111 + NS_ASSERTION(item, "failed to find zip entry"); 1.1112 + 1.1113 + uint32_t bufSize = item->RealSize(); 1.1114 + // We use fallible allocation here; if there's not enough RAM, we'll simply 1.1115 + // ignore the bundled fonts and fall back to the device's installed fonts. 1.1116 + nsAutoPtr<uint8_t> buf(static_cast<uint8_t*>(moz_malloc(bufSize))); 1.1117 + if (!buf) { 1.1118 + return; 1.1119 + } 1.1120 + 1.1121 + nsZipCursor cursor(item, aArchive, buf, bufSize); 1.1122 + uint8_t* data = cursor.Copy(&bufSize); 1.1123 + NS_ASSERTION(data && bufSize == item->RealSize(), 1.1124 + "error reading bundled font"); 1.1125 + if (!data) { 1.1126 + return; 1.1127 + } 1.1128 + 1.1129 + FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary(); 1.1130 + 1.1131 + FT_Face dummy; 1.1132 + if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, 0, &dummy)) { 1.1133 + return; 1.1134 + } 1.1135 + 1.1136 + for (FT_Long i = 0; i < dummy->num_faces; i++) { 1.1137 + FT_Face face; 1.1138 + if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, i, &face)) { 1.1139 + continue; 1.1140 + } 1.1141 + AddFaceToList(aEntryName, i, true, face, faceList); 1.1142 + FT_Done_Face(face); 1.1143 + } 1.1144 + 1.1145 + FT_Done_Face(dummy); 1.1146 + 1.1147 + if (aCache && !faceList.IsEmpty()) { 1.1148 + aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize); 1.1149 + } 1.1150 +} 1.1151 + 1.1152 +// Called on each family after all fonts are added to the list; 1.1153 +// this will sort faces to give priority to "standard" font files 1.1154 +// if aUserArg is non-null (i.e. we're using it as a boolean flag) 1.1155 +static PLDHashOperator 1.1156 +FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey, 1.1157 + nsRefPtr<gfxFontFamily>& aFamily, 1.1158 + void* aUserArg) 1.1159 +{ 1.1160 + gfxFontFamily *family = aFamily.get(); 1.1161 + bool sortFaces = (aUserArg != nullptr); 1.1162 + 1.1163 + family->SetHasStyles(true); 1.1164 + 1.1165 + if (sortFaces) { 1.1166 + family->SortAvailableFonts(); 1.1167 + } 1.1168 + family->CheckForSimpleFamily(); 1.1169 + 1.1170 + return PL_DHASH_NEXT; 1.1171 +} 1.1172 + 1.1173 +void 1.1174 +gfxFT2FontList::FindFonts() 1.1175 +{ 1.1176 +#ifdef XP_WIN 1.1177 + nsTArray<nsString> searchPaths(3); 1.1178 + nsTArray<nsString> fontPatterns(3); 1.1179 + fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttf")); 1.1180 + fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttc")); 1.1181 + fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.otf")); 1.1182 + wchar_t pathBuf[256]; 1.1183 + SHGetSpecialFolderPathW(0, pathBuf, CSIDL_WINDOWS, 0); 1.1184 + searchPaths.AppendElement(pathBuf); 1.1185 + SHGetSpecialFolderPathW(0, pathBuf, CSIDL_FONTS, 0); 1.1186 + searchPaths.AppendElement(pathBuf); 1.1187 + nsCOMPtr<nsIFile> resDir; 1.1188 + NS_GetSpecialDirectory(NS_APP_RES_DIR, getter_AddRefs(resDir)); 1.1189 + if (resDir) { 1.1190 + resDir->Append(NS_LITERAL_STRING("fonts")); 1.1191 + nsAutoString resPath; 1.1192 + resDir->GetPath(resPath); 1.1193 + searchPaths.AppendElement(resPath); 1.1194 + } 1.1195 + WIN32_FIND_DATAW results; 1.1196 + for (uint32_t i = 0; i < searchPaths.Length(); i++) { 1.1197 + const nsString& path(searchPaths[i]); 1.1198 + for (uint32_t j = 0; j < fontPatterns.Length(); j++) { 1.1199 + nsAutoString pattern(path); 1.1200 + pattern.Append(fontPatterns[j]); 1.1201 + HANDLE handle = FindFirstFileExW(pattern.get(), 1.1202 + FindExInfoStandard, 1.1203 + &results, 1.1204 + FindExSearchNameMatch, 1.1205 + nullptr, 1.1206 + 0); 1.1207 + bool moreFiles = handle != INVALID_HANDLE_VALUE; 1.1208 + while (moreFiles) { 1.1209 + nsAutoString filePath(path); 1.1210 + filePath.AppendLiteral("\\"); 1.1211 + filePath.Append(results.cFileName); 1.1212 + AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(filePath)); 1.1213 + moreFiles = FindNextFile(handle, &results); 1.1214 + } 1.1215 + if (handle != INVALID_HANDLE_VALUE) 1.1216 + FindClose(handle); 1.1217 + } 1.1218 + } 1.1219 +#elif defined(ANDROID) 1.1220 + gfxFontCache *fc = gfxFontCache::GetCache(); 1.1221 + if (fc) 1.1222 + fc->AgeAllGenerations(); 1.1223 + mPrefFonts.Clear(); 1.1224 + mCodepointsWithNoFonts.reset(); 1.1225 + 1.1226 + mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls 1.1227 + mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls 1.1228 + 1.1229 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.1230 + // Content process: ask the Chrome process to give us the list 1.1231 + InfallibleTArray<FontListEntry> fonts; 1.1232 + mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts); 1.1233 + for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) { 1.1234 + AppendFaceFromFontListEntry(fonts[i], false); 1.1235 + } 1.1236 + // Passing null for userdata tells Finalize that it does not need 1.1237 + // to sort faces (because they were already sorted by chrome, 1.1238 + // so we just maintain the existing order) 1.1239 + mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr); 1.1240 + LOG(("got font list from chrome process: %d faces in %d families", 1.1241 + fonts.Length(), mFontFamilies.Count())); 1.1242 + return; 1.1243 + } 1.1244 + 1.1245 + // Chrome process: get the cached list (if any) 1.1246 + FontNameCache fnc; 1.1247 + 1.1248 + // ANDROID_ROOT is the root of the android system, typically /system; 1.1249 + // font files are in /$ANDROID_ROOT/fonts/ 1.1250 + nsCString root; 1.1251 + char *androidRoot = PR_GetEnv("ANDROID_ROOT"); 1.1252 + if (androidRoot) { 1.1253 + root = androidRoot; 1.1254 + } else { 1.1255 + root = NS_LITERAL_CSTRING("/system"); 1.1256 + } 1.1257 + root.Append("/fonts"); 1.1258 + 1.1259 + FindFontsInDir(root, &fnc); 1.1260 + 1.1261 + if (mFontFamilies.Count() == 0) { 1.1262 + // if we can't find/read the font directory, we are doomed! 1.1263 + NS_RUNTIMEABORT("Could not read the system fonts directory"); 1.1264 + } 1.1265 +#endif // XP_WIN && ANDROID 1.1266 + 1.1267 + // Look for fonts stored in omnijar, unless we're on a low-memory 1.1268 + // device where we don't want to spend the RAM to decompress them. 1.1269 + // (Prefs may disable this, or force-enable it even with low memory.) 1.1270 + bool lowmem; 1.1271 + nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService(); 1.1272 + if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem && 1.1273 + Preferences::GetBool("gfx.bundled_fonts.enabled")) || 1.1274 + Preferences::GetBool("gfx.bundled_fonts.force-enabled")) { 1.1275 + FindFontsInOmnijar(&fnc); 1.1276 + } 1.1277 + 1.1278 + // look for locally-added fonts in a "fonts" subdir of the profile 1.1279 + nsCOMPtr<nsIFile> localDir; 1.1280 + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, 1.1281 + getter_AddRefs(localDir)); 1.1282 + if (NS_SUCCEEDED(rv) && 1.1283 + NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) { 1.1284 + nsCString localPath; 1.1285 + rv = localDir->GetNativePath(localPath); 1.1286 + if (NS_SUCCEEDED(rv)) { 1.1287 + FindFontsInDir(localPath, &fnc); 1.1288 + } 1.1289 + } 1.1290 + 1.1291 + // Finalize the families by sorting faces into standard order 1.1292 + // and marking "simple" families. 1.1293 + // Passing non-null userData here says that we want faces to be sorted. 1.1294 + mFontFamilies.Enumerate(FinalizeFamilyMemberList, this); 1.1295 +} 1.1296 + 1.1297 +#ifdef ANDROID 1.1298 +void 1.1299 +gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC) 1.1300 +{ 1.1301 + static const char* sStandardFonts[] = { 1.1302 + "DroidSans.ttf", 1.1303 + "DroidSans-Bold.ttf", 1.1304 + "DroidSerif-Regular.ttf", 1.1305 + "DroidSerif-Bold.ttf", 1.1306 + "DroidSerif-Italic.ttf", 1.1307 + "DroidSerif-BoldItalic.ttf", 1.1308 + "DroidSansMono.ttf", 1.1309 + "DroidSansArabic.ttf", 1.1310 + "DroidSansHebrew.ttf", 1.1311 + "DroidSansThai.ttf", 1.1312 + "MTLmr3m.ttf", 1.1313 + "MTLc3m.ttf", 1.1314 + "NanumGothic.ttf", 1.1315 + "DroidSansJapanese.ttf", 1.1316 + "DroidSansFallback.ttf" 1.1317 + }; 1.1318 + 1.1319 + DIR *d = opendir(aDir.get()); 1.1320 + if (!d) { 1.1321 + return; 1.1322 + } 1.1323 + 1.1324 + struct dirent *ent = nullptr; 1.1325 + while ((ent = readdir(d)) != nullptr) { 1.1326 + const char *ext = strrchr(ent->d_name, '.'); 1.1327 + if (!ext) { 1.1328 + continue; 1.1329 + } 1.1330 + if (strcasecmp(ext, ".ttf") == 0 || 1.1331 + strcasecmp(ext, ".otf") == 0 || 1.1332 + strcasecmp(ext, ".woff") == 0 || 1.1333 + strcasecmp(ext, ".ttc") == 0) { 1.1334 + bool isStdFont = false; 1.1335 + for (unsigned int i = 0; 1.1336 + i < ArrayLength(sStandardFonts) && !isStdFont; i++) { 1.1337 + isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0; 1.1338 + } 1.1339 + 1.1340 + nsCString s(aDir); 1.1341 + s.Append('/'); 1.1342 + s.Append(ent->d_name); 1.1343 + 1.1344 + // Add the face(s) from this file to our font list; 1.1345 + // note that if we have cached info for this file in fnc, 1.1346 + // and the file is unchanged, we won't actually need to read it. 1.1347 + // If the file is new/changed, this will update the FontNameCache. 1.1348 + AppendFacesFromFontFile(s, isStdFont, aFNC); 1.1349 + } 1.1350 + } 1.1351 + 1.1352 + closedir(d); 1.1353 +} 1.1354 +#endif 1.1355 + 1.1356 +void 1.1357 +gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE, 1.1358 + bool aStdFile) 1.1359 +{ 1.1360 + FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE); 1.1361 + if (fe) { 1.1362 + fe->mStandardFace = aStdFile; 1.1363 + nsAutoString name(aFLE.familyName()); 1.1364 + gfxFontFamily *family = mFontFamilies.GetWeak(name); 1.1365 + if (!family) { 1.1366 + family = new FT2FontFamily(name); 1.1367 + mFontFamilies.Put(name, family); 1.1368 + if (mSkipSpaceLookupCheckFamilies.Contains(name)) { 1.1369 + family->SetSkipSpaceFeatureCheck(true); 1.1370 + } 1.1371 + if (mBadUnderlineFamilyNames.Contains(name)) { 1.1372 + family->SetBadUnderlineFamily(); 1.1373 + } 1.1374 + } 1.1375 + family->AddFontEntry(fe); 1.1376 + 1.1377 + fe->CheckForBrokenFont(family); 1.1378 + } 1.1379 +} 1.1380 + 1.1381 +static PLDHashOperator 1.1382 +AddFamilyToFontList(nsStringHashKey::KeyType aKey, 1.1383 + nsRefPtr<gfxFontFamily>& aFamily, 1.1384 + void* aUserArg) 1.1385 +{ 1.1386 + InfallibleTArray<FontListEntry>* fontlist = 1.1387 + reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg); 1.1388 + 1.1389 + FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get()); 1.1390 + family->AddFacesToFontList(fontlist); 1.1391 + 1.1392 + return PL_DHASH_NEXT; 1.1393 +} 1.1394 + 1.1395 +void 1.1396 +gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue) 1.1397 +{ 1.1398 + mFontFamilies.Enumerate(AddFamilyToFontList, retValue); 1.1399 +} 1.1400 + 1.1401 +static void 1.1402 +LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck) 1.1403 +{ 1.1404 + nsAutoTArray<nsString, 5> skiplist; 1.1405 + gfxFontUtils::GetPrefsFontList( 1.1406 + "font.whitelist.skip_default_features_space_check", 1.1407 + skiplist); 1.1408 + uint32_t numFonts = skiplist.Length(); 1.1409 + for (uint32_t i = 0; i < numFonts; i++) { 1.1410 + ToLowerCase(skiplist[i]); 1.1411 + aSkipSpaceLookupCheck.PutEntry(skiplist[i]); 1.1412 + } 1.1413 +} 1.1414 + 1.1415 +nsresult 1.1416 +gfxFT2FontList::InitFontList() 1.1417 +{ 1.1418 + // reset font lists 1.1419 + gfxPlatformFontList::InitFontList(); 1.1420 + 1.1421 + LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies); 1.1422 + 1.1423 + FindFonts(); 1.1424 + 1.1425 + return NS_OK; 1.1426 +} 1.1427 + 1.1428 +struct FullFontNameSearch { 1.1429 + FullFontNameSearch(const nsAString& aFullName) 1.1430 + : mFullName(aFullName), mFontEntry(nullptr) 1.1431 + { } 1.1432 + 1.1433 + nsString mFullName; 1.1434 + FT2FontEntry *mFontEntry; 1.1435 +}; 1.1436 + 1.1437 +// callback called for each family name, based on the assumption that the 1.1438 +// first part of the full name is the family name 1.1439 +static PLDHashOperator 1.1440 +FindFullName(nsStringHashKey::KeyType aKey, 1.1441 + nsRefPtr<gfxFontFamily>& aFontFamily, 1.1442 + void* userArg) 1.1443 +{ 1.1444 + FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg); 1.1445 + 1.1446 + // does the family name match up to the length of the family name? 1.1447 + const nsString& family = aFontFamily->Name(); 1.1448 + 1.1449 + nsString fullNameFamily; 1.1450 + data->mFullName.Left(fullNameFamily, family.Length()); 1.1451 + 1.1452 + // if so, iterate over faces in this family to see if there is a match 1.1453 + if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) { 1.1454 + nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList(); 1.1455 + int index, len = fontList.Length(); 1.1456 + for (index = 0; index < len; index++) { 1.1457 + gfxFontEntry* fe = fontList[index]; 1.1458 + if (!fe) { 1.1459 + continue; 1.1460 + } 1.1461 + if (fe->Name().Equals(data->mFullName, 1.1462 + nsCaseInsensitiveStringComparator())) { 1.1463 + data->mFontEntry = static_cast<FT2FontEntry*>(fe); 1.1464 + return PL_DHASH_STOP; 1.1465 + } 1.1466 + } 1.1467 + } 1.1468 + 1.1469 + return PL_DHASH_NEXT; 1.1470 +} 1.1471 + 1.1472 +gfxFontEntry* 1.1473 +gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, 1.1474 + const nsAString& aFontName) 1.1475 +{ 1.1476 + // walk over list of names 1.1477 + FullFontNameSearch data(aFontName); 1.1478 + 1.1479 + mFontFamilies.Enumerate(FindFullName, &data); 1.1480 + 1.1481 + if (!data.mFontEntry) { 1.1482 + return nullptr; 1.1483 + } 1.1484 + 1.1485 + // Clone the font entry so that we can then set its style descriptors 1.1486 + // from the proxy rather than the actual font. 1.1487 + 1.1488 + // Ensure existence of mFTFace in the original entry 1.1489 + data.mFontEntry->CairoFontFace(); 1.1490 + if (!data.mFontEntry->mFTFace) { 1.1491 + return nullptr; 1.1492 + } 1.1493 + 1.1494 + FT2FontEntry* fe = 1.1495 + FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace, 1.1496 + data.mFontEntry->mFilename.get(), 1.1497 + data.mFontEntry->mFTFontIndex, 1.1498 + data.mFontEntry->Name(), nullptr); 1.1499 + if (fe) { 1.1500 + fe->mItalic = aProxyEntry->mItalic; 1.1501 + fe->mWeight = aProxyEntry->mWeight; 1.1502 + fe->mStretch = aProxyEntry->mStretch; 1.1503 + fe->mIsUserFont = fe->mIsLocalUserFont = true; 1.1504 + } 1.1505 + 1.1506 + return fe; 1.1507 +} 1.1508 + 1.1509 +gfxFontFamily* 1.1510 +gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle) 1.1511 +{ 1.1512 +#ifdef XP_WIN 1.1513 + HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT); 1.1514 + LOGFONTW logFont; 1.1515 + if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) { 1.1516 + nsAutoString resolvedName; 1.1517 + if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) { 1.1518 + return FindFamily(resolvedName); 1.1519 + } 1.1520 + } 1.1521 +#elif defined(MOZ_WIDGET_GONK) 1.1522 + nsAutoString resolvedName; 1.1523 + if (ResolveFontName(NS_LITERAL_STRING("Fira Sans OT"), resolvedName)) { 1.1524 + return FindFamily(resolvedName); 1.1525 + } 1.1526 +#elif defined(MOZ_WIDGET_ANDROID) 1.1527 + nsAutoString resolvedName; 1.1528 + if (ResolveFontName(NS_LITERAL_STRING("Roboto"), resolvedName) || 1.1529 + ResolveFontName(NS_LITERAL_STRING("Droid Sans"), resolvedName)) { 1.1530 + return FindFamily(resolvedName); 1.1531 + } 1.1532 +#endif 1.1533 + /* TODO: what about Qt or other platforms that may use this? */ 1.1534 + return nullptr; 1.1535 +} 1.1536 + 1.1537 +gfxFontEntry* 1.1538 +gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, 1.1539 + const uint8_t *aFontData, 1.1540 + uint32_t aLength) 1.1541 +{ 1.1542 + // The FT2 font needs the font data to persist, so we do NOT free it here 1.1543 + // but instead pass ownership to the font entry. 1.1544 + // Deallocation will happen later, when the font face is destroyed. 1.1545 + return FT2FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength); 1.1546 +} 1.1547 +