QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsfontutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfontutils.h
3 ---------------------
4 begin : June 5, 2013
5 copyright : (C) 2013 by Larry Shaffer
6 email : larrys at dakotacarto dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsfontutils.h"
17
18#include <memory>
19
20#include "qgis.h"
21#include "qgsapplication.h"
22#include "qgslogger.h"
23#include "qgssettings.h"
24
25#include <QApplication>
26#include <QFile>
27#include <QFont>
28#include <QFontDatabase>
29#include <QFontInfo>
30#include <QMimeData>
31#include <QStringList>
32
33bool QgsFontUtils::fontMatchOnSystem( const QFont &f )
34{
35 const QFontInfo fi = QFontInfo( f );
36 return fi.exactMatch();
37}
38
39bool QgsFontUtils::fontFamilyOnSystem( const QString &family )
40{
41 const QFont tmpFont = QFont( family );
42 // compare just beginning of family string in case 'family [foundry]' differs
43 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
44}
45
46bool QgsFontUtils::fontFamilyHasStyle( const QString &family, const QString &style )
47{
48 const QFontDatabase fontDB;
49 if ( !fontFamilyOnSystem( family ) )
50 return false;
51
52 if ( fontDB.styles( family ).contains( style ) )
53 return true;
54
55#ifdef Q_OS_WIN
56 QString modified( style );
57 if ( style == "Roman" )
58 modified = "Normal";
59 if ( style == "Oblique" )
60 modified = "Italic";
61 if ( style == "Bold Oblique" )
62 modified = "Bold Italic";
63 if ( fontDB.styles( family ).contains( modified ) )
64 return true;
65#endif
66
67 return false;
68}
69
70QString QgsFontUtils::resolveFontStyleName( const QFont &font )
71{
72 auto styleNameIsMatch = [&font]( const QString & candidate ) -> bool
73 {
74 // confirm that style name matches bold/italic flags
75 QFont testFont( font.family() );
76 testFont.setStyleName( candidate );
77 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
78 };
79
80 // attempt 1
81 const QFontInfo fontInfo( font );
82 QString styleName = fontInfo.styleName();
83 if ( !styleName.isEmpty() )
84 {
85 if ( styleNameIsMatch( styleName ) )
86 return styleName;
87 }
88
89 // attempt 2
90 styleName = QFontDatabase().styleString( font );
91 if ( !styleName.isEmpty() )
92 {
93 if ( styleNameIsMatch( styleName ) )
94 return styleName;
95 }
96
97 // failed
98 return QString();
99}
100
101bool QgsFontUtils::fontFamilyMatchOnSystem( const QString &family, QString *chosen, bool *match )
102{
103 const QFontDatabase fontDB;
104 const QStringList fontFamilies = fontDB.families();
105 bool found = false;
106
107 QList<QString>::const_iterator it = fontFamilies.constBegin();
108 for ( ; it != fontFamilies.constEnd(); ++it )
109 {
110 // first compare just beginning of 'family [foundry]' string
111 if ( it->startsWith( family, Qt::CaseInsensitive ) )
112 {
113 found = true;
114 // keep looking if match info is requested
115 if ( match )
116 {
117 // full 'family [foundry]' strings have to match
118 *match = ( *it == family );
119 if ( *match )
120 break;
121 }
122 else
123 {
124 break;
125 }
126 }
127 }
128
129 if ( found )
130 {
131 if ( chosen )
132 {
133 // retrieve the family actually assigned by matching algorithm
134 const QFont f = QFont( family );
135 *chosen = f.family();
136 }
137 }
138 else
139 {
140 if ( chosen )
141 {
142 *chosen = QString();
143 }
144
145 if ( match )
146 {
147 *match = false;
148 }
149 }
150
151 return found;
152}
153
154bool QgsFontUtils::updateFontViaStyle( QFont &f, const QString &fontstyle, bool fallback )
155{
156 if ( fontstyle.isEmpty() )
157 {
158 return false;
159 }
160
161 QFontDatabase fontDB;
162 QString actualFontStyle = fontstyle;
163
164 if ( !fallback )
165 {
166 // does the font even have the requested style?
167 bool hasstyle = fontFamilyHasStyle( f.family(), actualFontStyle );
168 if ( !hasstyle )
169 {
170 actualFontStyle = untranslateNamedStyle( fontstyle );
171 hasstyle = fontFamilyHasStyle( f.family(), actualFontStyle );
172 }
173
174 if ( !hasstyle )
175 {
176 return false;
177 }
178 }
179
180 // is the font's style already the same as requested?
181 if ( actualFontStyle == fontDB.styleString( f ) )
182 {
183 return false;
184 }
185
186 const QFont appfont = QApplication::font();
187 const int defaultSize = appfont.pointSize(); // QFontDatabase::font() needs an integer for size
188
189 QFont styledfont;
190 bool foundmatch = false;
191
192 // if fontDB.font() fails, it returns the default app font; but, that may be the target style
193 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
194 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
195 {
196 foundmatch = true;
197 }
198
199 // default to first found style if requested style is unavailable
200 // this helps in the situations where the passed-in font has to have a named style applied
201 if ( fallback && !foundmatch )
202 {
203 QFont testFont = QFont( f );
204 testFont.setPointSize( defaultSize );
205
206 // prefer a style that mostly matches the passed-in font
207 const auto constFamily = fontDB.styles( f.family() );
208 for ( const QString &style : constFamily )
209 {
210 styledfont = fontDB.font( f.family(), style, defaultSize );
211 styledfont = styledfont.resolve( f );
212 if ( testFont.toString() == styledfont.toString() )
213 {
214 foundmatch = true;
215 break;
216 }
217 }
218
219 // fallback to first style found that works
220 if ( !foundmatch )
221 {
222 for ( const QString &style : constFamily )
223 {
224 styledfont = fontDB.font( f.family(), style, defaultSize );
225 if ( QApplication::font() != styledfont )
226 {
227 foundmatch = true;
228 break;
229 }
230 }
231 }
232 }
233
234 // similar to QFont::resolve, but font may already have pixel size set
235 // and we want to make sure that's preserved
236 if ( foundmatch )
237 {
238 if ( !qgsDoubleNear( f.pointSizeF(), -1 ) )
239 {
240 styledfont.setPointSizeF( f.pointSizeF() );
241 }
242 else if ( f.pixelSize() != -1 )
243 {
244 styledfont.setPixelSize( f.pixelSize() );
245 }
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() );
251 f = styledfont;
252
253 return true;
254 }
255
256 return false;
257}
258
260{
261 return QStringLiteral( "QGIS Vera Sans" );
262}
263
264bool QgsFontUtils::loadStandardTestFonts( const QStringList &loadstyles )
265{
266 // load standard test font from filesystem or testdata.qrc (for unit tests and general testing)
267 bool fontsLoaded = false;
268
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" ) );
275
276 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
277 for ( ; f != fontStyles.constEnd(); ++f )
278 {
279 const QString fontpath( f.value() );
280 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( QStringLiteral( "All" ) ) ) )
281 {
282 continue;
283 }
284
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 );
287
288 if ( fontFamilyHasStyle( fontFamily, fontstyle ) )
289 {
290 QgsDebugMsgLevel( QStringLiteral( "Test font '%1 %2' already available" ).arg( fontFamily, fontstyle ), 2 );
291 }
292 else
293 {
294 bool loaded = false;
296 {
297 // workaround for bugs with Qt 4.8.5 (other versions?) on Mac 10.9, where fonts
298 // from qrc resources load but fail to work and default font is substituted [LS]:
299 // https://bugreports.qt.io/browse/QTBUG-30917
300 // https://bugreports.qt.io/browse/QTBUG-32789
301 const QString fontPath( QgsApplication::buildSourcePath() + "/tests/testdata/font/" + fontpath );
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 );
308 }
309 else
310 {
311 QFile fontResource( ":/testdata/font/" + fontpath );
312 if ( fontResource.open( QIODevice::ReadOnly ) )
313 {
314 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
315 loaded = ( fontID != -1 );
316 fontsLoaded = ( fontsLoaded || loaded );
317 }
318 QgsDebugMsgLevel( QStringLiteral( "Test font '%1' (%2) %3 from testdata.qrc" )
319 .arg( fontFamily, fontstyle, loaded ? "loaded" : "FAILED to load" ), 2 );
320 }
321 }
322 }
323
324 return fontsLoaded;
325}
326
327QFont QgsFontUtils::getStandardTestFont( const QString &style, int pointsize )
328{
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 );
331
332 if ( ! fontFamilyHasStyle( fontFamily, fontStyle ) )
333 {
334 loadStandardTestFonts( QStringList() << style );
335 }
336
337 const QFontDatabase fontDB;
338 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
339#ifdef Q_OS_WIN
340 if ( !f.exactMatch() )
341 {
342 QString modified;
343 if ( fontStyle == "Roman" )
344 modified = "Normal";
345 else if ( fontStyle == "Oblique" )
346 modified = "Italic";
347 else if ( fontStyle == "Bold Oblique" )
348 modified = "Bold Italic";
349 if ( !modified.isEmpty() )
350 f = fontDB.font( fontFamily, modified, pointsize );
351 }
352 if ( !f.exactMatch() )
353 {
354 QgsDebugMsgLevel( QStringLiteral( "Inexact font match - consider installing the %1 font." ).arg( fontFamily ), 2 );
355 QgsDebugMsgLevel( QStringLiteral( "Requested: %1" ).arg( f.toString() ), 2 );
356 QFontInfo fi( f );
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 );
358 }
359#endif
360 // in case above statement fails to set style
361 f.setBold( fontStyle.contains( QLatin1String( "Bold" ) ) );
362 f.setItalic( fontStyle.contains( QLatin1String( "Oblique" ) ) || fontStyle.contains( QLatin1String( "Italic" ) ) );
363
364 return f;
365}
366
367QDomElement QgsFontUtils::toXmlElement( const QFont &font, QDomDocument &document, const QString &elementName )
368{
369 QDomElement fontElem = document.createElement( elementName );
370 fontElem.setAttribute( QStringLiteral( "description" ), font.toString() );
371 fontElem.setAttribute( QStringLiteral( "style" ), untranslateNamedStyle( font.styleName() ) );
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' ) );
376 return fontElem;
377}
378
379bool QgsFontUtils::setFromXmlElement( QFont &font, const QDomElement &element )
380{
381 if ( element.isNull() )
382 {
383 return false;
384 }
385
386 font.fromString( element.attribute( QStringLiteral( "description" ) ) );
387
388 if ( element.hasAttribute( QStringLiteral( "bold" ) ) && element.attribute( QStringLiteral( "bold" ) ) == QChar( '1' ) )
389 {
390 font.setBold( true );
391 }
392 if ( element.hasAttribute( QStringLiteral( "italic" ) ) )
393 {
394 font.setItalic( element.attribute( QStringLiteral( "italic" ) ) == QChar( '1' ) );
395 }
396 if ( element.hasAttribute( QStringLiteral( "underline" ) ) )
397 {
398 font.setUnderline( element.attribute( QStringLiteral( "underline" ) ) == QChar( '1' ) );
399 }
400 if ( element.hasAttribute( QStringLiteral( "strikethrough" ) ) )
401 {
402 font.setStrikeOut( element.attribute( QStringLiteral( "strikethrough" ) ) == QChar( '1' ) );
403 }
404
405 if ( element.hasAttribute( QStringLiteral( "style" ) ) )
406 {
407 ( void )updateFontViaStyle( font, translateNamedStyle( element.attribute( QStringLiteral( "style" ) ) ) );
408 }
409
410 return true;
411}
412
413bool QgsFontUtils::setFromXmlChildNode( QFont &font, const QDomElement &element, const QString &childNode )
414{
415 if ( element.isNull() )
416 {
417 return false;
418 }
419
420 const QDomNodeList nodeList = element.elementsByTagName( childNode );
421 if ( !nodeList.isEmpty() )
422 {
423 const QDomElement fontElem = nodeList.at( 0 ).toElement();
424 return setFromXmlElement( font, fontElem );
425 }
426 else
427 {
428 return false;
429 }
430}
431
432QMimeData *QgsFontUtils::toMimeData( const QFont &font )
433{
434 std::unique_ptr< QMimeData >mimeData( new QMimeData );
435
436 QDomDocument fontDoc;
437 const QDomElement fontElem = toXmlElement( font, fontDoc, QStringLiteral( "font" ) );
438 fontDoc.appendChild( fontElem );
439 mimeData->setText( fontDoc.toString() );
440
441 return mimeData.release();
442}
443
444QFont QgsFontUtils::fromMimeData( const QMimeData *data, bool *ok )
445{
446 QFont font;
447 if ( ok )
448 *ok = false;
449
450 if ( !data )
451 return font;
452
453 const QString text = data->text();
454 if ( !text.isEmpty() )
455 {
456 QDomDocument doc;
457 QDomElement elem;
458
459 if ( doc.setContent( text ) )
460 {
461 elem = doc.documentElement();
462
463 if ( elem.nodeName() != QLatin1String( "font" ) )
464 elem = elem.firstChildElement( QStringLiteral( "font" ) );
465
466 if ( setFromXmlElement( font, elem ) )
467 {
468 if ( ok )
469 *ok = true;
470 }
471 return font;
472 }
473 }
474 return font;
475}
476
477static QMap<QString, QString> createTranslatedStyleMap()
478{
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 )
491 {
492 translatedStyleMap.insert( QCoreApplication::translate( "QFontDatabase", qPrintable( word ) ), word );
493 }
494 return translatedStyleMap;
495}
496
497QString QgsFontUtils::translateNamedStyle( const QString &namedStyle )
498{
499 QStringList words = namedStyle.split( ' ', Qt::SkipEmptyParts );
500 for ( int i = 0, n = words.length(); i < n; ++i )
501 {
502 words[i] = QCoreApplication::translate( "QFontDatabase", words[i].toLocal8Bit().constData() );
503 }
504 return words.join( QLatin1Char( ' ' ) );
505}
506
507QString QgsFontUtils::untranslateNamedStyle( const QString &namedStyle )
508{
509 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
510 QStringList words = namedStyle.split( ' ', Qt::SkipEmptyParts );
511
512 for ( int i = 0, n = words.length(); i < n; ++i )
513 {
514 if ( translatedStyleMap.contains( words[i] ) )
515 {
516 words[i] = translatedStyleMap.value( words[i] );
517 }
518 else
519 {
520 QgsDebugMsgLevel( QStringLiteral( "Warning: style map does not contain %1" ).arg( words[i] ), 2 );
521 }
522 }
523 return words.join( QLatin1Char( ' ' ) );
524}
525
526QString QgsFontUtils::asCSS( const QFont &font, double pointToPixelScale )
527{
528 QString css = QStringLiteral( "font-family: " ) + font.family() + ';';
529
530 //style
531 css += QLatin1String( "font-style: " );
532 switch ( font.style() )
533 {
534 case QFont::StyleNormal:
535 css += QLatin1String( "normal" );
536 break;
537 case QFont::StyleItalic:
538 css += QLatin1String( "italic" );
539 break;
540 case QFont::StyleOblique:
541 css += QLatin1String( "oblique" );
542 break;
543 }
544 css += ';';
545
546 //weight
547 int cssWeight = 400;
548 switch ( font.weight() )
549 {
550 case QFont::Light:
551 cssWeight = 300;
552 break;
553 case QFont::Normal:
554 cssWeight = 400;
555 break;
556 case QFont::DemiBold:
557 cssWeight = 600;
558 break;
559 case QFont::Bold:
560 cssWeight = 700;
561 break;
562 case QFont::Black:
563 cssWeight = 900;
564 break;
565 case QFont::Thin:
566 cssWeight = 100;
567 break;
568 case QFont::ExtraLight:
569 cssWeight = 200;
570 break;
571 case QFont::Medium:
572 cssWeight = 500;
573 break;
574 case QFont::ExtraBold:
575 cssWeight = 800;
576 break;
577 }
578 css += QStringLiteral( "font-weight: %1;" ).arg( cssWeight );
579
580 //size
581 css += QStringLiteral( "font-size: %1px;" ).arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
582
583 return css;
584}
585
586void QgsFontUtils::addRecentFontFamily( const QString &family )
587{
588 if ( family.isEmpty() )
589 {
590 return;
591 }
592
593 QgsSettings settings;
594 QStringList recentFamilies = settings.value( QStringLiteral( "fonts/recent" ) ).toStringList();
595
596 //remove matching families
597 recentFamilies.removeAll( family );
598
599 //then add to start of list
600 recentFamilies.prepend( family );
601
602 //trim to 10 fonts
603 recentFamilies = recentFamilies.mid( 0, 10 );
604
605 settings.setValue( QStringLiteral( "fonts/recent" ), recentFamilies );
606}
607
609{
610 const QgsSettings settings;
611 return settings.value( QStringLiteral( "fonts/recent" ) ).toStringList();
612}
613
614void QgsFontUtils::setFontFamily( QFont &font, const QString &family )
615{
616 font.setFamily( family );
617 if ( !font.exactMatch() )
618 {
619 // some Qt versions struggle with fonts with certain unusual characters
620 // in their names, eg "ESRI Oil, Gas, & Water". Calling "setFamilies"
621 // can workaround these issues... (in some cases!)
622 font.setFamilies( { family } );
623 }
624}
625
626QFont QgsFontUtils::createFont( const QString &family, int pointSize, int weight, bool italic )
627{
628 QFont font( family, pointSize, weight, italic );
629 if ( !font.exactMatch() )
630 {
631 // some Qt versions struggle with fonts with certain unusual characters
632 // in their names, eg "ESRI Oil, Gas, & Water". Calling "setFamilies"
633 // can workaround these issues... (in some cases!)
634 font.setFamilies( { family } );
635 }
636 return font;
637}
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.
Definition qgssettings.h:65
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).
Definition qgis.h:6607
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61