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;
161 QString actualFontStyle = fontstyle;
180 if ( actualFontStyle == fontDB.styleString( f ) )
185 const QFont appfont = QApplication::font();
186 const int defaultSize = appfont.pointSize();
189 bool foundmatch =
false;
192 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
193 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
200 if ( fallback && !foundmatch )
202 QFont testFont = QFont( f );
203 testFont.setPointSize( defaultSize );
206 const auto constFamily = fontDB.styles( f.family() );
207 for (
const QString &style : constFamily )
209 styledfont = fontDB.font( f.family(), style, defaultSize );
210 styledfont = styledfont.resolve( f );
211 if ( testFont.toString() == styledfont.toString() )
221 for (
const QString &style : constFamily )
223 styledfont = fontDB.font( f.family(), style, defaultSize );
224 if ( QApplication::font() != styledfont )
239 styledfont.setPointSizeF( f.pointSizeF() );
241 else if ( f.pixelSize() != -1 )
243 styledfont.setPixelSize( f.pixelSize() );
245 styledfont.setCapitalization( f.capitalization() );
246 styledfont.setUnderline( f.underline() );
247 styledfont.setStrikeOut( f.strikeOut() );
248 styledfont.setWordSpacing( f.wordSpacing() );
249 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
260 return QStringLiteral(
"QGIS Vera Sans" );
266 bool fontsLoaded =
false;
268 QMap<QString, QString> fontStyles;
269 fontStyles.insert( QStringLiteral(
"Roman" ), QStringLiteral(
"QGIS-Vera/QGIS-Vera.ttf" ) );
270 fontStyles.insert( QStringLiteral(
"Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraIt.ttf" ) );
271 fontStyles.insert( QStringLiteral(
"Bold" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBd.ttf" ) );
272 fontStyles.insert( QStringLiteral(
"Bold Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBI.ttf" ) );
273 fontStyles.insert( QStringLiteral(
"Deja Bold" ), QStringLiteral(
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf" ) );
275 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
276 for ( ; f != fontStyles.constEnd(); ++f )
278 const QString fontpath( f.value() );
279 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( QStringLiteral(
"All" ) ) ) )
284 const QString fontFamily = !f.key().startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
285 const QString fontstyle = !f.key().startsWith( QLatin1String(
"Deja" ) ) ? f.key() : f.key().mid( 5 );
289 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' already available" ).arg( fontFamily, fontstyle ), 2 );
301 const int fontID = QFontDatabase::addApplicationFont( fontPath );
302 loaded = ( fontID != -1 );
303 fontsLoaded = ( fontsLoaded || loaded );
304 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' %3 from filesystem [%4]" )
305 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
306 QgsDebugMsgLevel( QStringLiteral(
"font families in %1: %2" ).arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
310 QFile fontResource(
":/testdata/font/" + fontpath );
311 if ( fontResource.open( QIODevice::ReadOnly ) )
313 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
314 loaded = ( fontID != -1 );
315 fontsLoaded = ( fontsLoaded || loaded );
317 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1' (%2) %3 from testdata.qrc" )
318 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
328 const QString fontFamily = !style.startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
329 const QString fontStyle = !style.startsWith( QLatin1String(
"Deja" ) ) ? style : style.mid( 5 );
336 const QFontDatabase fontDB;
337 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
339 if ( !f.exactMatch() )
342 if ( fontStyle ==
"Roman" )
344 else if ( fontStyle ==
"Oblique" )
346 else if ( fontStyle ==
"Bold Oblique" )
347 modified =
"Bold Italic";
348 if ( !modified.isEmpty() )
349 f = fontDB.font( fontFamily, modified, pointsize );
351 if ( !f.exactMatch() )
353 QgsDebugMsgLevel( QStringLiteral(
"Inexact font match - consider installing the %1 font." ).arg( fontFamily ), 2 );
354 QgsDebugMsgLevel( QStringLiteral(
"Requested: %1" ).arg( f.toString() ), 2 );
356 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 );
360 f.setBold( fontStyle.contains( QLatin1String(
"Bold" ) ) );
361 f.setItalic( fontStyle.contains( QLatin1String(
"Oblique" ) ) || fontStyle.contains( QLatin1String(
"Italic" ) ) );
368 QDomElement fontElem = document.createElement( elementName );
369 fontElem.setAttribute( QStringLiteral(
"description" ), font.toString() );
371 fontElem.setAttribute( QStringLiteral(
"bold" ), font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
372 fontElem.setAttribute( QStringLiteral(
"italic" ), font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
373 fontElem.setAttribute( QStringLiteral(
"underline" ), font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
374 fontElem.setAttribute( QStringLiteral(
"strikethrough" ), font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
380 if ( element.isNull() )
385 font.fromString( element.attribute( QStringLiteral(
"description" ) ) );
387 if ( element.hasAttribute( QStringLiteral(
"bold" ) ) && element.attribute( QStringLiteral(
"bold" ) ) == QChar(
'1' ) )
389 font.setBold(
true );
391 if ( element.hasAttribute( QStringLiteral(
"italic" ) ) )
393 font.setItalic( element.attribute( QStringLiteral(
"italic" ) ) == QChar(
'1' ) );
395 if ( element.hasAttribute( QStringLiteral(
"underline" ) ) )
397 font.setUnderline( element.attribute( QStringLiteral(
"underline" ) ) == QChar(
'1' ) );
399 if ( element.hasAttribute( QStringLiteral(
"strikethrough" ) ) )
401 font.setStrikeOut( element.attribute( QStringLiteral(
"strikethrough" ) ) == QChar(
'1' ) );
404 if ( element.hasAttribute( QStringLiteral(
"style" ) ) )
414 if ( element.isNull() )
419 const QDomNodeList nodeList = element.elementsByTagName( childNode );
420 if ( !nodeList.isEmpty() )
422 const QDomElement fontElem = nodeList.at( 0 ).toElement();
433 std::unique_ptr< QMimeData >mimeData(
new QMimeData );
435 QDomDocument fontDoc;
436 const QDomElement fontElem =
toXmlElement( font, fontDoc, QStringLiteral(
"font" ) );
437 fontDoc.appendChild( fontElem );
438 mimeData->setText( fontDoc.toString() );
440 return mimeData.release();
452 const QString text = data->text();
453 if ( !text.isEmpty() )
458 if ( doc.setContent( text ) )
460 elem = doc.documentElement();
462 if ( elem.nodeName() != QLatin1String(
"font" ) )
463 elem = elem.firstChildElement( QStringLiteral(
"font" ) );
476static QMap<QString, QString> createTranslatedStyleMap()
478 QMap<QString, QString> translatedStyleMap;
479 const QStringList words = QStringList()
480 << QStringLiteral(
"Normal" )
481 << QStringLiteral(
"Regular" )
482 << QStringLiteral(
"Light" )
483 << QStringLiteral(
"Bold" )
484 << QStringLiteral(
"Black" )
485 << QStringLiteral(
"Demi" )
486 << QStringLiteral(
"Italic" )
487 << QStringLiteral(
"Oblique" );
488 const auto constWords = words;
489 for (
const QString &word : constWords )
491 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
493 return translatedStyleMap;
498 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
499 for (
int i = 0, n = words.length(); i < n; ++i )
501 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
503 return words.join( QLatin1Char(
' ' ) );
508 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
509 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
511 for (
int i = 0, n = words.length(); i < n; ++i )
513 if ( translatedStyleMap.contains( words[i] ) )
515 words[i] = translatedStyleMap.value( words[i] );
519 QgsDebugMsgLevel( QStringLiteral(
"Warning: style map does not contain %1" ).arg( words[i] ), 2 );
522 return words.join( QLatin1Char(
' ' ) );
527 QString css = QStringLiteral(
"font-family: " ) + font.family() +
';';
530 css += QLatin1String(
"font-style: " );
531 switch ( font.style() )
533 case QFont::StyleNormal:
534 css += QLatin1String(
"normal" );
536 case QFont::StyleItalic:
537 css += QLatin1String(
"italic" );
539 case QFont::StyleOblique:
540 css += QLatin1String(
"oblique" );
547 switch ( font.weight() )
555 case QFont::DemiBold:
567 case QFont::ExtraLight:
573 case QFont::ExtraBold:
577 css += QStringLiteral(
"font-weight: %1;" ).arg( cssWeight );
580 css += QStringLiteral(
"font-size: %1px;" ).arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
587 if ( family.isEmpty() )
593 QStringList recentFamilies = settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
596 recentFamilies.removeAll( family );
599 recentFamilies.prepend( family );
602 recentFamilies = recentFamilies.mid( 0, 10 );
604 settings.
setValue( QStringLiteral(
"fonts/recent" ), recentFamilies );
610 return settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
615 font.setFamily( family );
616 if ( !font.exactMatch() )
621 font.setFamilies( { family } );
627 QFont font( family, pointSize, weight, italic );
628 if ( !font.exactMatch() )
633 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.
Stores settings for use within QGIS.
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)