LibWeb: Load and use fonts described by @font-face rules :^)

When encountering a @font-face rule, StyleComputer will now fire off
a resource request and download the first source URL specified.

Once downloaded, we try to parse it as a TrueType font file, and if it
works, it's added to a cache in StyleComputer. This effectively makes
fonts per-document since every document has its own StyleComputer.

This is very unoptimized and could definitely use some caching, etc.
But it does work on Acid3. :^)
This commit is contained in:
Andreas Kling 2022-03-29 02:14:20 +02:00
parent dcb24e943d
commit 1f9aed2617
Notes: sideshowbarker 2024-07-17 16:35:41 +09:00
2 changed files with 84 additions and 1 deletions

View File

@ -12,6 +12,8 @@
#include <LibGfx/Font.h>
#include <LibGfx/FontDatabase.h>
#include <LibGfx/FontStyleMapping.h>
#include <LibGfx/TrueTypeFont/Font.h>
#include <LibWeb/CSS/CSSFontFaceRule.h>
#include <LibWeb/CSS/CSSStyleRule.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/SelectorEngine.h>
@ -21,6 +23,7 @@
#include <LibWeb/DOM/Element.h>
#include <LibWeb/FontCache.h>
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/Loader/ResourceLoader.h>
#include <stdio.h>
namespace Web::CSS {
@ -30,6 +33,47 @@ StyleComputer::StyleComputer(DOM::Document& document)
{
}
StyleComputer::~StyleComputer() = default;
class StyleComputer::FontLoader : public ResourceClient {
public:
explicit FontLoader(StyleComputer& style_computer, FlyString family_name, AK::URL url)
: m_style_computer(style_computer)
, m_family_name(move(family_name))
{
LoadRequest request;
request.set_url(move(url));
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
}
virtual ~FontLoader() override { }
virtual void resource_did_load() override
{
auto result = TTF::Font::try_load_from_externally_owned_memory(resource()->encoded_data());
if (result.is_error())
return;
m_ttf_font = result.release_value();
m_style_computer.did_load_font(m_family_name);
}
virtual void resource_did_fail() override
{
}
RefPtr<Gfx::Font> font_with_point_size(float point_size) const
{
if (!m_ttf_font)
return nullptr;
return adopt_ref(*new TTF::ScaledFont(*m_ttf_font, point_size, point_size));
}
private:
StyleComputer& m_style_computer;
FlyString m_family_name;
RefPtr<TTF::Font> m_ttf_font;
};
static StyleSheet& default_stylesheet()
{
static StyleSheet* sheet;
@ -880,6 +924,12 @@ void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* ele
float font_size_in_pt = font_size_in_px * 0.75f;
font_selector = { family, font_size_in_pt, weight, slope };
if (auto it = m_loaded_fonts.find(family); it != m_loaded_fonts.end()) {
auto& loader = *it->value;
if (auto found_font = loader.font_with_point_size(font_size_in_pt))
return found_font;
}
if (auto found_font = FontCache::the().get(font_selector))
return found_font;
@ -1041,6 +1091,7 @@ NonnullRefPtr<StyleProperties> StyleComputer::create_document_style() const
NonnullRefPtr<StyleProperties> StyleComputer::compute_style(DOM::Element& element, Optional<CSS::Selector::PseudoElement> pseudo_element) const
{
load_fonts_if_needed();
build_rule_cache_if_needed();
auto style = StyleProperties::create();
@ -1183,4 +1234,28 @@ Gfx::IntRect StyleComputer::viewport_rect() const
return {};
}
void StyleComputer::did_load_font([[maybe_unused]] FlyString const& family_name)
{
document().invalidate_style();
}
void StyleComputer::load_fonts_if_needed() const
{
for_each_stylesheet(CascadeOrigin::Author, [&](StyleSheet const& sheet) {
for (auto const& rule : static_cast<CSSStyleSheet const&>(sheet).rules()) {
if (!is<CSSFontFaceRule>(rule))
continue;
auto const& font_face = static_cast<CSSFontFaceRule const&>(rule).font_face();
if (font_face.sources().is_empty())
continue;
if (m_loaded_fonts.contains(font_face.font_family()))
continue;
LoadRequest request;
auto url = m_document.parse_url(font_face.sources().first().url.to_string());
auto loader = make<FontLoader>(const_cast<StyleComputer&>(*this), font_face.font_family(), move(url));
const_cast<StyleComputer&>(*this).m_loaded_fonts.set(font_face.font_family(), move(loader));
}
});
}
}

View File

@ -11,6 +11,7 @@
#include <AK/NonnullRefPtrVector.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <LibWeb/CSS/CSSFontFaceRule.h>
#include <LibWeb/CSS/CSSStyleDeclaration.h>
#include <LibWeb/CSS/Parser/StyleComponentValueRule.h>
#include <LibWeb/CSS/Selector.h>
@ -48,7 +49,7 @@ private:
class StyleComputer {
public:
explicit StyleComputer(DOM::Document&);
~StyleComputer() = default;
~StyleComputer();
DOM::Document& document() { return m_document; }
DOM::Document const& document() const { return m_document; }
@ -71,6 +72,8 @@ public:
Gfx::Font const& initial_font() const;
void did_load_font(FlyString const& family_name);
private:
void compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement>) const;
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
@ -109,6 +112,11 @@ private:
Vector<MatchingRule> other_rules;
};
OwnPtr<RuleCache> m_rule_cache;
void load_fonts_if_needed() const;
class FontLoader;
HashMap<String, NonnullOwnPtr<FontLoader>> m_loaded_fonts;
};
}