25#include <QApplication>
28#include <QFontDatabase>
35 const QFontInfo fi = QFontInfo( f );
36 return fi.exactMatch();
41 const QFont tmpFont = QFont( family );
43 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
48 const QFontDatabase fontDB;
52 if ( fontDB.styles( family ).contains( style ) )
56 QString modified( style );
57 if ( style ==
"Roman" )
59 if ( style ==
"Oblique" )
61 if ( style ==
"Bold Oblique" )
62 modified =
"Bold Italic";
63 if ( fontDB.styles( family ).contains( modified ) )
72 auto styleNameIsMatch = [&font](
const QString & candidate ) ->
bool
75 QFont testFont( font.family() );
76 testFont.setStyleName( candidate );
77 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
81 const QFontInfo fontInfo( font );
82 QString styleName = fontInfo.styleName();
83 if ( !styleName.isEmpty() )
85 if ( styleNameIsMatch( styleName ) )
90 styleName = QFontDatabase().styleString( font );
91 if ( !styleName.isEmpty() )
93 if ( styleNameIsMatch( styleName ) )
103 const QFontDatabase fontDB;
104 const QStringList fontFamilies = fontDB.families();
107 QList<QString>::const_iterator it = fontFamilies.constBegin();
108 for ( ; it != fontFamilies.constEnd(); ++it )
111 if ( it->startsWith( family, Qt::CaseInsensitive ) )
118 *match = ( *it == family );
134 const QFont f = QFont( family );
135 *chosen = f.family();
156 if ( fontstyle.isEmpty() )
161 QFontDatabase fontDB;
162 QString actualFontStyle = fontstyle;
181 if ( actualFontStyle == fontDB.styleString( f ) )
186 const QFont appfont = QApplication::font();
187 const int defaultSize = appfont.pointSize();
190 bool foundmatch =
false;
193 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
194 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
201 if ( fallback && !foundmatch )
203 QFont testFont = QFont( f );
204 testFont.setPointSize( defaultSize );
207 const auto constFamily = fontDB.styles( f.family() );
208 for (
const QString &style : constFamily )
210 styledfont = fontDB.font( f.family(), style, defaultSize );
211 styledfont = styledfont.resolve( f );
212 if ( testFont.toString() == styledfont.toString() )
222 for (
const QString &style : constFamily )
224 styledfont = fontDB.font( f.family(), style, defaultSize );
225 if ( QApplication::font() != styledfont )
240 styledfont.setPointSizeF( f.pointSizeF() );
242 else if ( f.pixelSize() != -1 )
244 styledfont.setPixelSize( f.pixelSize() );
246 styledfont.setCapitalization( f.capitalization() );
247 styledfont.setUnderline( f.underline() );
248 styledfont.setStrikeOut( f.strikeOut() );
249 styledfont.setWordSpacing( f.wordSpacing() );
250 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
261 return QStringLiteral(
"QGIS Vera Sans" );
267 bool fontsLoaded =
false;
269 QMap<QString, QString> fontStyles;
270 fontStyles.insert( QStringLiteral(
"Roman" ), QStringLiteral(
"QGIS-Vera/QGIS-Vera.ttf" ) );
271 fontStyles.insert( QStringLiteral(
"Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraIt.ttf" ) );
272 fontStyles.insert( QStringLiteral(
"Bold" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBd.ttf" ) );
273 fontStyles.insert( QStringLiteral(
"Bold Oblique" ), QStringLiteral(
"QGIS-Vera/QGIS-VeraBI.ttf" ) );
274 fontStyles.insert( QStringLiteral(
"Deja Bold" ), QStringLiteral(
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf" ) );
276 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
277 for ( ; f != fontStyles.constEnd(); ++f )
279 const QString fontpath( f.value() );
280 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( QStringLiteral(
"All" ) ) ) )
285 const QString fontFamily = !f.key().startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
286 const QString fontstyle = !f.key().startsWith( QLatin1String(
"Deja" ) ) ? f.key() : f.key().mid( 5 );
290 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' already available" ).arg( fontFamily, fontstyle ), 2 );
302 const int fontID = QFontDatabase::addApplicationFont( fontPath );
303 loaded = ( fontID != -1 );
304 fontsLoaded = ( fontsLoaded || loaded );
305 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1 %2' %3 from filesystem [%4]" )
306 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
307 QgsDebugMsgLevel( QStringLiteral(
"font families in %1: %2" ).arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
311 QFile fontResource(
":/testdata/font/" + fontpath );
312 if ( fontResource.open( QIODevice::ReadOnly ) )
314 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
315 loaded = ( fontID != -1 );
316 fontsLoaded = ( fontsLoaded || loaded );
318 QgsDebugMsgLevel( QStringLiteral(
"Test font '%1' (%2) %3 from testdata.qrc" )
319 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
329 const QString fontFamily = !style.startsWith( QLatin1String(
"Deja" ) ) ?
standardTestFontFamily() : QStringLiteral(
"QGIS DejaVu Sans" );
330 const QString fontStyle = !style.startsWith( QLatin1String(
"Deja" ) ) ? style : style.mid( 5 );
337 const QFontDatabase fontDB;
338 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
340 if ( !f.exactMatch() )
343 if ( fontStyle ==
"Roman" )
345 else if ( fontStyle ==
"Oblique" )
347 else if ( fontStyle ==
"Bold Oblique" )
348 modified =
"Bold Italic";
349 if ( !modified.isEmpty() )
350 f = fontDB.font( fontFamily, modified, pointsize );
352 if ( !f.exactMatch() )
354 QgsDebugMsgLevel( QStringLiteral(
"Inexact font match - consider installing the %1 font." ).arg( fontFamily ), 2 );
355 QgsDebugMsgLevel( QStringLiteral(
"Requested: %1" ).arg( f.toString() ), 2 );
357 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 );
361 f.setBold( fontStyle.contains( QLatin1String(
"Bold" ) ) );
362 f.setItalic( fontStyle.contains( QLatin1String(
"Oblique" ) ) || fontStyle.contains( QLatin1String(
"Italic" ) ) );
369 QDomElement fontElem = document.createElement( elementName );
370 fontElem.setAttribute( QStringLiteral(
"description" ), font.toString() );
372 fontElem.setAttribute( QStringLiteral(
"bold" ), font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
373 fontElem.setAttribute( QStringLiteral(
"italic" ), font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
374 fontElem.setAttribute( QStringLiteral(
"underline" ), font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
375 fontElem.setAttribute( QStringLiteral(
"strikethrough" ), font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
381 if ( element.isNull() )
386 font.fromString( element.attribute( QStringLiteral(
"description" ) ) );
388 if ( element.hasAttribute( QStringLiteral(
"bold" ) ) && element.attribute( QStringLiteral(
"bold" ) ) == QChar(
'1' ) )
390 font.setBold(
true );
392 if ( element.hasAttribute( QStringLiteral(
"italic" ) ) )
394 font.setItalic( element.attribute( QStringLiteral(
"italic" ) ) == QChar(
'1' ) );
396 if ( element.hasAttribute( QStringLiteral(
"underline" ) ) )
398 font.setUnderline( element.attribute( QStringLiteral(
"underline" ) ) == QChar(
'1' ) );
400 if ( element.hasAttribute( QStringLiteral(
"strikethrough" ) ) )
402 font.setStrikeOut( element.attribute( QStringLiteral(
"strikethrough" ) ) == QChar(
'1' ) );
405 if ( element.hasAttribute( QStringLiteral(
"style" ) ) )
415 if ( element.isNull() )
420 const QDomNodeList nodeList = element.elementsByTagName( childNode );
421 if ( !nodeList.isEmpty() )
423 const QDomElement fontElem = nodeList.at( 0 ).toElement();
434 std::unique_ptr< QMimeData >mimeData(
new QMimeData );
436 QDomDocument fontDoc;
437 const QDomElement fontElem =
toXmlElement( font, fontDoc, QStringLiteral(
"font" ) );
438 fontDoc.appendChild( fontElem );
439 mimeData->setText( fontDoc.toString() );
441 return mimeData.release();
453 const QString text = data->text();
454 if ( !text.isEmpty() )
459 if ( doc.setContent( text ) )
461 elem = doc.documentElement();
463 if ( elem.nodeName() != QLatin1String(
"font" ) )
464 elem = elem.firstChildElement( QStringLiteral(
"font" ) );
477static QMap<QString, QString> createTranslatedStyleMap()
479 QMap<QString, QString> translatedStyleMap;
480 const QStringList words = QStringList()
481 << QStringLiteral(
"Normal" )
482 << QStringLiteral(
"Regular" )
483 << QStringLiteral(
"Light" )
484 << QStringLiteral(
"Bold" )
485 << QStringLiteral(
"Black" )
486 << QStringLiteral(
"Demi" )
487 << QStringLiteral(
"Italic" )
488 << QStringLiteral(
"Oblique" );
489 const auto constWords = words;
490 for (
const QString &word : constWords )
492 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
494 return translatedStyleMap;
499 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
500 for (
int i = 0, n = words.length(); i < n; ++i )
502 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
504 return words.join( QLatin1Char(
' ' ) );
509 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
510 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
512 for (
int i = 0, n = words.length(); i < n; ++i )
514 if ( translatedStyleMap.contains( words[i] ) )
516 words[i] = translatedStyleMap.value( words[i] );
520 QgsDebugMsgLevel( QStringLiteral(
"Warning: style map does not contain %1" ).arg( words[i] ), 2 );
523 return words.join( QLatin1Char(
' ' ) );
528 QString css = QStringLiteral(
"font-family: " ) + font.family() +
';';
531 css += QLatin1String(
"font-style: " );
532 switch ( font.style() )
534 case QFont::StyleNormal:
535 css += QLatin1String(
"normal" );
537 case QFont::StyleItalic:
538 css += QLatin1String(
"italic" );
540 case QFont::StyleOblique:
541 css += QLatin1String(
"oblique" );
548 switch ( font.weight() )
556 case QFont::DemiBold:
568 case QFont::ExtraLight:
574 case QFont::ExtraBold:
578 css += QStringLiteral(
"font-weight: %1;" ).arg( cssWeight );
581 css += QStringLiteral(
"font-size: %1px;" ).arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
588 if ( family.isEmpty() )
594 QStringList recentFamilies = settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
597 recentFamilies.removeAll( family );
600 recentFamilies.prepend( family );
603 recentFamilies = recentFamilies.mid( 0, 10 );
605 settings.
setValue( QStringLiteral(
"fonts/recent" ), recentFamilies );
611 return settings.
value( QStringLiteral(
"fonts/recent" ) ).toStringList();
616 font.setFamily( family );
617 if ( !font.exactMatch() )
622 font.setFamilies( { family } );
628 QFont font( family, pointSize, weight, italic );
629 if ( !font.exactMatch() )
634 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)