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 ) )
59 QString modified( style );
60 if ( style ==
"Roman" )
62 if ( style ==
"Oblique" )
64 if ( style ==
"Bold Oblique" )
65 modified =
"Bold Italic";
66 if ( fontDB.styles( family ).contains( modified ) )
75 auto styleNameIsMatch = [&font](
const QString & candidate ) ->
bool
78 QFont testFont( font.family() );
79 testFont.setStyleName( candidate );
80 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
84 const QFontInfo fontInfo( font );
85 QString styleName = fontInfo.styleName();
86 if ( !styleName.isEmpty() )
88 if ( styleNameIsMatch( styleName ) )
93 styleName = QFontDatabase().styleString( font );
94 if ( !styleName.isEmpty() )
96 if ( styleNameIsMatch( styleName ) )
106 const QFontDatabase fontDB;
107 const QStringList fontFamilies = fontDB.families();
110 QList<QString>::const_iterator it = fontFamilies.constBegin();
111 for ( ; it != fontFamilies.constEnd(); ++it )
114 if ( it->startsWith( family, Qt::CaseInsensitive ) )
121 *match = ( *it == family );
137 const QFont f = QFont( family );
138 *chosen = f.family();
159 if ( fontstyle.isEmpty() )
164 QFontDatabase fontDB;
165 QString actualFontStyle = fontstyle;
184 if ( actualFontStyle == fontDB.styleString( f ) )
189 const QFont appfont = QApplication::font();
190 const int defaultSize = appfont.pointSize();
193 bool foundmatch =
false;
196 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
197 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
204 if ( fallback && !foundmatch )
206 QFont testFont = QFont( f );
207 testFont.setPointSize( defaultSize );
210 const auto constFamily = fontDB.styles( f.family() );
211 for (
const QString &style : constFamily )
213 styledfont = fontDB.font( f.family(), style, defaultSize );
214 styledfont = styledfont.resolve( f );
215 if ( testFont.toString() == styledfont.toString() )
225 for (
const QString &style : constFamily )
227 styledfont = fontDB.font( f.family(), style, defaultSize );
228 if ( QApplication::font() != styledfont )
243 styledfont.setPointSizeF( f.pointSizeF() );
245 else if ( f.pixelSize() != -1 )
247 styledfont.setPixelSize( f.pixelSize() );
249 styledfont.setCapitalization( f.capitalization() );
250 styledfont.setUnderline( f.underline() );
251 styledfont.setStrikeOut( f.strikeOut() );
252 styledfont.setWordSpacing( f.wordSpacing() );
253 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
264 return u
"QGIS Vera Sans"_s;
270 bool fontsLoaded =
false;
272 QMap<QString, QString> fontStyles;
273 fontStyles.insert( u
"Roman"_s, u
"QGIS-Vera/QGIS-Vera.ttf"_s );
274 fontStyles.insert( u
"Oblique"_s, u
"QGIS-Vera/QGIS-VeraIt.ttf"_s );
275 fontStyles.insert( u
"Bold"_s, u
"QGIS-Vera/QGIS-VeraBd.ttf"_s );
276 fontStyles.insert( u
"Bold Oblique"_s, u
"QGIS-Vera/QGIS-VeraBI.ttf"_s );
277 fontStyles.insert( u
"Deja Bold"_s, u
"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf"_s );
279 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
280 for ( ; f != fontStyles.constEnd(); ++f )
282 const QString fontpath( f.value() );
283 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( u
"All"_s ) ) )
288 const QString fontFamily = !f.key().startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
289 const QString fontstyle = !f.key().startsWith(
"Deja"_L1 ) ? f.key() : f.key().mid( 5 );
293 QgsDebugMsgLevel( u
"Test font '%1 %2' already available"_s.arg( fontFamily, fontstyle ), 2 );
305 const int fontID = QFontDatabase::addApplicationFont( fontPath );
306 loaded = ( fontID != -1 );
307 fontsLoaded = ( fontsLoaded || loaded );
309 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load", fontPath ), 2 );
310 QgsDebugMsgLevel( u
"font families in %1: %2"_s.arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join(
"," ) ), 2 );
314 QFile fontResource(
":/testdata/font/" + fontpath );
315 if ( fontResource.open( QIODevice::ReadOnly ) )
317 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
318 loaded = ( fontID != -1 );
319 fontsLoaded = ( fontsLoaded || loaded );
322 .arg( fontFamily, fontstyle, loaded ?
"loaded" :
"FAILED to load" ), 2 );
332 const QString fontFamily = !style.startsWith(
"Deja"_L1 ) ?
standardTestFontFamily() : u
"QGIS DejaVu Sans"_s;
333 const QString fontStyle = !style.startsWith(
"Deja"_L1 ) ? style : style.mid( 5 );
340 const QFontDatabase fontDB;
341 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
343 if ( !f.exactMatch() )
346 if ( fontStyle ==
"Roman" )
348 else if ( fontStyle ==
"Oblique" )
350 else if ( fontStyle ==
"Bold Oblique" )
351 modified =
"Bold Italic";
352 if ( !modified.isEmpty() )
353 f = fontDB.font( fontFamily, modified, pointsize );
355 if ( !f.exactMatch() )
357 QgsDebugMsgLevel( u
"Inexact font match - consider installing the %1 font."_s.arg( fontFamily ), 2 );
360 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 );
364 f.setBold( fontStyle.contains(
"Bold"_L1 ) );
365 f.setItalic( fontStyle.contains(
"Oblique"_L1 ) || fontStyle.contains(
"Italic"_L1 ) );
372 QDomElement fontElem = document.createElement( elementName );
373 fontElem.setAttribute( u
"description"_s, font.toString() );
375 fontElem.setAttribute( u
"bold"_s, font.bold() ? QChar(
'1' ) : QChar(
'0' ) );
376 fontElem.setAttribute( u
"italic"_s, font.italic() ? QChar(
'1' ) : QChar(
'0' ) );
377 fontElem.setAttribute( u
"underline"_s, font.underline() ? QChar(
'1' ) : QChar(
'0' ) );
378 fontElem.setAttribute( u
"strikethrough"_s, font.strikeOut() ? QChar(
'1' ) : QChar(
'0' ) );
384 if ( element.isNull() )
389 font.fromString( element.attribute( u
"description"_s ) );
391 if ( element.hasAttribute( u
"bold"_s ) && element.attribute( u
"bold"_s ) == QChar(
'1' ) )
393 font.setBold(
true );
395 if ( element.hasAttribute( u
"italic"_s ) )
397 font.setItalic( element.attribute( u
"italic"_s ) == QChar(
'1' ) );
399 if ( element.hasAttribute( u
"underline"_s ) )
401 font.setUnderline( element.attribute( u
"underline"_s ) == QChar(
'1' ) );
403 if ( element.hasAttribute( u
"strikethrough"_s ) )
405 font.setStrikeOut( element.attribute( u
"strikethrough"_s ) == QChar(
'1' ) );
408 if ( element.hasAttribute( u
"style"_s ) )
418 if ( element.isNull() )
423 const QDomNodeList nodeList = element.elementsByTagName( childNode );
424 if ( !nodeList.isEmpty() )
426 const QDomElement fontElem = nodeList.at( 0 ).toElement();
437 std::unique_ptr< QMimeData >mimeData(
new QMimeData );
439 QDomDocument fontDoc;
440 const QDomElement fontElem =
toXmlElement( font, fontDoc, u
"font"_s );
441 fontDoc.appendChild( fontElem );
442 mimeData->setText( fontDoc.toString() );
444 return mimeData.release();
456 const QString text = data->text();
457 if ( !text.isEmpty() )
462 if ( doc.setContent( text ) )
464 elem = doc.documentElement();
466 if ( elem.nodeName() !=
"font"_L1 )
467 elem = elem.firstChildElement( u
"font"_s );
480static QMap<QString, QString> createTranslatedStyleMap()
482 QMap<QString, QString> translatedStyleMap;
483 const QStringList words = QStringList()
492 const auto constWords = words;
493 for (
const QString &word : constWords )
495 translatedStyleMap.insert( QCoreApplication::translate(
"QFontDatabase", qPrintable( word ) ), word );
497 return translatedStyleMap;
502 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
503 for (
int i = 0, n = words.length(); i < n; ++i )
505 words[i] = QCoreApplication::translate(
"QFontDatabase", words[i].toLocal8Bit().constData() );
507 return words.join(
' '_L1 );
512 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
513 QStringList words = namedStyle.split(
' ', Qt::SkipEmptyParts );
515 for (
int i = 0, n = words.length(); i < n; ++i )
517 if ( translatedStyleMap.contains( words[i] ) )
519 words[i] = translatedStyleMap.value( words[i] );
523 QgsDebugMsgLevel( u
"Warning: style map does not contain %1"_s.arg( words[i] ), 2 );
526 return words.join(
' '_L1 );
531 QString css = u
"font-family: "_s + font.family() +
';';
534 css +=
"font-style: "_L1;
535 switch ( font.style() )
537 case QFont::StyleNormal:
540 case QFont::StyleItalic:
543 case QFont::StyleOblique:
551 switch ( font.weight() )
559 case QFont::DemiBold:
571 case QFont::ExtraLight:
577 case QFont::ExtraBold:
581 css += u
"font-weight: %1;"_s.arg( cssWeight );
584 css += u
"font-size: %1px;"_s.arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
591 if ( family.isEmpty() )
597 QStringList recentFamilies = settings.
value( u
"fonts/recent"_s ).toStringList();
600 recentFamilies.removeAll( family );
603 recentFamilies.prepend( family );
606 recentFamilies = recentFamilies.mid( 0, 10 );
608 settings.
setValue( u
"fonts/recent"_s, recentFamilies );
614 return settings.
value( u
"fonts/recent"_s ).toStringList();
619 font.setFamily( family );
620 if ( !font.exactMatch() )
625 font.setFamilies( { family } );
631 QFont font( family, pointSize, weight, italic );
632 if ( !font.exactMatch() )
637 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)