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