gfxFontconfigUtils.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  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 "gfxFontconfigUtils.h"
  7. #include "gfxFont.h"
  8. #include "nsGkAtoms.h"
  9. #include <locale.h>
  10. #include <fontconfig/fontconfig.h>
  11. #include "nsServiceManagerUtils.h"
  12. #include "nsILanguageAtomService.h"
  13. #include "nsTArray.h"
  14. #include "mozilla/Preferences.h"
  15. #include "nsDirectoryServiceUtils.h"
  16. #include "nsDirectoryServiceDefs.h"
  17. #include "nsAppDirectoryServiceDefs.h"
  18. #include "nsIAtom.h"
  19. #include "nsCRT.h"
  20. #include "gfxFontConstants.h"
  21. #include "mozilla/gfx/2D.h"
  22. using namespace mozilla;
  23. /* static */ gfxFontconfigUtils* gfxFontconfigUtils::sUtils = nullptr;
  24. static nsILanguageAtomService* gLangService = nullptr;
  25. /* static */ void
  26. gfxFontconfigUtils::Shutdown() {
  27. if (sUtils) {
  28. delete sUtils;
  29. sUtils = nullptr;
  30. }
  31. NS_IF_RELEASE(gLangService);
  32. }
  33. /* static */ uint8_t
  34. gfxFontconfigUtils::FcSlantToThebesStyle(int aFcSlant)
  35. {
  36. switch (aFcSlant) {
  37. case FC_SLANT_ITALIC:
  38. return NS_FONT_STYLE_ITALIC;
  39. case FC_SLANT_OBLIQUE:
  40. return NS_FONT_STYLE_OBLIQUE;
  41. default:
  42. return NS_FONT_STYLE_NORMAL;
  43. }
  44. }
  45. /* static */ uint8_t
  46. gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
  47. {
  48. int slant;
  49. if (FcPatternGetInteger(aPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
  50. return NS_FONT_STYLE_NORMAL;
  51. }
  52. return FcSlantToThebesStyle(slant);
  53. }
  54. /* static */ int
  55. gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
  56. {
  57. if (aFontStyle.style == NS_FONT_STYLE_ITALIC)
  58. return FC_SLANT_ITALIC;
  59. if (aFontStyle.style == NS_FONT_STYLE_OBLIQUE)
  60. return FC_SLANT_OBLIQUE;
  61. return FC_SLANT_ROMAN;
  62. }
  63. // OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
  64. #ifndef FC_WEIGHT_THIN
  65. #define FC_WEIGHT_THIN 0 // 2.1.93
  66. #define FC_WEIGHT_EXTRALIGHT 40 // 2.1.93
  67. #define FC_WEIGHT_REGULAR 80 // 2.1.93
  68. #define FC_WEIGHT_EXTRABOLD 205 // 2.1.93
  69. #endif
  70. // book was introduced in fontconfig-2.2.90 (and so fontconfig-2.3.0 in 2005)
  71. #ifndef FC_WEIGHT_BOOK
  72. #define FC_WEIGHT_BOOK 75
  73. #endif
  74. // extra black was introduced in fontconfig-2.4.91 (2007)
  75. #ifndef FC_WEIGHT_EXTRABLACK
  76. #define FC_WEIGHT_EXTRABLACK 215
  77. #endif
  78. /* static */ uint16_t
  79. gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
  80. {
  81. int weight;
  82. if (FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
  83. return NS_FONT_WEIGHT_NORMAL;
  84. if (weight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2)
  85. return 100;
  86. if (weight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2)
  87. return 200;
  88. if (weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2)
  89. return 300;
  90. if (weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
  91. // This includes FC_WEIGHT_BOOK
  92. return 400;
  93. if (weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
  94. return 500;
  95. if (weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
  96. return 600;
  97. if (weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2)
  98. return 700;
  99. if (weight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2)
  100. return 800;
  101. if (weight <= FC_WEIGHT_BLACK)
  102. return 900;
  103. // including FC_WEIGHT_EXTRABLACK
  104. return 901;
  105. }
  106. /* static */ int
  107. gfxFontconfigUtils::FcWeightForBaseWeight(int8_t aBaseWeight)
  108. {
  109. NS_PRECONDITION(aBaseWeight >= 0 && aBaseWeight <= 10,
  110. "base weight out of range");
  111. switch (aBaseWeight) {
  112. case 2:
  113. return FC_WEIGHT_EXTRALIGHT;
  114. case 3:
  115. return FC_WEIGHT_LIGHT;
  116. case 4:
  117. return FC_WEIGHT_REGULAR;
  118. case 5:
  119. return FC_WEIGHT_MEDIUM;
  120. case 6:
  121. return FC_WEIGHT_DEMIBOLD;
  122. case 7:
  123. return FC_WEIGHT_BOLD;
  124. case 8:
  125. return FC_WEIGHT_EXTRABOLD;
  126. case 9:
  127. return FC_WEIGHT_BLACK;
  128. }
  129. // extremes
  130. return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
  131. }
  132. /* static */ int16_t
  133. gfxFontconfigUtils::GetThebesStretch(FcPattern *aPattern)
  134. {
  135. int width;
  136. if (FcPatternGetInteger(aPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
  137. return NS_FONT_STRETCH_NORMAL;
  138. }
  139. if (width <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
  140. return NS_FONT_STRETCH_ULTRA_CONDENSED;
  141. }
  142. if (width <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
  143. return NS_FONT_STRETCH_EXTRA_CONDENSED;
  144. }
  145. if (width <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
  146. return NS_FONT_STRETCH_CONDENSED;
  147. }
  148. if (width <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
  149. return NS_FONT_STRETCH_SEMI_CONDENSED;
  150. }
  151. if (width <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
  152. return NS_FONT_STRETCH_NORMAL;
  153. }
  154. if (width <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
  155. return NS_FONT_STRETCH_SEMI_EXPANDED;
  156. }
  157. if (width <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
  158. return NS_FONT_STRETCH_EXPANDED;
  159. }
  160. if (width <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
  161. return NS_FONT_STRETCH_EXTRA_EXPANDED;
  162. }
  163. return NS_FONT_STRETCH_ULTRA_EXPANDED;
  164. }
  165. /* static */ int
  166. gfxFontconfigUtils::FcWidthForThebesStretch(int16_t aStretch)
  167. {
  168. switch (aStretch) {
  169. default: // this will catch "normal" (0) as well as out-of-range values
  170. return FC_WIDTH_NORMAL;
  171. case NS_FONT_STRETCH_ULTRA_CONDENSED:
  172. return FC_WIDTH_ULTRACONDENSED;
  173. case NS_FONT_STRETCH_EXTRA_CONDENSED:
  174. return FC_WIDTH_EXTRACONDENSED;
  175. case NS_FONT_STRETCH_CONDENSED:
  176. return FC_WIDTH_CONDENSED;
  177. case NS_FONT_STRETCH_SEMI_CONDENSED:
  178. return FC_WIDTH_SEMICONDENSED;
  179. case NS_FONT_STRETCH_SEMI_EXPANDED:
  180. return FC_WIDTH_SEMIEXPANDED;
  181. case NS_FONT_STRETCH_EXPANDED:
  182. return FC_WIDTH_EXPANDED;
  183. case NS_FONT_STRETCH_EXTRA_EXPANDED:
  184. return FC_WIDTH_EXTRAEXPANDED;
  185. case NS_FONT_STRETCH_ULTRA_EXPANDED:
  186. return FC_WIDTH_ULTRAEXPANDED;
  187. }
  188. }
  189. // This makes a guess at an FC_WEIGHT corresponding to a base weight and
  190. // offset (without any knowledge of which weights are available).
  191. /* static */ int
  192. GuessFcWeight(const gfxFontStyle& aFontStyle)
  193. {
  194. /*
  195. * weights come in two parts crammed into one
  196. * integer -- the "base" weight is weight / 100,
  197. * the rest of the value is the "offset" from that
  198. * weight -- the number of steps to move to adjust
  199. * the weight in the list of supported font weights,
  200. * this value can be negative or positive.
  201. */
  202. int8_t weight = aFontStyle.ComputeWeight();
  203. // ComputeWeight trimmed the range of weights for us
  204. NS_ASSERTION(weight >= 0 && weight <= 10,
  205. "base weight out of range");
  206. return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
  207. }
  208. static void
  209. AddString(FcPattern *aPattern, const char *object, const char *aString)
  210. {
  211. FcPatternAddString(aPattern, object,
  212. gfxFontconfigUtils::ToFcChar8(aString));
  213. }
  214. static void
  215. AddWeakString(FcPattern *aPattern, const char *object, const char *aString)
  216. {
  217. FcValue value;
  218. value.type = FcTypeString;
  219. value.u.s = gfxFontconfigUtils::ToFcChar8(aString);
  220. FcPatternAddWeak(aPattern, object, value, FcTrue);
  221. }
  222. static void
  223. AddLangGroup(FcPattern *aPattern, nsIAtom *aLangGroup)
  224. {
  225. // Translate from mozilla's internal mapping into fontconfig's
  226. nsAutoCString lang;
  227. gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
  228. if (!lang.IsEmpty()) {
  229. AddString(aPattern, FC_LANG, lang.get());
  230. }
  231. }
  232. nsReturnRef<FcPattern>
  233. gfxFontconfigUtils::NewPattern(const nsTArray<nsString>& aFamilies,
  234. const gfxFontStyle& aFontStyle,
  235. const char *aLang)
  236. {
  237. static const char* sFontconfigGenerics[] =
  238. { "sans-serif", "serif", "monospace", "fantasy", "cursive" };
  239. nsAutoRef<FcPattern> pattern(FcPatternCreate());
  240. if (!pattern)
  241. return nsReturnRef<FcPattern>();
  242. FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
  243. FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
  244. FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
  245. FcPatternAddInteger(pattern, FC_WIDTH, FcWidthForThebesStretch(aFontStyle.stretch));
  246. if (aLang) {
  247. AddString(pattern, FC_LANG, aLang);
  248. }
  249. bool useWeakBinding = false;
  250. for (uint32_t i = 0; i < aFamilies.Length(); ++i) {
  251. NS_ConvertUTF16toUTF8 family(aFamilies[i]);
  252. if (!useWeakBinding) {
  253. AddString(pattern, FC_FAMILY, family.get());
  254. // fontconfig generic families are typically implemented with weak
  255. // aliases (so that the preferred font depends on language).
  256. // However, this would give them lower priority than subsequent
  257. // non-generic families in the list. To ensure that subsequent
  258. // families do not have a higher priority, they are given weak
  259. // bindings.
  260. for (uint32_t g = 0;
  261. g < ArrayLength(sFontconfigGenerics);
  262. ++g) {
  263. if (0 == FcStrCmpIgnoreCase(ToFcChar8(sFontconfigGenerics[g]),
  264. ToFcChar8(family.get()))) {
  265. useWeakBinding = true;
  266. break;
  267. }
  268. }
  269. } else {
  270. AddWeakString(pattern, FC_FAMILY, family.get());
  271. }
  272. }
  273. return pattern.out();
  274. }
  275. gfxFontconfigUtils::gfxFontconfigUtils()
  276. : mFontsByFamily(32)
  277. , mFontsByFullname(32)
  278. , mLangSupportTable(32)
  279. , mLastConfig(nullptr)
  280. #ifdef MOZ_BUNDLED_FONTS
  281. , mBundledFontsInitialized(false)
  282. #endif
  283. {
  284. UpdateFontListInternal();
  285. }
  286. nsresult
  287. gfxFontconfigUtils::GetFontList(nsIAtom *aLangGroup,
  288. const nsACString& aGenericFamily,
  289. nsTArray<nsString>& aListOfFonts)
  290. {
  291. aListOfFonts.Clear();
  292. nsTArray<nsCString> fonts;
  293. nsresult rv = GetFontListInternal(fonts, aLangGroup);
  294. if (NS_FAILED(rv))
  295. return rv;
  296. for (uint32_t i = 0; i < fonts.Length(); ++i) {
  297. aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(fonts[i]));
  298. }
  299. aListOfFonts.Sort();
  300. int32_t serif = 0, sansSerif = 0, monospace = 0;
  301. // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
  302. // "monospace", slightly different from CSS's 5.
  303. if (aGenericFamily.IsEmpty())
  304. serif = sansSerif = monospace = 1;
  305. else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
  306. serif = 1;
  307. else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
  308. sansSerif = 1;
  309. else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
  310. monospace = 1;
  311. else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
  312. aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
  313. serif = sansSerif = 1;
  314. else
  315. NS_NOTREACHED("unexpected CSS generic font family");
  316. // The first in the list becomes the default in
  317. // FontBuilder.readFontSelection() if the preference-selected font is not
  318. // available, so put system configured defaults first.
  319. if (monospace)
  320. aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
  321. if (sansSerif)
  322. aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
  323. if (serif)
  324. aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
  325. return NS_OK;
  326. }
  327. struct MozLangGroupData {
  328. nsIAtom* const& mozLangGroup;
  329. const char *defaultLang;
  330. };
  331. const MozLangGroupData MozLangGroups[] = {
  332. { nsGkAtoms::x_western, "en" },
  333. { nsGkAtoms::x_cyrillic, "ru" },
  334. { nsGkAtoms::x_devanagari, "hi" },
  335. { nsGkAtoms::x_tamil, "ta" },
  336. { nsGkAtoms::x_armn, "hy" },
  337. { nsGkAtoms::x_beng, "bn" },
  338. { nsGkAtoms::x_cans, "iu" },
  339. { nsGkAtoms::x_ethi, "am" },
  340. { nsGkAtoms::x_geor, "ka" },
  341. { nsGkAtoms::x_gujr, "gu" },
  342. { nsGkAtoms::x_guru, "pa" },
  343. { nsGkAtoms::x_khmr, "km" },
  344. { nsGkAtoms::x_knda, "kn" },
  345. { nsGkAtoms::x_mlym, "ml" },
  346. { nsGkAtoms::x_orya, "or" },
  347. { nsGkAtoms::x_sinh, "si" },
  348. { nsGkAtoms::x_telu, "te" },
  349. { nsGkAtoms::x_tibt, "bo" },
  350. { nsGkAtoms::Unicode, 0 },
  351. };
  352. static bool
  353. TryLangForGroup(const nsACString& aOSLang, nsIAtom *aLangGroup,
  354. nsACString *aFcLang)
  355. {
  356. // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
  357. // aOSLang is in the form "language[_territory][.codeset][@modifier]".
  358. // fontconfig takes languages in the form "language-territory".
  359. // nsILanguageAtomService takes languages in the form language-subtag,
  360. // where subtag may be a territory. fontconfig and nsILanguageAtomService
  361. // handle case-conversion for us.
  362. const char *pos, *end;
  363. aOSLang.BeginReading(pos);
  364. aOSLang.EndReading(end);
  365. aFcLang->Truncate();
  366. while (pos < end) {
  367. switch (*pos) {
  368. case '.':
  369. case '@':
  370. end = pos;
  371. break;
  372. case '_':
  373. aFcLang->Append('-');
  374. break;
  375. default:
  376. aFcLang->Append(*pos);
  377. }
  378. ++pos;
  379. }
  380. nsIAtom *atom =
  381. gLangService->LookupLanguage(*aFcLang);
  382. return atom == aLangGroup;
  383. }
  384. /* static */ void
  385. gfxFontconfigUtils::GetSampleLangForGroup(nsIAtom *aLangGroup,
  386. nsACString *aFcLang)
  387. {
  388. NS_PRECONDITION(aFcLang != nullptr, "aFcLang must not be NULL");
  389. const MozLangGroupData *langGroup = nullptr;
  390. for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
  391. if (aLangGroup == MozLangGroups[i].mozLangGroup) {
  392. langGroup = &MozLangGroups[i];
  393. break;
  394. }
  395. }
  396. if (!langGroup) {
  397. // Not a special mozilla language group.
  398. // Use aLangGroup as a language code.
  399. aLangGroup->ToUTF8String(*aFcLang);
  400. return;
  401. }
  402. // Check the environment for the users preferred language that corresponds
  403. // to this langGroup.
  404. if (!gLangService) {
  405. CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
  406. }
  407. if (gLangService) {
  408. const char *languages = getenv("LANGUAGE");
  409. if (languages) {
  410. const char separator = ':';
  411. for (const char *pos = languages; true; ++pos) {
  412. if (*pos == '\0' || *pos == separator) {
  413. if (languages < pos &&
  414. TryLangForGroup(Substring(languages, pos),
  415. aLangGroup, aFcLang))
  416. return;
  417. if (*pos == '\0')
  418. break;
  419. languages = pos + 1;
  420. }
  421. }
  422. }
  423. const char *ctype = setlocale(LC_CTYPE, nullptr);
  424. if (ctype &&
  425. TryLangForGroup(nsDependentCString(ctype), aLangGroup, aFcLang))
  426. return;
  427. }
  428. if (langGroup->defaultLang) {
  429. aFcLang->Assign(langGroup->defaultLang);
  430. } else {
  431. aFcLang->Truncate();
  432. }
  433. }
  434. nsresult
  435. gfxFontconfigUtils::GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
  436. nsIAtom *aLangGroup)
  437. {
  438. FcPattern *pat = nullptr;
  439. FcObjectSet *os = nullptr;
  440. FcFontSet *fs = nullptr;
  441. nsresult rv = NS_ERROR_FAILURE;
  442. aListOfFonts.Clear();
  443. pat = FcPatternCreate();
  444. if (!pat)
  445. goto end;
  446. os = FcObjectSetBuild(FC_FAMILY, nullptr);
  447. if (!os)
  448. goto end;
  449. // take the pattern and add the lang group to it
  450. if (aLangGroup) {
  451. AddLangGroup(pat, aLangGroup);
  452. }
  453. fs = FcFontList(nullptr, pat, os);
  454. if (!fs)
  455. goto end;
  456. for (int i = 0; i < fs->nfont; i++) {
  457. char *family;
  458. if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
  459. (FcChar8 **) &family) != FcResultMatch)
  460. {
  461. continue;
  462. }
  463. // Remove duplicates...
  464. nsAutoCString strFamily(family);
  465. if (aListOfFonts.Contains(strFamily))
  466. continue;
  467. aListOfFonts.AppendElement(strFamily);
  468. }
  469. rv = NS_OK;
  470. end:
  471. if (NS_FAILED(rv))
  472. aListOfFonts.Clear();
  473. if (pat)
  474. FcPatternDestroy(pat);
  475. if (os)
  476. FcObjectSetDestroy(os);
  477. if (fs)
  478. FcFontSetDestroy(fs);
  479. return rv;
  480. }
  481. nsresult
  482. gfxFontconfigUtils::UpdateFontList()
  483. {
  484. return UpdateFontListInternal(true);
  485. }
  486. nsresult
  487. gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
  488. {
  489. if (!aForce) {
  490. // This checks periodically according to fontconfig's configured
  491. // <rescan> interval.
  492. FcInitBringUptoDate();
  493. } else if (!FcConfigUptoDate(nullptr)) { // check now with aForce
  494. mLastConfig = nullptr;
  495. FcInitReinitialize();
  496. }
  497. // FcInitReinitialize() (used by FcInitBringUptoDate) creates a new config
  498. // before destroying the old config, so the only way that we'd miss an
  499. // update is if fontconfig did more than one update and the memory for the
  500. // most recent config happened to be at the same location as the original
  501. // config.
  502. FcConfig *currentConfig = FcConfigGetCurrent();
  503. if (currentConfig == mLastConfig)
  504. return NS_OK;
  505. #ifdef MOZ_BUNDLED_FONTS
  506. ActivateBundledFonts();
  507. #endif
  508. // These FcFontSets are owned by fontconfig
  509. FcFontSet *fontSets[] = {
  510. FcConfigGetFonts(currentConfig, FcSetSystem)
  511. #ifdef MOZ_BUNDLED_FONTS
  512. , FcConfigGetFonts(currentConfig, FcSetApplication)
  513. #endif
  514. };
  515. mFontsByFamily.Clear();
  516. mFontsByFullname.Clear();
  517. mLangSupportTable.Clear();
  518. // Record the existing font families
  519. for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
  520. FcFontSet *fontSet = fontSets[fs];
  521. if (!fontSet) { // the application set might not exist
  522. continue;
  523. }
  524. for (int f = 0; f < fontSet->nfont; ++f) {
  525. FcPattern *font = fontSet->fonts[f];
  526. FcChar8 *family;
  527. for (int v = 0;
  528. FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
  529. ++v) {
  530. FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
  531. if (entry) {
  532. bool added = entry->AddFont(font);
  533. if (!entry->mKey) {
  534. // The reference to the font pattern keeps the pointer
  535. // to string for the key valid. If adding the font
  536. // failed then the entry must be removed.
  537. if (added) {
  538. entry->mKey = family;
  539. } else {
  540. mFontsByFamily.RemoveEntry(entry);
  541. }
  542. }
  543. }
  544. }
  545. }
  546. }
  547. mLastConfig = currentConfig;
  548. return NS_OK;
  549. }
  550. nsresult
  551. gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
  552. {
  553. aFamilyName.Truncate();
  554. // The fontconfig has generic family names in the font list.
  555. if (aFontName.EqualsLiteral("serif") ||
  556. aFontName.EqualsLiteral("sans-serif") ||
  557. aFontName.EqualsLiteral("monospace")) {
  558. aFamilyName.Assign(aFontName);
  559. return NS_OK;
  560. }
  561. nsresult rv = UpdateFontListInternal();
  562. if (NS_FAILED(rv))
  563. return rv;
  564. NS_ConvertUTF16toUTF8 fontname(aFontName);
  565. // return empty string if no such family exists
  566. if (!IsExistingFamily(fontname))
  567. return NS_OK;
  568. FcPattern *pat = nullptr;
  569. FcObjectSet *os = nullptr;
  570. FcFontSet *givenFS = nullptr;
  571. nsTArray<nsCString> candidates;
  572. FcFontSet *candidateFS = nullptr;
  573. rv = NS_ERROR_FAILURE;
  574. pat = FcPatternCreate();
  575. if (!pat)
  576. goto end;
  577. FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
  578. os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, nullptr);
  579. if (!os)
  580. goto end;
  581. givenFS = FcFontList(nullptr, pat, os);
  582. if (!givenFS)
  583. goto end;
  584. // The first value associated with a FC_FAMILY property is the family
  585. // returned by GetFontList(), so use this value if appropriate.
  586. // See if there is a font face with first family equal to the given family.
  587. for (int i = 0; i < givenFS->nfont; ++i) {
  588. char *firstFamily;
  589. if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
  590. (FcChar8 **) &firstFamily) != FcResultMatch)
  591. continue;
  592. nsDependentCString first(firstFamily);
  593. if (!candidates.Contains(first)) {
  594. candidates.AppendElement(first);
  595. if (fontname.Equals(first)) {
  596. aFamilyName.Assign(aFontName);
  597. rv = NS_OK;
  598. goto end;
  599. }
  600. }
  601. }
  602. // See if any of the first family names represent the same set of font
  603. // faces as the given family.
  604. for (uint32_t j = 0; j < candidates.Length(); ++j) {
  605. FcPatternDel(pat, FC_FAMILY);
  606. FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
  607. candidateFS = FcFontList(nullptr, pat, os);
  608. if (!candidateFS)
  609. goto end;
  610. if (candidateFS->nfont != givenFS->nfont)
  611. continue;
  612. bool equal = true;
  613. for (int i = 0; i < givenFS->nfont; ++i) {
  614. if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
  615. equal = false;
  616. break;
  617. }
  618. }
  619. if (equal) {
  620. AppendUTF8toUTF16(candidates[j], aFamilyName);
  621. rv = NS_OK;
  622. goto end;
  623. }
  624. }
  625. // No match found; return empty string.
  626. rv = NS_OK;
  627. end:
  628. if (pat)
  629. FcPatternDestroy(pat);
  630. if (os)
  631. FcObjectSetDestroy(os);
  632. if (givenFS)
  633. FcFontSetDestroy(givenFS);
  634. if (candidateFS)
  635. FcFontSetDestroy(candidateFS);
  636. return rv;
  637. }
  638. bool
  639. gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
  640. {
  641. return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName)) != nullptr;
  642. }
  643. const nsTArray< nsCountedRef<FcPattern> >&
  644. gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
  645. {
  646. FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
  647. if (!entry)
  648. return mEmptyPatternArray;
  649. return entry->GetFonts();
  650. }
  651. // Fontconfig only provides a fullname property for fonts in formats with SFNT
  652. // wrappers. For other font formats (including PCF and PS Type 1), a fullname
  653. // must be generated from the family and style properties. Only the first
  654. // family and style is checked, but that should be OK, as I don't expect
  655. // non-SFNT fonts to have multiple families or styles.
  656. bool
  657. gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(FcPattern *aFont,
  658. nsACString *aFullname)
  659. {
  660. FcChar8 *family;
  661. if (FcPatternGetString(aFont, FC_FAMILY, 0, &family) != FcResultMatch)
  662. return false;
  663. aFullname->Truncate();
  664. aFullname->Append(ToCString(family));
  665. FcChar8 *style;
  666. if (FcPatternGetString(aFont, FC_STYLE, 0, &style) == FcResultMatch &&
  667. strcmp(ToCString(style), "Regular") != 0) {
  668. aFullname->Append(' ');
  669. aFullname->Append(ToCString(style));
  670. }
  671. return true;
  672. }
  673. bool
  674. gfxFontconfigUtils::FontsByFullnameEntry::KeyEquals(KeyTypePointer aKey) const
  675. {
  676. const FcChar8 *key = mKey;
  677. // If mKey is nullptr, key comes from the style and family of the first
  678. // font.
  679. nsAutoCString fullname;
  680. if (!key) {
  681. NS_ASSERTION(mFonts.Length(), "No font in FontsByFullnameEntry!");
  682. GetFullnameFromFamilyAndStyle(mFonts[0], &fullname);
  683. key = ToFcChar8(fullname);
  684. }
  685. return FcStrCmpIgnoreCase(aKey, key) == 0;
  686. }
  687. void
  688. gfxFontconfigUtils::AddFullnameEntries()
  689. {
  690. // These FcFontSets are owned by fontconfig
  691. FcFontSet *fontSets[] = {
  692. FcConfigGetFonts(nullptr, FcSetSystem)
  693. #ifdef MOZ_BUNDLED_FONTS
  694. , FcConfigGetFonts(nullptr, FcSetApplication)
  695. #endif
  696. };
  697. for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
  698. FcFontSet *fontSet = fontSets[fs];
  699. if (!fontSet) {
  700. continue;
  701. }
  702. // Record the existing font families
  703. for (int f = 0; f < fontSet->nfont; ++f) {
  704. FcPattern *font = fontSet->fonts[f];
  705. int v = 0;
  706. FcChar8 *fullname;
  707. while (FcPatternGetString(font,
  708. FC_FULLNAME, v, &fullname) == FcResultMatch) {
  709. FontsByFullnameEntry *entry =
  710. mFontsByFullname.PutEntry(fullname);
  711. if (entry) {
  712. // entry always has space for one font, so the first
  713. // AddFont will always succeed, and so the entry will
  714. // always have a font from which to obtain the key.
  715. bool added = entry->AddFont(font);
  716. // The key may be nullptr either if this is the first
  717. // font, or if the first font does not have a fullname
  718. // property, and so the key is obtained from the font.
  719. // Set the key in both cases. The check that AddFont
  720. // succeeded is required for the second case.
  721. if (!entry->mKey && added) {
  722. entry->mKey = fullname;
  723. }
  724. }
  725. ++v;
  726. }
  727. // Fontconfig does not provide a fullname property for all fonts.
  728. if (v == 0) {
  729. nsAutoCString name;
  730. if (!GetFullnameFromFamilyAndStyle(font, &name))
  731. continue;
  732. FontsByFullnameEntry *entry =
  733. mFontsByFullname.PutEntry(ToFcChar8(name));
  734. if (entry) {
  735. entry->AddFont(font);
  736. // Either entry->mKey has been set for a previous font or it
  737. // remains nullptr to indicate that the key is obtained from
  738. // the first font.
  739. }
  740. }
  741. }
  742. }
  743. }
  744. const nsTArray< nsCountedRef<FcPattern> >&
  745. gfxFontconfigUtils::GetFontsForFullname(const FcChar8 *aFullname)
  746. {
  747. if (mFontsByFullname.Count() == 0) {
  748. AddFullnameEntries();
  749. }
  750. FontsByFullnameEntry *entry = mFontsByFullname.GetEntry(aFullname);
  751. if (!entry)
  752. return mEmptyPatternArray;
  753. return entry->GetFonts();
  754. }
  755. static FcLangResult
  756. CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
  757. FcLangResult result = FcLangDifferentLang;
  758. for (uint32_t i = 0; ; ++i) {
  759. FcChar8 a = FcToLower(aLangA[i]);
  760. FcChar8 b = FcToLower(aLangB[i]);
  761. if (a != b) {
  762. if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
  763. return FcLangDifferentCountry;
  764. return result;
  765. }
  766. if (a == '\0')
  767. return FcLangEqual;
  768. if (a == '-') {
  769. result = FcLangDifferentCountry;
  770. }
  771. }
  772. }
  773. /* static */
  774. FcLangResult
  775. gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
  776. {
  777. // When fontconfig builds a pattern for a system font, it will set a
  778. // single LangSet property value for the font. That value may be removed
  779. // and additional string values may be added through FcConfigSubsitute
  780. // with FcMatchScan. Values that are neither LangSet nor string are
  781. // considered errors in fontconfig sort and match functions.
  782. //
  783. // If no string nor LangSet value is found, then either the font is a
  784. // system font and the LangSet has been removed through FcConfigSubsitute,
  785. // or the font is a web font and its language support is unknown.
  786. // Returning FcLangDifferentLang for these fonts ensures that this font
  787. // will not be assumed to satisfy the language, and so language will be
  788. // prioritized in sorting fallback fonts.
  789. FcValue value;
  790. FcLangResult best = FcLangDifferentLang;
  791. for (int v = 0;
  792. FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch;
  793. ++v) {
  794. FcLangResult support;
  795. switch (value.type) {
  796. case FcTypeLangSet:
  797. support = FcLangSetHasLang(value.u.l, aLang);
  798. break;
  799. case FcTypeString:
  800. support = CompareLangString(value.u.s, aLang);
  801. break;
  802. default:
  803. // error. continue to see if there is a useful value.
  804. continue;
  805. }
  806. if (support < best) { // lower is better
  807. if (support == FcLangEqual)
  808. return support;
  809. best = support;
  810. }
  811. }
  812. return best;
  813. }
  814. gfxFontconfigUtils::LangSupportEntry *
  815. gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts)
  816. {
  817. // Currently any unrecognized languages from documents will be converted
  818. // to x-unicode by nsILanguageAtomService, so there is a limit on the
  819. // langugages that will be added here. Reconsider when/if document
  820. // languages are passed to this routine.
  821. LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
  822. if (!entry)
  823. return nullptr;
  824. FcLangResult best = FcLangDifferentLang;
  825. if (!entry->IsKeyInitialized()) {
  826. entry->InitKey(aLang);
  827. } else {
  828. // mSupport is already initialized.
  829. if (!aWithFonts)
  830. return entry;
  831. best = entry->mSupport;
  832. // If there is support for this language, an empty font list indicates
  833. // that the list hasn't been initialized yet.
  834. if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
  835. return entry;
  836. }
  837. // These FcFontSets are owned by fontconfig
  838. FcFontSet *fontSets[] = {
  839. FcConfigGetFonts(nullptr, FcSetSystem)
  840. #ifdef MOZ_BUNDLED_FONTS
  841. , FcConfigGetFonts(nullptr, FcSetApplication)
  842. #endif
  843. };
  844. AutoTArray<FcPattern*,100> fonts;
  845. for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
  846. FcFontSet *fontSet = fontSets[fs];
  847. if (!fontSet) {
  848. continue;
  849. }
  850. for (int f = 0; f < fontSet->nfont; ++f) {
  851. FcPattern *font = fontSet->fonts[f];
  852. FcLangResult support = GetLangSupport(font, aLang);
  853. if (support < best) { // lower is better
  854. best = support;
  855. if (aWithFonts) {
  856. fonts.Clear();
  857. } else if (best == FcLangEqual) {
  858. break;
  859. }
  860. }
  861. // The font list in the LangSupportEntry is expected to be used
  862. // only when no default fonts support the language. There would
  863. // be a large number of fonts in entries for languages using Latin
  864. // script but these do not need to be created because default
  865. // fonts already support these languages.
  866. if (aWithFonts && support != FcLangDifferentLang &&
  867. support == best) {
  868. fonts.AppendElement(font);
  869. }
  870. }
  871. }
  872. entry->mSupport = best;
  873. if (aWithFonts) {
  874. if (fonts.Length() != 0) {
  875. entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
  876. } else if (best != FcLangDifferentLang) {
  877. // Previously there was a font that supported this language at the
  878. // level of entry->mSupport, but it has now disappeared. At least
  879. // entry->mSupport needs to be recalculated, but this is an
  880. // indication that the set of installed fonts has changed, so
  881. // update all caches.
  882. mLastConfig = nullptr; // invalidates caches
  883. UpdateFontListInternal(true);
  884. return GetLangSupportEntry(aLang, aWithFonts);
  885. }
  886. }
  887. return entry;
  888. }
  889. FcLangResult
  890. gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
  891. {
  892. UpdateFontListInternal();
  893. LangSupportEntry *entry = GetLangSupportEntry(aLang, false);
  894. if (!entry)
  895. return FcLangEqual;
  896. return entry->mSupport;
  897. }
  898. const nsTArray< nsCountedRef<FcPattern> >&
  899. gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
  900. {
  901. LangSupportEntry *entry = GetLangSupportEntry(aLang, true);
  902. if (!entry)
  903. return mEmptyPatternArray;
  904. return entry->mFonts;
  905. }
  906. #ifdef MOZ_BUNDLED_FONTS
  907. void
  908. gfxFontconfigUtils::ActivateBundledFonts()
  909. {
  910. if (!mBundledFontsInitialized) {
  911. mBundledFontsInitialized = true;
  912. nsCOMPtr<nsIFile> localDir;
  913. nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
  914. if (NS_FAILED(rv)) {
  915. return;
  916. }
  917. if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
  918. return;
  919. }
  920. bool isDir;
  921. if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
  922. return;
  923. }
  924. if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
  925. return;
  926. }
  927. }
  928. if (!mBundledFontsPath.IsEmpty()) {
  929. FcConfigAppFontAddDir(nullptr, (const FcChar8*)mBundledFontsPath.get());
  930. }
  931. }
  932. #endif
  933. gfxFontconfigFontBase::gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
  934. FcPattern *aPattern,
  935. gfxFontEntry *aFontEntry,
  936. const gfxFontStyle *aFontStyle)
  937. : gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
  938. , mPattern(aPattern)
  939. {
  940. }