From 36a8ad9157500b00e36bdce46e2bd42285804f87 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Fri, 11 Oct 2024 18:17:07 -0600 Subject: [PATCH] LibGfx: Move FontDatabase internals to SystemFontProvider interface This will be the first step is making better use of system libraries like fontconfig and CoreText to load system fonts for use by the UI process and the CSS style computer. --- Ladybird/FontPlugin.cpp | 15 +++- Ladybird/FontPlugin.h | 3 +- Ladybird/WebContent/main.cpp | 9 +- Tests/LibGfx/TestWOFF2.cpp | 11 +++ Userland/Libraries/LibGfx/CMakeLists.txt | 1 + .../Libraries/LibGfx/Font/FontDatabase.cpp | 82 +++---------------- Userland/Libraries/LibGfx/Font/FontDatabase.h | 22 ++--- .../LibGfx/Font/PathFontProvider.cpp | 78 ++++++++++++++++++ .../Libraries/LibGfx/Font/PathFontProvider.h | 39 +++++++++ .../Libraries/LibGfx/Font/TypefaceSkia.cpp | 2 +- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 1 + 11 files changed, 174 insertions(+), 89 deletions(-) create mode 100644 Userland/Libraries/LibGfx/Font/PathFontProvider.cpp create mode 100644 Userland/Libraries/LibGfx/Font/PathFontProvider.h diff --git a/Ladybird/FontPlugin.cpp b/Ladybird/FontPlugin.cpp index 67106e01146..c854fee143e 100644 --- a/Ladybird/FontPlugin.cpp +++ b/Ladybird/FontPlugin.cpp @@ -8,9 +8,11 @@ #include "FontPlugin.h" #include #include +#include #include #include #include +#include #ifdef USE_FONTCONFIG # include @@ -18,7 +20,7 @@ namespace Ladybird { -FontPlugin::FontPlugin(bool is_layout_test_mode) +FontPlugin::FontPlugin(bool is_layout_test_mode, Gfx::SystemFontProvider* font_provider) : m_is_layout_test_mode(is_layout_test_mode) { #ifdef USE_FONTCONFIG @@ -28,9 +30,14 @@ FontPlugin::FontPlugin(bool is_layout_test_mode) } #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))); + if (!font_provider) + font_provider = &static_cast(Gfx::FontDatabase::the().install_system_font_provider(make())); + if (is(*font_provider)) { + auto& path_font_provider = static_cast(*font_provider); + // 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()) + path_font_provider.load_all_fonts_from_uri(MUST(String::formatted("file://{}", path))); + } update_generic_fonts(); diff --git a/Ladybird/FontPlugin.h b/Ladybird/FontPlugin.h index 481d2775f56..0cef79adecf 100644 --- a/Ladybird/FontPlugin.h +++ b/Ladybird/FontPlugin.h @@ -8,13 +8,14 @@ #include #include +#include #include namespace Ladybird { class FontPlugin final : public Web::Platform::FontPlugin { public: - FontPlugin(bool is_layout_test_mode); + FontPlugin(bool is_layout_test_mode, Gfx::SystemFontProvider* = nullptr); virtual ~FontPlugin(); virtual Gfx::Font& default_font() override; diff --git a/Ladybird/WebContent/main.cpp b/Ladybird/WebContent/main.cpp index 67d52dcc348..a0ccf5328b1 100644 --- a/Ladybird/WebContent/main.cpp +++ b/Ladybird/WebContent/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -128,11 +129,11 @@ ErrorOr serenity_main(Main::Arguments arguments) Core::Process::wait_for_debugger_and_break(); } + auto& font_provider = static_cast(Gfx::FontDatabase::the().install_system_font_provider(make())); if (force_fontconfig) { - Gfx::FontDatabase::the().set_force_fontconfig(true); + font_provider.set_name_but_fixme_should_create_custom_system_font_provider("FontConfig"_string); } - - Gfx::FontDatabase::the().load_all_fonts_from_uri("resource://fonts"sv); + font_provider.load_all_fonts_from_uri("resource://fonts"sv); // Layout test mode implies internals object is exposed and the Skia CPU backend is used if (is_layout_test_mode) { @@ -167,7 +168,7 @@ ErrorOr serenity_main(Main::Arguments arguments) Web::HTML::Window::set_internals_object_exposed(expose_internals_object); - Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode)); + Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode, &font_provider)); TRY(Web::Bindings::initialize_main_thread_vm(Web::HTML::EventLoop::Type::Window)); diff --git a/Tests/LibGfx/TestWOFF2.cpp b/Tests/LibGfx/TestWOFF2.cpp index 59d787cdf12..1a8c7021a70 100644 --- a/Tests/LibGfx/TestWOFF2.cpp +++ b/Tests/LibGfx/TestWOFF2.cpp @@ -4,11 +4,22 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #define TEST_INPUT(x) ("test-inputs/" x) +namespace { +struct Global { + Global() + { + Gfx::FontDatabase::the().install_system_font_provider(make()); + } +} global; +} + TEST_CASE(tolerate_incorrect_sfnt_size) { auto file = MUST(Core::MappedFile::map(TEST_INPUT("woff2/incorrect_sfnt_size.woff2"sv))); diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index a847fd1a108..64dca64083f 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES Font/Font.cpp Font/FontData.cpp Font/FontDatabase.cpp + Font/PathFontProvider.cpp Font/ScaledFont.cpp Font/ScaledFontSkia.cpp Font/Typeface.cpp diff --git a/Userland/Libraries/LibGfx/Font/FontDatabase.cpp b/Userland/Libraries/LibGfx/Font/FontDatabase.cpp index 023f6abc62a..9317b7eabc3 100644 --- a/Userland/Libraries/LibGfx/Font/FontDatabase.cpp +++ b/Userland/Libraries/LibGfx/Font/FontDatabase.cpp @@ -4,100 +4,44 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include -#include -#include -#include #include #include -#include -#include namespace Gfx { +// Key function for SystemFontProvider to emit the vtable here +SystemFontProvider::~SystemFontProvider() = default; + FontDatabase& FontDatabase::the() { static FontDatabase s_the; return s_the; } -struct FontDatabase::Private { - bool force_fontconfig { false }; - HashMap>, AK::ASCIICaseInsensitiveFlyStringTraits> typeface_by_family; -}; - -void FontDatabase::set_force_fontconfig(bool force_fontconfig) +SystemFontProvider& FontDatabase::install_system_font_provider(NonnullOwnPtr provider) { - m_private->force_fontconfig = force_fontconfig; + VERIFY(!m_system_font_provider); + m_system_font_provider = move(provider); + return *m_system_font_provider; } -bool FontDatabase::should_force_fontconfig() const +StringView FontDatabase::system_font_provider_name() const { - return m_private->force_fontconfig; + VERIFY(m_system_font_provider); + return m_system_font_provider->name(); } -void FontDatabase::load_all_fonts_from_uri(StringView uri) -{ - auto root_or_error = Core::Resource::load_from_uri(uri); - if (root_or_error.is_error()) { - if (root_or_error.error().is_errno() && root_or_error.error().code() == ENOENT) { - return; - } - dbgln("FontDatabase::load_all_fonts_from_uri('{}'): {}", uri, root_or_error.error()); - return; - } - auto root = root_or_error.release_value(); - - root->for_each_descendant_file([this](Core::Resource const& resource) -> IterationDecision { - auto uri = resource.uri(); - auto path = LexicalPath(uri.bytes_as_string_view()); - if (path.has_extension(".ttf"sv) || path.has_extension(".ttc"sv)) { - // FIXME: What about .otf - if (auto font_or_error = Typeface::try_load_from_resource(resource); !font_or_error.is_error()) { - auto font = font_or_error.release_value(); - auto& family = m_private->typeface_by_family.ensure(font->family(), [] { - return Vector> {}; - }); - family.append(font); - } - } else if (path.has_extension(".woff"sv)) { - if (auto font_or_error = WOFF::try_load_from_resource(resource); !font_or_error.is_error()) { - auto font = font_or_error.release_value(); - auto& family = m_private->typeface_by_family.ensure(font->family(), [] { - return Vector> {}; - }); - family.append(font); - } - } - return IterationDecision::Continue; - }); -} - -FontDatabase::FontDatabase() - : m_private(make()) -{ -} +FontDatabase::FontDatabase() = default; RefPtr FontDatabase::get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope) { - auto it = m_private->typeface_by_family.find(family); - if (it == m_private->typeface_by_family.end()) - return nullptr; - for (auto const& typeface : it->value) { - if (typeface->weight() == weight && typeface->width() == width && typeface->slope() == slope) - return typeface->scaled_font(point_size); - } - return nullptr; + return m_system_font_provider->get_font(family, point_size, weight, width, slope); } void FontDatabase::for_each_typeface_with_family_name(FlyString const& family_name, Function callback) { - auto it = m_private->typeface_by_family.find(family_name); - if (it == m_private->typeface_by_family.end()) - return; - for (auto const& typeface : it->value) - callback(*typeface); + m_system_font_provider->for_each_typeface_with_family_name(family_name, move(callback)); } } diff --git a/Userland/Libraries/LibGfx/Font/FontDatabase.h b/Userland/Libraries/LibGfx/Font/FontDatabase.h index 3cf4a828e16..111bc908131 100644 --- a/Userland/Libraries/LibGfx/Font/FontDatabase.h +++ b/Userland/Libraries/LibGfx/Font/FontDatabase.h @@ -6,36 +6,38 @@ #pragma once -#include #include #include #include #include -#include #include #include namespace Gfx { +class SystemFontProvider { +public: + virtual ~SystemFontProvider(); + + virtual StringView name() const = 0; + virtual RefPtr get_font(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope) = 0; + virtual void for_each_typeface_with_family_name(FlyString const& family_name, Function) = 0; +}; + class FontDatabase { public: static FontDatabase& the(); + SystemFontProvider& install_system_font_provider(NonnullOwnPtr); RefPtr get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope); - void for_each_typeface_with_family_name(FlyString const& family_name, Function); - - void load_all_fonts_from_uri(StringView); - - void set_force_fontconfig(bool); - [[nodiscard]] bool should_force_fontconfig() const; + [[nodiscard]] StringView system_font_provider_name() const; private: FontDatabase(); ~FontDatabase() = default; - struct Private; - OwnPtr m_private; + OwnPtr m_system_font_provider; }; } diff --git a/Userland/Libraries/LibGfx/Font/PathFontProvider.cpp b/Userland/Libraries/LibGfx/Font/PathFontProvider.cpp new file mode 100644 index 00000000000..a0e31cd6a51 --- /dev/null +++ b/Userland/Libraries/LibGfx/Font/PathFontProvider.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +PathFontProvider::PathFontProvider() = default; +PathFontProvider::~PathFontProvider() = default; + +void PathFontProvider::load_all_fonts_from_uri(StringView uri) +{ + auto root_or_error = Core::Resource::load_from_uri(uri); + if (root_or_error.is_error()) { + if (root_or_error.error().is_errno() && root_or_error.error().code() == ENOENT) { + return; + } + dbgln("PathFontProvider::load_all_fonts_from_uri('{}'): {}", uri, root_or_error.error()); + return; + } + auto root = root_or_error.release_value(); + + root->for_each_descendant_file([this](Core::Resource const& resource) -> IterationDecision { + auto uri = resource.uri(); + auto path = LexicalPath(uri.bytes_as_string_view()); + if (path.has_extension(".ttf"sv) || path.has_extension(".ttc"sv)) { + // FIXME: What about .otf + if (auto font_or_error = Typeface::try_load_from_resource(resource); !font_or_error.is_error()) { + auto font = font_or_error.release_value(); + auto& family = m_typeface_by_family.ensure(font->family(), [] { + return Vector> {}; + }); + family.append(font); + } + } else if (path.has_extension(".woff"sv)) { + if (auto font_or_error = WOFF::try_load_from_resource(resource); !font_or_error.is_error()) { + auto font = font_or_error.release_value(); + auto& family = m_typeface_by_family.ensure(font->family(), [] { + return Vector> {}; + }); + family.append(font); + } + } + return IterationDecision::Continue; + }); +} + +RefPtr PathFontProvider::get_font(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope) +{ + auto it = m_typeface_by_family.find(family); + if (it == m_typeface_by_family.end()) + return nullptr; + for (auto const& typeface : it->value) { + if (typeface->weight() == weight && typeface->width() == width && typeface->slope() == slope) + return typeface->scaled_font(point_size); + } + return nullptr; +} + +void PathFontProvider::for_each_typeface_with_family_name(FlyString const& family_name, Function callback) +{ + auto it = m_typeface_by_family.find(family_name); + if (it == m_typeface_by_family.end()) + return; + for (auto const& typeface : it->value) { + callback(*typeface); + } +} + +} diff --git a/Userland/Libraries/LibGfx/Font/PathFontProvider.h b/Userland/Libraries/LibGfx/Font/PathFontProvider.h new file mode 100644 index 00000000000..6e62e4595a9 --- /dev/null +++ b/Userland/Libraries/LibGfx/Font/PathFontProvider.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +class PathFontProvider final : public SystemFontProvider { + AK_MAKE_NONCOPYABLE(PathFontProvider); + AK_MAKE_NONMOVABLE(PathFontProvider); + +public: + PathFontProvider(); + virtual ~PathFontProvider() override; + + void set_name_but_fixme_should_create_custom_system_font_provider(String name) { m_name = move(name); } + + void load_all_fonts_from_uri(StringView); + + virtual RefPtr get_font(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope) override; + virtual void for_each_typeface_with_family_name(FlyString const& family_name, Function) override; + virtual StringView name() const override { return m_name.bytes_as_string_view(); } + +private: + HashMap>, AK::ASCIICaseInsensitiveFlyStringTraits> m_typeface_by_family; + String m_name { "Path"_string }; +}; + +} diff --git a/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp b/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp index 293eb7e9456..9be2ef443c5 100644 --- a/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp +++ b/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp @@ -35,7 +35,7 @@ ErrorOr> TypefaceSkia::load_from_buffer(AK::Readonly { if (!s_font_manager) { #ifdef AK_OS_MACOS - if (!Gfx::FontDatabase::the().should_force_fontconfig()) { + if (Gfx::FontDatabase::the().system_font_provider_name() != "FontConfig"sv) { s_font_manager = SkFontMgr_New_CoreText(nullptr); } #endif diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index c862f3fbb23..76ac8aa0646 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include