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