2022-09-08 10:44:17 +00:00
/*
2024-10-04 11:19:50 +00:00
* Copyright ( c ) 2022 - 2023 , Andreas Kling < andreas @ ladybird . org >
2023-01-11 20:34:59 +00:00
* Copyright ( c ) 2023 , Linus Groh < linusg @ serenityos . org >
2022-09-08 10:44:17 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-08-02 18:01:17 +00:00
# include "FontPlugin.h"
2023-12-16 14:19:34 +00:00
# include <AK/ByteString.h>
2023-01-11 20:34:59 +00:00
# include <AK/String.h>
2024-10-12 00:17:07 +00:00
# include <AK/TypeCasts.h>
2024-03-19 17:54:38 +00:00
# include <LibCore/Resource.h>
2023-01-11 20:34:59 +00:00
# include <LibCore/StandardPaths.h>
2022-09-08 10:44:17 +00:00
# include <LibGfx/Font/FontDatabase.h>
2024-10-12 00:17:07 +00:00
# include <LibGfx/Font/PathFontProvider.h>
2022-09-08 10:44:17 +00:00
2024-06-07 17:50:00 +00:00
# ifdef USE_FONTCONFIG
# include <fontconfig / fontconfig.h>
# endif
2022-09-08 10:44:17 +00:00
namespace Ladybird {
2024-10-12 00:17:07 +00:00
FontPlugin : : FontPlugin ( bool is_layout_test_mode , Gfx : : SystemFontProvider * font_provider )
2023-05-06 10:46:14 +00:00
: m_is_layout_test_mode ( is_layout_test_mode )
2022-09-08 10:44:17 +00:00
{
2024-06-07 17:50:00 +00:00
# ifdef USE_FONTCONFIG
{
auto fontconfig_initialized = FcInit ( ) ;
VERIFY ( fontconfig_initialized ) ;
}
# endif
2024-10-12 00:17:07 +00:00
if ( ! font_provider )
font_provider = & static_cast < Gfx : : PathFontProvider & > ( Gfx : : FontDatabase : : the ( ) . install_system_font_provider ( make < Gfx : : PathFontProvider > ( ) ) ) ;
if ( is < Gfx : : PathFontProvider > ( * font_provider ) ) {
auto & path_font_provider = static_cast < Gfx : : PathFontProvider & > ( * font_provider ) ;
// Load anything we can find in the system's font directories
for ( auto const & path : Core : : StandardPaths : : font_directories ( ) . release_value_but_fixme_should_propagate_errors ( ) )
path_font_provider . load_all_fonts_from_uri ( MUST ( String : : formatted ( " file://{} " , path ) ) ) ;
}
2022-09-08 10:44:17 +00:00
update_generic_fonts ( ) ;
2022-09-17 19:25:15 +00:00
auto default_font_name = generic_font_name ( Web : : Platform : : GenericFont : : UiSansSerif ) ;
2023-09-06 05:45:47 +00:00
m_default_font = Gfx : : FontDatabase : : the ( ) . get ( default_font_name , 12.0 , 400 , Gfx : : FontWidth : : Normal , 0 ) ;
2022-09-17 19:25:15 +00:00
VERIFY ( m_default_font ) ;
2022-09-18 00:11:48 +00:00
auto default_fixed_width_font_name = generic_font_name ( Web : : Platform : : GenericFont : : UiMonospace ) ;
2023-09-06 05:45:47 +00:00
m_default_fixed_width_font = Gfx : : FontDatabase : : the ( ) . get ( default_fixed_width_font_name , 12.0 , 400 , Gfx : : FontWidth : : Normal , 0 ) ;
2022-09-18 00:11:48 +00:00
VERIFY ( m_default_fixed_width_font ) ;
2022-09-08 10:44:17 +00:00
}
2023-08-02 18:01:17 +00:00
FontPlugin : : ~ FontPlugin ( ) = default ;
2022-09-08 10:44:17 +00:00
2023-08-02 18:01:17 +00:00
Gfx : : Font & FontPlugin : : default_font ( )
2022-09-17 19:25:15 +00:00
{
return * m_default_font ;
}
2023-08-02 18:01:17 +00:00
Gfx : : Font & FontPlugin : : default_fixed_width_font ( )
2022-09-17 19:25:15 +00:00
{
return * m_default_fixed_width_font ;
}
2024-09-05 20:35:01 +00:00
RefPtr < Gfx : : Font > FontPlugin : : default_emoji_font ( float point_size )
{
2024-09-20 22:39:33 +00:00
FlyString default_emoji_font_name ;
if ( m_is_layout_test_mode ) {
default_emoji_font_name = " Noto Emoji " _fly_string ;
} else {
2024-09-05 20:35:01 +00:00
# ifdef AK_OS_MACOS
2024-09-20 22:39:33 +00:00
default_emoji_font_name = " Apple Color Emoji " _fly_string ;
2024-09-05 20:35:01 +00:00
# else
2024-09-20 22:39:33 +00:00
default_emoji_font_name = " Noto Color Emoji " _fly_string ;
2024-09-05 20:35:01 +00:00
# endif
2024-09-20 22:39:33 +00:00
}
2024-09-05 20:35:01 +00:00
return Gfx : : FontDatabase : : the ( ) . get ( default_emoji_font_name , point_size , 400 , Gfx : : FontWidth : : Normal , 0 ) ;
}
2024-06-07 17:50:00 +00:00
# ifdef USE_FONTCONFIG
static Optional < String > query_fontconfig_for_generic_family ( Web : : Platform : : GenericFont generic_font )
{
char const * pattern_string = nullptr ;
switch ( generic_font ) {
case Web : : Platform : : GenericFont : : Cursive :
pattern_string = " cursive " ;
break ;
case Web : : Platform : : GenericFont : : Fantasy :
pattern_string = " fantasy " ;
break ;
case Web : : Platform : : GenericFont : : Monospace :
pattern_string = " monospace " ;
break ;
case Web : : Platform : : GenericFont : : SansSerif :
pattern_string = " sans-serif " ;
break ;
case Web : : Platform : : GenericFont : : Serif :
pattern_string = " serif " ;
break ;
case Web : : Platform : : GenericFont : : UiMonospace :
pattern_string = " monospace " ;
break ;
case Web : : Platform : : GenericFont : : UiRounded :
pattern_string = " sans-serif " ;
break ;
case Web : : Platform : : GenericFont : : UiSansSerif :
pattern_string = " sans-serif " ;
break ;
case Web : : Platform : : GenericFont : : UiSerif :
pattern_string = " serif " ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
auto * config = FcConfigGetCurrent ( ) ;
VERIFY ( config ) ;
FcPattern * pattern = FcNameParse ( reinterpret_cast < FcChar8 const * > ( pattern_string ) ) ;
VERIFY ( pattern ) ;
auto success = FcConfigSubstitute ( config , pattern , FcMatchPattern ) ;
VERIFY ( success ) ;
FcDefaultSubstitute ( pattern ) ;
// Never select bitmap fonts.
success = FcPatternAddBool ( pattern , FC_SCALABLE , FcTrue ) ;
VERIFY ( success ) ;
// FIXME: Enable this once we can handle OpenType variable fonts.
success = FcPatternAddBool ( pattern , FC_VARIABLE , FcFalse ) ;
VERIFY ( success ) ;
Optional < String > name ;
FcResult result { } ;
if ( auto * matched = FcFontMatch ( config , pattern , & result ) ) {
FcChar8 * family = nullptr ;
if ( FcPatternGetString ( matched , FC_FAMILY , 0 , & family ) = = FcResultMatch ) {
auto const * family_cstring = reinterpret_cast < char const * > ( family ) ;
if ( auto string = String : : from_utf8 ( StringView { family_cstring , strlen ( family_cstring ) } ) ; ! string . is_error ( ) ) {
name = string . release_value ( ) ;
}
}
FcPatternDestroy ( matched ) ;
}
FcPatternDestroy ( pattern ) ;
return name ;
}
# endif
2023-08-02 18:01:17 +00:00
void FontPlugin : : update_generic_fonts ( )
2022-09-08 10:44:17 +00:00
{
// How we choose which system font to use for each CSS font:
2023-07-29 08:54:33 +00:00
// 1. Try a list of known-suitable fonts with their names hard-coded below.
2022-09-08 10:44:17 +00:00
2023-07-29 08:54:33 +00:00
// This is rather weird, but it's how things work right now.
// We should eventually have a way to query the system for the default font.
// Furthermore, we should allow overriding via some kind of configuration mechanism.
2022-09-08 10:44:17 +00:00
m_generic_font_names . resize ( static_cast < size_t > ( Web : : Platform : : GenericFont : : __Count ) ) ;
2023-09-06 05:45:47 +00:00
auto update_mapping = [ & ] ( Web : : Platform : : GenericFont generic_font , ReadonlySpan < FlyString > fallbacks ) {
2023-05-06 10:46:14 +00:00
if ( m_is_layout_test_mode ) {
2023-09-06 05:45:47 +00:00
m_generic_font_names [ static_cast < size_t > ( generic_font ) ] = " SerenitySans " _fly_string ;
2023-05-06 10:46:14 +00:00
return ;
}
2023-07-29 08:54:33 +00:00
RefPtr < Gfx : : Font const > gfx_font ;
2023-04-26 12:17:40 +00:00
2024-06-07 17:50:00 +00:00
# ifdef USE_FONTCONFIG
auto name = query_fontconfig_for_generic_family ( generic_font ) ;
if ( name . has_value ( ) ) {
gfx_font = Gfx : : FontDatabase : : the ( ) . get ( name . value ( ) , 16 , 400 , Gfx : : FontWidth : : Normal , 0 ) ;
}
# endif
if ( ! gfx_font ) {
for ( auto const & fallback : fallbacks ) {
gfx_font = Gfx : : FontDatabase : : the ( ) . get ( fallback , 16 , 400 , Gfx : : FontWidth : : Normal , 0 ) ;
if ( gfx_font )
break ;
}
2022-09-08 10:44:17 +00:00
}
2024-06-04 13:14:14 +00:00
m_generic_font_names [ static_cast < size_t > ( generic_font ) ] = gfx_font ? gfx_font - > family ( ) : String { } ;
2022-09-08 10:44:17 +00:00
} ;
2023-07-31 23:52:36 +00:00
// Fallback fonts to look for if Gfx::Font can't load expected font
2022-09-08 10:44:17 +00:00
// The lists are basically arbitrary, taken from https://www.w3.org/Style/Examples/007/fonts.en.html
2024-07-03 20:06:11 +00:00
// (We also add Android-specific font names to the list from W3 where required.)
Vector < FlyString > cursive_fallbacks { " Comic Sans MS " _fly_string , " Comic Sans " _fly_string , " Apple Chancery " _fly_string , " Bradley Hand " _fly_string , " Brush Script MT " _fly_string , " Snell Roundhand " _fly_string , " URW Chancery L " _fly_string , " Dancing Script " _fly_string } ;
Vector < FlyString > fantasy_fallbacks { " Impact " _fly_string , " Luminari " _fly_string , " Chalkduster " _fly_string , " Jazz LET " _fly_string , " Blippo " _fly_string , " Stencil Std " _fly_string , " Marker Felt " _fly_string , " Trattatello " _fly_string , " Coming Soon " _fly_string } ;
Vector < FlyString > monospace_fallbacks { " Andale Mono " _fly_string , " Courier New " _fly_string , " Courier " _fly_string , " FreeMono " _fly_string , " OCR A Std " _fly_string , " DejaVu Sans Mono " _fly_string , " Droid Sans Mono " _fly_string , " Liberation Mono " _fly_string } ;
Vector < FlyString > sans_serif_fallbacks { " Arial " _fly_string , " Helvetica " _fly_string , " Verdana " _fly_string , " Trebuchet MS " _fly_string , " Gill Sans " _fly_string , " Noto Sans " _fly_string , " Avantgarde " _fly_string , " Optima " _fly_string , " Arial Narrow " _fly_string , " Liberation Sans " _fly_string , " Roboto " _fly_string } ;
Vector < FlyString > serif_fallbacks { " Times " _fly_string , " Times New Roman " _fly_string , " Didot " _fly_string , " Georgia " _fly_string , " Palatino " _fly_string , " Bookman " _fly_string , " New Century Schoolbook " _fly_string , " American Typewriter " _fly_string , " Liberation Serif " _fly_string , " Roman " _fly_string , " Noto Serif " _fly_string } ;
2022-09-08 10:44:17 +00:00
2023-07-29 08:54:33 +00:00
update_mapping ( Web : : Platform : : GenericFont : : Cursive , cursive_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : Fantasy , fantasy_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : Monospace , monospace_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : SansSerif , sans_serif_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : Serif , serif_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : UiMonospace , monospace_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : UiRounded , sans_serif_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : UiSansSerif , sans_serif_fallbacks ) ;
update_mapping ( Web : : Platform : : GenericFont : : UiSerif , serif_fallbacks ) ;
2022-09-08 10:44:17 +00:00
}
2023-09-06 05:45:47 +00:00
FlyString FontPlugin : : generic_font_name ( Web : : Platform : : GenericFont generic_font )
2022-09-08 10:44:17 +00:00
{
return m_generic_font_names [ static_cast < size_t > ( generic_font ) ] ;
}
}