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 {
85 QFont testFont( font.family() );
86 testFont.setStyleName( candidate );
87 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
91 const QFontInfo fontInfo( font );
92 QString styleName = fontInfo.styleName();
93 if ( !styleName.isEmpty() )
95 if ( styleNameIsMatch( styleName ) )
100 styleName = QFontDatabase().styleString( font );
101 if ( !styleName.isEmpty() )
103 if ( styleNameIsMatch( styleName ) )
113 const QFontDatabase fontDB;
114 const QStringList fontFamilies = fontDB.families();
117 QList<QString>::const_iterator it = fontFamilies.constBegin();
118 for ( ; it != fontFamilies.constEnd(); ++it )
121 if ( it->startsWith( family, Qt::CaseInsensitive ) )
128 *match = ( *it == family );
144 const QFont f = QFont( family );
145 *chosen = f.family();
166 if ( fontstyle.isEmpty() )
171 QFontDatabase fontDB;
172 QString actualFontStyle = fontstyle;
191 if ( actualFontStyle == fontDB.styleString( f ) )
193 if ( f.styleName().isEmpty() )
195 f.setStyleName( actualFontStyle );
201 const QFont appfont = QApplication::font();
202 const int defaultSize = appfont.pointSize();
205 bool foundmatch =
false;
208 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
209 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
216 if ( fallback && !foundmatch )
218 QFont testFont = QFont( f );
219 testFont.setPointSize( defaultSize );
222 const auto constFamily = fontDB.styles( f.family() );
223 for (
const QString &style : constFamily )
225 styledfont = fontDB.font( f.family(), style, defaultSize );
226 styledfont = styledfont.resolve( f );
227 if ( testFont.toString() == styledfont.toString() )
237 for (
const QString &style : constFamily )
239 styledfont = fontDB.font( f.family(), style, defaultSize );
240 if ( QApplication::font() != styledfont )
255 styledfont.setPointSizeF( f.pointSizeF() );
257 else if ( f.pixelSize() != -1 )
259 styledfont.setPixelSize( f.pixelSize() );
261 styledfont.setCapitalization( f.capitalization() );
262 styledfont.setUnderline( f.underline() );
263 styledfont.setStrikeOut( f.strikeOut() );
264 styledfont.setWordSpacing( f.wordSpacing() );
265 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
276 return u
"QGIS Vera Sans"_s;
282 bool fontsLoaded =
false;
284 QMap<QString, QString> fontStyles;
285 fontStyles.insert( u
"Roman"_s, u
"QGIS-Vera/QGIS-Vera.ttf"_s );
286 fontStyles.insert( u
"Oblique"_s, u
"QGIS-Vera/QGIS-VeraIt.ttf"_s );
287 fontStyles.insert( u
"Bold"_s, u
"QGIS-Vera/QGIS-VeraBd.ttf"_s );
288 fontStyles.insert( u
"Bold Oblique"_s, u
"QGIS-Vera/QGIS-VeraBI.ttf"_s );
289 fontStyles.insert( u
"Deja Bold"_s, u
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf"_s );
291 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
292 for ( ; f != fontStyles.constEnd(); ++f )
294 const QString fontpath( f.value() );
295 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( u
"All"_s ) ) )
300 const QString fontFamily = !f.key().startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
301 const QString fontstyle = !f.key().startsWith(
"Deja"_L1 ) ? f.key() : f.key().mid( 5 );
305 QgsDebugMsgLevel( u
"Test font '%1 %2' already available"_s.arg( fontFamily, fontstyle ), 2 );
317 const int fontID = QFontDatabase::addApplicationFont( fontPath );
318 loaded = ( fontID != -1 );
319 fontsLoaded = ( fontsLoaded || loaded );
320 QgsDebugMsgLevel( u
"Test font '%1 %2' %3 from filesystem [%4]"_s.arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
321 QgsDebugMsgLevel( u
"font families in %1: %2"_s.arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
325 QFile fontResource(
":/testdata/font/" + fontpath );
326 if ( fontResource.open( QIODevice::ReadOnly ) )
328 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
329 loaded = ( fontID != -1 );
330 fontsLoaded = ( fontsLoaded || loaded );
332 QgsDebugMsgLevel( u
"Test font '%1' (%2) %3 from testdata.qrc"_s.arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
342 const QString fontFamily = !style.startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
343 const QString fontStyle = !style.startsWith(
"Deja"_L1 ) ? style : style.mid( 5 );
350 const QFontDatabase fontDB;
351 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
353 if ( !f.exactMatch() )
356 if ( fontStyle ==
"Roman" )
358 else if ( fontStyle ==
"Oblique" )
360 else if ( fontStyle ==
"Bold Oblique" )
361 modified =
"Bold Italic";
362 if ( !modified.isEmpty() )
363 f = fontDB.font( fontFamily, modified, pointsize );
365 if ( !f.exactMatch() )
367 QgsDebugMsgLevel( u
"Inexact font match - consider installing the %1 font."_s.arg( fontFamily ), 2 );
371 u
"Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9"_s.arg( fi.family() )
372 .arg( fi.pointSizeF() )
373 .arg( fi.pixelSize() )
374 .arg( fi.styleHint() )
377 .arg( fi.underline() )
378 .arg( fi.strikeOut() )
379 .arg( fi.fixedPitch() ),
385 f.setBold( fontStyle.contains(
"Bold"_L1 ) );
386 f.setItalic( fontStyle.contains(
"Oblique"_L1 ) || fontStyle.contains(
"Italic"_L1 ) );
393 QDomElement fontElem = document.createElement( elementName );
394 fontElem.setAttribute( u
"description"_s, font.toString() );
396 fontElem.setAttribute( u
"bold"_s, font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
397 fontElem.setAttribute( u
"italic"_s, font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
398 fontElem.setAttribute( u
"underline"_s, font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
399 fontElem.setAttribute( u
"strikethrough"_s, font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
405 if ( element.isNull() )
410 font.fromString( element.attribute( u
"description"_s ) );
412 if ( element.hasAttribute( u
"bold"_s ) && element.attribute( u
"bold"_s ) == QChar(
'1' ) )
414 font.setBold(
true );
416 if ( element.hasAttribute( u
"italic"_s ) )
418 font.setItalic( element.attribute( u
"italic"_s ) == QChar(
'1' ) );
420 if ( element.hasAttribute( u
"underline"_s ) )
422 font.setUnderline( element.attribute( u
"underline"_s ) == QChar(
'1' ) );
424 if ( element.hasAttribute( u
"strikethrough"_s ) )
426 font.setStrikeOut( element.attribute( u
"strikethrough"_s ) == QChar(
'1' ) );
429 if ( element.hasAttribute( u
"style"_s ) )
439 if ( element.isNull() )
444 const QDomNodeList nodeList = element.elementsByTagName( childNode );
445 if ( !nodeList.isEmpty() )
447 const QDomElement fontElem = nodeList.at( 0 ).toElement();
458 std::unique_ptr< QMimeData > mimeData(
new QMimeData );
460 QDomDocument fontDoc;
461 const QDomElement fontElem =
toXmlElement( font, fontDoc, u
"font"_s );
462 fontDoc.appendChild( fontElem );
463 mimeData->setText( fontDoc.toString() );
465 return mimeData.release();
477 const QString text = data->text();
478 if ( !text.isEmpty() )
483 if ( doc.setContent( text ) )
485 elem = doc.documentElement();
487 if ( elem.nodeName() !=
"font"_L1 )
488 elem = elem.firstChildElement( u
"font"_s );
501static QMap<QString, QString> createTranslatedStyleMap()
503 QMap<QString, QString> translatedStyleMap;
504 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;
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.
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)