From 2f68e361370040d8cdc75a8ed8af4239134ae481 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 7 Jun 2024 19:50:00 +0200 Subject: [PATCH] Ladybird: Use fontconfig to choose generic fonts on non-Apple systems If we get a suggestion from fontconfig, we try those fonts first, before falling back on the hard coded list of known suitable fonts for each generic family. --- Ladybird/FontPlugin.cpp | 101 +++++++++++++++++++++++++++-- Ladybird/WebContent/CMakeLists.txt | 6 ++ Ladybird/WebWorker/CMakeLists.txt | 6 ++ Meta/CMake/fontconfig.cmake | 5 ++ vcpkg.json | 10 ++- 5 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 Meta/CMake/fontconfig.cmake diff --git a/Ladybird/FontPlugin.cpp b/Ladybird/FontPlugin.cpp index 2d870557b3a..47af0582fe7 100644 --- a/Ladybird/FontPlugin.cpp +++ b/Ladybird/FontPlugin.cpp @@ -13,11 +13,22 @@ #include #include +#ifdef USE_FONTCONFIG +# include +#endif + namespace Ladybird { FontPlugin::FontPlugin(bool is_layout_test_mode) : m_is_layout_test_mode(is_layout_test_mode) { +#ifdef USE_FONTCONFIG + { + auto fontconfig_initialized = FcInit(); + VERIFY(fontconfig_initialized); + } +#endif + // Load anything we can find in the system's font directories for (auto const& path : Core::StandardPaths::font_directories().release_value_but_fixme_should_propagate_errors()) Gfx::FontDatabase::the().load_all_fonts_from_uri(MUST(String::formatted("file://{}", path))); @@ -50,6 +61,79 @@ Gfx::Font& FontPlugin::default_fixed_width_font() return *m_default_fixed_width_font; } +#ifdef USE_FONTCONFIG +static Optional query_fontconfig_for_generic_family(Web::Platform::GenericFont generic_font) +{ + char const* pattern_string = nullptr; + switch (generic_font) { + case Web::Platform::GenericFont::Cursive: + pattern_string = "cursive"; + break; + case Web::Platform::GenericFont::Fantasy: + pattern_string = "fantasy"; + break; + case Web::Platform::GenericFont::Monospace: + pattern_string = "monospace"; + break; + case Web::Platform::GenericFont::SansSerif: + pattern_string = "sans-serif"; + break; + case Web::Platform::GenericFont::Serif: + pattern_string = "serif"; + break; + case Web::Platform::GenericFont::UiMonospace: + pattern_string = "monospace"; + break; + case Web::Platform::GenericFont::UiRounded: + pattern_string = "sans-serif"; + break; + case Web::Platform::GenericFont::UiSansSerif: + pattern_string = "sans-serif"; + break; + case Web::Platform::GenericFont::UiSerif: + pattern_string = "serif"; + break; + default: + VERIFY_NOT_REACHED(); + } + + auto* config = FcConfigGetCurrent(); + VERIFY(config); + + FcPattern* pattern = FcNameParse(reinterpret_cast(pattern_string)); + VERIFY(pattern); + + auto success = FcConfigSubstitute(config, pattern, FcMatchPattern); + VERIFY(success); + + FcDefaultSubstitute(pattern); + + // Never select bitmap fonts. + success = FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); + VERIFY(success); + + // FIXME: Enable this once we can handle OpenType variable fonts. + success = FcPatternAddBool(pattern, FC_VARIABLE, FcFalse); + VERIFY(success); + + Optional name; + FcResult result {}; + + if (auto* matched = FcFontMatch(config, pattern, &result)) { + FcChar8* family = nullptr; + if (FcPatternGetString(matched, FC_FAMILY, 0, &family) == FcResultMatch) { + auto const* family_cstring = reinterpret_cast(family); + if (auto string = String::from_utf8(StringView { family_cstring, strlen(family_cstring) }); !string.is_error()) { + name = string.release_value(); + } + } + FcPatternDestroy(matched); + } + FcPatternDestroy(pattern); + return name; +} +#endif + void FontPlugin::update_generic_fonts() { // How we choose which system font to use for each CSS font: @@ -69,10 +153,19 @@ void FontPlugin::update_generic_fonts() RefPtr gfx_font; - for (auto& fallback : fallbacks) { - gfx_font = Gfx::FontDatabase::the().get(fallback, 16, 400, Gfx::FontWidth::Normal, 0); - if (gfx_font) - break; +#ifdef USE_FONTCONFIG + auto name = query_fontconfig_for_generic_family(generic_font); + if (name.has_value()) { + gfx_font = Gfx::FontDatabase::the().get(name.value(), 16, 400, Gfx::FontWidth::Normal, 0); + } +#endif + + if (!gfx_font) { + for (auto const& fallback : fallbacks) { + gfx_font = Gfx::FontDatabase::the().get(fallback, 16, 400, Gfx::FontWidth::Normal, 0); + if (gfx_font) + break; + } } m_generic_font_names[static_cast(generic_font)] = gfx_font ? gfx_font->family() : String {}; diff --git a/Ladybird/WebContent/CMakeLists.txt b/Ladybird/WebContent/CMakeLists.txt index bc3019052e9..bf3c07af417 100644 --- a/Ladybird/WebContent/CMakeLists.txt +++ b/Ladybird/WebContent/CMakeLists.txt @@ -1,4 +1,6 @@ include(accelerated_graphics) +include(fontconfig) + set(WEBCONTENT_SOURCE_DIR ${LADYBIRD_SOURCE_DIR}/Userland/Services/WebContent/) set(WEBCONTENT_SOURCES @@ -70,6 +72,10 @@ else() add_executable(WebContent main.cpp) endif() +if (HAS_FONTCONFIG) + target_link_libraries(webcontent PRIVATE Fontconfig::Fontconfig) +endif() + target_link_libraries(WebContent PRIVATE webcontent LibURL) target_sources(webcontent PUBLIC FILE_SET ladybird TYPE HEADERS diff --git a/Ladybird/WebWorker/CMakeLists.txt b/Ladybird/WebWorker/CMakeLists.txt index 4276da17d67..84652684bef 100644 --- a/Ladybird/WebWorker/CMakeLists.txt +++ b/Ladybird/WebWorker/CMakeLists.txt @@ -1,3 +1,5 @@ +include(fontconfig) + set(WEBWORKER_SOURCE_DIR ${LADYBIRD_SOURCE_DIR}/Userland/Services/WebWorker) set(CMAKE_AUTOMOC OFF) @@ -22,6 +24,10 @@ target_include_directories(webworker PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) target_include_directories(webworker PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..) target_link_libraries(webworker PUBLIC LibCore LibFileSystem LibGfx LibIPC LibJS LibProtocol LibWeb LibWebView LibLocale LibImageDecoderClient LibMain LibURL) +if (HAS_FONTCONFIG) + target_link_libraries(webworker PRIVATE Fontconfig::Fontconfig) +endif() + add_executable(WebWorker main.cpp) target_include_directories(WebWorker PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) target_link_libraries(WebWorker PRIVATE webworker) diff --git a/Meta/CMake/fontconfig.cmake b/Meta/CMake/fontconfig.cmake new file mode 100644 index 00000000000..b1c533fc533 --- /dev/null +++ b/Meta/CMake/fontconfig.cmake @@ -0,0 +1,5 @@ +if (NOT APPLE) + find_package(Fontconfig REQUIRED) + set(HAS_FONTCONFIG ON CACHE BOOL "" FORCE) + add_compile_definitions(USE_FONTCONFIG=1) +endif() diff --git a/vcpkg.json b/vcpkg.json index dfe14428e60..5f7b0d1bce1 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,7 +2,11 @@ "builtin-baseline": "01f602195983451bc83e72f4214af2cbc495aa94", "dependencies": [ "sqlite3", - "woff2" + "woff2", + { + "name": "fontconfig", + "platform": "linux | freebsd | openbsd" + } ], "overrides": [ { @@ -12,6 +16,10 @@ { "name": "woff2", "version": "1.0.2#4" + }, + { + "name": "fontconfig", + "version": "2.14.2#1" } ] }