mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:22:45 +00:00 
			
		
		
		
	LibWeb: Implement font-fallback
If the font-family property is set to a StyleValueList, we now iterate through it, looking up each font in turn until one is found. StyleResolver no longer needs to handle FontFamily specifically, which is a nice bonus. Serenity's current dependence on bitmap fonts leads to some weirdness here - for example, the `if (!found_font)` path can trigger even if a generic font family like "sans-serif" is used, since our default sans-serif font might not be available in the desired size or weight. The `monospace` variable only exists for that reason. This is not a complete solution, by a long way! Serenity's font support is still quite basic, so more work needs to be done there before we can start implementing the spec's font-matching algorithm. But this is still an improvement. :^)
This commit is contained in:
		
							parent
							
								
									ceece1c75e
								
							
						
					
					
						commit
						b64b97ef88
					
				
					 2 changed files with 59 additions and 42 deletions
				
			
		|  | @ -94,22 +94,9 @@ Color StyleProperties::color_or_fallback(CSS::PropertyID id, const DOM::Document | |||
| 
 | ||||
| void StyleProperties::load_font(Layout::Node const& node) const | ||||
| { | ||||
|     auto family_value = string_or_fallback(CSS::PropertyID::FontFamily, "Katica"); | ||||
|     auto font_size = property(CSS::PropertyID::FontSize).value_or(IdentifierStyleValue::create(CSS::ValueID::Medium)); | ||||
|     auto font_weight = property(CSS::PropertyID::FontWeight).value_or(IdentifierStyleValue::create(CSS::ValueID::Normal)); | ||||
| 
 | ||||
|     auto family_parts = family_value.split(','); | ||||
|     auto family = family_parts[0]; | ||||
| 
 | ||||
|     auto monospace = false; | ||||
| 
 | ||||
|     if (family.is_one_of("monospace", "ui-monospace")) { | ||||
|         monospace = true; | ||||
|         family = "Csilla"; | ||||
|     } else if (family.is_one_of("serif", "sans-serif", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-rounded")) { | ||||
|         family = "Katica"; | ||||
|     } | ||||
| 
 | ||||
|     int weight = Gfx::FontWeight::Regular; | ||||
|     if (font_weight->is_identifier()) { | ||||
|         switch (static_cast<const IdentifierStyleValue&>(*font_weight).id()) { | ||||
|  | @ -190,21 +177,68 @@ void StyleProperties::load_font(Layout::Node const& node) const | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     FontSelector font_selector { family, size, weight }; | ||||
|     // FIXME: Implement the full font-matching algorithm: https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm
 | ||||
| 
 | ||||
|     auto found_font = FontCache::the().get(font_selector); | ||||
|     if (found_font) { | ||||
|         m_font = found_font; | ||||
|         return; | ||||
|     // Note: This is modified by the find_font() lambda
 | ||||
|     FontSelector font_selector; | ||||
|     bool monospace = false; | ||||
| 
 | ||||
|     auto find_font = [&](String const& family) -> RefPtr<Gfx::Font> { | ||||
|         font_selector = { family, size, weight }; | ||||
| 
 | ||||
|         if (auto found_font = FontCache::the().get(font_selector)) | ||||
|             return found_font; | ||||
| 
 | ||||
|         if (auto found_font = Gfx::FontDatabase::the().get(family, size, weight)) | ||||
|             return found_font; | ||||
| 
 | ||||
|         return {}; | ||||
|     }; | ||||
| 
 | ||||
|     // FIXME: Replace hard-coded font names with a relevant call to FontDatabase.
 | ||||
|     // Currently, we cannot request the default font's name, or request it at a specific size and weight.
 | ||||
|     // So, hard-coded font names it is.
 | ||||
|     auto find_generic_font = [&](ValueID font_id) -> RefPtr<Gfx::Font> { | ||||
|         switch (font_id) { | ||||
|         case ValueID::Monospace: | ||||
|         case ValueID::UiMonospace: | ||||
|             monospace = true; | ||||
|             return find_font("Csilla"); | ||||
|         case ValueID::Serif: | ||||
|         case ValueID::SansSerif: | ||||
|         case ValueID::Cursive: | ||||
|         case ValueID::Fantasy: | ||||
|         case ValueID::UiSerif: | ||||
|         case ValueID::UiSansSerif: | ||||
|         case ValueID::UiRounded: | ||||
|             return find_font("Katica"); | ||||
|         default: | ||||
|             return {}; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     RefPtr<Gfx::Font> found_font {}; | ||||
| 
 | ||||
|     auto family_value = property(PropertyID::FontFamily).value_or(StringStyleValue::create("Katica")); | ||||
|     if (family_value->is_value_list()) { | ||||
|         auto& family_list = static_cast<StyleValueList const&>(*family_value).values(); | ||||
|         for (auto& family : family_list) { | ||||
|             if (family.is_identifier()) { | ||||
|                 found_font = find_generic_font(family.to_identifier()); | ||||
|             } else if (family.is_string()) { | ||||
|                 found_font = find_font(family.to_string()); | ||||
|             } | ||||
|             if (found_font) | ||||
|                 break; | ||||
|         } | ||||
|     } else if (family_value->is_identifier()) { | ||||
|         found_font = find_generic_font(family_value->to_identifier()); | ||||
|     } else if (family_value->is_string()) { | ||||
|         found_font = find_font(family_value->to_string()); | ||||
|     } | ||||
| 
 | ||||
|     Gfx::FontDatabase::the().for_each_font([&](auto& font) { | ||||
|         if (font.family() == family && font.weight() == weight && font.presentation_size() == size) | ||||
|             found_font = font; | ||||
|     }); | ||||
| 
 | ||||
|     if (!found_font) { | ||||
|         dbgln("Font not found: '{}' {} {}", family, size, weight); | ||||
|         dbgln("Font not found: '{}' {} {}", family_value->to_string(), size, weight); | ||||
|         found_font = font_fallback(monospace, bold); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -442,7 +442,7 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope | |||
|         if (value.is_font()) { | ||||
|             auto& font_shorthand = static_cast<CSS::FontStyleValue const&>(value); | ||||
|             style.set_property(CSS::PropertyID::FontSize, font_shorthand.font_size()); | ||||
|             set_property_expanding_shorthands(style, CSS::PropertyID::FontFamily, *font_shorthand.font_families(), document); | ||||
|             style.set_property(CSS::PropertyID::FontFamily, font_shorthand.font_families()); | ||||
|             style.set_property(CSS::PropertyID::FontStyle, font_shorthand.font_style()); | ||||
|             style.set_property(CSS::PropertyID::FontWeight, font_shorthand.font_weight()); | ||||
|             style.set_property(CSS::PropertyID::LineHeight, font_shorthand.line_height()); | ||||
|  | @ -451,7 +451,6 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope | |||
|         } | ||||
|         if (value.is_builtin()) { | ||||
|             style.set_property(CSS::PropertyID::FontSize, value); | ||||
|             // FIXME: Support multiple font-families
 | ||||
|             style.set_property(CSS::PropertyID::FontFamily, value); | ||||
|             style.set_property(CSS::PropertyID::FontStyle, value); | ||||
|             style.set_property(CSS::PropertyID::FontWeight, value); | ||||
|  | @ -462,22 +461,6 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (property_id == CSS::PropertyID::FontFamily) { | ||||
|         if (value.is_value_list()) { | ||||
|             auto& values_list = static_cast<StyleValueList const&>(value).values(); | ||||
|             // FIXME: Support multiple font-families
 | ||||
|             if (!values_list.is_empty()) { | ||||
|                 style.set_property(CSS::PropertyID::FontFamily, values_list.first()); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         if (value.is_builtin() || value.is_string() || value.is_identifier()) { | ||||
|             style.set_property(CSS::PropertyID::FontFamily, value); | ||||
|             return; | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (property_id == CSS::PropertyID::Flex) { | ||||
|         if (value.is_flex()) { | ||||
|             auto& flex = static_cast<CSS::FlexStyleValue const&>(value); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Atkins
						Sam Atkins