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.
This commit is contained in:
Andrew Kaster 2024-10-11 18:17:07 -06:00 committed by Andrew Kaster
commit 36a8ad9157
Notes: github-actions[bot] 2024-10-15 21:10:07 +00:00
11 changed files with 174 additions and 89 deletions

View file

@ -8,9 +8,11 @@
#include "FontPlugin.h" #include "FontPlugin.h"
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/TypeCasts.h>
#include <LibCore/Resource.h> #include <LibCore/Resource.h>
#include <LibCore/StandardPaths.h> #include <LibCore/StandardPaths.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#ifdef USE_FONTCONFIG #ifdef USE_FONTCONFIG
# include <fontconfig/fontconfig.h> # include <fontconfig/fontconfig.h>
@ -18,7 +20,7 @@
namespace Ladybird { 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) : m_is_layout_test_mode(is_layout_test_mode)
{ {
#ifdef USE_FONTCONFIG #ifdef USE_FONTCONFIG
@ -28,9 +30,14 @@ FontPlugin::FontPlugin(bool is_layout_test_mode)
} }
#endif #endif
// Load anything we can find in the system's font directories if (!font_provider)
for (auto const& path : Core::StandardPaths::font_directories().release_value_but_fixme_should_propagate_errors()) font_provider = &static_cast<Gfx::PathFontProvider&>(Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>()));
Gfx::FontDatabase::the().load_all_fonts_from_uri(MUST(String::formatted("file://{}", path))); if (is<Gfx::PathFontProvider>(*font_provider)) {
auto& path_font_provider = static_cast<Gfx::PathFontProvider&>(*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(); update_generic_fonts();

View file

@ -8,13 +8,14 @@
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibWeb/Platform/FontPlugin.h> #include <LibWeb/Platform/FontPlugin.h>
namespace Ladybird { namespace Ladybird {
class FontPlugin final : public Web::Platform::FontPlugin { class FontPlugin final : public Web::Platform::FontPlugin {
public: public:
FontPlugin(bool is_layout_test_mode); FontPlugin(bool is_layout_test_mode, Gfx::SystemFontProvider* = nullptr);
virtual ~FontPlugin(); virtual ~FontPlugin();
virtual Gfx::Font& default_font() override; virtual Gfx::Font& default_font() override;

View file

@ -15,6 +15,7 @@
#include <LibCore/Resource.h> #include <LibCore/Resource.h>
#include <LibCore/SystemServerTakeover.h> #include <LibCore/SystemServerTakeover.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibIPC/ConnectionFromClient.h> #include <LibIPC/ConnectionFromClient.h>
#include <LibJS/Bytecode/Interpreter.h> #include <LibJS/Bytecode/Interpreter.h>
#include <LibMain/Main.h> #include <LibMain/Main.h>
@ -128,11 +129,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Core::Process::wait_for_debugger_and_break(); Core::Process::wait_for_debugger_and_break();
} }
auto& font_provider = static_cast<Gfx::PathFontProvider&>(Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>()));
if (force_fontconfig) { if (force_fontconfig) {
Gfx::FontDatabase::the().set_force_fontconfig(true); font_provider.set_name_but_fixme_should_create_custom_system_font_provider("FontConfig"_string);
} }
font_provider.load_all_fonts_from_uri("resource://fonts"sv);
Gfx::FontDatabase::the().load_all_fonts_from_uri("resource://fonts"sv);
// Layout test mode implies internals object is exposed and the Skia CPU backend is used // Layout test mode implies internals object is exposed and the Skia CPU backend is used
if (is_layout_test_mode) { if (is_layout_test_mode) {
@ -167,7 +168,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Web::HTML::Window::set_internals_object_exposed(expose_internals_object); 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)); TRY(Web::Bindings::initialize_main_thread_vm(Web::HTML::EventLoop::Type::Window));

View file

@ -4,11 +4,22 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibGfx/Font/WOFF2/Loader.h> #include <LibGfx/Font/WOFF2/Loader.h>
#include <LibTest/TestCase.h> #include <LibTest/TestCase.h>
#define TEST_INPUT(x) ("test-inputs/" x) #define TEST_INPUT(x) ("test-inputs/" x)
namespace {
struct Global {
Global()
{
Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>());
}
} global;
}
TEST_CASE(tolerate_incorrect_sfnt_size) TEST_CASE(tolerate_incorrect_sfnt_size)
{ {
auto file = MUST(Core::MappedFile::map(TEST_INPUT("woff2/incorrect_sfnt_size.woff2"sv))); auto file = MUST(Core::MappedFile::map(TEST_INPUT("woff2/incorrect_sfnt_size.woff2"sv)));

View file

@ -15,6 +15,7 @@ set(SOURCES
Font/Font.cpp Font/Font.cpp
Font/FontData.cpp Font/FontData.cpp
Font/FontDatabase.cpp Font/FontDatabase.cpp
Font/PathFontProvider.cpp
Font/ScaledFont.cpp Font/ScaledFont.cpp
Font/ScaledFontSkia.cpp Font/ScaledFontSkia.cpp
Font/Typeface.cpp Font/Typeface.cpp

View file

@ -4,100 +4,44 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/DeprecatedFlyString.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/LexicalPath.h>
#include <AK/Queue.h>
#include <LibCore/Resource.h>
#include <LibGfx/Font/Font.h> #include <LibGfx/Font/Font.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/WOFF/Loader.h>
namespace Gfx { namespace Gfx {
// Key function for SystemFontProvider to emit the vtable here
SystemFontProvider::~SystemFontProvider() = default;
FontDatabase& FontDatabase::the() FontDatabase& FontDatabase::the()
{ {
static FontDatabase s_the; static FontDatabase s_the;
return s_the; return s_the;
} }
struct FontDatabase::Private { SystemFontProvider& FontDatabase::install_system_font_provider(NonnullOwnPtr<SystemFontProvider> provider)
bool force_fontconfig { false };
HashMap<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> typeface_by_family;
};
void FontDatabase::set_force_fontconfig(bool force_fontconfig)
{ {
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) FontDatabase::FontDatabase() = default;
{
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<NonnullRefPtr<Typeface>> {};
});
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<NonnullRefPtr<Typeface>> {};
});
family.append(font);
}
}
return IterationDecision::Continue;
});
}
FontDatabase::FontDatabase()
: m_private(make<Private>())
{
}
RefPtr<Gfx::Font> FontDatabase::get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope) RefPtr<Gfx::Font> FontDatabase::get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope)
{ {
auto it = m_private->typeface_by_family.find(family); return m_system_font_provider->get_font(family, point_size, weight, width, slope);
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;
} }
void FontDatabase::for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)> callback) void FontDatabase::for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)> callback)
{ {
auto it = m_private->typeface_by_family.find(family_name); m_system_font_provider->for_each_typeface_with_family_name(family_name, move(callback));
if (it == m_private->typeface_by_family.end())
return;
for (auto const& typeface : it->value)
callback(*typeface);
} }
} }

View file

@ -6,36 +6,38 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <LibGfx/Font/FontWeight.h>
#include <LibGfx/Font/Typeface.h> #include <LibGfx/Font/Typeface.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
namespace Gfx { namespace Gfx {
class SystemFontProvider {
public:
virtual ~SystemFontProvider();
virtual StringView name() const = 0;
virtual RefPtr<Gfx::Font> 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<void(Typeface const&)>) = 0;
};
class FontDatabase { class FontDatabase {
public: public:
static FontDatabase& the(); static FontDatabase& the();
SystemFontProvider& install_system_font_provider(NonnullOwnPtr<SystemFontProvider>);
RefPtr<Gfx::Font> get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope); RefPtr<Gfx::Font> 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(Typeface const&)>); void for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)>);
[[nodiscard]] StringView system_font_provider_name() const;
void load_all_fonts_from_uri(StringView);
void set_force_fontconfig(bool);
[[nodiscard]] bool should_force_fontconfig() const;
private: private:
FontDatabase(); FontDatabase();
~FontDatabase() = default; ~FontDatabase() = default;
struct Private; OwnPtr<SystemFontProvider> m_system_font_provider;
OwnPtr<Private> m_private;
}; };
} }

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <LibCore/Resource.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/WOFF/Loader.h>
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<NonnullRefPtr<Typeface>> {};
});
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<NonnullRefPtr<Typeface>> {};
});
family.append(font);
}
}
return IterationDecision::Continue;
});
}
RefPtr<Gfx::Font> 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<void(Typeface const&)> 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);
}
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/Typeface.h>
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<Gfx::Font> 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<void(Typeface const&)>) override;
virtual StringView name() const override { return m_name.bytes_as_string_view(); }
private:
HashMap<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> m_typeface_by_family;
String m_name { "Path"_string };
};
}

View file

@ -35,7 +35,7 @@ ErrorOr<NonnullRefPtr<TypefaceSkia>> TypefaceSkia::load_from_buffer(AK::Readonly
{ {
if (!s_font_manager) { if (!s_font_manager) {
#ifdef AK_OS_MACOS #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); s_font_manager = SkFontMgr_New_CoreText(nullptr);
} }
#endif #endif

View file

@ -19,6 +19,7 @@
#include <LibGfx/Font/Font.h> #include <LibGfx/Font/Font.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/FontStyleMapping.h> #include <LibGfx/Font/FontStyleMapping.h>
#include <LibGfx/Font/FontWeight.h>
#include <LibGfx/Font/ScaledFont.h> #include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Typeface.h> #include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/WOFF/Loader.h> #include <LibGfx/Font/WOFF/Loader.h>