gfx/thebes/gfxFT2FontList.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/ArrayUtils.h"
michael@0 7 #include "mozilla/MemoryReporting.h"
michael@0 8
michael@0 9 #if (MOZ_WIDGET_GTK == 2)
michael@0 10 #include "gfxPlatformGtk.h"
michael@0 11 #define gfxToolkitPlatform gfxPlatformGtk
michael@0 12 #elif defined(MOZ_WIDGET_QT)
michael@0 13 #include <qfontinfo.h>
michael@0 14 #include "gfxQtPlatform.h"
michael@0 15 #define gfxToolkitPlatform gfxQtPlatform
michael@0 16 #elif defined(XP_WIN)
michael@0 17 #include "gfxWindowsPlatform.h"
michael@0 18 #define gfxToolkitPlatform gfxWindowsPlatform
michael@0 19 #elif defined(ANDROID)
michael@0 20 #include "mozilla/dom/ContentChild.h"
michael@0 21 #include "gfxAndroidPlatform.h"
michael@0 22 #include "mozilla/Omnijar.h"
michael@0 23 #include "nsIInputStream.h"
michael@0 24 #include "nsNetUtil.h"
michael@0 25 #define gfxToolkitPlatform gfxAndroidPlatform
michael@0 26 #endif
michael@0 27
michael@0 28 #ifdef ANDROID
michael@0 29 #include "nsXULAppAPI.h"
michael@0 30 #include <dirent.h>
michael@0 31 #include <android/log.h>
michael@0 32 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
michael@0 33 #endif
michael@0 34
michael@0 35 #include "ft2build.h"
michael@0 36 #include FT_FREETYPE_H
michael@0 37 #include FT_TRUETYPE_TAGS_H
michael@0 38 #include FT_TRUETYPE_TABLES_H
michael@0 39 #include "cairo-ft.h"
michael@0 40
michael@0 41 #include "gfxFT2FontList.h"
michael@0 42 #include "gfxFT2Fonts.h"
michael@0 43 #include "gfxUserFontSet.h"
michael@0 44 #include "gfxFontUtils.h"
michael@0 45
michael@0 46 #include "nsServiceManagerUtils.h"
michael@0 47 #include "nsTArray.h"
michael@0 48 #include "nsUnicharUtils.h"
michael@0 49
michael@0 50 #include "nsDirectoryServiceUtils.h"
michael@0 51 #include "nsDirectoryServiceDefs.h"
michael@0 52 #include "nsAppDirectoryServiceDefs.h"
michael@0 53 #include "nsISimpleEnumerator.h"
michael@0 54 #include "nsIMemory.h"
michael@0 55 #include "gfxFontConstants.h"
michael@0 56
michael@0 57 #include "mozilla/Preferences.h"
michael@0 58 #include "mozilla/scache/StartupCache.h"
michael@0 59 #include <sys/stat.h>
michael@0 60
michael@0 61 #ifdef XP_WIN
michael@0 62 #include "nsIWindowsRegKey.h"
michael@0 63 #include <windows.h>
michael@0 64 #endif
michael@0 65
michael@0 66 using namespace mozilla;
michael@0 67
michael@0 68 #ifdef PR_LOGGING
michael@0 69 static PRLogModuleInfo *
michael@0 70 GetFontInfoLog()
michael@0 71 {
michael@0 72 static PRLogModuleInfo *sLog;
michael@0 73 if (!sLog)
michael@0 74 sLog = PR_NewLogModule("fontInfoLog");
michael@0 75 return sLog;
michael@0 76 }
michael@0 77 #endif /* PR_LOGGING */
michael@0 78
michael@0 79 #undef LOG
michael@0 80 #define LOG(args) PR_LOG(GetFontInfoLog(), PR_LOG_DEBUG, args)
michael@0 81 #define LOG_ENABLED() PR_LOG_TEST(GetFontInfoLog(), PR_LOG_DEBUG)
michael@0 82
michael@0 83 static cairo_user_data_key_t sFTUserFontDataKey;
michael@0 84
michael@0 85 static __inline void
michael@0 86 BuildKeyNameFromFontName(nsAString &aName)
michael@0 87 {
michael@0 88 #ifdef XP_WIN
michael@0 89 if (aName.Length() >= LF_FACESIZE)
michael@0 90 aName.Truncate(LF_FACESIZE - 1);
michael@0 91 #endif
michael@0 92 ToLowerCase(aName);
michael@0 93 }
michael@0 94
michael@0 95 // Helper to access the FT_Face for a given FT2FontEntry,
michael@0 96 // creating a temporary face if the entry does not have one yet.
michael@0 97 // This allows us to read font names, tables, etc if necessary
michael@0 98 // without permanently instantiating a freetype face and consuming
michael@0 99 // memory long-term.
michael@0 100 // This may fail (resulting in a null FT_Face), e.g. if it fails to
michael@0 101 // allocate memory to uncompress a font from omnijar.
michael@0 102 class AutoFTFace {
michael@0 103 public:
michael@0 104 AutoFTFace(FT2FontEntry* aFontEntry)
michael@0 105 : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
michael@0 106 {
michael@0 107 if (aFontEntry->mFTFace) {
michael@0 108 mFace = aFontEntry->mFTFace;
michael@0 109 return;
michael@0 110 }
michael@0 111
michael@0 112 NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
michael@0 113 "can't use AutoFTFace for fonts without a filename");
michael@0 114 FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary();
michael@0 115
michael@0 116 // A relative path (no initial "/") means this is a resource in
michael@0 117 // omnijar, not an installed font on the device.
michael@0 118 // The NS_ASSERTIONs here should never fail, as the resource must have
michael@0 119 // been read successfully during font-list initialization or we'd never
michael@0 120 // have created the font entry. The only legitimate runtime failure
michael@0 121 // here would be memory allocation, in which case mFace remains null.
michael@0 122 if (aFontEntry->mFilename[0] != '/') {
michael@0 123 nsRefPtr<nsZipArchive> reader =
michael@0 124 Omnijar::GetReader(Omnijar::Type::GRE);
michael@0 125 nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
michael@0 126 NS_ASSERTION(item, "failed to find zip entry");
michael@0 127
michael@0 128 uint32_t bufSize = item->RealSize();
michael@0 129 mFontDataBuf = static_cast<uint8_t*>(moz_malloc(bufSize));
michael@0 130 if (mFontDataBuf) {
michael@0 131 nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
michael@0 132 cursor.Copy(&bufSize);
michael@0 133 NS_ASSERTION(bufSize == item->RealSize(),
michael@0 134 "error reading bundled font");
michael@0 135
michael@0 136 if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
michael@0 137 aFontEntry->mFTFontIndex,
michael@0 138 &mFace)) {
michael@0 139 NS_WARNING("failed to create freetype face");
michael@0 140 }
michael@0 141 }
michael@0 142 } else {
michael@0 143 if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
michael@0 144 aFontEntry->mFTFontIndex, &mFace)) {
michael@0 145 NS_WARNING("failed to create freetype face");
michael@0 146 }
michael@0 147 }
michael@0 148 if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
michael@0 149 NS_WARNING("failed to select Unicode charmap");
michael@0 150 }
michael@0 151 mOwnsFace = true;
michael@0 152 }
michael@0 153
michael@0 154 ~AutoFTFace() {
michael@0 155 if (mFace && mOwnsFace) {
michael@0 156 FT_Done_Face(mFace);
michael@0 157 if (mFontDataBuf) {
michael@0 158 moz_free(mFontDataBuf);
michael@0 159 }
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 operator FT_Face() { return mFace; }
michael@0 164
michael@0 165 // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
michael@0 166 // we do -not- free the mFontDataBuf (if used); that also becomes the
michael@0 167 // responsibility of the new owner of the face.
michael@0 168 FT_Face forget() {
michael@0 169 NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
michael@0 170 mOwnsFace = false;
michael@0 171 return mFace;
michael@0 172 }
michael@0 173
michael@0 174 const uint8_t* FontData() const { return mFontDataBuf; }
michael@0 175
michael@0 176 private:
michael@0 177 FT_Face mFace;
michael@0 178 uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
michael@0 179 // or null for fonts instantiated from a file.
michael@0 180 // If non-null, this must survive as long as the
michael@0 181 // FT_Face.
michael@0 182 bool mOwnsFace;
michael@0 183 };
michael@0 184
michael@0 185 /*
michael@0 186 * FT2FontEntry
michael@0 187 * gfxFontEntry subclass corresponding to a specific face that can be
michael@0 188 * rendered by freetype. This is associated with a face index in a
michael@0 189 * file (normally a .ttf/.otf file holding a single face, but in principle
michael@0 190 * there could be .ttc files with multiple faces).
michael@0 191 * The FT2FontEntry can create the necessary FT_Face on demand, and can
michael@0 192 * then create a Cairo font_face and scaled_font for drawing.
michael@0 193 */
michael@0 194
michael@0 195 cairo_scaled_font_t *
michael@0 196 FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
michael@0 197 {
michael@0 198 cairo_font_face_t *cairoFace = CairoFontFace();
michael@0 199 if (!cairoFace) {
michael@0 200 return nullptr;
michael@0 201 }
michael@0 202
michael@0 203 cairo_scaled_font_t *scaledFont = nullptr;
michael@0 204
michael@0 205 cairo_matrix_t sizeMatrix;
michael@0 206 cairo_matrix_t identityMatrix;
michael@0 207
michael@0 208 // XXX deal with adjusted size
michael@0 209 cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
michael@0 210 cairo_matrix_init_identity(&identityMatrix);
michael@0 211
michael@0 212 // synthetic oblique by skewing via the font matrix
michael@0 213 bool needsOblique = !IsItalic() &&
michael@0 214 (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
michael@0 215
michael@0 216 if (needsOblique) {
michael@0 217 const double kSkewFactor = 0.25;
michael@0 218
michael@0 219 cairo_matrix_t style;
michael@0 220 cairo_matrix_init(&style,
michael@0 221 1, //xx
michael@0 222 0, //yx
michael@0 223 -1 * kSkewFactor, //xy
michael@0 224 1, //yy
michael@0 225 0, //x0
michael@0 226 0); //y0
michael@0 227 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
michael@0 228 }
michael@0 229
michael@0 230 cairo_font_options_t *fontOptions = cairo_font_options_create();
michael@0 231
michael@0 232 if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
michael@0 233 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
michael@0 234 }
michael@0 235
michael@0 236 scaledFont = cairo_scaled_font_create(cairoFace,
michael@0 237 &sizeMatrix,
michael@0 238 &identityMatrix, fontOptions);
michael@0 239 cairo_font_options_destroy(fontOptions);
michael@0 240
michael@0 241 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
michael@0 242 "Failed to make scaled font");
michael@0 243
michael@0 244 return scaledFont;
michael@0 245 }
michael@0 246
michael@0 247 FT2FontEntry::~FT2FontEntry()
michael@0 248 {
michael@0 249 // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
michael@0 250 mFTFace = nullptr;
michael@0 251
michael@0 252 #ifndef ANDROID
michael@0 253 if (mFontFace) {
michael@0 254 cairo_font_face_destroy(mFontFace);
michael@0 255 mFontFace = nullptr;
michael@0 256 }
michael@0 257 #endif
michael@0 258 }
michael@0 259
michael@0 260 gfxFont*
michael@0 261 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
michael@0 262 {
michael@0 263 cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
michael@0 264 if (!scaledFont) {
michael@0 265 return nullptr;
michael@0 266 }
michael@0 267 gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
michael@0 268 cairo_scaled_font_destroy(scaledFont);
michael@0 269 return font;
michael@0 270 }
michael@0 271
michael@0 272 /* static */
michael@0 273 FT2FontEntry*
michael@0 274 FT2FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
michael@0 275 const uint8_t *aFontData,
michael@0 276 uint32_t aLength)
michael@0 277 {
michael@0 278 // Ownership of aFontData is passed in here; the fontEntry must
michael@0 279 // retain it as long as the FT_Face needs it, and ensure it is
michael@0 280 // eventually deleted.
michael@0 281 FT_Face face;
michael@0 282 FT_Error error =
michael@0 283 FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
michael@0 284 aFontData, aLength, 0, &face);
michael@0 285 if (error != FT_Err_Ok) {
michael@0 286 NS_Free((void*)aFontData);
michael@0 287 return nullptr;
michael@0 288 }
michael@0 289 if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
michael@0 290 FT_Done_Face(face);
michael@0 291 NS_Free((void*)aFontData);
michael@0 292 return nullptr;
michael@0 293 }
michael@0 294 // Create our FT2FontEntry, which inherits the name of the proxy
michael@0 295 // as it's not guaranteed that the face has valid names (bug 737315)
michael@0 296 FT2FontEntry* fe =
michael@0 297 FT2FontEntry::CreateFontEntry(face, nullptr, 0, aProxyEntry.Name(),
michael@0 298 aFontData);
michael@0 299 if (fe) {
michael@0 300 fe->mItalic = aProxyEntry.mItalic;
michael@0 301 fe->mWeight = aProxyEntry.mWeight;
michael@0 302 fe->mStretch = aProxyEntry.mStretch;
michael@0 303 fe->mIsUserFont = true;
michael@0 304 }
michael@0 305 return fe;
michael@0 306 }
michael@0 307
michael@0 308 class FTUserFontData {
michael@0 309 public:
michael@0 310 FTUserFontData(FT_Face aFace, const uint8_t* aData)
michael@0 311 : mFace(aFace), mFontData(aData)
michael@0 312 {
michael@0 313 }
michael@0 314
michael@0 315 ~FTUserFontData()
michael@0 316 {
michael@0 317 FT_Done_Face(mFace);
michael@0 318 if (mFontData) {
michael@0 319 NS_Free((void*)mFontData);
michael@0 320 }
michael@0 321 }
michael@0 322
michael@0 323 const uint8_t *FontData() const { return mFontData; }
michael@0 324
michael@0 325 private:
michael@0 326 FT_Face mFace;
michael@0 327 const uint8_t *mFontData;
michael@0 328 };
michael@0 329
michael@0 330 static void
michael@0 331 FTFontDestroyFunc(void *data)
michael@0 332 {
michael@0 333 FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
michael@0 334 delete userFontData;
michael@0 335 }
michael@0 336
michael@0 337 /* static */
michael@0 338 FT2FontEntry*
michael@0 339 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
michael@0 340 {
michael@0 341 FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
michael@0 342 fe->mFilename = aFLE.filepath();
michael@0 343 fe->mFTFontIndex = aFLE.index();
michael@0 344 fe->mWeight = aFLE.weight();
michael@0 345 fe->mStretch = aFLE.stretch();
michael@0 346 fe->mItalic = aFLE.italic();
michael@0 347 return fe;
michael@0 348 }
michael@0 349
michael@0 350 // Helpers to extract font entry properties from an FT_Face
michael@0 351 static bool
michael@0 352 FTFaceIsItalic(FT_Face aFace)
michael@0 353 {
michael@0 354 return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
michael@0 355 }
michael@0 356
michael@0 357 static uint16_t
michael@0 358 FTFaceGetWeight(FT_Face aFace)
michael@0 359 {
michael@0 360 TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
michael@0 361 uint16_t os2weight = 0;
michael@0 362 if (os2 && os2->version != 0xffff) {
michael@0 363 // Technically, only 100 to 900 are valid, but some fonts
michael@0 364 // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
michael@0 365 // it set to 6 instead of 600. We try to be nice and handle that
michael@0 366 // as well.
michael@0 367 if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
michael@0 368 os2weight = os2->usWeightClass;
michael@0 369 } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
michael@0 370 os2weight = os2->usWeightClass * 100;
michael@0 371 }
michael@0 372 }
michael@0 373
michael@0 374 uint16_t result;
michael@0 375 if (os2weight != 0) {
michael@0 376 result = os2weight;
michael@0 377 } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
michael@0 378 result = 700;
michael@0 379 } else {
michael@0 380 result = 400;
michael@0 381 }
michael@0 382
michael@0 383 NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
michael@0 384
michael@0 385 return result;
michael@0 386 }
michael@0 387
michael@0 388 // Used to create the font entry for installed faces on the device,
michael@0 389 // when iterating over the fonts directories.
michael@0 390 // We use the FT_Face to retrieve the details needed for the font entry,
michael@0 391 // but unless we have been passed font data (i.e. for a user font),
michael@0 392 // we do *not* save a reference to it, nor create a cairo face,
michael@0 393 // as we don't want to keep a freetype face for every installed font
michael@0 394 // permanently in memory.
michael@0 395 /* static */
michael@0 396 FT2FontEntry*
michael@0 397 FT2FontEntry::CreateFontEntry(FT_Face aFace,
michael@0 398 const char* aFilename, uint8_t aIndex,
michael@0 399 const nsAString& aName,
michael@0 400 const uint8_t *aFontData)
michael@0 401 {
michael@0 402 FT2FontEntry *fe = new FT2FontEntry(aName);
michael@0 403 fe->mItalic = FTFaceIsItalic(aFace);
michael@0 404 fe->mWeight = FTFaceGetWeight(aFace);
michael@0 405 fe->mFilename = aFilename;
michael@0 406 fe->mFTFontIndex = aIndex;
michael@0 407
michael@0 408 if (aFontData) {
michael@0 409 fe->mFTFace = aFace;
michael@0 410 int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
michael@0 411 FT_LOAD_DEFAULT :
michael@0 412 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
michael@0 413 fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
michael@0 414 FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
michael@0 415 cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
michael@0 416 userFontData, FTFontDestroyFunc);
michael@0 417 }
michael@0 418
michael@0 419 return fe;
michael@0 420 }
michael@0 421
michael@0 422 // construct font entry name for an installed font from names in the FT_Face,
michael@0 423 // and then create our FT2FontEntry
michael@0 424 static FT2FontEntry*
michael@0 425 CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
michael@0 426 {
michael@0 427 if (!aFace->family_name) {
michael@0 428 return nullptr;
michael@0 429 }
michael@0 430 nsAutoString fontName;
michael@0 431 AppendUTF8toUTF16(aFace->family_name, fontName);
michael@0 432 if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
michael@0 433 fontName.AppendLiteral(" ");
michael@0 434 AppendUTF8toUTF16(aFace->style_name, fontName);
michael@0 435 }
michael@0 436 return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
michael@0 437 }
michael@0 438
michael@0 439 FT2FontEntry*
michael@0 440 gfxFT2Font::GetFontEntry()
michael@0 441 {
michael@0 442 return static_cast<FT2FontEntry*> (mFontEntry.get());
michael@0 443 }
michael@0 444
michael@0 445 cairo_font_face_t *
michael@0 446 FT2FontEntry::CairoFontFace()
michael@0 447 {
michael@0 448 if (!mFontFace) {
michael@0 449 AutoFTFace face(this);
michael@0 450 if (!face) {
michael@0 451 return nullptr;
michael@0 452 }
michael@0 453 mFTFace = face.forget();
michael@0 454 int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
michael@0 455 FT_LOAD_DEFAULT :
michael@0 456 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
michael@0 457 mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
michael@0 458 FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
michael@0 459 cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
michael@0 460 userFontData, FTFontDestroyFunc);
michael@0 461 }
michael@0 462 return mFontFace;
michael@0 463 }
michael@0 464
michael@0 465 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
michael@0 466 // Complex scripts will not render correctly unless Graphite or OT
michael@0 467 // layout tables are present.
michael@0 468 // For OpenType, we also check that the GSUB table supports the relevant
michael@0 469 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
michael@0 470 // the characters, but lacks OpenType support).
michael@0 471
michael@0 472 // TODO: consider whether we should move this to gfxFontEntry and do similar
michael@0 473 // cmap-masking on all platforms to avoid using fonts that won't shape
michael@0 474 // properly.
michael@0 475
michael@0 476 nsresult
michael@0 477 FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
michael@0 478 {
michael@0 479 if (mCharacterMap) {
michael@0 480 return NS_OK;
michael@0 481 }
michael@0 482
michael@0 483 nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
michael@0 484
michael@0 485 AutoFallibleTArray<uint8_t,16384> buffer;
michael@0 486 nsresult rv = CopyFontTable(TTAG_cmap, buffer);
michael@0 487
michael@0 488 if (NS_SUCCEEDED(rv)) {
michael@0 489 bool unicodeFont;
michael@0 490 bool symbolFont;
michael@0 491 rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
michael@0 492 *charmap, mUVSOffset,
michael@0 493 unicodeFont, symbolFont);
michael@0 494 }
michael@0 495
michael@0 496 if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
michael@0 497 // We assume a Graphite font knows what it's doing,
michael@0 498 // and provides whatever shaping is needed for the
michael@0 499 // characters it supports, so only check/clear the
michael@0 500 // complex-script ranges for non-Graphite fonts
michael@0 501
michael@0 502 // for layout support, check for the presence of opentype layout tables
michael@0 503 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
michael@0 504
michael@0 505 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
michael@0 506 sr->rangeStart; sr++) {
michael@0 507 // check to see if the cmap includes complex script codepoints
michael@0 508 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
michael@0 509 // We check for GSUB here, as GPOS alone would not be ok.
michael@0 510 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
michael@0 511 continue;
michael@0 512 }
michael@0 513 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
michael@0 514 }
michael@0 515 }
michael@0 516 }
michael@0 517
michael@0 518 #ifdef MOZ_WIDGET_ANDROID
michael@0 519 // Hack for the SamsungDevanagari font, bug 1012365:
michael@0 520 // pretend the font supports U+0972.
michael@0 521 if (!charmap->test(0x0972) &&
michael@0 522 charmap->test(0x0905) && charmap->test(0x0945)) {
michael@0 523 charmap->set(0x0972);
michael@0 524 }
michael@0 525 #endif
michael@0 526
michael@0 527 mHasCmapTable = NS_SUCCEEDED(rv);
michael@0 528 if (mHasCmapTable) {
michael@0 529 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
michael@0 530 mCharacterMap = pfl->FindCharMap(charmap);
michael@0 531 } else {
michael@0 532 // if error occurred, initialize to null cmap
michael@0 533 mCharacterMap = new gfxCharacterMap();
michael@0 534 }
michael@0 535 return rv;
michael@0 536 }
michael@0 537
michael@0 538 nsresult
michael@0 539 FT2FontEntry::CopyFontTable(uint32_t aTableTag,
michael@0 540 FallibleTArray<uint8_t>& aBuffer)
michael@0 541 {
michael@0 542 AutoFTFace face(this);
michael@0 543 if (!face) {
michael@0 544 return NS_ERROR_FAILURE;
michael@0 545 }
michael@0 546
michael@0 547 FT_Error status;
michael@0 548 FT_ULong len = 0;
michael@0 549 status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
michael@0 550 if (status != FT_Err_Ok || len == 0) {
michael@0 551 return NS_ERROR_FAILURE;
michael@0 552 }
michael@0 553
michael@0 554 if (!aBuffer.SetLength(len)) {
michael@0 555 return NS_ERROR_OUT_OF_MEMORY;
michael@0 556 }
michael@0 557 uint8_t *buf = aBuffer.Elements();
michael@0 558 status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
michael@0 559 NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
michael@0 560
michael@0 561 return NS_OK;
michael@0 562 }
michael@0 563
michael@0 564 hb_blob_t*
michael@0 565 FT2FontEntry::GetFontTable(uint32_t aTableTag)
michael@0 566 {
michael@0 567 if (mFontFace) {
michael@0 568 // if there's a cairo font face, we may be able to return a blob
michael@0 569 // that just wraps a range of the attached user font data
michael@0 570 FTUserFontData *userFontData = static_cast<FTUserFontData*>(
michael@0 571 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
michael@0 572 if (userFontData && userFontData->FontData()) {
michael@0 573 return GetTableFromFontData(userFontData->FontData(), aTableTag);
michael@0 574 }
michael@0 575 }
michael@0 576
michael@0 577 // otherwise, use the default method (which in turn will call our
michael@0 578 // implementation of CopyFontTable)
michael@0 579 return gfxFontEntry::GetFontTable(aTableTag);
michael@0 580 }
michael@0 581
michael@0 582 void
michael@0 583 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 584 FontListSizes* aSizes) const
michael@0 585 {
michael@0 586 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 587 aSizes->mFontListSize +=
michael@0 588 mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 589 }
michael@0 590
michael@0 591 void
michael@0 592 FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 593 FontListSizes* aSizes) const
michael@0 594 {
michael@0 595 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 596 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 597 }
michael@0 598
michael@0 599 /*
michael@0 600 * FT2FontFamily
michael@0 601 * A standard gfxFontFamily; just adds a method used to support sending
michael@0 602 * the font list from chrome to content via IPC.
michael@0 603 */
michael@0 604
michael@0 605 void
michael@0 606 FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
michael@0 607 {
michael@0 608 for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
michael@0 609 const FT2FontEntry *fe =
michael@0 610 static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
michael@0 611 if (!fe) {
michael@0 612 continue;
michael@0 613 }
michael@0 614
michael@0 615 aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
michael@0 616 fe->mFilename,
michael@0 617 fe->Weight(), fe->Stretch(),
michael@0 618 fe->IsItalic(),
michael@0 619 fe->mFTFontIndex));
michael@0 620 }
michael@0 621 }
michael@0 622
michael@0 623 /*
michael@0 624 * Startup cache support for the font list:
michael@0 625 * We store the list of families and faces, with their style attributes and the
michael@0 626 * corresponding font files, in the startup cache.
michael@0 627 * This allows us to recreate the gfxFT2FontList collection of families and
michael@0 628 * faces without instantiating Freetype faces for each font file (in order to
michael@0 629 * find their attributes), leading to significantly quicker startup.
michael@0 630 */
michael@0 631
michael@0 632 #define CACHE_KEY "font.cached-list"
michael@0 633
michael@0 634 class FontNameCache {
michael@0 635 public:
michael@0 636 FontNameCache()
michael@0 637 : mWriteNeeded(false)
michael@0 638 {
michael@0 639 mOps = (PLDHashTableOps) {
michael@0 640 PL_DHashAllocTable,
michael@0 641 PL_DHashFreeTable,
michael@0 642 StringHash,
michael@0 643 HashMatchEntry,
michael@0 644 MoveEntry,
michael@0 645 PL_DHashClearEntryStub,
michael@0 646 PL_DHashFinalizeStub,
michael@0 647 nullptr
michael@0 648 };
michael@0 649
michael@0 650 PL_DHashTableInit(&mMap, &mOps, nullptr, sizeof(FNCMapEntry), 0);
michael@0 651
michael@0 652 NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default,
michael@0 653 "StartupCacheFontNameCache should only be used in chrome process");
michael@0 654 mCache = mozilla::scache::StartupCache::GetSingleton();
michael@0 655
michael@0 656 Init();
michael@0 657 }
michael@0 658
michael@0 659 ~FontNameCache()
michael@0 660 {
michael@0 661 if (!mMap.ops) {
michael@0 662 return;
michael@0 663 }
michael@0 664 if (!mWriteNeeded || !mCache) {
michael@0 665 PL_DHashTableFinish(&mMap);
michael@0 666 return;
michael@0 667 }
michael@0 668
michael@0 669 nsAutoCString buf;
michael@0 670 PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf);
michael@0 671 PL_DHashTableFinish(&mMap);
michael@0 672 mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
michael@0 673 }
michael@0 674
michael@0 675 void Init()
michael@0 676 {
michael@0 677 if (!mMap.ops || !mCache) {
michael@0 678 return;
michael@0 679 }
michael@0 680 uint32_t size;
michael@0 681 char* buf;
michael@0 682 if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
michael@0 683 return;
michael@0 684 }
michael@0 685
michael@0 686 LOG(("got: %s from the cache", nsDependentCString(buf, size).get()));
michael@0 687
michael@0 688 const char* beginning = buf;
michael@0 689 const char* end = strchr(beginning, ';');
michael@0 690 while (end) {
michael@0 691 nsCString filename(beginning, end - beginning);
michael@0 692 beginning = end + 1;
michael@0 693 if (!(end = strchr(beginning, ';'))) {
michael@0 694 break;
michael@0 695 }
michael@0 696 nsCString faceList(beginning, end - beginning);
michael@0 697 beginning = end + 1;
michael@0 698 if (!(end = strchr(beginning, ';'))) {
michael@0 699 break;
michael@0 700 }
michael@0 701 uint32_t timestamp = strtoul(beginning, nullptr, 10);
michael@0 702 beginning = end + 1;
michael@0 703 if (!(end = strchr(beginning, ';'))) {
michael@0 704 break;
michael@0 705 }
michael@0 706 uint32_t filesize = strtoul(beginning, nullptr, 10);
michael@0 707
michael@0 708 FNCMapEntry* mapEntry =
michael@0 709 static_cast<FNCMapEntry*>
michael@0 710 (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD));
michael@0 711 if (mapEntry) {
michael@0 712 mapEntry->mFilename.Assign(filename);
michael@0 713 mapEntry->mTimestamp = timestamp;
michael@0 714 mapEntry->mFilesize = filesize;
michael@0 715 mapEntry->mFaces.Assign(faceList);
michael@0 716 // entries from the startupcache are marked "non-existing"
michael@0 717 // until we have confirmed that the file still exists
michael@0 718 mapEntry->mFileExists = false;
michael@0 719 }
michael@0 720
michael@0 721 beginning = end + 1;
michael@0 722 end = strchr(beginning, ';');
michael@0 723 }
michael@0 724
michael@0 725 // Should we use free() or delete[] here? See bug 684700.
michael@0 726 free(buf);
michael@0 727 }
michael@0 728
michael@0 729 virtual void
michael@0 730 GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
michael@0 731 uint32_t *aTimestamp, uint32_t *aFilesize)
michael@0 732 {
michael@0 733 if (!mMap.ops) {
michael@0 734 return;
michael@0 735 }
michael@0 736 PLDHashEntryHdr *hdr =
michael@0 737 PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP);
michael@0 738 if (!hdr) {
michael@0 739 return;
michael@0 740 }
michael@0 741 FNCMapEntry* entry = static_cast<FNCMapEntry*>(hdr);
michael@0 742 if (entry && entry->mFilesize) {
michael@0 743 *aTimestamp = entry->mTimestamp;
michael@0 744 *aFilesize = entry->mFilesize;
michael@0 745 aFaceList.Assign(entry->mFaces);
michael@0 746 // this entry does correspond to an existing file
michael@0 747 // (although it might not be up-to-date, in which case
michael@0 748 // it will get overwritten via CacheFileInfo)
michael@0 749 entry->mFileExists = true;
michael@0 750 }
michael@0 751 }
michael@0 752
michael@0 753 virtual void
michael@0 754 CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
michael@0 755 uint32_t aTimestamp, uint32_t aFilesize)
michael@0 756 {
michael@0 757 if (!mMap.ops) {
michael@0 758 return;
michael@0 759 }
michael@0 760 FNCMapEntry* entry =
michael@0 761 static_cast<FNCMapEntry*>
michael@0 762 (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD));
michael@0 763 if (entry) {
michael@0 764 entry->mFilename.Assign(aFileName);
michael@0 765 entry->mTimestamp = aTimestamp;
michael@0 766 entry->mFilesize = aFilesize;
michael@0 767 entry->mFaces.Assign(aFaceList);
michael@0 768 entry->mFileExists = true;
michael@0 769 }
michael@0 770 mWriteNeeded = true;
michael@0 771 }
michael@0 772
michael@0 773 private:
michael@0 774 mozilla::scache::StartupCache* mCache;
michael@0 775 PLDHashTable mMap;
michael@0 776 bool mWriteNeeded;
michael@0 777
michael@0 778 PLDHashTableOps mOps;
michael@0 779
michael@0 780 static PLDHashOperator WriteOutMap(PLDHashTable *aTable,
michael@0 781 PLDHashEntryHdr *aHdr,
michael@0 782 uint32_t aNumber, void *aData)
michael@0 783 {
michael@0 784 FNCMapEntry* entry = static_cast<FNCMapEntry*>(aHdr);
michael@0 785 if (!entry->mFileExists) {
michael@0 786 // skip writing entries for files that are no longer present
michael@0 787 return PL_DHASH_NEXT;
michael@0 788 }
michael@0 789
michael@0 790 nsAutoCString* buf = reinterpret_cast<nsAutoCString*>(aData);
michael@0 791 buf->Append(entry->mFilename);
michael@0 792 buf->Append(';');
michael@0 793 buf->Append(entry->mFaces);
michael@0 794 buf->Append(';');
michael@0 795 buf->AppendInt(entry->mTimestamp);
michael@0 796 buf->Append(';');
michael@0 797 buf->AppendInt(entry->mFilesize);
michael@0 798 buf->Append(';');
michael@0 799 return PL_DHASH_NEXT;
michael@0 800 }
michael@0 801
michael@0 802 typedef struct : public PLDHashEntryHdr {
michael@0 803 public:
michael@0 804 nsCString mFilename;
michael@0 805 uint32_t mTimestamp;
michael@0 806 uint32_t mFilesize;
michael@0 807 nsCString mFaces;
michael@0 808 bool mFileExists;
michael@0 809 } FNCMapEntry;
michael@0 810
michael@0 811 static PLDHashNumber StringHash(PLDHashTable *table, const void *key)
michael@0 812 {
michael@0 813 return HashString(reinterpret_cast<const char*>(key));
michael@0 814 }
michael@0 815
michael@0 816 static bool HashMatchEntry(PLDHashTable *table,
michael@0 817 const PLDHashEntryHdr *aHdr, const void *key)
michael@0 818 {
michael@0 819 const FNCMapEntry* entry =
michael@0 820 static_cast<const FNCMapEntry*>(aHdr);
michael@0 821 return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
michael@0 822 }
michael@0 823
michael@0 824 static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
michael@0 825 PLDHashEntryHdr *aTo)
michael@0 826 {
michael@0 827 FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
michael@0 828 const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
michael@0 829 to->mFilename.Assign(from->mFilename);
michael@0 830 to->mTimestamp = from->mTimestamp;
michael@0 831 to->mFilesize = from->mFilesize;
michael@0 832 to->mFaces.Assign(from->mFaces);
michael@0 833 to->mFileExists = from->mFileExists;
michael@0 834 }
michael@0 835 };
michael@0 836
michael@0 837 /***************************************************************
michael@0 838 *
michael@0 839 * gfxFT2FontList
michael@0 840 *
michael@0 841 */
michael@0 842
michael@0 843 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
michael@0 844 // scanning the system's Fonts directory for OpenType and TrueType files.
michael@0 845
michael@0 846 gfxFT2FontList::gfxFT2FontList()
michael@0 847 {
michael@0 848 }
michael@0 849
michael@0 850 void
michael@0 851 gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName,
michael@0 852 bool aStdFile,
michael@0 853 const nsCString& aFaceList)
michael@0 854 {
michael@0 855 const char *beginning = aFaceList.get();
michael@0 856 const char *end = strchr(beginning, ',');
michael@0 857 while (end) {
michael@0 858 nsString familyName =
michael@0 859 NS_ConvertUTF8toUTF16(beginning, end - beginning);
michael@0 860 ToLowerCase(familyName);
michael@0 861 beginning = end + 1;
michael@0 862 if (!(end = strchr(beginning, ','))) {
michael@0 863 break;
michael@0 864 }
michael@0 865 nsString faceName =
michael@0 866 NS_ConvertUTF8toUTF16(beginning, end - beginning);
michael@0 867 beginning = end + 1;
michael@0 868 if (!(end = strchr(beginning, ','))) {
michael@0 869 break;
michael@0 870 }
michael@0 871 uint32_t index = strtoul(beginning, nullptr, 10);
michael@0 872 beginning = end + 1;
michael@0 873 if (!(end = strchr(beginning, ','))) {
michael@0 874 break;
michael@0 875 }
michael@0 876 bool italic = (*beginning != '0');
michael@0 877 beginning = end + 1;
michael@0 878 if (!(end = strchr(beginning, ','))) {
michael@0 879 break;
michael@0 880 }
michael@0 881 uint32_t weight = strtoul(beginning, nullptr, 10);
michael@0 882 beginning = end + 1;
michael@0 883 if (!(end = strchr(beginning, ','))) {
michael@0 884 break;
michael@0 885 }
michael@0 886 int32_t stretch = strtol(beginning, nullptr, 10);
michael@0 887
michael@0 888 FontListEntry fle(familyName, faceName, aFileName,
michael@0 889 weight, stretch, italic, index);
michael@0 890 AppendFaceFromFontListEntry(fle, aStdFile);
michael@0 891
michael@0 892 beginning = end + 1;
michael@0 893 end = strchr(beginning, ',');
michael@0 894 }
michael@0 895 }
michael@0 896
michael@0 897 static void
michael@0 898 AppendToFaceList(nsCString& aFaceList,
michael@0 899 nsAString& aFamilyName, FT2FontEntry* aFontEntry)
michael@0 900 {
michael@0 901 aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
michael@0 902 aFaceList.Append(',');
michael@0 903 aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
michael@0 904 aFaceList.Append(',');
michael@0 905 aFaceList.AppendInt(aFontEntry->mFTFontIndex);
michael@0 906 aFaceList.Append(',');
michael@0 907 aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
michael@0 908 aFaceList.Append(',');
michael@0 909 aFaceList.AppendInt(aFontEntry->Weight());
michael@0 910 aFaceList.Append(',');
michael@0 911 aFaceList.AppendInt(aFontEntry->Stretch());
michael@0 912 aFaceList.Append(',');
michael@0 913 }
michael@0 914
michael@0 915 void
michael@0 916 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
michael@0 917 {
michael@0 918 // note if the family is in the "bad underline" blacklist
michael@0 919 if (aFamily->IsBadUnderlineFamily()) {
michael@0 920 mIsBadUnderlineFont = true;
michael@0 921 }
michael@0 922
michael@0 923 // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
michael@0 924 // because of unwanted on-by-default "ae" ligature.
michael@0 925 // (See also AppendFaceFromFontListEntry.)
michael@0 926 if (aFamily->Name().EqualsLiteral("roboto")) {
michael@0 927 mIgnoreGSUB = true;
michael@0 928 }
michael@0 929
michael@0 930 // bug 706888 - set the IgnoreGSUB flag on the broken version of
michael@0 931 // Droid Sans Arabic from certain phones, as identified by the
michael@0 932 // font checksum in the 'head' table
michael@0 933 else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
michael@0 934 AutoFTFace face(this);
michael@0 935 if (face) {
michael@0 936 const TT_Header *head = static_cast<const TT_Header*>
michael@0 937 (FT_Get_Sfnt_Table(face, ft_sfnt_head));
michael@0 938 if (head && head->CheckSum_Adjust == 0xe445242) {
michael@0 939 mIgnoreGSUB = true;
michael@0 940 }
michael@0 941 }
michael@0 942 }
michael@0 943 }
michael@0 944
michael@0 945 void
michael@0 946 gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
michael@0 947 bool aStdFile,
michael@0 948 FontNameCache *aCache)
michael@0 949 {
michael@0 950 nsCString faceList;
michael@0 951 uint32_t filesize = 0, timestamp = 0;
michael@0 952 if (aCache) {
michael@0 953 aCache->GetInfoForFile(aFileName, faceList, &timestamp, &filesize);
michael@0 954 }
michael@0 955
michael@0 956 struct stat s;
michael@0 957 int statRetval = stat(aFileName.get(), &s);
michael@0 958 if (!faceList.IsEmpty() && 0 == statRetval &&
michael@0 959 s.st_mtime == timestamp && s.st_size == filesize)
michael@0 960 {
michael@0 961 LOG(("using cached font info for %s", aFileName.get()));
michael@0 962 AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList);
michael@0 963 return;
michael@0 964 }
michael@0 965
michael@0 966 #ifdef XP_WIN
michael@0 967 FT_Library ftLibrary = gfxWindowsPlatform::GetPlatform()->GetFTLibrary();
michael@0 968 #elif defined(ANDROID)
michael@0 969 FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
michael@0 970 #endif
michael@0 971 FT_Face dummy;
michael@0 972 if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
michael@0 973 LOG(("reading font info via FreeType for %s", aFileName.get()));
michael@0 974 nsCString faceList;
michael@0 975 timestamp = s.st_mtime;
michael@0 976 filesize = s.st_size;
michael@0 977 for (FT_Long i = 0; i < dummy->num_faces; i++) {
michael@0 978 FT_Face face;
michael@0 979 if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
michael@0 980 continue;
michael@0 981 }
michael@0 982 AddFaceToList(aFileName, i, aStdFile, face, faceList);
michael@0 983 FT_Done_Face(face);
michael@0 984 }
michael@0 985 FT_Done_Face(dummy);
michael@0 986 if (aCache && 0 == statRetval && !faceList.IsEmpty()) {
michael@0 987 aCache->CacheFileInfo(aFileName, faceList, timestamp, filesize);
michael@0 988 }
michael@0 989 }
michael@0 990 }
michael@0 991
michael@0 992 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
michael@0 993
michael@0 994 void
michael@0 995 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
michael@0 996 {
michael@0 997 bool jarChanged = false;
michael@0 998
michael@0 999 mozilla::scache::StartupCache* cache =
michael@0 1000 mozilla::scache::StartupCache::GetSingleton();
michael@0 1001 char *cachedModifiedTimeBuf;
michael@0 1002 uint32_t longSize;
michael@0 1003 int64_t jarModifiedTime;
michael@0 1004 if (cache &&
michael@0 1005 NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
michael@0 1006 &cachedModifiedTimeBuf,
michael@0 1007 &longSize)) &&
michael@0 1008 longSize == sizeof(int64_t))
michael@0 1009 {
michael@0 1010 nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
michael@0 1011 jarFile->GetLastModifiedTime(&jarModifiedTime);
michael@0 1012 if (jarModifiedTime > *(int64_t*)cachedModifiedTimeBuf) {
michael@0 1013 jarChanged = true;
michael@0 1014 }
michael@0 1015 }
michael@0 1016
michael@0 1017 static const char* sJarSearchPaths[] = {
michael@0 1018 "res/fonts/*.ttf$",
michael@0 1019 };
michael@0 1020 nsRefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
michael@0 1021 for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
michael@0 1022 nsZipFind* find;
michael@0 1023 if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
michael@0 1024 const char* path;
michael@0 1025 uint16_t len;
michael@0 1026 while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
michael@0 1027 nsCString entryName(path, len);
michael@0 1028 AppendFacesFromOmnijarEntry(reader, entryName, aCache,
michael@0 1029 jarChanged);
michael@0 1030 }
michael@0 1031 delete find;
michael@0 1032 }
michael@0 1033 }
michael@0 1034
michael@0 1035 if (cache) {
michael@0 1036 cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime,
michael@0 1037 sizeof(jarModifiedTime));
michael@0 1038 }
michael@0 1039 }
michael@0 1040
michael@0 1041 // Given the freetype face corresponding to an entryName and face index,
michael@0 1042 // add the face to the available font list and to the faceList string
michael@0 1043 void
michael@0 1044 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
michael@0 1045 bool aStdFile, FT_Face aFace,
michael@0 1046 nsCString& aFaceList)
michael@0 1047 {
michael@0 1048 if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
michael@0 1049 // ignore faces that don't support a Unicode charmap
michael@0 1050 return;
michael@0 1051 }
michael@0 1052
michael@0 1053 // build the font entry name and create an FT2FontEntry,
michael@0 1054 // but do -not- keep a reference to the FT_Face
michael@0 1055 FT2FontEntry* fe =
michael@0 1056 CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
michael@0 1057
michael@0 1058 if (fe) {
michael@0 1059 NS_ConvertUTF8toUTF16 name(aFace->family_name);
michael@0 1060 BuildKeyNameFromFontName(name);
michael@0 1061 gfxFontFamily *family = mFontFamilies.GetWeak(name);
michael@0 1062 if (!family) {
michael@0 1063 family = new FT2FontFamily(name);
michael@0 1064 mFontFamilies.Put(name, family);
michael@0 1065 if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
michael@0 1066 family->SetSkipSpaceFeatureCheck(true);
michael@0 1067 }
michael@0 1068 if (mBadUnderlineFamilyNames.Contains(name)) {
michael@0 1069 family->SetBadUnderlineFamily();
michael@0 1070 }
michael@0 1071 }
michael@0 1072 fe->mStandardFace = aStdFile;
michael@0 1073 family->AddFontEntry(fe);
michael@0 1074
michael@0 1075 fe->CheckForBrokenFont(family);
michael@0 1076
michael@0 1077 AppendToFaceList(aFaceList, name, fe);
michael@0 1078 #ifdef PR_LOGGING
michael@0 1079 if (LOG_ENABLED()) {
michael@0 1080 LOG(("(fontinit) added (%s) to family (%s)"
michael@0 1081 " with style: %s weight: %d stretch: %d",
michael@0 1082 NS_ConvertUTF16toUTF8(fe->Name()).get(),
michael@0 1083 NS_ConvertUTF16toUTF8(family->Name()).get(),
michael@0 1084 fe->IsItalic() ? "italic" : "normal",
michael@0 1085 fe->Weight(), fe->Stretch()));
michael@0 1086 }
michael@0 1087 #endif
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 void
michael@0 1092 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
michael@0 1093 const nsCString& aEntryName,
michael@0 1094 FontNameCache *aCache,
michael@0 1095 bool aJarChanged)
michael@0 1096 {
michael@0 1097 nsCString faceList;
michael@0 1098 if (aCache && !aJarChanged) {
michael@0 1099 uint32_t filesize, timestamp;
michael@0 1100 aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
michael@0 1101 if (faceList.Length() > 0) {
michael@0 1102 AppendFacesFromCachedFaceList(aEntryName, true, faceList);
michael@0 1103 return;
michael@0 1104 }
michael@0 1105 }
michael@0 1106
michael@0 1107 nsZipItem *item = aArchive->GetItem(aEntryName.get());
michael@0 1108 NS_ASSERTION(item, "failed to find zip entry");
michael@0 1109
michael@0 1110 uint32_t bufSize = item->RealSize();
michael@0 1111 // We use fallible allocation here; if there's not enough RAM, we'll simply
michael@0 1112 // ignore the bundled fonts and fall back to the device's installed fonts.
michael@0 1113 nsAutoPtr<uint8_t> buf(static_cast<uint8_t*>(moz_malloc(bufSize)));
michael@0 1114 if (!buf) {
michael@0 1115 return;
michael@0 1116 }
michael@0 1117
michael@0 1118 nsZipCursor cursor(item, aArchive, buf, bufSize);
michael@0 1119 uint8_t* data = cursor.Copy(&bufSize);
michael@0 1120 NS_ASSERTION(data && bufSize == item->RealSize(),
michael@0 1121 "error reading bundled font");
michael@0 1122 if (!data) {
michael@0 1123 return;
michael@0 1124 }
michael@0 1125
michael@0 1126 FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
michael@0 1127
michael@0 1128 FT_Face dummy;
michael@0 1129 if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, 0, &dummy)) {
michael@0 1130 return;
michael@0 1131 }
michael@0 1132
michael@0 1133 for (FT_Long i = 0; i < dummy->num_faces; i++) {
michael@0 1134 FT_Face face;
michael@0 1135 if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, i, &face)) {
michael@0 1136 continue;
michael@0 1137 }
michael@0 1138 AddFaceToList(aEntryName, i, true, face, faceList);
michael@0 1139 FT_Done_Face(face);
michael@0 1140 }
michael@0 1141
michael@0 1142 FT_Done_Face(dummy);
michael@0 1143
michael@0 1144 if (aCache && !faceList.IsEmpty()) {
michael@0 1145 aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
michael@0 1146 }
michael@0 1147 }
michael@0 1148
michael@0 1149 // Called on each family after all fonts are added to the list;
michael@0 1150 // this will sort faces to give priority to "standard" font files
michael@0 1151 // if aUserArg is non-null (i.e. we're using it as a boolean flag)
michael@0 1152 static PLDHashOperator
michael@0 1153 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
michael@0 1154 nsRefPtr<gfxFontFamily>& aFamily,
michael@0 1155 void* aUserArg)
michael@0 1156 {
michael@0 1157 gfxFontFamily *family = aFamily.get();
michael@0 1158 bool sortFaces = (aUserArg != nullptr);
michael@0 1159
michael@0 1160 family->SetHasStyles(true);
michael@0 1161
michael@0 1162 if (sortFaces) {
michael@0 1163 family->SortAvailableFonts();
michael@0 1164 }
michael@0 1165 family->CheckForSimpleFamily();
michael@0 1166
michael@0 1167 return PL_DHASH_NEXT;
michael@0 1168 }
michael@0 1169
michael@0 1170 void
michael@0 1171 gfxFT2FontList::FindFonts()
michael@0 1172 {
michael@0 1173 #ifdef XP_WIN
michael@0 1174 nsTArray<nsString> searchPaths(3);
michael@0 1175 nsTArray<nsString> fontPatterns(3);
michael@0 1176 fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttf"));
michael@0 1177 fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttc"));
michael@0 1178 fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.otf"));
michael@0 1179 wchar_t pathBuf[256];
michael@0 1180 SHGetSpecialFolderPathW(0, pathBuf, CSIDL_WINDOWS, 0);
michael@0 1181 searchPaths.AppendElement(pathBuf);
michael@0 1182 SHGetSpecialFolderPathW(0, pathBuf, CSIDL_FONTS, 0);
michael@0 1183 searchPaths.AppendElement(pathBuf);
michael@0 1184 nsCOMPtr<nsIFile> resDir;
michael@0 1185 NS_GetSpecialDirectory(NS_APP_RES_DIR, getter_AddRefs(resDir));
michael@0 1186 if (resDir) {
michael@0 1187 resDir->Append(NS_LITERAL_STRING("fonts"));
michael@0 1188 nsAutoString resPath;
michael@0 1189 resDir->GetPath(resPath);
michael@0 1190 searchPaths.AppendElement(resPath);
michael@0 1191 }
michael@0 1192 WIN32_FIND_DATAW results;
michael@0 1193 for (uint32_t i = 0; i < searchPaths.Length(); i++) {
michael@0 1194 const nsString& path(searchPaths[i]);
michael@0 1195 for (uint32_t j = 0; j < fontPatterns.Length(); j++) {
michael@0 1196 nsAutoString pattern(path);
michael@0 1197 pattern.Append(fontPatterns[j]);
michael@0 1198 HANDLE handle = FindFirstFileExW(pattern.get(),
michael@0 1199 FindExInfoStandard,
michael@0 1200 &results,
michael@0 1201 FindExSearchNameMatch,
michael@0 1202 nullptr,
michael@0 1203 0);
michael@0 1204 bool moreFiles = handle != INVALID_HANDLE_VALUE;
michael@0 1205 while (moreFiles) {
michael@0 1206 nsAutoString filePath(path);
michael@0 1207 filePath.AppendLiteral("\\");
michael@0 1208 filePath.Append(results.cFileName);
michael@0 1209 AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(filePath));
michael@0 1210 moreFiles = FindNextFile(handle, &results);
michael@0 1211 }
michael@0 1212 if (handle != INVALID_HANDLE_VALUE)
michael@0 1213 FindClose(handle);
michael@0 1214 }
michael@0 1215 }
michael@0 1216 #elif defined(ANDROID)
michael@0 1217 gfxFontCache *fc = gfxFontCache::GetCache();
michael@0 1218 if (fc)
michael@0 1219 fc->AgeAllGenerations();
michael@0 1220 mPrefFonts.Clear();
michael@0 1221 mCodepointsWithNoFonts.reset();
michael@0 1222
michael@0 1223 mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
michael@0 1224 mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
michael@0 1225
michael@0 1226 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 1227 // Content process: ask the Chrome process to give us the list
michael@0 1228 InfallibleTArray<FontListEntry> fonts;
michael@0 1229 mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
michael@0 1230 for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
michael@0 1231 AppendFaceFromFontListEntry(fonts[i], false);
michael@0 1232 }
michael@0 1233 // Passing null for userdata tells Finalize that it does not need
michael@0 1234 // to sort faces (because they were already sorted by chrome,
michael@0 1235 // so we just maintain the existing order)
michael@0 1236 mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
michael@0 1237 LOG(("got font list from chrome process: %d faces in %d families",
michael@0 1238 fonts.Length(), mFontFamilies.Count()));
michael@0 1239 return;
michael@0 1240 }
michael@0 1241
michael@0 1242 // Chrome process: get the cached list (if any)
michael@0 1243 FontNameCache fnc;
michael@0 1244
michael@0 1245 // ANDROID_ROOT is the root of the android system, typically /system;
michael@0 1246 // font files are in /$ANDROID_ROOT/fonts/
michael@0 1247 nsCString root;
michael@0 1248 char *androidRoot = PR_GetEnv("ANDROID_ROOT");
michael@0 1249 if (androidRoot) {
michael@0 1250 root = androidRoot;
michael@0 1251 } else {
michael@0 1252 root = NS_LITERAL_CSTRING("/system");
michael@0 1253 }
michael@0 1254 root.Append("/fonts");
michael@0 1255
michael@0 1256 FindFontsInDir(root, &fnc);
michael@0 1257
michael@0 1258 if (mFontFamilies.Count() == 0) {
michael@0 1259 // if we can't find/read the font directory, we are doomed!
michael@0 1260 NS_RUNTIMEABORT("Could not read the system fonts directory");
michael@0 1261 }
michael@0 1262 #endif // XP_WIN && ANDROID
michael@0 1263
michael@0 1264 // Look for fonts stored in omnijar, unless we're on a low-memory
michael@0 1265 // device where we don't want to spend the RAM to decompress them.
michael@0 1266 // (Prefs may disable this, or force-enable it even with low memory.)
michael@0 1267 bool lowmem;
michael@0 1268 nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
michael@0 1269 if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
michael@0 1270 Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
michael@0 1271 Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
michael@0 1272 FindFontsInOmnijar(&fnc);
michael@0 1273 }
michael@0 1274
michael@0 1275 // look for locally-added fonts in a "fonts" subdir of the profile
michael@0 1276 nsCOMPtr<nsIFile> localDir;
michael@0 1277 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
michael@0 1278 getter_AddRefs(localDir));
michael@0 1279 if (NS_SUCCEEDED(rv) &&
michael@0 1280 NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
michael@0 1281 nsCString localPath;
michael@0 1282 rv = localDir->GetNativePath(localPath);
michael@0 1283 if (NS_SUCCEEDED(rv)) {
michael@0 1284 FindFontsInDir(localPath, &fnc);
michael@0 1285 }
michael@0 1286 }
michael@0 1287
michael@0 1288 // Finalize the families by sorting faces into standard order
michael@0 1289 // and marking "simple" families.
michael@0 1290 // Passing non-null userData here says that we want faces to be sorted.
michael@0 1291 mFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
michael@0 1292 }
michael@0 1293
michael@0 1294 #ifdef ANDROID
michael@0 1295 void
michael@0 1296 gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
michael@0 1297 {
michael@0 1298 static const char* sStandardFonts[] = {
michael@0 1299 "DroidSans.ttf",
michael@0 1300 "DroidSans-Bold.ttf",
michael@0 1301 "DroidSerif-Regular.ttf",
michael@0 1302 "DroidSerif-Bold.ttf",
michael@0 1303 "DroidSerif-Italic.ttf",
michael@0 1304 "DroidSerif-BoldItalic.ttf",
michael@0 1305 "DroidSansMono.ttf",
michael@0 1306 "DroidSansArabic.ttf",
michael@0 1307 "DroidSansHebrew.ttf",
michael@0 1308 "DroidSansThai.ttf",
michael@0 1309 "MTLmr3m.ttf",
michael@0 1310 "MTLc3m.ttf",
michael@0 1311 "NanumGothic.ttf",
michael@0 1312 "DroidSansJapanese.ttf",
michael@0 1313 "DroidSansFallback.ttf"
michael@0 1314 };
michael@0 1315
michael@0 1316 DIR *d = opendir(aDir.get());
michael@0 1317 if (!d) {
michael@0 1318 return;
michael@0 1319 }
michael@0 1320
michael@0 1321 struct dirent *ent = nullptr;
michael@0 1322 while ((ent = readdir(d)) != nullptr) {
michael@0 1323 const char *ext = strrchr(ent->d_name, '.');
michael@0 1324 if (!ext) {
michael@0 1325 continue;
michael@0 1326 }
michael@0 1327 if (strcasecmp(ext, ".ttf") == 0 ||
michael@0 1328 strcasecmp(ext, ".otf") == 0 ||
michael@0 1329 strcasecmp(ext, ".woff") == 0 ||
michael@0 1330 strcasecmp(ext, ".ttc") == 0) {
michael@0 1331 bool isStdFont = false;
michael@0 1332 for (unsigned int i = 0;
michael@0 1333 i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
michael@0 1334 isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
michael@0 1335 }
michael@0 1336
michael@0 1337 nsCString s(aDir);
michael@0 1338 s.Append('/');
michael@0 1339 s.Append(ent->d_name);
michael@0 1340
michael@0 1341 // Add the face(s) from this file to our font list;
michael@0 1342 // note that if we have cached info for this file in fnc,
michael@0 1343 // and the file is unchanged, we won't actually need to read it.
michael@0 1344 // If the file is new/changed, this will update the FontNameCache.
michael@0 1345 AppendFacesFromFontFile(s, isStdFont, aFNC);
michael@0 1346 }
michael@0 1347 }
michael@0 1348
michael@0 1349 closedir(d);
michael@0 1350 }
michael@0 1351 #endif
michael@0 1352
michael@0 1353 void
michael@0 1354 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
michael@0 1355 bool aStdFile)
michael@0 1356 {
michael@0 1357 FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
michael@0 1358 if (fe) {
michael@0 1359 fe->mStandardFace = aStdFile;
michael@0 1360 nsAutoString name(aFLE.familyName());
michael@0 1361 gfxFontFamily *family = mFontFamilies.GetWeak(name);
michael@0 1362 if (!family) {
michael@0 1363 family = new FT2FontFamily(name);
michael@0 1364 mFontFamilies.Put(name, family);
michael@0 1365 if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
michael@0 1366 family->SetSkipSpaceFeatureCheck(true);
michael@0 1367 }
michael@0 1368 if (mBadUnderlineFamilyNames.Contains(name)) {
michael@0 1369 family->SetBadUnderlineFamily();
michael@0 1370 }
michael@0 1371 }
michael@0 1372 family->AddFontEntry(fe);
michael@0 1373
michael@0 1374 fe->CheckForBrokenFont(family);
michael@0 1375 }
michael@0 1376 }
michael@0 1377
michael@0 1378 static PLDHashOperator
michael@0 1379 AddFamilyToFontList(nsStringHashKey::KeyType aKey,
michael@0 1380 nsRefPtr<gfxFontFamily>& aFamily,
michael@0 1381 void* aUserArg)
michael@0 1382 {
michael@0 1383 InfallibleTArray<FontListEntry>* fontlist =
michael@0 1384 reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
michael@0 1385
michael@0 1386 FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
michael@0 1387 family->AddFacesToFontList(fontlist);
michael@0 1388
michael@0 1389 return PL_DHASH_NEXT;
michael@0 1390 }
michael@0 1391
michael@0 1392 void
michael@0 1393 gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
michael@0 1394 {
michael@0 1395 mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
michael@0 1396 }
michael@0 1397
michael@0 1398 static void
michael@0 1399 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
michael@0 1400 {
michael@0 1401 nsAutoTArray<nsString, 5> skiplist;
michael@0 1402 gfxFontUtils::GetPrefsFontList(
michael@0 1403 "font.whitelist.skip_default_features_space_check",
michael@0 1404 skiplist);
michael@0 1405 uint32_t numFonts = skiplist.Length();
michael@0 1406 for (uint32_t i = 0; i < numFonts; i++) {
michael@0 1407 ToLowerCase(skiplist[i]);
michael@0 1408 aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
michael@0 1409 }
michael@0 1410 }
michael@0 1411
michael@0 1412 nsresult
michael@0 1413 gfxFT2FontList::InitFontList()
michael@0 1414 {
michael@0 1415 // reset font lists
michael@0 1416 gfxPlatformFontList::InitFontList();
michael@0 1417
michael@0 1418 LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
michael@0 1419
michael@0 1420 FindFonts();
michael@0 1421
michael@0 1422 return NS_OK;
michael@0 1423 }
michael@0 1424
michael@0 1425 struct FullFontNameSearch {
michael@0 1426 FullFontNameSearch(const nsAString& aFullName)
michael@0 1427 : mFullName(aFullName), mFontEntry(nullptr)
michael@0 1428 { }
michael@0 1429
michael@0 1430 nsString mFullName;
michael@0 1431 FT2FontEntry *mFontEntry;
michael@0 1432 };
michael@0 1433
michael@0 1434 // callback called for each family name, based on the assumption that the
michael@0 1435 // first part of the full name is the family name
michael@0 1436 static PLDHashOperator
michael@0 1437 FindFullName(nsStringHashKey::KeyType aKey,
michael@0 1438 nsRefPtr<gfxFontFamily>& aFontFamily,
michael@0 1439 void* userArg)
michael@0 1440 {
michael@0 1441 FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg);
michael@0 1442
michael@0 1443 // does the family name match up to the length of the family name?
michael@0 1444 const nsString& family = aFontFamily->Name();
michael@0 1445
michael@0 1446 nsString fullNameFamily;
michael@0 1447 data->mFullName.Left(fullNameFamily, family.Length());
michael@0 1448
michael@0 1449 // if so, iterate over faces in this family to see if there is a match
michael@0 1450 if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
michael@0 1451 nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList();
michael@0 1452 int index, len = fontList.Length();
michael@0 1453 for (index = 0; index < len; index++) {
michael@0 1454 gfxFontEntry* fe = fontList[index];
michael@0 1455 if (!fe) {
michael@0 1456 continue;
michael@0 1457 }
michael@0 1458 if (fe->Name().Equals(data->mFullName,
michael@0 1459 nsCaseInsensitiveStringComparator())) {
michael@0 1460 data->mFontEntry = static_cast<FT2FontEntry*>(fe);
michael@0 1461 return PL_DHASH_STOP;
michael@0 1462 }
michael@0 1463 }
michael@0 1464 }
michael@0 1465
michael@0 1466 return PL_DHASH_NEXT;
michael@0 1467 }
michael@0 1468
michael@0 1469 gfxFontEntry*
michael@0 1470 gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
michael@0 1471 const nsAString& aFontName)
michael@0 1472 {
michael@0 1473 // walk over list of names
michael@0 1474 FullFontNameSearch data(aFontName);
michael@0 1475
michael@0 1476 mFontFamilies.Enumerate(FindFullName, &data);
michael@0 1477
michael@0 1478 if (!data.mFontEntry) {
michael@0 1479 return nullptr;
michael@0 1480 }
michael@0 1481
michael@0 1482 // Clone the font entry so that we can then set its style descriptors
michael@0 1483 // from the proxy rather than the actual font.
michael@0 1484
michael@0 1485 // Ensure existence of mFTFace in the original entry
michael@0 1486 data.mFontEntry->CairoFontFace();
michael@0 1487 if (!data.mFontEntry->mFTFace) {
michael@0 1488 return nullptr;
michael@0 1489 }
michael@0 1490
michael@0 1491 FT2FontEntry* fe =
michael@0 1492 FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace,
michael@0 1493 data.mFontEntry->mFilename.get(),
michael@0 1494 data.mFontEntry->mFTFontIndex,
michael@0 1495 data.mFontEntry->Name(), nullptr);
michael@0 1496 if (fe) {
michael@0 1497 fe->mItalic = aProxyEntry->mItalic;
michael@0 1498 fe->mWeight = aProxyEntry->mWeight;
michael@0 1499 fe->mStretch = aProxyEntry->mStretch;
michael@0 1500 fe->mIsUserFont = fe->mIsLocalUserFont = true;
michael@0 1501 }
michael@0 1502
michael@0 1503 return fe;
michael@0 1504 }
michael@0 1505
michael@0 1506 gfxFontFamily*
michael@0 1507 gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
michael@0 1508 {
michael@0 1509 #ifdef XP_WIN
michael@0 1510 HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT);
michael@0 1511 LOGFONTW logFont;
michael@0 1512 if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
michael@0 1513 nsAutoString resolvedName;
michael@0 1514 if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) {
michael@0 1515 return FindFamily(resolvedName);
michael@0 1516 }
michael@0 1517 }
michael@0 1518 #elif defined(MOZ_WIDGET_GONK)
michael@0 1519 nsAutoString resolvedName;
michael@0 1520 if (ResolveFontName(NS_LITERAL_STRING("Fira Sans OT"), resolvedName)) {
michael@0 1521 return FindFamily(resolvedName);
michael@0 1522 }
michael@0 1523 #elif defined(MOZ_WIDGET_ANDROID)
michael@0 1524 nsAutoString resolvedName;
michael@0 1525 if (ResolveFontName(NS_LITERAL_STRING("Roboto"), resolvedName) ||
michael@0 1526 ResolveFontName(NS_LITERAL_STRING("Droid Sans"), resolvedName)) {
michael@0 1527 return FindFamily(resolvedName);
michael@0 1528 }
michael@0 1529 #endif
michael@0 1530 /* TODO: what about Qt or other platforms that may use this? */
michael@0 1531 return nullptr;
michael@0 1532 }
michael@0 1533
michael@0 1534 gfxFontEntry*
michael@0 1535 gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
michael@0 1536 const uint8_t *aFontData,
michael@0 1537 uint32_t aLength)
michael@0 1538 {
michael@0 1539 // The FT2 font needs the font data to persist, so we do NOT free it here
michael@0 1540 // but instead pass ownership to the font entry.
michael@0 1541 // Deallocation will happen later, when the font face is destroyed.
michael@0 1542 return FT2FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);
michael@0 1543 }
michael@0 1544

mercurial