pdf_fontconfig_matching.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/services/font/pdf_fontconfig_matching.h"
  5. #include <fcntl.h>
  6. #include <fontconfig/fontconfig.h>
  7. #include <stddef.h>
  8. #include <string.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <memory>
  12. #include <string>
  13. #include "base/posix/eintr_wrapper.h"
  14. #include "base/strings/string_util.h"
  15. #include "third_party/blink/public/platform/web_font_description.h"
  16. #include "third_party/pdfium/public/fpdf_sysfontinfo.h"
  17. namespace {
  18. // MSCharSetToFontconfig translates a Microsoft charset identifier to a
  19. // fontconfig language set by appending to |langset|.
  20. // Returns true if |langset| is Latin/Greek/Cyrillic.
  21. bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
  22. // We have need to translate raw fdwCharSet values into terms that
  23. // fontconfig can understand. (See the description of fdwCharSet in the MSDN
  24. // documentation for CreateFont:
  25. // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
  26. //
  27. // Although the argument is /called/ 'charset', the actual values conflate
  28. // character sets (which are sets of Unicode code points) and character
  29. // encodings (which are algorithms for turning a series of bits into a
  30. // series of code points.) Sometimes the values will name a language,
  31. // sometimes they'll name an encoding. In the latter case I'm assuming that
  32. // they mean the set of code points in the domain of that encoding.
  33. //
  34. // fontconfig deals with ISO 639-1 language codes:
  35. // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
  36. //
  37. // So, for each of the documented fdwCharSet values I've had to take a
  38. // guess at the set of ISO 639-1 languages intended.
  39. bool is_lgc = false;
  40. switch (fdwCharSet) {
  41. case FXFONT_ANSI_CHARSET:
  42. // These values I don't really know what to do with, so I'm going to map
  43. // them to English also.
  44. case FXFONT_DEFAULT_CHARSET:
  45. case FXFONT_SYMBOL_CHARSET:
  46. is_lgc = true;
  47. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
  48. break;
  49. case FXFONT_SHIFTJIS_CHARSET:
  50. // Japanese
  51. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
  52. break;
  53. case FXFONT_HANGEUL_CHARSET:
  54. // Korean
  55. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
  56. break;
  57. case FXFONT_GB2312_CHARSET:
  58. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn"));
  59. break;
  60. case FXFONT_CHINESEBIG5_CHARSET:
  61. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw"));
  62. break;
  63. case FXFONT_GREEK_CHARSET:
  64. is_lgc = true;
  65. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
  66. break;
  67. case FXFONT_VIETNAMESE_CHARSET:
  68. is_lgc = true;
  69. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
  70. break;
  71. case FXFONT_HEBREW_CHARSET:
  72. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
  73. break;
  74. case FXFONT_ARABIC_CHARSET:
  75. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
  76. break;
  77. case FXFONT_CYRILLIC_CHARSET:
  78. is_lgc = true;
  79. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
  80. break;
  81. case FXFONT_THAI_CHARSET:
  82. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
  83. break;
  84. case FXFONT_EASTERNEUROPEAN_CHARSET:
  85. // A scattering of eastern European languages.
  86. is_lgc = true;
  87. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
  88. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
  89. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
  90. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
  91. FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
  92. break;
  93. // default:
  94. // Don't add any languages in that case that we don't recognise the
  95. // constant.
  96. }
  97. return is_lgc;
  98. }
  99. } // namespace
  100. namespace font_service {
  101. int MatchFontFaceWithFallback(const std::string& face,
  102. bool is_bold,
  103. bool is_italic,
  104. uint32_t charset,
  105. uint32_t fallback_family) {
  106. std::unique_ptr<FcLangSet, decltype(&FcLangSetDestroy)> langset(
  107. FcLangSetCreate(), &FcLangSetDestroy);
  108. bool is_lgc = MSCharSetToFontconfig(langset.get(), charset);
  109. std::unique_ptr<FcPattern, decltype(&FcPatternDestroy)> pattern(
  110. FcPatternCreate(), &FcPatternDestroy);
  111. FcPatternAddString(pattern.get(), FC_FAMILY,
  112. reinterpret_cast<const FcChar8*>(face.c_str()));
  113. // TODO(thestig) Check if we can access Chrome's per-script font preference
  114. // here and select better default fonts for non-LGC case.
  115. std::string generic_font_name;
  116. if (is_lgc) {
  117. switch (fallback_family) {
  118. case blink::WebFontDescription::kGenericFamilySerif:
  119. generic_font_name = "Times New Roman";
  120. break;
  121. case blink::WebFontDescription::kGenericFamilySansSerif:
  122. generic_font_name = "Arial";
  123. break;
  124. case blink::WebFontDescription::kGenericFamilyMonospace:
  125. generic_font_name = "Courier New";
  126. break;
  127. }
  128. }
  129. if (!generic_font_name.empty()) {
  130. const FcChar8* fc_generic_font_name =
  131. reinterpret_cast<const FcChar8*>(generic_font_name.c_str());
  132. FcPatternAddString(pattern.get(), FC_FAMILY, fc_generic_font_name);
  133. }
  134. if (is_bold)
  135. FcPatternAddInteger(pattern.get(), FC_WEIGHT, FC_WEIGHT_BOLD);
  136. if (is_italic)
  137. FcPatternAddInteger(pattern.get(), FC_SLANT, FC_SLANT_ITALIC);
  138. FcPatternAddLangSet(pattern.get(), FC_LANG, langset.get());
  139. FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
  140. FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
  141. FcDefaultSubstitute(pattern.get());
  142. FcResult result;
  143. std::unique_ptr<FcFontSet, decltype(&FcFontSetDestroy)> font_set(
  144. FcFontSort(nullptr, pattern.get(), 0, nullptr, &result),
  145. &FcFontSetDestroy);
  146. int font_fd = -1;
  147. int good_enough_index = -1;
  148. bool good_enough_index_set = false;
  149. const char* c_filename;
  150. const char* c_sysroot =
  151. reinterpret_cast<const char*>(FcConfigGetSysRoot(nullptr));
  152. const std::string sysroot = c_sysroot ? c_sysroot : "";
  153. if (font_set) {
  154. for (int i = 0; i < font_set->nfont; ++i) {
  155. FcPattern* current = font_set->fonts[i];
  156. // Older versions of fontconfig have a bug where they cannot select
  157. // only scalable fonts so we have to manually filter the results.
  158. FcBool is_scalable;
  159. if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) !=
  160. FcResultMatch ||
  161. !is_scalable) {
  162. continue;
  163. }
  164. if (FcPatternGetString(current, FC_FILE, 0,
  165. reinterpret_cast<FcChar8**>(const_cast<char**>(
  166. &c_filename))) != FcResultMatch) {
  167. continue;
  168. }
  169. const std::string filename = sysroot + c_filename;
  170. // We only want to return sfnt (TrueType) based fonts. We don't have a
  171. // very good way of detecting this so we'll filter based on the
  172. // filename.
  173. bool is_sfnt = false;
  174. static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc"};
  175. for (const char* extension : kSFNTExtensions) {
  176. if (base::EndsWith(filename, extension, base::CompareCase::SENSITIVE)) {
  177. is_sfnt = true;
  178. break;
  179. }
  180. }
  181. if (!is_sfnt)
  182. continue;
  183. // This font is good enough to pass muster, but we might be able to do
  184. // better with subsequent ones.
  185. if (!good_enough_index_set) {
  186. good_enough_index = i;
  187. good_enough_index_set = true;
  188. }
  189. FcValue matrix;
  190. bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
  191. if (is_italic && have_matrix) {
  192. // we asked for an italic font, but fontconfig is giving us a
  193. // non-italic font with a transformation matrix.
  194. continue;
  195. }
  196. FcValue embolden;
  197. const bool have_embolden =
  198. FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
  199. if (is_bold && have_embolden) {
  200. // we asked for a bold font, but fontconfig gave us a non-bold font
  201. // and asked us to apply fake bolding.
  202. continue;
  203. }
  204. font_fd = HANDLE_EINTR(open(filename.c_str(), O_RDONLY));
  205. if (font_fd >= 0)
  206. break;
  207. }
  208. }
  209. if (font_fd == -1 && good_enough_index_set) {
  210. // We didn't find a font that we liked, so we fallback to something
  211. // acceptable.
  212. FcPattern* current = font_set->fonts[good_enough_index];
  213. if (!FcPatternGetString(
  214. current, FC_FILE, 0,
  215. reinterpret_cast<FcChar8**>(const_cast<char**>(&c_filename)))) {
  216. const std::string filename = sysroot + c_filename;
  217. font_fd = HANDLE_EINTR(open(filename.c_str(), O_RDONLY));
  218. }
  219. }
  220. return font_fd;
  221. }
  222. } // namespace font_service