LibGfx: Move FontDatabase internals to SystemFontProvider interface
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-14, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run

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
parent e8ff9b6eb4
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 <AK/ByteString.h>
#include <AK/String.h>
#include <AK/TypeCasts.h>
#include <LibCore/Resource.h>
#include <LibCore/StandardPaths.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#ifdef USE_FONTCONFIG
# include <fontconfig/fontconfig.h>
@ -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::PathFontProvider&>(Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>()));
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();

View file

@ -8,13 +8,14 @@
#include <AK/RefPtr.h>
#include <AK/Vector.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibWeb/Platform/FontPlugin.h>
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;

View file

@ -15,6 +15,7 @@
#include <LibCore/Resource.h>
#include <LibCore/SystemServerTakeover.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibIPC/ConnectionFromClient.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibMain/Main.h>
@ -128,11 +129,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
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) {
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<int> 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));

View file

@ -4,11 +4,22 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibGfx/Font/WOFF2/Loader.h>
#include <LibTest/TestCase.h>
#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)
{
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/FontData.cpp
Font/FontDatabase.cpp
Font/PathFontProvider.cpp
Font/ScaledFont.cpp
Font/ScaledFontSkia.cpp
Font/Typeface.cpp

View file

@ -4,100 +4,44 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/DeprecatedFlyString.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/FontDatabase.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/WOFF/Loader.h>
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<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> typeface_by_family;
};
void FontDatabase::set_force_fontconfig(bool force_fontconfig)
SystemFontProvider& FontDatabase::install_system_font_provider(NonnullOwnPtr<SystemFontProvider> 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<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>())
{
}
FontDatabase::FontDatabase() = default;
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);
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<void(Typeface const&)> 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));
}
}

View file

@ -6,36 +6,38 @@
#pragma once
#include <AK/ByteString.h>
#include <AK/FlyString.h>
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <LibGfx/Font/FontWeight.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Forward.h>
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 {
public:
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);
void for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)>);
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<Private> m_private;
OwnPtr<SystemFontProvider> m_system_font_provider;
};
}

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) {
#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

View file

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