mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-28 02:27:19 +02:00
Previously we would resolve font features (https://drafts.csswg.org/css-fonts-4/#feature-variation-precedence) per element, while this works for the current subset of the font feature resolution algorithm that we support, some as yet unimplemented parts require us to know whether we are resolving against a CSS @font-face rule, and if so which one (e.g. applying descriptors from the @font-face rule, deciding which @font-feature-values rules to apply, etc). To achieve this we store the data required to resolve font features in a struct and pass that to `FontComputer` which resolves the font features and stores them with the computed `Font`. We no longer need to invalidate the font shaping cache when features change since the features are defined per font (and therefore won't ever change).
148 lines
4.8 KiB
C++
148 lines
4.8 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2026, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/FlyString.h>
|
|
#include <LibCore/StandardPaths.h>
|
|
#include <LibGfx/Font/Font.h>
|
|
#include <LibGfx/Font/FontDatabase.h>
|
|
#include <LibGfx/Font/TypefaceSkia.h>
|
|
|
|
#if defined(AK_OS_HAIKU)
|
|
# include <FindDirectory.h>
|
|
#endif
|
|
|
|
#ifdef USE_FONTCONFIG
|
|
# include <LibGfx/Font/GlobalFontConfig.h>
|
|
#endif
|
|
|
|
namespace Gfx {
|
|
|
|
// Key function for SystemFontProvider to emit the vtable here
|
|
SystemFontProvider::~SystemFontProvider() = default;
|
|
|
|
FontDatabase& FontDatabase::the()
|
|
{
|
|
static FontDatabase s_the;
|
|
return s_the;
|
|
}
|
|
|
|
SystemFontProvider& FontDatabase::install_system_font_provider(NonnullOwnPtr<SystemFontProvider> provider)
|
|
{
|
|
VERIFY(!m_system_font_provider);
|
|
m_system_font_provider = move(provider);
|
|
return *m_system_font_provider;
|
|
}
|
|
|
|
StringView FontDatabase::system_font_provider_name() const
|
|
{
|
|
VERIFY(m_system_font_provider);
|
|
return m_system_font_provider->name();
|
|
}
|
|
|
|
FontDatabase::FontDatabase() = default;
|
|
|
|
RefPtr<Gfx::Font> FontDatabase::get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope, Optional<FontVariationSettings> const& font_variation_settings, Optional<Gfx::ShapeFeatures> const& shape_features)
|
|
{
|
|
return m_system_font_provider->get_font(family, point_size, weight, width, slope, font_variation_settings, shape_features);
|
|
}
|
|
|
|
RefPtr<Gfx::Font> FontDatabase::get_font_for_code_point(u32 code_point, float point_size, u16 weight, u16 width, u8 slope)
|
|
{
|
|
CodePointFallbackKey key { code_point, weight, width, slope };
|
|
auto& entry = m_code_point_fallback_cache.ensure(key, [&]() -> CodePointFallbackEntry {
|
|
auto typeface_or_error = TypefaceSkia::find_typeface_for_code_point(code_point, weight, width, slope);
|
|
if (typeface_or_error.is_error() || !typeface_or_error.value())
|
|
return { {}, nullptr };
|
|
|
|
auto typeface = typeface_or_error.release_value();
|
|
return { typeface->family(), typeface };
|
|
});
|
|
|
|
// FIXME: Does it matter that we don't pass a FontVariationSettings or ShapeFeatures here?
|
|
if (entry.typeface)
|
|
return entry.typeface->font(point_size, {});
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FontDatabase::for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)> callback)
|
|
{
|
|
m_system_font_provider->for_each_typeface_with_family_name(family_name, move(callback));
|
|
}
|
|
|
|
ErrorOr<Vector<String>> FontDatabase::font_directories()
|
|
{
|
|
#if defined(USE_FONTCONFIG)
|
|
Vector<String> paths;
|
|
FcConfig* config = Gfx::GlobalFontConfig::the().get();
|
|
FcStrList* dirs = FcConfigGetFontDirs(config);
|
|
while (FcChar8* dir = FcStrListNext(dirs)) {
|
|
char const* dir_cstring = reinterpret_cast<char const*>(dir);
|
|
paths.append(TRY(String::from_utf8(StringView { dir_cstring, strlen(dir_cstring) })));
|
|
}
|
|
FcStrListDone(dirs);
|
|
return paths;
|
|
|
|
#elif defined(AK_OS_HAIKU)
|
|
Vector<String> paths_vector;
|
|
char** paths;
|
|
size_t paths_count;
|
|
if (find_paths(B_FIND_PATH_FONTS_DIRECTORY, NULL, &paths, &paths_count) == B_OK) {
|
|
for (size_t i = 0; i < paths_count; ++i) {
|
|
StringBuilder builder;
|
|
builder.append(paths[i], strlen(paths[i]));
|
|
paths_vector.append(TRY(builder.to_string()));
|
|
}
|
|
}
|
|
return paths_vector;
|
|
|
|
#else
|
|
# if defined(AK_OS_SERENITY)
|
|
return Vector<String> { {
|
|
"/res/fonts"_string,
|
|
} };
|
|
|
|
# elif defined(AK_OS_MACOS)
|
|
return Vector<String> { {
|
|
"/System/Library/Fonts"_string,
|
|
"/Library/Fonts"_string,
|
|
TRY(String::formatted("{}/Library/Fonts"sv, Core::StandardPaths::home_directory())),
|
|
} };
|
|
|
|
# elif defined(AK_OS_ANDROID)
|
|
return Vector<String> { {
|
|
// FIXME: We should be using the ASystemFontIterator NDK API here.
|
|
// There is no guarantee that this will continue to exist on future versions of Android.
|
|
"/system/fonts"_string,
|
|
} };
|
|
|
|
# elif defined(AK_OS_WINDOWS)
|
|
return Vector<String> { {
|
|
TRY(String::formatted(R"({}\Fonts)"sv, getenv("WINDIR"))),
|
|
TRY(String::formatted(R"({}\Microsoft\Windows\Fonts)"sv, getenv("LOCALAPPDATA"))),
|
|
} };
|
|
|
|
# else
|
|
Vector<String> paths;
|
|
|
|
auto user_data_directory = Core::StandardPaths::user_data_directory();
|
|
paths.append(TRY(String::formatted("{}/fonts", user_data_directory)));
|
|
paths.append(TRY(String::formatted("{}/X11/fonts", user_data_directory)));
|
|
|
|
auto data_directories = Core::StandardPaths::system_data_directories();
|
|
for (auto& data_directory : data_directories) {
|
|
paths.append(TRY(String::formatted("{}/fonts", data_directory)));
|
|
paths.append(TRY(String::formatted("{}/X11/fonts", data_directory)));
|
|
}
|
|
|
|
return paths;
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
}
|