diff --git a/common/font/fontconfig.cpp b/common/font/fontconfig.cpp index ecd84bcd75..0f01352d53 100644 --- a/common/font/fontconfig.cpp +++ b/common/font/fontconfig.cpp @@ -61,9 +61,19 @@ FONTCONFIG* Fontconfig() } -bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile ) +FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString &aFontFile, + bool aBold, bool aItalic ) { - FcPattern* pat = FcNameParse( wxStringToFcChar8( aFontName ) ); + FF_RESULT retval = FF_RESULT::ERROR; + wxString qualifiedFontName = aFontName; + + if( aBold ) + qualifiedFontName << wxS( ":Bold" ); + + if( aItalic ) + qualifiedFontName << wxS( ":Italic" ); + + FcPattern* pat = FcNameParse( wxStringToFcChar8( qualifiedFontName ) ); FcConfigSubstitute( nullptr, pat, FcMatchPattern ); FcDefaultSubstitute( pat ); @@ -71,8 +81,6 @@ bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile ) FcPattern* font = FcFontMatch( nullptr, pat, &r ); wxString fontName; - bool ok = false; - bool substituted = false; if( font ) { @@ -81,17 +89,19 @@ bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile ) if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch ) { aFontFile = wxString::FromUTF8( (char*) file ); - + wxString styleStr; FcChar8* family = nullptr; FcChar8* style = nullptr; + retval = FF_RESULT::SUBSTITUTE; + if( FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch ) { fontName = wxString::FromUTF8( (char*) family ); if( FcPatternGetString( font, FC_STYLE, 0, &style ) == FcResultMatch ) { - wxString styleStr = wxString::FromUTF8( (char*) style ); + styleStr = wxString::FromUTF8( (char*) style ); if( !styleStr.IsEmpty() ) { @@ -100,27 +110,51 @@ bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile ) } } - // TODO: report Regular vs Book, Italic vs Oblique, etc. or filter them out? + bool has_bold = false; + bool has_ital = false; + wxString lower_style = styleStr.Lower(); - if( aFontName.Contains( ":" ) ) - substituted = aFontName.CmpNoCase( fontName ) != 0; - else - substituted = !fontName.StartsWith( aFontName ); - } + if( lower_style.Contains( wxS( "bold" ) ) + || lower_style.Contains( wxS( "black" ) ) + || lower_style.Contains( wxS( "thick" ) ) + || lower_style.Contains( wxS( "dark" ) ) ) + { + has_bold = true; + } + + if( lower_style.Contains( wxS( "italic" ) ) + || lower_style.Contains( wxS( "oblique" ) ) + || lower_style.Contains( wxS( "slant" ) ) ) + { + has_ital = true; + } - ok = true; + if( fontName.Lower().StartsWith( aFontName.Lower() ) ) + { + if( ( aBold && !has_bold ) && ( aItalic && !has_ital ) ) + retval = FF_RESULT::MISSING_BOLD_ITAL; + else if( aBold && !has_bold ) + retval = FF_RESULT::MISSING_BOLD; + else if( aItalic && !has_ital ) + retval = FF_RESULT::MISSING_ITAL; + else if( ( aBold != has_bold ) || ( aItalic != has_ital ) ) + retval = FF_RESULT::SUBSTITUTE; + else + retval = FF_RESULT::OK; + } + } } FcPatternDestroy( font ); } - if( !ok ) - wxLogWarning( _( "Error loading font '%s'." ), aFontName ); - else if( substituted ) - wxLogWarning( _( "Font '%s' not found; substituting '%s'." ), aFontName, fontName ); + if( retval == FF_RESULT::ERROR ) + wxLogWarning( _( "Error loading font '%s'." ), qualifiedFontName ); + else if( retval == FF_RESULT::SUBSTITUTE ) + wxLogWarning( _( "Font '%s' not found; substituting '%s'." ), qualifiedFontName, fontName ); FcPatternDestroy( pat ); - return ok; + return retval; } diff --git a/common/font/outline_font.cpp b/common/font/outline_font.cpp index b8db7faa1d..45fdc3e4d1 100644 --- a/common/font/outline_font.cpp +++ b/common/font/outline_font.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include FT_GLYPH_H #include FT_BBOX_H @@ -46,7 +47,9 @@ FT_Library OUTLINE_FONT::m_freeType = nullptr; OUTLINE_FONT::OUTLINE_FONT() : m_face(NULL), - m_faceSize( 16 ) + m_faceSize( 16 ), + m_fakeBold( false ), + m_fakeItal( false ) { if( !m_freeType ) FT_Init_FreeType( &m_freeType ); @@ -90,15 +93,17 @@ OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, boo OUTLINE_FONT* font = new OUTLINE_FONT(); wxString fontFile; - wxString qualifiedFontName = aFontName; + using fc = fontconfig::FONTCONFIG; + + fc::FF_RESULT retval = Fontconfig()->FindFont( aFontName, fontFile, aBold, aItalic ); - if( aBold ) - qualifiedFontName << wxS( ":Bold" ); + if( retval == fc::FF_RESULT::MISSING_BOLD || retval == fc::FF_RESULT::MISSING_BOLD_ITAL ) + font->SetFakeBold(); - if( aItalic ) - qualifiedFontName << wxS( ":Italic" ); + if( retval == fc::FF_RESULT::MISSING_ITAL || retval == fc::FF_RESULT::MISSING_BOLD_ITAL ) + font->SetFakeItal(); - if( Fontconfig()->FindFont( qualifiedFontName, fontFile ) ) + if( retval != fc::FF_RESULT::ERROR ) (void) font->loadFace( fontFile ); font->m_fontName = aFontName; // Keep asked-for name, even if we substituted. @@ -357,8 +362,24 @@ VECTOR2I OUTLINE_FONT::getTextAsGlyphs( BOX2I* aBBox, std::vectorglyph->outline, 1 << 6 ); + // contours is a collection of all outlines in the glyph; for example the 'o' glyph // generally contains 2 contours, one for the glyph outline and one for the hole CONTOURS contours; diff --git a/include/font/fontconfig.h b/include/font/fontconfig.h index 26954155be..19c60eb8ba 100644 --- a/include/font/fontconfig.h +++ b/include/font/fontconfig.h @@ -36,13 +36,23 @@ public: static wxString Version(); + enum class FF_RESULT + { + OK, + ERROR, + SUBSTITUTE, + MISSING_BOLD, + MISSING_ITAL, + MISSING_BOLD_ITAL + }; + /** * Given a fully-qualified font name ("Times:Bold:Italic") find the closest matching font * and return its filepath in \a aFontFile. * * A return value of false indicates a serious error in the font system. */ - bool FindFont( const wxString& aFontName, wxString& aFontFile ); + FF_RESULT FindFont( const wxString& aFontName, wxString& aFontFile, bool aBold, bool aItalic ); /** * List the current available font families. diff --git a/include/font/outline_font.h b/include/font/outline_font.h index 5096d700e8..5c1e19902f 100644 --- a/include/font/outline_font.h +++ b/include/font/outline_font.h @@ -63,12 +63,22 @@ public: bool IsBold() const override { - return m_face && ( m_face->style_flags & FT_STYLE_FLAG_BOLD ); + return m_face && ( m_fakeBold || ( m_face->style_flags & FT_STYLE_FLAG_BOLD ) ); } bool IsItalic() const override { - return m_face && ( m_face->style_flags & FT_STYLE_FLAG_ITALIC ); + return m_face && ( m_fakeItal || ( m_face->style_flags & FT_STYLE_FLAG_ITALIC ) ); + } + + void SetFakeBold() + { + m_fakeBold = true; + } + + void SetFakeItal() + { + m_fakeItal = true; } /** @@ -128,6 +138,8 @@ private: static FT_Library m_freeType; FT_Face m_face; const int m_faceSize; + bool m_fakeBold; + bool m_fakeItal; // cache for glyphs converted to straight segments // key is glyph index (FT_GlyphSlot field glyph_index)