23#include <QApplication>
26#include <QFontDatabase>
34 const QFontInfo fi = QFontInfo( f );
35 return fi.exactMatch();
40 const QFont tmpFont = QFont( family );
42 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
47 const QFontDatabase fontDB;
51 if ( fontDB.styles( family ).contains( style ) )
55 QString modified( style );
56 if ( style ==
"Roman" )
58 if ( style ==
"Oblique" )
60 if ( style ==
"Bold Oblique" )
61 modified =
"Bold Italic";
62 if ( fontDB.styles( family ).contains( modified ) )
71 auto styleNameIsMatch = [&font](
const QString & candidate ) ->
bool
74 QFont testFont( font.family() );
75 testFont.setStyleName( candidate );
76 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
80 const QFontInfo fontInfo( font );
81 QString styleName = fontInfo.styleName();
82 if ( !styleName.isEmpty() )
84 if ( styleNameIsMatch( styleName ) )
89 styleName = QFontDatabase().styleString( font );
90 if ( !styleName.isEmpty() )
92 if ( styleNameIsMatch( styleName ) )
102 const QFontDatabase fontDB;
103 const QStringList fontFamilies = fontDB.families();
106 QList<QString>::const_iterator it = fontFamilies.constBegin();
107 for ( ; it != fontFamilies.constEnd(); ++it )
110 if ( it->startsWith( family, Qt::CaseInsensitive ) )
117 *match = ( *it == family );
133 const QFont f = QFont( family );
134 *chosen = f.family();
155 if ( fontstyle.isEmpty() )
160 QFontDatabase fontDB;
173 if ( fontstyle == fontDB.styleString( f ) )
178 const QFont appfont = QApplication::font();
179 const int defaultSize = appfont.pointSize();
182 bool foundmatch =
false;
185 styledfont = fontDB.font( f.family(), fontstyle, defaultSize );
186 if ( appfont != styledfont || fontstyle != fontDB.styleString( f ) )
193 if ( fallback && !foundmatch )
195 QFont testFont = QFont( f );
196 testFont.setPointSize( defaultSize );
199 const auto constFamily = fontDB.styles( f.family() );
200 for (
const QString &style : constFamily )
202 styledfont = fontDB.font( f.family(), style, defaultSize );
203 styledfont = styledfont.resolve( f );
204 if ( testFont.toString() == styledfont.toString() )
214 for (
const QString &style : constFamily )
216 styledfont = fontDB.font( f.family(), style, defaultSize );
217 if ( QApplication::font() != styledfont )
232 styledfont.setPointSizeF( f.pointSizeF() );
234 else if ( f.pixelSize() != -1 )
236 styledfont.setPixelSize( f.pixelSize() );
238 styledfont.setCapitalization( f.capitalization() );
239 styledfont.setUnderline( f.underline() );
240 styledfont.setStrikeOut( f.strikeOut() );
241 styledfont.setWordSpacing( f.wordSpacing() );
242 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
253 return QStringLiteral(
"QGIS Vera Sans" );
259 bool fontsLoaded =
false;
261 QMap<QString, QString> fontStyles;
262 fontStyles.insert( QStringLiteral(
"Roman" ), QStringLiteral(
"QGIS-Vera/QGIS-Vera.ttf" ) );
263 fontStyles.insert( QStringLiteral(
"Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraIt.ttf" ) );
264 fontStyles.insert( QStringLiteral(
"Bold" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBd.ttf" ) );
265 fontStyles.insert( QStringLiteral(
"Bold Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBI.ttf" ) );
266 fontStyles.insert( QStringLiteral(
"Deja Bold" ), QStringLiteral(
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf" ) );
268 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
269 for ( ; f != fontStyles.constEnd(); ++f )
271 const QString fontpath( f.value() );
272 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( QStringLiteral(
"All" ) ) ) )
277 const QString fontFamily = !f.key().startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
278 const QString fontstyle = !f.key().startsWith( QLatin1String(
"Deja" ) ) ? f.key() : f.key().mid( 5 );
282 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' already available" ).arg( fontFamily, fontstyle ), 2 );
294 const int fontID = QFontDatabase::addApplicationFont( fontPath );
295 loaded = ( fontID != -1 );
296 fontsLoaded = ( fontsLoaded || loaded );
297 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' %3 from filesystem [%4]" )
298 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
299 QgsDebugMsgLevel( QStringLiteral(
"font families in %1: %2" ).arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
303 QFile fontResource(
":/testdata/font/" + fontpath );
304 if ( fontResource.open( QIODevice::ReadOnly ) )
306 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
307 loaded = ( fontID != -1 );
308 fontsLoaded = ( fontsLoaded || loaded );
310 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1' (%2) %3 from testdata.qrc" )
311 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
321 const QString fontFamily = !style.startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
322 const QString fontStyle = !style.startsWith( QLatin1String(
"Deja" ) ) ? style : style.mid( 5 );
329 const QFontDatabase fontDB;
330 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
332 if ( !f.exactMatch() )
335 if ( fontStyle ==
"Roman" )
337 else if ( fontStyle ==
"Oblique" )
339 else if ( fontStyle ==
"Bold Oblique" )
340 modified =
"Bold Italic";
341 if ( !modified.isEmpty() )
342 f = fontDB.font( fontFamily, modified, pointsize );
344 if ( !f.exactMatch() )
346 QgsDebugMsgLevel( QStringLiteral(
"Inexact font match - consider installing the %1 font." ).arg( fontFamily ), 2 );
347 QgsDebugMsgLevel( QStringLiteral(
"Requested: %1" ).arg( f.toString() ), 2 );
349 QgsDebugMsgLevel( QStringLiteral(
"Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9" ).arg( fi.family() ).arg( fi.pointSizeF() ).arg( fi.pixelSize() ).arg( fi.styleHint() ).arg( fi.weight() ).arg( fi.style() ).arg( fi.underline() ).arg( fi.strikeOut() ).arg( fi.fixedPitch() ), 2 );
353 f.setBold( fontStyle.contains( QLatin1String(
"Bold" ) ) );
354 f.setItalic( fontStyle.contains( QLatin1String(
"Oblique" ) ) || fontStyle.contains( QLatin1String(
"Italic" ) ) );
361 QDomElement fontElem = document.createElement( elementName );
362 fontElem.setAttribute( QStringLiteral(
"description" ), font.toString() );
364 fontElem.setAttribute( QStringLiteral(
"bold" ), font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
365 fontElem.setAttribute( QStringLiteral(
"italic" ), font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
366 fontElem.setAttribute( QStringLiteral(
"underline" ), font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
367 fontElem.setAttribute( QStringLiteral(
"strikethrough" ), font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
373 if ( element.isNull() )
378 font.fromString( element.attribute( QStringLiteral(
"description" ) ) );
380 if ( element.hasAttribute( QStringLiteral(
"bold" ) ) && element.attribute( QStringLiteral(
"bold" ) ) == QChar(
'1' ) )
382 font.setBold(
true );
384 if ( element.hasAttribute( QStringLiteral(
"italic" ) ) )
386 font.setItalic( element.attribute( QStringLiteral(
"italic" ) ) == QChar(
'1' ) );
388 if ( element.hasAttribute( QStringLiteral(
"underline" ) ) )
390 font.setUnderline( element.attribute( QStringLiteral(
"underline" ) ) == QChar(
'1' ) );
392 if ( element.hasAttribute( QStringLiteral(
"strikethrough" ) ) )
394 font.setStrikeOut( element.attribute( QStringLiteral(
"strikethrough" ) ) == QChar(
'1' ) );
397 if ( element.hasAttribute( QStringLiteral(
"style" ) ) )
407 if ( element.isNull() )
412 const QDomNodeList nodeList = element.elementsByTagName( childNode );
413 if ( !nodeList.isEmpty() )
415 const QDomElement fontElem = nodeList.at( 0 ).toElement();
426 std::unique_ptr< QMimeData >mimeData(
new QMimeData );
428 QDomDocument fontDoc;
429 const QDomElement fontElem =
toXmlElement( font, fontDoc, QStringLiteral(
"font" ) );
430 fontDoc.appendChild( fontElem );
431 mimeData->setText( fontDoc.toString() );
433 return mimeData.release();
445 const QString text = data->text();
446 if ( !text.isEmpty() )
451 if ( doc.setContent( text ) )
453 elem = doc.documentElement();
455 if ( elem.nodeName() != QLatin1String(
"font" ) )
456 elem = elem.firstChildElement( QStringLiteral(
"font" ) );
469static QMap<QString, QString> createTranslatedStyleMap()
471 QMap<QString, QString> translatedStyleMap;
472 const QStringList words = QStringList()
473 << QStringLiteral(
"Normal" )
474 << QStringLiteral(
"Regular" )
475 << QStringLiteral(
"Light" )
476 << QStringLiteral(
"Bold" )
477 << QStringLiteral(
"Black" )
478 << QStringLiteral(
"Demi" )
479 << QStringLiteral(
"Italic" )
480 << QStringLiteral(
"Oblique" );
481 const auto constWords = words;
482 for (
const QString &word : constWords )
484 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
486 return translatedStyleMap;
491 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
492 for (
int i = 0, n = words.length(); i < n; ++i )
494 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
496 return words.join( QLatin1Char(
' ' ) );
501 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
502 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
504 for (
int i = 0, n = words.length(); i < n; ++i )
506 if ( translatedStyleMap.contains( words[i] ) )
508 words[i] = translatedStyleMap.value( words[i] );
512 QgsDebugMsgLevel( QStringLiteral(
"Warning: style map does not contain %1" ).arg( words[i] ), 2 );
515 return words.join( QLatin1Char(
' ' ) );
520 QString css = QStringLiteral(
"font-family: " ) + font.family() +
';';
523 css += QLatin1String(
"font-style: " );
524 switch ( font.style() )
526 case QFont::StyleNormal:
527 css += QLatin1String(
"normal" );
529 case QFont::StyleItalic:
530 css += QLatin1String(
"italic" );
532 case QFont::StyleOblique:
533 css += QLatin1String(
"oblique" );
540 switch ( font.weight() )
548 case QFont::DemiBold:
560 case QFont::ExtraLight:
566 case QFont::ExtraBold:
570 css += QStringLiteral(
"font-weight: %1;" ).arg( cssWeight );
573 css += QStringLiteral(
"font-size: %1px;" ).arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
580 if ( family.isEmpty() )
586 QStringList recentFamilies = settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
589 recentFamilies.removeAll( family );
592 recentFamilies.prepend( family );
595 recentFamilies = recentFamilies.mid( 0, 10 );
597 settings.
setValue( QStringLiteral(
"fonts/recent" ), recentFamilies );
603 return settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
608 font.setFamily( family );
609 if ( !font.exactMatch() )
614 font.setFamilies( { family } );
620 QFont font( family, pointSize, weight, italic );
621 if ( !font.exactMatch() )
626 font.setFamilies( { family } );
static QString buildSourcePath()
Returns path to the source directory. Valid only when running from build directory.
static bool isRunningFromBuildDir()
Indicates whether running from build directory (not installed)
static QString resolveFontStyleName(const QFont &font)
Attempts to resolve the style name corresponding to the specified font object.
static QString asCSS(const QFont &font, double pointToPixelMultiplier=1.0)
Returns a CSS string representing the specified font as closely as possible.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
static bool setFromXmlElement(QFont &font, const QDomElement &element)
Sets the properties of a font to match the properties stored in an XML element.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static QMimeData * toMimeData(const QFont &font)
Returns new mime data representing the specified font settings.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static bool fontMatchOnSystem(const QFont &f)
Check whether exact font is on system.
static bool loadStandardTestFonts(const QStringList &loadstyles)
Loads standard test fonts from filesystem or qrc resource.
static QFont getStandardTestFont(const QString &style="Roman", int pointsize=12)
Gets standard test font with specific style.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
static void addRecentFontFamily(const QString &family)
Adds a font family to the list of recently used font families.
static QString standardTestFontFamily()
Gets standard test font family.
static QFont fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QFont.
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static QStringList recentFontFamilies()
Returns a list of recently used font families.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugMsgLevel(str, level)