gfxFT2FontList.cpp 53 KB

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