diff --git a/common/font/fontconfig.cpp b/common/font/fontconfig.cpp
index 8f0a1fe69b..0b6efd60ac 100644
--- a/common/font/fontconfig.cpp
+++ b/common/font/fontconfig.cpp
@@ -18,23 +18,25 @@
* with this program. If not, see .
*/
-#include
#include
#include
#include
#include
+#include
+#include
using namespace fontconfig;
static FONTCONFIG* g_config = nullptr;
-
-inline static FcChar8* wxStringToFcChar8( const wxString& str )
+/**
+ * A simple wrapper to avoid exporing fontconfig in the header
+ */
+struct fontconfig::FONTCONFIG_PAT
{
- wxScopedCharBuffer const fcBuffer = str.ToUTF8();
- return (FcChar8*) fcBuffer.data();
-}
+ FcPattern* pat;
+};
wxString FONTCONFIG::Version()
@@ -61,19 +63,112 @@ FONTCONFIG* Fontconfig()
}
+bool FONTCONFIG::isLanguageMatch( const wxString& aSearchLang, const wxString& aSupportedLang )
+{
+ if( aSearchLang.Lower() == aSupportedLang.Lower() )
+ return true;
+
+ if( aSupportedLang.empty() )
+ return false;
+
+ if( aSearchLang.empty() )
+ return false;
+
+ wxArrayString supportedLangBits;
+ wxStringSplit( aSupportedLang.Lower(), supportedLangBits, wxS( '-' ) );
+
+ wxArrayString searhcLangBits;
+ wxStringSplit( aSearchLang.Lower(), searhcLangBits, wxS( '-' ) );
+
+ // if either side of the comparison have only one section, then its a broad match but fine
+ // i.e. the haystack is declaring broad support or the search language is broad
+ if( searhcLangBits.size() == 1 || supportedLangBits.size() == 1 )
+ {
+ return searhcLangBits[0] == supportedLangBits[0];
+ }
+
+ // the full two part comparison should have passed the initial shortcut
+
+ return false;
+}
+
+
+std::string FONTCONFIG::getFcString( FONTCONFIG_PAT& aPat, const char* aObj, int aIdx )
+{
+ FcChar8* str;
+ std::string res;
+
+ if( FcPatternGetString( aPat.pat, aObj, aIdx, &str ) == FcResultMatch )
+ res = std::string( reinterpret_cast( str ) );
+
+ return res;
+}
+
+
+void FONTCONFIG::getAllFamilyStrings( FONTCONFIG_PAT& aPat,
+ std::unordered_map& aFamStringMap )
+{
+ std::string famLang;
+ std::string fam;
+
+ int langIdx = 0;
+ do
+ {
+ famLang = getFcString( aPat, FC_FAMILYLANG, langIdx );
+
+ if( famLang.empty() && langIdx != 0 )
+ break;
+ else
+ {
+ fam = getFcString( aPat, FC_FAMILY, langIdx );
+ aFamStringMap.insert_or_assign( famLang, fam );
+ }
+ } while( langIdx++ < std::numeric_limits<
+ int8_t>::max() ); //arbitrary to avoid getting stuck for any reason
+}
+
+
+std::string FONTCONFIG::getFamilyStringByLang( FONTCONFIG_PAT& aPat, const wxString& aDesiredLang )
+{
+ std::unordered_map famStrings;
+ getAllFamilyStrings( aPat, famStrings );
+
+ if( famStrings.empty() )
+ return "";
+
+ for( auto const& [key, val] : famStrings )
+ {
+ if( isLanguageMatch( aDesiredLang, FROM_UTF8( key.c_str() ) ) )
+ {
+ return val;
+ }
+ }
+
+ // fall back to the first and maybe only available name
+ // most fonts by review don't even bother declaring more than one font family name
+ // and they don't even bother declare the language tag either, they just leave it blank
+ return famStrings.begin()->second;
+}
+
+
FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString &aFontFile,
bool aBold, bool aItalic )
{
FF_RESULT retval = FF_RESULT::FF_ERROR;
wxString qualifiedFontName = aFontName;
+ wxScopedCharBuffer const fcBuffer = qualifiedFontName.ToUTF8();
+
+ FcPattern* pat = FcPatternCreate();
+
if( aBold )
- qualifiedFontName << wxS( ":Bold" );
+ FcPatternAddString( pat, FC_STYLE, (const FcChar8*) "Bold" );
if( aItalic )
- qualifiedFontName << wxS( ":Italic" );
+ FcPatternAddString( pat, FC_STYLE, (const FcChar8*) "Italic" );
+
+ FcPatternAddString( pat, FC_FAMILY, (FcChar8*) fcBuffer.data() );
- FcPattern* pat = FcNameParse( wxStringToFcChar8( qualifiedFontName ) );
FcConfigSubstitute( nullptr, pat, FcMatchPattern );
FcDefaultSubstitute( pat );
@@ -95,6 +190,9 @@ FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString
retval = FF_RESULT::FF_SUBSTITUTE;
+ std::unordered_map famStrings;
+ FONTCONFIG_PAT patHolder{ font };
+ getAllFamilyStrings( patHolder, famStrings );
if( FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch )
{
fontName = wxString::FromUTF8( (char*) family );
@@ -129,19 +227,29 @@ FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString
has_ital = true;
}
- if( fontName.Lower().StartsWith( aFontName.Lower() ) )
+ for( auto const& [key, val] : famStrings )
{
- if( ( aBold && !has_bold ) && ( aItalic && !has_ital ) )
- retval = FF_RESULT::FF_MISSING_BOLD_ITAL;
- else if( aBold && !has_bold )
- retval = FF_RESULT::FF_MISSING_BOLD;
- else if( aItalic && !has_ital )
- retval = FF_RESULT::FF_MISSING_ITAL;
- else if( ( aBold != has_bold ) || ( aItalic != has_ital ) )
- retval = FF_RESULT::FF_SUBSTITUTE;
- else
- retval = FF_RESULT::FF_OK;
+ wxString searchFont;
+ searchFont = wxString::FromUTF8( (char*) val.data() );
+
+ if( searchFont.Lower().StartsWith( aFontName.Lower() ) )
+ {
+ if( ( aBold && !has_bold ) && ( aItalic && !has_ital ) )
+ retval = FF_RESULT::FF_MISSING_BOLD_ITAL;
+ else if( aBold && !has_bold )
+ retval = FF_RESULT::FF_MISSING_BOLD;
+ else if( aItalic && !has_ital )
+ retval = FF_RESULT::FF_MISSING_ITAL;
+ else if( ( aBold != has_bold ) || ( aItalic != has_ital ) )
+ retval = FF_RESULT::FF_SUBSTITUTE;
+ else
+ retval = FF_RESULT::FF_OK;
+
+ break;
+ }
}
+
+
}
}
@@ -158,13 +266,14 @@ FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString
}
-void FONTCONFIG::ListFonts( std::vector& aFonts )
+void FONTCONFIG::ListFonts( std::vector& aFonts, const std::string& aDesiredLang )
{
- if( m_fonts.empty() )
+ // be sure to cache bust if the language changed
+ if( m_fontInfoCache.empty() || m_fontCacheLastLang != aDesiredLang )
{
FcPattern* pat = FcPatternCreate();
- FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, FC_OUTLINE,
- nullptr );
+ FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_FAMILYLANG, FC_STYLE, FC_LANG, FC_FILE,
+ FC_OUTLINE, nullptr );
FcFontSet* fs = FcFontList( nullptr, pat, os );
for( int i = 0; fs && i < fs->nfont; ++i )
@@ -172,12 +281,10 @@ void FONTCONFIG::ListFonts( std::vector& aFonts )
FcPattern* font = fs->fonts[i];
FcChar8* file;
FcChar8* style;
- FcChar8* family;
FcLangSet* langSet;
FcBool outline;
if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch
- && FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch
&& FcPatternGetString( font, FC_STYLE, 0, &style ) == FcResultMatch
&& FcPatternGetLangSet( font, FC_LANG, 0, &langSet ) == FcResultMatch
&& FcPatternGetBool( font, FC_OUTLINE, 0, &outline ) == FcResultMatch )
@@ -185,7 +292,9 @@ void FONTCONFIG::ListFonts( std::vector& aFonts )
if( !outline )
continue;
- std::string theFamily( reinterpret_cast( family ) );
+ FONTCONFIG_PAT patHolder{ font };
+ std::string theFamily =
+ getFamilyStringByLang( patHolder, FROM_UTF8( aDesiredLang.c_str() ) );
#ifdef __WXMAC__
// On Mac (at least) some of the font names are in their own language. If
@@ -238,10 +347,10 @@ void FONTCONFIG::ListFonts( std::vector& aFonts )
if( theFamily.length() > 0 && theFamily.front() == '.' )
continue;
- std::map::iterator it = m_fonts.find( theFamily );
+ std::map::iterator it = m_fontInfoCache.find( theFamily );
- if( it == m_fonts.end() )
- m_fonts.emplace( theFamily, fontInfo );
+ if( it == m_fontInfoCache.end() )
+ m_fontInfoCache.emplace( theFamily, fontInfo );
else
it->second.Children().push_back( fontInfo );
}
@@ -249,8 +358,10 @@ void FONTCONFIG::ListFonts( std::vector& aFonts )
if( fs )
FcFontSetDestroy( fs );
+
+ m_fontCacheLastLang = aDesiredLang;
}
- for( const std::pair& entry : m_fonts )
+ for( const std::pair& entry : m_fontInfoCache )
aFonts.push_back( entry.second.Family() );
}
diff --git a/common/pgm_base.cpp b/common/pgm_base.cpp
index f44950af1a..0e74b023f7 100644
--- a/common/pgm_base.cpp
+++ b/common/pgm_base.cpp
@@ -775,6 +775,22 @@ void PGM_BASE::SetLanguageIdentifier( int menu_id )
}
+wxString PGM_BASE::GetLanguageTag()
+{
+ const wxLanguageInfo* langInfo = wxLocale::GetLanguageInfo( m_language_id );
+
+ if( !langInfo )
+ return "";
+ else
+ {
+ wxString str = langInfo->GetCanonicalWithRegion();
+ str.Replace( "_", "-" );
+
+ return str;
+ }
+}
+
+
void PGM_BASE::SetLanguagePath()
{
wxLocale::AddCatalogLookupPathPrefix( PATHS::GetLocaleDataPath() );
diff --git a/common/widgets/font_choice.cpp b/common/widgets/font_choice.cpp
index d34146aed7..42db88c345 100644
--- a/common/widgets/font_choice.cpp
+++ b/common/widgets/font_choice.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
// The "official" name of the building Kicad stroke font (always existing)
#include
@@ -32,7 +33,7 @@ FONT_CHOICE::FONT_CHOICE( wxWindow* aParent, int aId, wxPoint aPosition, wxSize
m_systemFontCount = wxChoice::GetCount();
std::vector fontNames;
- Fontconfig()->ListFonts( fontNames );
+ Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ) );
wxArrayString menuList;
diff --git a/eeschema/fields_grid_table.cpp b/eeschema/fields_grid_table.cpp
index c0e2d156db..d557403dc0 100644
--- a/eeschema/fields_grid_table.cpp
+++ b/eeschema/fields_grid_table.cpp
@@ -46,6 +46,7 @@
#include
#include
#include
+#include
enum
@@ -275,7 +276,7 @@ void FIELDS_GRID_TABLE::initGrid( WX_GRID* aGrid )
wxArrayString fonts;
std::vector fontNames;
- Fontconfig()->ListFonts( fontNames );
+ Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ) );
for( const std::string& name : fontNames )
fonts.Add( wxString( name ) );
diff --git a/include/font/fontconfig.h b/include/font/fontconfig.h
index 4018299c1d..0e690b2099 100644
--- a/include/font/fontconfig.h
+++ b/include/font/fontconfig.h
@@ -21,6 +21,8 @@
#ifndef KICAD_FONTCONFIG_H
#define KICAD_FONTCONFIG_H
+#include
+
#include
#include
#include