FontPlugin.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "FontPlugin.h"
  8. #include <AK/ByteString.h>
  9. #include <AK/String.h>
  10. #include <AK/TypeCasts.h>
  11. #include <LibCore/Resource.h>
  12. #include <LibCore/StandardPaths.h>
  13. #include <LibGfx/Font/FontDatabase.h>
  14. #include <LibGfx/Font/PathFontProvider.h>
  15. #ifdef USE_FONTCONFIG
  16. # include <fontconfig/fontconfig.h>
  17. #endif
  18. namespace Ladybird {
  19. FontPlugin::FontPlugin(bool is_layout_test_mode, Gfx::SystemFontProvider* font_provider)
  20. : m_is_layout_test_mode(is_layout_test_mode)
  21. {
  22. #ifdef USE_FONTCONFIG
  23. {
  24. auto fontconfig_initialized = FcInit();
  25. VERIFY(fontconfig_initialized);
  26. }
  27. #endif
  28. if (!font_provider)
  29. font_provider = &static_cast<Gfx::PathFontProvider&>(Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>()));
  30. if (is<Gfx::PathFontProvider>(*font_provider)) {
  31. auto& path_font_provider = static_cast<Gfx::PathFontProvider&>(*font_provider);
  32. // Load anything we can find in the system's font directories
  33. for (auto const& path : Core::StandardPaths::font_directories().release_value_but_fixme_should_propagate_errors())
  34. path_font_provider.load_all_fonts_from_uri(MUST(String::formatted("file://{}", path)));
  35. }
  36. update_generic_fonts();
  37. auto default_font_name = generic_font_name(Web::Platform::GenericFont::UiSansSerif);
  38. m_default_font = Gfx::FontDatabase::the().get(default_font_name, 12.0, 400, Gfx::FontWidth::Normal, 0);
  39. VERIFY(m_default_font);
  40. auto default_fixed_width_font_name = generic_font_name(Web::Platform::GenericFont::UiMonospace);
  41. m_default_fixed_width_font = Gfx::FontDatabase::the().get(default_fixed_width_font_name, 12.0, 400, Gfx::FontWidth::Normal, 0);
  42. VERIFY(m_default_fixed_width_font);
  43. }
  44. FontPlugin::~FontPlugin() = default;
  45. Gfx::Font& FontPlugin::default_font()
  46. {
  47. return *m_default_font;
  48. }
  49. Gfx::Font& FontPlugin::default_fixed_width_font()
  50. {
  51. return *m_default_fixed_width_font;
  52. }
  53. RefPtr<Gfx::Font> FontPlugin::default_emoji_font(float point_size)
  54. {
  55. FlyString default_emoji_font_name;
  56. if (m_is_layout_test_mode) {
  57. default_emoji_font_name = "Noto Emoji"_fly_string;
  58. } else {
  59. #ifdef AK_OS_MACOS
  60. default_emoji_font_name = "Apple Color Emoji"_fly_string;
  61. #else
  62. default_emoji_font_name = "Noto Color Emoji"_fly_string;
  63. #endif
  64. }
  65. return Gfx::FontDatabase::the().get(default_emoji_font_name, point_size, 400, Gfx::FontWidth::Normal, 0);
  66. }
  67. #ifdef USE_FONTCONFIG
  68. static Optional<String> query_fontconfig_for_generic_family(Web::Platform::GenericFont generic_font)
  69. {
  70. char const* pattern_string = nullptr;
  71. switch (generic_font) {
  72. case Web::Platform::GenericFont::Cursive:
  73. pattern_string = "cursive";
  74. break;
  75. case Web::Platform::GenericFont::Fantasy:
  76. pattern_string = "fantasy";
  77. break;
  78. case Web::Platform::GenericFont::Monospace:
  79. pattern_string = "monospace";
  80. break;
  81. case Web::Platform::GenericFont::SansSerif:
  82. pattern_string = "sans-serif";
  83. break;
  84. case Web::Platform::GenericFont::Serif:
  85. pattern_string = "serif";
  86. break;
  87. case Web::Platform::GenericFont::UiMonospace:
  88. pattern_string = "monospace";
  89. break;
  90. case Web::Platform::GenericFont::UiRounded:
  91. pattern_string = "sans-serif";
  92. break;
  93. case Web::Platform::GenericFont::UiSansSerif:
  94. pattern_string = "sans-serif";
  95. break;
  96. case Web::Platform::GenericFont::UiSerif:
  97. pattern_string = "serif";
  98. break;
  99. default:
  100. VERIFY_NOT_REACHED();
  101. }
  102. auto* config = FcConfigGetCurrent();
  103. VERIFY(config);
  104. FcPattern* pattern = FcNameParse(reinterpret_cast<FcChar8 const*>(pattern_string));
  105. VERIFY(pattern);
  106. auto success = FcConfigSubstitute(config, pattern, FcMatchPattern);
  107. VERIFY(success);
  108. FcDefaultSubstitute(pattern);
  109. // Never select bitmap fonts.
  110. success = FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
  111. VERIFY(success);
  112. // FIXME: Enable this once we can handle OpenType variable fonts.
  113. success = FcPatternAddBool(pattern, FC_VARIABLE, FcFalse);
  114. VERIFY(success);
  115. Optional<String> name;
  116. FcResult result {};
  117. if (auto* matched = FcFontMatch(config, pattern, &result)) {
  118. FcChar8* family = nullptr;
  119. if (FcPatternGetString(matched, FC_FAMILY, 0, &family) == FcResultMatch) {
  120. auto const* family_cstring = reinterpret_cast<char const*>(family);
  121. if (auto string = String::from_utf8(StringView { family_cstring, strlen(family_cstring) }); !string.is_error()) {
  122. name = string.release_value();
  123. }
  124. }
  125. FcPatternDestroy(matched);
  126. }
  127. FcPatternDestroy(pattern);
  128. return name;
  129. }
  130. #endif
  131. void FontPlugin::update_generic_fonts()
  132. {
  133. // How we choose which system font to use for each CSS font:
  134. // 1. Try a list of known-suitable fonts with their names hard-coded below.
  135. // This is rather weird, but it's how things work right now.
  136. // We should eventually have a way to query the system for the default font.
  137. // Furthermore, we should allow overriding via some kind of configuration mechanism.
  138. m_generic_font_names.resize(static_cast<size_t>(Web::Platform::GenericFont::__Count));
  139. auto update_mapping = [&](Web::Platform::GenericFont generic_font, ReadonlySpan<FlyString> fallbacks) {
  140. if (m_is_layout_test_mode) {
  141. m_generic_font_names[static_cast<size_t>(generic_font)] = "SerenitySans"_fly_string;
  142. return;
  143. }
  144. RefPtr<Gfx::Font const> gfx_font;
  145. #ifdef USE_FONTCONFIG
  146. auto name = query_fontconfig_for_generic_family(generic_font);
  147. if (name.has_value()) {
  148. gfx_font = Gfx::FontDatabase::the().get(name.value(), 16, 400, Gfx::FontWidth::Normal, 0);
  149. }
  150. #endif
  151. if (!gfx_font) {
  152. for (auto const& fallback : fallbacks) {
  153. gfx_font = Gfx::FontDatabase::the().get(fallback, 16, 400, Gfx::FontWidth::Normal, 0);
  154. if (gfx_font)
  155. break;
  156. }
  157. }
  158. m_generic_font_names[static_cast<size_t>(generic_font)] = gfx_font ? gfx_font->family() : String {};
  159. };
  160. // Fallback fonts to look for if Gfx::Font can't load expected font
  161. // The lists are basically arbitrary, taken from https://www.w3.org/Style/Examples/007/fonts.en.html
  162. // (We also add Android-specific font names to the list from W3 where required.)
  163. Vector<FlyString> cursive_fallbacks { "Comic Sans MS"_fly_string, "Comic Sans"_fly_string, "Apple Chancery"_fly_string, "Bradley Hand"_fly_string, "Brush Script MT"_fly_string, "Snell Roundhand"_fly_string, "URW Chancery L"_fly_string, "Dancing Script"_fly_string };
  164. Vector<FlyString> fantasy_fallbacks { "Impact"_fly_string, "Luminari"_fly_string, "Chalkduster"_fly_string, "Jazz LET"_fly_string, "Blippo"_fly_string, "Stencil Std"_fly_string, "Marker Felt"_fly_string, "Trattatello"_fly_string, "Coming Soon"_fly_string };
  165. Vector<FlyString> monospace_fallbacks { "Andale Mono"_fly_string, "Courier New"_fly_string, "Courier"_fly_string, "FreeMono"_fly_string, "OCR A Std"_fly_string, "DejaVu Sans Mono"_fly_string, "Droid Sans Mono"_fly_string, "Liberation Mono"_fly_string };
  166. Vector<FlyString> sans_serif_fallbacks { "Arial"_fly_string, "Helvetica"_fly_string, "Verdana"_fly_string, "Trebuchet MS"_fly_string, "Gill Sans"_fly_string, "Noto Sans"_fly_string, "Avantgarde"_fly_string, "Optima"_fly_string, "Arial Narrow"_fly_string, "Liberation Sans"_fly_string, "Roboto"_fly_string };
  167. Vector<FlyString> serif_fallbacks { "Times"_fly_string, "Times New Roman"_fly_string, "Didot"_fly_string, "Georgia"_fly_string, "Palatino"_fly_string, "Bookman"_fly_string, "New Century Schoolbook"_fly_string, "American Typewriter"_fly_string, "Liberation Serif"_fly_string, "Roman"_fly_string, "Noto Serif"_fly_string };
  168. update_mapping(Web::Platform::GenericFont::Cursive, cursive_fallbacks);
  169. update_mapping(Web::Platform::GenericFont::Fantasy, fantasy_fallbacks);
  170. update_mapping(Web::Platform::GenericFont::Monospace, monospace_fallbacks);
  171. update_mapping(Web::Platform::GenericFont::SansSerif, sans_serif_fallbacks);
  172. update_mapping(Web::Platform::GenericFont::Serif, serif_fallbacks);
  173. update_mapping(Web::Platform::GenericFont::UiMonospace, monospace_fallbacks);
  174. update_mapping(Web::Platform::GenericFont::UiRounded, sans_serif_fallbacks);
  175. update_mapping(Web::Platform::GenericFont::UiSansSerif, sans_serif_fallbacks);
  176. update_mapping(Web::Platform::GenericFont::UiSerif, serif_fallbacks);
  177. }
  178. FlyString FontPlugin::generic_font_name(Web::Platform::GenericFont generic_font)
  179. {
  180. return m_generic_font_names[static_cast<size_t>(generic_font)];
  181. }
  182. }