mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 20:57:41 +00:00
LibWeb: Make font selection closer to specification
Add matching on family name, style and weight. This improves the fonts used by the MDN examples.
This commit is contained in:
parent
518679b0dd
commit
e48074e401
2 changed files with 96 additions and 10 deletions
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
|
#include <AK/Find.h>
|
||||||
|
#include <AK/Function.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/QuickSort.h>
|
#include <AK/QuickSort.h>
|
||||||
#include <AK/TemporaryChange.h>
|
#include <AK/TemporaryChange.h>
|
||||||
|
@ -1574,6 +1576,89 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(StyleProp
|
||||||
return font_metrics;
|
return font_metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<Gfx::Font const> StyleComputer::find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive)
|
||||||
|
{
|
||||||
|
using Fn = AK::Function<bool(MatchingFontCandidate const&)>;
|
||||||
|
auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= target_weight; })
|
||||||
|
: Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight > target_weight; });
|
||||||
|
auto it = find_if(candidates.begin(), candidates.end(), pred);
|
||||||
|
for (; it != candidates.end(); ++it)
|
||||||
|
if (auto found_font = it->loader->font_with_point_size(font_size_in_pt))
|
||||||
|
return found_font;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Gfx::Font const> StyleComputer::find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive)
|
||||||
|
{
|
||||||
|
using Fn = AK::Function<bool(MatchingFontCandidate const&)>;
|
||||||
|
auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight <= target_weight; })
|
||||||
|
: Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight < target_weight; });
|
||||||
|
auto it = find_if(candidates.rbegin(), candidates.rend(), pred);
|
||||||
|
for (; it != candidates.rend(); ++it)
|
||||||
|
if (auto found_font = it->loader->font_with_point_size(font_size_in_pt))
|
||||||
|
return found_font;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial implementation of the font-matching algorithm: https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm
|
||||||
|
// FIXME: This should be replaced by the full CSS font selection algorithm.
|
||||||
|
RefPtr<Gfx::Font const> StyleComputer::font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const
|
||||||
|
{
|
||||||
|
// If a font family match occurs, the user agent assembles the set of font faces in that family and then
|
||||||
|
// narrows the set to a single face using other font properties in the order given below.
|
||||||
|
Vector<MatchingFontCandidate> matching_family_fonts;
|
||||||
|
for (auto const& font_key_and_loader : m_loaded_fonts) {
|
||||||
|
if (font_key_and_loader.key.family_name.equals_ignoring_ascii_case(key.family_name))
|
||||||
|
matching_family_fonts.empend(font_key_and_loader.key, font_key_and_loader.value.ptr());
|
||||||
|
}
|
||||||
|
// FIXME: 1. font-stretch is tried first.
|
||||||
|
// FIXME: 2. font-style is tried next.
|
||||||
|
// We don't have complete support of italic and oblique fonts, so matching on font-style can be simplified to:
|
||||||
|
// If a matching slope is found, all faces which don't have that matching slope are excluded from the matching set.
|
||||||
|
auto style_it = find_if(matching_family_fonts.begin(), matching_family_fonts.end(),
|
||||||
|
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.slope == key.slope; });
|
||||||
|
if (style_it != matching_family_fonts.end()) {
|
||||||
|
matching_family_fonts.remove_all_matching([&](auto const& matching_font_candidate) {
|
||||||
|
return matching_font_candidate.key.slope != key.slope;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 3. font-weight is matched next.
|
||||||
|
// If the desired weight is inclusively between 400 and 500, weights greater than or equal to the target weight
|
||||||
|
// are checked in ascending order until 500 is hit and checked, followed by weights less than the target weight
|
||||||
|
// in descending order, followed by weights greater than 500, until a match is found.
|
||||||
|
if (key.weight >= 400 && key.weight <= 500) {
|
||||||
|
auto it = find_if(matching_family_fonts.begin(), matching_family_fonts.end(),
|
||||||
|
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= key.weight; });
|
||||||
|
for (; it != matching_family_fonts.end() && it->key.weight <= 500; ++it) {
|
||||||
|
if (auto found_font = it->loader->font_with_point_size(font_size_in_pt))
|
||||||
|
return found_font;
|
||||||
|
}
|
||||||
|
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||||
|
return found_font;
|
||||||
|
for (; it != matching_family_fonts.end(); ++it) {
|
||||||
|
if (auto found_font = it->loader->font_with_point_size(font_size_in_pt))
|
||||||
|
return found_font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the desired weight is less than 400, weights less than or equal to the desired weight are checked in descending order
|
||||||
|
// followed by weights above the desired weight in ascending order until a match is found.
|
||||||
|
if (key.weight < 400) {
|
||||||
|
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, true))
|
||||||
|
return found_font;
|
||||||
|
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||||
|
return found_font;
|
||||||
|
}
|
||||||
|
// If the desired weight is greater than 500, weights greater than or equal to the desired weight are checked in ascending order
|
||||||
|
// followed by weights below the desired weight in descending order until a match is found.
|
||||||
|
if (key.weight > 500) {
|
||||||
|
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, key.weight, font_size_in_pt, true))
|
||||||
|
return found_font;
|
||||||
|
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||||
|
return found_font;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement> pseudo_element) const
|
void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement> pseudo_element) const
|
||||||
{
|
{
|
||||||
// To compute the font, first ensure that we've defaulted the relevant CSS font properties.
|
// To compute the font, first ensure that we've defaulted the relevant CSS font properties.
|
||||||
|
@ -1752,15 +1837,8 @@ void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* ele
|
||||||
return found_font;
|
return found_font;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We couldn't find this font with a specific weight and slope, so try again without them.
|
if (auto found_font = font_matching_algorithm(key, font_size_in_pt))
|
||||||
// FIXME: This should be replaced by a proper CSS font selection algorithm.
|
|
||||||
key.weight = 0;
|
|
||||||
key.slope = 0;
|
|
||||||
if (auto it = m_loaded_fonts.find(key); 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;
|
return found_font;
|
||||||
}
|
|
||||||
|
|
||||||
if (auto found_font = FontCache::the().get(font_selector))
|
if (auto found_font = FontCache::the().get(font_selector))
|
||||||
return found_font;
|
return found_font;
|
||||||
|
|
|
@ -101,8 +101,17 @@ private:
|
||||||
CreatePseudoElementStyleIfNeeded,
|
CreatePseudoElementStyleIfNeeded,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FontLoader;
|
||||||
|
struct MatchingFontCandidate {
|
||||||
|
FontFaceKey key;
|
||||||
|
FontLoader* loader;
|
||||||
|
};
|
||||||
|
|
||||||
ErrorOr<RefPtr<StyleProperties>> compute_style_impl(DOM::Element&, Optional<CSS::Selector::PseudoElement>, ComputeStyleMode) const;
|
ErrorOr<RefPtr<StyleProperties>> compute_style_impl(DOM::Element&, Optional<CSS::Selector::PseudoElement>, ComputeStyleMode) const;
|
||||||
ErrorOr<void> compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement>, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const;
|
ErrorOr<void> compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement>, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const;
|
||||||
|
static RefPtr<Gfx::Font const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||||
|
static RefPtr<Gfx::Font const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||||
|
RefPtr<Gfx::Font const> font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const;
|
||||||
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
||||||
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
||||||
ErrorOr<void> absolutize_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
ErrorOr<void> absolutize_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
|
||||||
|
@ -159,7 +168,6 @@ private:
|
||||||
OwnPtr<RuleCache> m_author_rule_cache;
|
OwnPtr<RuleCache> m_author_rule_cache;
|
||||||
OwnPtr<RuleCache> m_user_agent_rule_cache;
|
OwnPtr<RuleCache> m_user_agent_rule_cache;
|
||||||
|
|
||||||
class FontLoader;
|
|
||||||
HashMap<FontFaceKey, NonnullOwnPtr<FontLoader>> m_loaded_fonts;
|
HashMap<FontFaceKey, NonnullOwnPtr<FontLoader>> m_loaded_fonts;
|
||||||
|
|
||||||
Length::FontMetrics m_default_font_metrics;
|
Length::FontMetrics m_default_font_metrics;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue