25#include <QApplication>
28#include <QFontDatabase>
34using namespace Qt::StringLiterals;
38 const QFontInfo fi = QFontInfo( f );
39 return fi.exactMatch();
44 const QFont tmpFont = QFont( family );
46 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
51 const QFontDatabase fontDB;
55 if ( fontDB.styles( family ).contains( style ) )
58 QString modified( style );
60 if ( style ==
"Roman" )
62 else if ( style ==
"Oblique" )
64 else if ( style ==
"Bold Oblique" )
65 modified =
"Bold Italic";
66 if ( fontDB.styles( family ).contains( modified ) )
71 if ( modified ==
"Normal" )
73 else if ( modified == QCoreApplication::translate(
"QFontDatabase",
"Normal" ) )
74 modified = QCoreApplication::translate(
"QFontDatabase",
"Regular" );
75 if ( fontDB.styles( family ).contains( modified ) )
83 auto styleNameIsMatch = [&font](
const QString & candidate ) ->
bool
86 QFont testFont( font.family() );
87 testFont.setStyleName( candidate );
88 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
92 const QFontInfo fontInfo( font );
93 QString styleName = fontInfo.styleName();
94 if ( !styleName.isEmpty() )
96 if ( styleNameIsMatch( styleName ) )
101 styleName = QFontDatabase().styleString( font );
102 if ( !styleName.isEmpty() )
104 if ( styleNameIsMatch( styleName ) )
114 const QFontDatabase fontDB;
115 const QStringList fontFamilies = fontDB.families();
118 QList<QString>::const_iterator it = fontFamilies.constBegin();
119 for ( ; it != fontFamilies.constEnd(); ++it )
122 if ( it->startsWith( family, Qt::CaseInsensitive ) )
129 *match = ( *it == family );
145 const QFont f = QFont( family );
146 *chosen = f.family();
167 if ( fontstyle.isEmpty() )
172 QFontDatabase fontDB;
173 QString actualFontStyle = fontstyle;
192 if ( actualFontStyle == fontDB.styleString( f ) )
194 if ( f.styleName().isEmpty() )
196 f.setStyleName( actualFontStyle );
202 const QFont appfont = QApplication::font();
203 const int defaultSize = appfont.pointSize();
206 bool foundmatch =
false;
209 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
210 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
217 if ( fallback && !foundmatch )
219 QFont testFont = QFont( f );
220 testFont.setPointSize( defaultSize );
223 const auto constFamily = fontDB.styles( f.family() );
224 for (
const QString &style : constFamily )
226 styledfont = fontDB.font( f.family(), style, defaultSize );
227 styledfont = styledfont.resolve( f );
228 if ( testFont.toString() == styledfont.toString() )
238 for (
const QString &style : constFamily )
240 styledfont = fontDB.font( f.family(), style, defaultSize );
241 if ( QApplication::font() != styledfont )
256 styledfont.setPointSizeF( f.pointSizeF() );
258 else if ( f.pixelSize() != -1 )
260 styledfont.setPixelSize( f.pixelSize() );
262 styledfont.setCapitalization( f.capitalization() );
263 styledfont.setUnderline( f.underline() );
264 styledfont.setStrikeOut( f.strikeOut() );
265 styledfont.setWordSpacing( f.wordSpacing() );
266 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
277 return u
"QGIS Vera Sans"_s;
283 bool fontsLoaded =
false;
285 QMap<QString, QString> fontStyles;
286 fontStyles.insert( u
"Roman"_s, u
"QGIS-Vera/QGIS-Vera.ttf"_s );
287 fontStyles.insert( u
"Oblique"_s, u
"QGIS-Vera/QGIS-VeraIt.ttf"_s );
288 fontStyles.insert( u
"Bold"_s, u
"QGIS-Vera/QGIS-VeraBd.ttf"_s );
289 fontStyles.insert( u
"Bold Oblique"_s, u
"QGIS-Vera/QGIS-VeraBI.ttf"_s );
290 fontStyles.insert( u
"Deja Bold"_s, u
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf"_s );
292 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
293 for ( ; f != fontStyles.constEnd(); ++f )
295 const QString fontpath( f.value() );
296 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( u
"All"_s ) ) )
301 const QString fontFamily = !f.key().startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
302 const QString fontstyle = !f.key().startsWith(
"Deja"_L1 ) ? f.key() : f.key().mid( 5 );
306 QgsDebugMsgLevel( u
"Test font '%1 %2' already available"_s.arg( fontFamily, fontstyle ), 2 );
318 const int fontID = QFontDatabase::addApplicationFont( fontPath );
319 loaded = ( fontID != -1 );
320 fontsLoaded = ( fontsLoaded || loaded );
322 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
323 QgsDebugMsgLevel( u
"font families in %1: %2"_s.arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
327 QFile fontResource(
":/testdata/font/" + fontpath );
328 if ( fontResource.open( QIODevice::ReadOnly ) )
330 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
331 loaded = ( fontID != -1 );
332 fontsLoaded = ( fontsLoaded || loaded );
335 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
345 const QString fontFamily = !style.startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
346 const QString fontStyle = !style.startsWith(
"Deja"_L1 ) ? style : style.mid( 5 );
353 const QFontDatabase fontDB;
354 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
356 if ( !f.exactMatch() )
359 if ( fontStyle ==
"Roman" )
361 else if ( fontStyle ==
"Oblique" )
363 else if ( fontStyle ==
"Bold Oblique" )
364 modified =
"Bold Italic";
365 if ( !modified.isEmpty() )
366 f = fontDB.font( fontFamily, modified, pointsize );
368 if ( !f.exactMatch() )
370 QgsDebugMsgLevel( u
"Inexact font match - consider installing the %1 font."_s.arg( fontFamily ), 2 );
373 QgsDebugMsgLevel( u
"Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9"_s.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 );
377 f.setBold( fontStyle.contains(
"Bold"_L1 ) );
378 f.setItalic( fontStyle.contains(
"Oblique"_L1 ) || fontStyle.contains(
"Italic"_L1 ) );
385 QDomElement fontElem = document.createElement( elementName );
386 fontElem.setAttribute( u
"description"_s, font.toString() );
388 fontElem.setAttribute( u
"bold"_s, font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
389 fontElem.setAttribute( u
"italic"_s, font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
390 fontElem.setAttribute( u
"underline"_s, font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
391 fontElem.setAttribute( u
"strikethrough"_s, font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
397 if ( element.isNull() )
402 font.fromString( element.attribute( u
"description"_s ) );
404 if ( element.hasAttribute( u
"bold"_s ) && element.attribute( u
"bold"_s ) == QChar(
'1' ) )
406 font.setBold(
true );
408 if ( element.hasAttribute( u
"italic"_s ) )
410 font.setItalic( element.attribute( u
"italic"_s ) == QChar(
'1' ) );
412 if ( element.hasAttribute( u
"underline"_s ) )
414 font.setUnderline( element.attribute( u
"underline"_s ) == QChar(
'1' ) );
416 if ( element.hasAttribute( u
"strikethrough"_s ) )
418 font.setStrikeOut( element.attribute( u
"strikethrough"_s ) == QChar(
'1' ) );
421 if ( element.hasAttribute( u
"style"_s ) )
431 if ( element.isNull() )
436 const QDomNodeList nodeList = element.elementsByTagName( childNode );
437 if ( !nodeList.isEmpty() )
439 const QDomElement fontElem = nodeList.at( 0 ).toElement();
450 std::unique_ptr< QMimeData >mimeData(
new QMimeData );
452 QDomDocument fontDoc;
453 const QDomElement fontElem =
toXmlElement( font, fontDoc, u
"font"_s );
454 fontDoc.appendChild( fontElem );
455 mimeData->setText( fontDoc.toString() );
457 return mimeData.release();
469 const QString text = data->text();
470 if ( !text.isEmpty() )
475 if ( doc.setContent( text ) )
477 elem = doc.documentElement();
479 if ( elem.nodeName() !=
"font"_L1 )
480 elem = elem.firstChildElement( u
"font"_s );
493static QMap<QString, QString> createTranslatedStyleMap()
495 QMap<QString, QString> translatedStyleMap;
496 const QStringList words = QStringList()
505 const auto constWords = words;
506 for (
const QString &word : constWords )
508 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
510 return translatedStyleMap;
515 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
516 for (
int i = 0, n = words.length(); i < n; ++i )
518 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
520 return words.join(
' '_L1 );
525 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
526 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
528 for (
int i = 0, n = words.length(); i < n; ++i )
530 if ( translatedStyleMap.contains( words[i] ) )
532 words[i] = translatedStyleMap.value( words[i] );
536 QgsDebugMsgLevel( u
"Warning: style map does not contain %1"_s.arg( words[i] ), 2 );
539 return words.join(
' '_L1 );
544 QString css = u
"font-family: "_s + font.family() +
';';
547 css +=
"font-style: "_L1;
548 switch ( font.style() )
550 case QFont::StyleNormal:
553 case QFont::StyleItalic:
556 case QFont::StyleOblique:
564 switch ( font.weight() )
572 case QFont::DemiBold:
584 case QFont::ExtraLight:
590 case QFont::ExtraBold:
594 css += u
"font-weight: %1;"_s.arg( cssWeight );
597 css += u
"font-size: %1px;"_s.arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
604 if ( family.isEmpty() )
610 QStringList recentFamilies = settings.
value( u
"fonts/recent"_s ).toStringList();
613 recentFamilies.removeAll( family );
616 recentFamilies.prepend( family );
619 recentFamilies = recentFamilies.mid( 0, 10 );
621 settings.
setValue( u
"fonts/recent"_s, recentFamilies );
627 return settings.
value( u
"fonts/recent"_s ).toStringList();
632 font.setFamily( family );
633 if ( !font.exactMatch() )
638 font.setFamilies( { family } );
644 QFont font( family, pointSize, weight, italic );
645 if ( !font.exactMatch() )
650 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)