26#include <QApplication>
29#include <QFontDatabase>
35using namespace Qt::StringLiterals;
42 const QFontInfo fi = QFontInfo( f );
43 return fi.exactMatch();
48 const QFont tmpFont = QFont( family );
50 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
55 const QFontDatabase fontDB;
59 if ( fontDB.styles( family ).contains( style ) )
62 QString modified( style );
64 if ( style ==
"Roman" )
66 else if ( style ==
"Oblique" )
68 else if ( style ==
"Bold Oblique" )
69 modified =
"Bold Italic";
70 if ( fontDB.styles( family ).contains( modified ) )
75 if ( modified ==
"Normal" )
77 else if ( modified == QCoreApplication::translate(
"QFontDatabase",
"Normal" ) )
78 modified = QCoreApplication::translate(
"QFontDatabase",
"Regular" );
79 if ( fontDB.styles( family ).contains( modified ) )
87 auto styleNameIsMatch = [&font](
const QString &candidate ) ->
bool {
89 QFont testFont( font.family() );
90 testFont.setStyleName( candidate );
91 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
95 const QFontInfo fontInfo( font );
96 QString styleName = fontInfo.styleName();
97 if ( !styleName.isEmpty() )
99 if ( styleNameIsMatch( styleName ) )
104 styleName = QFontDatabase().styleString( font );
105 if ( !styleName.isEmpty() )
107 if ( styleNameIsMatch( styleName ) )
117 const QFontDatabase fontDB;
118 const QStringList fontFamilies = fontDB.families();
121 QList<QString>::const_iterator it = fontFamilies.constBegin();
122 for ( ; it != fontFamilies.constEnd(); ++it )
125 if ( it->startsWith( family, Qt::CaseInsensitive ) )
132 *match = ( *it == family );
148 const QFont f = QFont( family );
149 *chosen = f.family();
170 if ( fontstyle.isEmpty() )
175 QFontDatabase fontDB;
176 QString actualFontStyle = fontstyle;
195 if ( actualFontStyle == fontDB.styleString( f ) )
197 if ( f.styleName().isEmpty() )
199 f.setStyleName( actualFontStyle );
205 const QFont appfont = QApplication::font();
206 const int defaultSize = appfont.pointSize();
209 bool foundmatch =
false;
212 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
213 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
220 if ( fallback && !foundmatch )
222 QFont testFont = QFont( f );
223 testFont.setPointSize( defaultSize );
226 const auto constFamily = fontDB.styles( f.family() );
227 for (
const QString &style : constFamily )
229 styledfont = fontDB.font( f.family(), style, defaultSize );
230 styledfont = styledfont.resolve( f );
231 if ( testFont.toString() == styledfont.toString() )
241 for (
const QString &style : constFamily )
243 styledfont = fontDB.font( f.family(), style, defaultSize );
244 if ( QApplication::font() != styledfont )
259 styledfont.setPointSizeF( f.pointSizeF() );
261 else if ( f.pixelSize() != -1 )
263 styledfont.setPixelSize( f.pixelSize() );
265 styledfont.setCapitalization( f.capitalization() );
266 styledfont.setUnderline( f.underline() );
267 styledfont.setStrikeOut( f.strikeOut() );
268 styledfont.setWordSpacing( f.wordSpacing() );
269 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
280 return u
"QGIS Vera Sans"_s;
286 bool fontsLoaded =
false;
288 QMap<QString, QString> fontStyles;
289 fontStyles.insert( u
"Roman"_s, u
"QGIS-Vera/QGIS-Vera.ttf"_s );
290 fontStyles.insert( u
"Oblique"_s, u
"QGIS-Vera/QGIS-VeraIt.ttf"_s );
291 fontStyles.insert( u
"Bold"_s, u
"QGIS-Vera/QGIS-VeraBd.ttf"_s );
292 fontStyles.insert( u
"Bold Oblique"_s, u
"QGIS-Vera/QGIS-VeraBI.ttf"_s );
293 fontStyles.insert( u
"Deja Bold"_s, u
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf"_s );
295 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
296 for ( ; f != fontStyles.constEnd(); ++f )
298 const QString fontpath( f.value() );
299 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( u
"All"_s ) ) )
304 const QString fontFamily = !f.key().startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
305 const QString fontstyle = !f.key().startsWith(
"Deja"_L1 ) ? f.key() : f.key().mid( 5 );
309 QgsDebugMsgLevel( u
"Test font '%1 %2' already available"_s.arg( fontFamily, fontstyle ), 2 );
321 const int fontID = QFontDatabase::addApplicationFont( fontPath );
322 loaded = ( fontID != -1 );
323 fontsLoaded = ( fontsLoaded || loaded );
324 QgsDebugMsgLevel( u
"Test font '%1 %2' %3 from filesystem [%4]"_s.arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
325 QgsDebugMsgLevel( u
"font families in %1: %2"_s.arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
329 QFile fontResource(
":/testdata/font/" + fontpath );
330 if ( fontResource.open( QIODevice::ReadOnly ) )
332 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
333 loaded = ( fontID != -1 );
334 fontsLoaded = ( fontsLoaded || loaded );
336 QgsDebugMsgLevel( u
"Test font '%1' (%2) %3 from testdata.qrc"_s.arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
346 const QString fontFamily = !style.startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
347 const QString fontStyle = !style.startsWith(
"Deja"_L1 ) ? style : style.mid( 5 );
354 const QFontDatabase fontDB;
355 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
357 if ( !f.exactMatch() )
360 if ( fontStyle ==
"Roman" )
362 else if ( fontStyle ==
"Oblique" )
364 else if ( fontStyle ==
"Bold Oblique" )
365 modified =
"Bold Italic";
366 if ( !modified.isEmpty() )
367 f = fontDB.font( fontFamily, modified, pointsize );
369 if ( !f.exactMatch() )
371 QgsDebugMsgLevel( u
"Inexact font match - consider installing the %1 font."_s.arg( fontFamily ), 2 );
375 u
"Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9"_s.arg( fi.family() )
376 .arg( fi.pointSizeF() )
377 .arg( fi.pixelSize() )
378 .arg( fi.styleHint() )
381 .arg( fi.underline() )
382 .arg( fi.strikeOut() )
383 .arg( fi.fixedPitch() ),
389 f.setBold( fontStyle.contains(
"Bold"_L1 ) );
390 f.setItalic( fontStyle.contains(
"Oblique"_L1 ) || fontStyle.contains(
"Italic"_L1 ) );
397 QDomElement fontElem = document.createElement( elementName );
398 fontElem.setAttribute( u
"description"_s, font.toString() );
400 fontElem.setAttribute( u
"bold"_s, font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
401 fontElem.setAttribute( u
"italic"_s, font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
402 fontElem.setAttribute( u
"underline"_s, font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
403 fontElem.setAttribute( u
"strikethrough"_s, font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
409 if ( element.isNull() )
414 font.fromString( element.attribute( u
"description"_s ) );
416 if ( element.hasAttribute( u
"bold"_s ) && element.attribute( u
"bold"_s ) == QChar(
'1' ) )
418 font.setBold(
true );
420 if ( element.hasAttribute( u
"italic"_s ) )
422 font.setItalic( element.attribute( u
"italic"_s ) == QChar(
'1' ) );
424 if ( element.hasAttribute( u
"underline"_s ) )
426 font.setUnderline( element.attribute( u
"underline"_s ) == QChar(
'1' ) );
428 if ( element.hasAttribute( u
"strikethrough"_s ) )
430 font.setStrikeOut( element.attribute( u
"strikethrough"_s ) == QChar(
'1' ) );
433 if ( element.hasAttribute( u
"style"_s ) )
443 if ( element.isNull() )
448 const QDomNodeList nodeList = element.elementsByTagName( childNode );
449 if ( !nodeList.isEmpty() )
451 const QDomElement fontElem = nodeList.at( 0 ).toElement();
462 std::unique_ptr< QMimeData > mimeData(
new QMimeData );
464 QDomDocument fontDoc;
465 const QDomElement fontElem =
toXmlElement( font, fontDoc, u
"font"_s );
466 fontDoc.appendChild( fontElem );
467 mimeData->setText( fontDoc.toString() );
469 return mimeData.release();
481 const QString text = data->text();
482 if ( !text.isEmpty() )
487 if ( doc.setContent( text ) )
489 elem = doc.documentElement();
491 if ( elem.nodeName() !=
"font"_L1 )
492 elem = elem.firstChildElement( u
"font"_s );
505static QMap<QString, QString> createTranslatedStyleMap()
507 QMap<QString, QString> translatedStyleMap;
508 const QStringList words = QStringList() << u
"Normal"_s << u
"Regular"_s << u
"Light"_s << u
"Bold"_s << u
"Black"_s << u
"Demi"_s << u
"Italic"_s << u
"Oblique"_s;
509 const auto constWords = words;
510 for (
const QString &word : constWords )
512 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
514 return translatedStyleMap;
519 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
520 for (
int i = 0, n = words.length(); i < n; ++i )
522 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
524 return words.join(
' '_L1 );
529 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
530 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
532 for (
int i = 0, n = words.length(); i < n; ++i )
534 if ( translatedStyleMap.contains( words[i] ) )
536 words[i] = translatedStyleMap.value( words[i] );
540 QgsDebugMsgLevel( u
"Warning: style map does not contain %1"_s.arg( words[i] ), 2 );
543 return words.join(
' '_L1 );
548 QString css = u
"font-family: "_s + font.family() +
';';
551 css +=
"font-style: "_L1;
552 switch ( font.style() )
554 case QFont::StyleNormal:
557 case QFont::StyleItalic:
560 case QFont::StyleOblique:
568 switch ( font.weight() )
576 case QFont::DemiBold:
588 case QFont::ExtraLight:
594 case QFont::ExtraBold:
598 css += u
"font-weight: %1;"_s.arg( cssWeight );
601 css += u
"font-size: %1px;"_s.arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
608 if ( family.isEmpty() )
616 recentFamilies.removeAll( family );
619 recentFamilies.prepend( family );
622 recentFamilies = recentFamilies.mid( 0, 10 );
634 font.setFamily( family );
635 if ( !font.exactMatch() )
640 font.setFamilies( { family } );
646 QFont font( family, pointSize, weight, italic );
647 if ( !font.exactMatch() )
652 font.setFamilies( { family } );
static QString buildSourcePath()
Returns path to the source 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 const QgsSettingsEntryStringList * settingsRecentFontFamilies
static QStringList recentFontFamilies()
Returns a list of recently used font families.
A string list settings entry.
static QgsSettingsTreeNode * sTreeFonts
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)