mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-05-05 06:32:30 +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).
127 lines
4.9 KiB
C++
127 lines
4.9 KiB
C++
/*
|
|
* 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/Font.h>
|
|
#include <LibGfx/Font/PathFontProvider.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) || path.has_extension(".otf"sv)) {
|
|
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, Optional<FontVariationSettings> const& font_variation_settings, Optional<Gfx::ShapeFeatures> const& shape_features)
|
|
{
|
|
auto const compute_default_font_variation_settings = [&](unsigned weight, unsigned width) {
|
|
FontVariationSettings default_font_variation_settings;
|
|
default_font_variation_settings.set_weight(static_cast<float>(weight));
|
|
|
|
switch (width) {
|
|
case FontWidth::UltraCondensed:
|
|
default_font_variation_settings.set_width(50);
|
|
break;
|
|
case FontWidth::ExtraCondensed:
|
|
default_font_variation_settings.set_width(62.5);
|
|
break;
|
|
case FontWidth::Condensed:
|
|
default_font_variation_settings.set_width(75);
|
|
break;
|
|
case FontWidth::SemiCondensed:
|
|
default_font_variation_settings.set_width(87.5);
|
|
break;
|
|
case FontWidth::Normal:
|
|
default_font_variation_settings.set_width(100);
|
|
break;
|
|
case FontWidth::SemiExpanded:
|
|
default_font_variation_settings.set_width(112.5);
|
|
break;
|
|
case FontWidth::Expanded:
|
|
default_font_variation_settings.set_width(125);
|
|
break;
|
|
case FontWidth::ExtraExpanded:
|
|
default_font_variation_settings.set_width(150);
|
|
break;
|
|
case FontWidth::UltraExpanded:
|
|
default_font_variation_settings.set_width(200);
|
|
break;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
return default_font_variation_settings;
|
|
};
|
|
|
|
auto const compute_default_shape_features = [&]() {
|
|
// NB: These shape features match those applied when all CSS properties are initial values
|
|
Gfx::ShapeFeatures shape_features;
|
|
shape_features.append({ { 'c', 'l', 'i', 'g' }, 1 });
|
|
shape_features.append({ { 'k', 'e', 'r', 'n' }, 1 });
|
|
shape_features.append({ { 'l', 'i', 'g', 'a' }, 1 });
|
|
return shape_features;
|
|
};
|
|
|
|
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->font(point_size, font_variation_settings.value_or_lazy_evaluated([&] { return compute_default_font_variation_settings(weight, width); }), shape_features.value_or_lazy_evaluated([&] { return compute_default_shape_features(); }));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|