Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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, ×tamp, &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, ×tamp, &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 |