2022-01-01 01:21:03 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2021 Ola Rinta-Koski
|
|
|
|
* Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2022-02-06 19:31:02 +00:00
|
|
|
#include <fontconfig/fontconfig.h>
|
|
|
|
|
2022-01-01 01:21:03 +00:00
|
|
|
#include <font/fontconfig.h>
|
|
|
|
#include <pgm_base.h>
|
2022-01-09 00:44:52 +00:00
|
|
|
#include <wx/log.h>
|
2022-01-18 12:43:31 +00:00
|
|
|
#include <trace_helpers.h>
|
2022-01-01 01:21:03 +00:00
|
|
|
|
|
|
|
using namespace fontconfig;
|
|
|
|
|
|
|
|
static FONTCONFIG* g_config = nullptr;
|
|
|
|
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-01 01:21:03 +00:00
|
|
|
inline static FcChar8* wxStringToFcChar8( const wxString& str )
|
|
|
|
{
|
|
|
|
wxScopedCharBuffer const fcBuffer = str.ToUTF8();
|
|
|
|
return (FcChar8*) fcBuffer.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-07 14:01:37 +00:00
|
|
|
wxString FONTCONFIG::Version()
|
|
|
|
{
|
|
|
|
return wxString::Format( "%d.%d.%d", FC_MAJOR, FC_MINOR, FC_REVISION );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-01 01:21:03 +00:00
|
|
|
FONTCONFIG::FONTCONFIG()
|
|
|
|
{
|
2022-01-09 00:44:52 +00:00
|
|
|
(void) FcInitLoadConfigAndFonts();
|
2022-01-01 01:21:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-01-09 11:32:37 +00:00
|
|
|
FONTCONFIG* Fontconfig()
|
2022-01-01 01:21:03 +00:00
|
|
|
{
|
|
|
|
if( !g_config )
|
|
|
|
{
|
|
|
|
FcInit();
|
|
|
|
g_config = new FONTCONFIG();
|
|
|
|
}
|
|
|
|
|
2022-01-09 11:32:37 +00:00
|
|
|
return g_config;
|
2022-01-01 01:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile )
|
|
|
|
{
|
|
|
|
FcPattern* pat = FcNameParse( wxStringToFcChar8( aFontName ) );
|
|
|
|
FcConfigSubstitute( nullptr, pat, FcMatchPattern );
|
|
|
|
FcDefaultSubstitute( pat );
|
|
|
|
|
|
|
|
FcResult r = FcResultNoMatch;
|
|
|
|
FcPattern* font = FcFontMatch( nullptr, pat, &r );
|
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
wxString fontName;
|
|
|
|
bool ok = false;
|
|
|
|
bool substituted = false;
|
2022-01-01 01:21:03 +00:00
|
|
|
|
|
|
|
if( font )
|
|
|
|
{
|
|
|
|
FcChar8* file = nullptr;
|
|
|
|
|
|
|
|
if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch )
|
|
|
|
{
|
|
|
|
aFontFile = wxString::FromUTF8( (char*) file );
|
2022-01-09 00:44:52 +00:00
|
|
|
|
|
|
|
FcChar8* family = nullptr;
|
|
|
|
FcChar8* style = nullptr;
|
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
if( FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch )
|
|
|
|
{
|
|
|
|
fontName = wxString::FromUTF8( (char*) family );
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
if( FcPatternGetString( font, FC_STYLE, 0, &style ) == FcResultMatch )
|
|
|
|
{
|
|
|
|
wxString styleStr = wxString::FromUTF8( (char*) style );
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
if( !styleStr.IsEmpty() )
|
|
|
|
{
|
|
|
|
styleStr.Replace( " ", ":" );
|
|
|
|
fontName += ":" + styleStr;
|
|
|
|
}
|
|
|
|
}
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
// TODO: report Regular vs Book, Italic vs Oblique, etc. or filter them out?
|
2022-01-09 11:09:59 +00:00
|
|
|
|
2022-01-10 01:53:01 +00:00
|
|
|
if( aFontName.Contains( ":" ) )
|
|
|
|
substituted = aFontName.CmpNoCase( fontName ) != 0;
|
|
|
|
else
|
|
|
|
substituted = !fontName.StartsWith( aFontName );
|
|
|
|
}
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-01 01:21:03 +00:00
|
|
|
ok = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
FcPatternDestroy( font );
|
|
|
|
}
|
|
|
|
|
2022-01-09 00:44:52 +00:00
|
|
|
if( !ok )
|
|
|
|
wxLogWarning( _( "Error loading font '%s'." ), aFontName );
|
2022-01-10 01:53:01 +00:00
|
|
|
else if( substituted )
|
|
|
|
wxLogWarning( _( "Font '%s' not found; substituting '%s'." ), aFontName, fontName );
|
2022-01-09 00:44:52 +00:00
|
|
|
|
2022-01-01 01:21:03 +00:00
|
|
|
FcPatternDestroy( pat );
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FONTCONFIG::ListFonts( std::vector<std::string>& aFonts )
|
|
|
|
{
|
|
|
|
if( m_fonts.empty() )
|
|
|
|
{
|
|
|
|
FcPattern* pat = FcPatternCreate();
|
2022-01-08 22:02:17 +00:00
|
|
|
FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, FC_OUTLINE,
|
|
|
|
nullptr );
|
2022-01-01 01:21:03 +00:00
|
|
|
FcFontSet* fs = FcFontList( nullptr, pat, os );
|
|
|
|
|
|
|
|
for( int i = 0; fs && i < fs->nfont; ++i )
|
|
|
|
{
|
|
|
|
FcPattern* font = fs->fonts[i];
|
|
|
|
FcChar8* file;
|
|
|
|
FcChar8* style;
|
|
|
|
FcChar8* family;
|
2022-01-08 22:02:17 +00:00
|
|
|
FcLangSet* langSet;
|
|
|
|
FcBool outline;
|
2022-01-01 01:21:03 +00:00
|
|
|
|
|
|
|
if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch
|
|
|
|
&& FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch
|
2022-01-08 22:02:17 +00:00
|
|
|
&& FcPatternGetString( font, FC_STYLE, 0, &style ) == FcResultMatch
|
|
|
|
&& FcPatternGetLangSet( font, FC_LANG, 0, &langSet ) == FcResultMatch
|
|
|
|
&& FcPatternGetBool( font, FC_OUTLINE, 0, &outline ) == FcResultMatch )
|
2022-01-01 01:21:03 +00:00
|
|
|
{
|
2022-01-08 22:02:17 +00:00
|
|
|
if( !outline )
|
|
|
|
continue;
|
|
|
|
|
2022-01-18 12:43:31 +00:00
|
|
|
std::string theFamily( reinterpret_cast<char *>( family ) );
|
|
|
|
|
2022-01-18 14:19:57 +00:00
|
|
|
#ifdef __WXMAC__
|
|
|
|
// On Mac (at least) some of the font names are in their own language. If
|
|
|
|
// the OS doesn't support this language then we get a bunch of garbage names
|
|
|
|
// in the font menu.
|
|
|
|
//
|
|
|
|
// GTK, on the other hand, doesn't appear to support wxLocale::IsAvailable(),
|
|
|
|
// so we can't run these checks.
|
|
|
|
|
2022-02-07 16:32:19 +00:00
|
|
|
FcStrSet* langStrSet = FcLangSetGetLangs( langSet );
|
|
|
|
FcStrList* langStrList = FcStrListCreate( langStrSet );
|
|
|
|
FcChar8* langStr = FcStrListNext( langStrList );
|
2022-01-18 14:19:57 +00:00
|
|
|
bool langSupported = false;
|
|
|
|
|
2022-01-09 11:32:37 +00:00
|
|
|
if( !langStr )
|
|
|
|
{
|
|
|
|
// Symbol fonts (Wingdings, etc.) have no language
|
|
|
|
langSupported = true;
|
|
|
|
}
|
|
|
|
else while( langStr )
|
2022-01-08 22:02:17 +00:00
|
|
|
{
|
|
|
|
wxString langWxStr( reinterpret_cast<char *>( langStr ) );
|
|
|
|
const wxLanguageInfo* langInfo = wxLocale::FindLanguageInfo( langWxStr );
|
|
|
|
|
|
|
|
if( langInfo && wxLocale::IsAvailable( langInfo->Language ) )
|
|
|
|
{
|
|
|
|
langSupported = true;
|
|
|
|
break;
|
|
|
|
}
|
2022-01-18 12:43:31 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
wxLogTrace( traceFonts, "Font '%s' language '%s' not supported by OS.",
|
|
|
|
theFamily, langWxStr );
|
|
|
|
}
|
2022-01-09 11:32:37 +00:00
|
|
|
|
|
|
|
langStr = FcStrListNext( langStrList );
|
2022-01-08 22:02:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FcStrListDone( langStrList );
|
|
|
|
FcStrSetDestroy( langStrSet );
|
|
|
|
|
|
|
|
if( !langSupported )
|
|
|
|
continue;
|
2022-01-18 14:19:57 +00:00
|
|
|
#endif
|
2022-01-08 22:02:17 +00:00
|
|
|
|
2022-01-04 18:59:10 +00:00
|
|
|
std::string theFile( reinterpret_cast<char *>( file ) );
|
|
|
|
std::string theStyle( reinterpret_cast<char *>( style ) );
|
2022-01-01 01:21:03 +00:00
|
|
|
FONTINFO fontInfo( theFile, theStyle, theFamily );
|
|
|
|
|
|
|
|
if( theFamily.length() > 0 && theFamily.front() == '.' )
|
|
|
|
continue;
|
|
|
|
|
2022-01-09 11:32:37 +00:00
|
|
|
std::map<std::string, FONTINFO>::iterator it = m_fonts.find( theFamily );
|
2022-01-01 01:21:03 +00:00
|
|
|
|
|
|
|
if( it == m_fonts.end() )
|
2022-02-06 00:53:31 +00:00
|
|
|
m_fonts.emplace( theFamily, fontInfo );
|
2022-01-01 01:21:03 +00:00
|
|
|
else
|
|
|
|
it->second.Children().push_back( fontInfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fs )
|
|
|
|
FcFontSetDestroy( fs );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( const std::pair<const std::string, FONTINFO>& entry : m_fonts )
|
|
|
|
aFonts.push_back( entry.second.Family() );
|
|
|
|
}
|