QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 "qgsapplication.h"
19 #include "qgslogger.h"
20 
21 #include <QApplication>
22 #include <QFile>
23 #include <QFont>
24 #include <QFontDatabase>
25 #include <QFontInfo>
26 #include <QStringList>
27 
28 
29 bool QgsFontUtils::fontMatchOnSystem( const QFont& f )
30 {
31  QFontInfo fi = QFontInfo( f );
32  return fi.exactMatch();
33 }
34 
35 bool QgsFontUtils::fontFamilyOnSystem( const QString& family )
36 {
37  QFont tmpFont = QFont( family );
38  // compare just beginning of family string in case 'family [foundry]' differs
39  return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
40 }
41 
42 bool QgsFontUtils::fontFamilyHasStyle( const QString& family, const QString& style )
43 {
44  QFontDatabase fontDB;
45  return ( fontFamilyOnSystem( family ) && fontDB.styles( family ).contains( style ) );
46 }
47 
48 bool QgsFontUtils::fontFamilyMatchOnSystem( const QString& family, QString* chosen, bool* match )
49 {
50  QFontDatabase fontDB;
51  QStringList fontFamilies = fontDB.families();
52  bool found = false;
53 
54  QList<QString>::const_iterator it = fontFamilies.constBegin();
55  for ( ; it != fontFamilies.constEnd(); ++it )
56  {
57  // first compare just beginning of 'family [foundry]' string
58  if ( it->startsWith( family, Qt::CaseInsensitive ) )
59  {
60  found = true;
61  // keep looking if match info is requested
62  if ( match )
63  {
64  // full 'family [foundry]' strings have to match
65  *match = ( *it == family );
66  if ( *match )
67  break;
68  }
69  else
70  {
71  break;
72  }
73  }
74  }
75 
76  if ( found )
77  {
78  if ( chosen )
79  {
80  // retrieve the family actually assigned by matching algorithm
81  QFont f = QFont( family );
82  *chosen = f.family();
83  }
84  }
85  else
86  {
87  if ( chosen )
88  {
89  *chosen = QString();
90  }
91 
92  if ( match )
93  {
94  *match = false;
95  }
96  }
97 
98  return found;
99 }
100 
101 bool QgsFontUtils::updateFontViaStyle( QFont& f, const QString& fontstyle, bool fallback )
102 {
103  if ( fontstyle.isEmpty() )
104  {
105  return false;
106  }
107 
108  QFontDatabase fontDB;
109 
110  if ( !fallback )
111  {
112  // does the font even have the requested style?
113  bool hasstyle = fontFamilyHasStyle( f.family(), fontstyle );
114  if ( !hasstyle )
115  {
116  return false;
117  }
118  }
119 
120  // is the font's style already the same as requested?
121  if ( fontstyle == fontDB.styleString( f ) )
122  {
123  return false;
124  }
125 
126  QFont appfont = QApplication::font();
127  int defaultSize = appfont.pointSize(); // QFontDatabase::font() needs an integer for size
128 
129  QFont styledfont;
130  bool foundmatch = false;
131 
132  // if fontDB.font() fails, it returns the default app font; but, that may be the target style
133  styledfont = fontDB.font( f.family(), fontstyle, defaultSize );
134  if ( appfont != styledfont || fontstyle != fontDB.styleString( f ) )
135  {
136  foundmatch = true;
137  }
138 
139  // default to first found style if requested style is unavailable
140  // this helps in the situations where the passed-in font has to have a named style applied
141  if ( fallback && !foundmatch )
142  {
143  QFont testFont = QFont( f );
144  testFont.setPointSize( defaultSize );
145 
146  // prefer a style that mostly matches the passed-in font
147  foreach ( const QString &style, fontDB.styles( f.family() ) )
148  {
149  styledfont = fontDB.font( f.family(), style, defaultSize );
150  styledfont = styledfont.resolve( f );
151  if ( testFont.toString() == styledfont.toString() )
152  {
153  foundmatch = true;
154  break;
155  }
156  }
157 
158  // fallback to first style found that works
159  if ( !foundmatch )
160  {
161  foreach ( const QString &style, fontDB.styles( f.family() ) )
162  {
163  styledfont = fontDB.font( f.family(), style, defaultSize );
164  if ( QApplication::font() != styledfont )
165  {
166  foundmatch = true;
167  break;
168  }
169  }
170  }
171  }
172 
173  // similar to QFont::resolve, but font may already have pixel size set
174  // and we want to make sure that's preserved
175  if ( foundmatch )
176  {
177  if ( f.pointSizeF() != -1 )
178  {
179  styledfont.setPointSizeF( f.pointSizeF() );
180  }
181  else if ( f.pixelSize() != -1 )
182  {
183  styledfont.setPixelSize( f.pixelSize() );
184  }
185  styledfont.setCapitalization( f.capitalization() );
186  styledfont.setUnderline( f.underline() );
187  styledfont.setStrikeOut( f.strikeOut() );
188  styledfont.setWordSpacing( f.wordSpacing() );
189  styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
190  f = styledfont;
191 
192  return true;
193  }
194 
195  return false;
196 }
197 
199 {
200  return "QGIS Vera Sans";
201 }
202 
203 bool QgsFontUtils::loadStandardTestFonts( QStringList loadstyles )
204 {
205  // load standard test font from filesystem or testdata.qrc (for unit tests and general testing)
206  bool fontsLoaded = false;
207 
208  QString fontFamily = standardTestFontFamily();
209  QMap<QString, QString> fontStyles;
210  fontStyles.insert( "Roman", "QGIS-Vera/QGIS-Vera.ttf" );
211  fontStyles.insert( "Oblique", "QGIS-Vera/QGIS-VeraIt.ttf" );
212  fontStyles.insert( "Bold", "QGIS-Vera/QGIS-VeraBd.ttf" );
213  fontStyles.insert( "Bold Oblique", "QGIS-Vera/QGIS-VeraBI.ttf" );
214 
215  QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
216  for ( ; f != fontStyles.constEnd(); ++f )
217  {
218  QString fontstyle( f.key() );
219  QString fontpath( f.value() );
220  if ( !( loadstyles.contains( fontstyle ) || loadstyles.contains( "All" ) ) )
221  {
222  continue;
223  }
224  QString familyStyle = QString( "%1 %2" ).arg( fontFamily ).arg( fontstyle );
225 
226  if ( fontFamilyHasStyle( fontFamily, fontstyle ) )
227  {
228  fontsLoaded = ( fontsLoaded || false );
229  QgsDebugMsg( QString( "Test font '%1' already available" ).arg( familyStyle ) );
230  }
231  else
232  {
233  bool loaded = false;
235  {
236  // workaround for bugs with Qt 4.8.5 (other versions?) on Mac 10.9, where fonts
237  // from qrc resources load but fail to work and default font is substituted [LS]:
238  // https://bugreports.qt-project.org/browse/QTBUG-30917
239  // https://bugreports.qt-project.org/browse/QTBUG-32789
240  QString fontPath( QgsApplication::buildSourcePath() + "/tests/testdata/font/" + fontpath );
241  int fontID = QFontDatabase::addApplicationFont( fontPath );
242  loaded = ( fontID != -1 );
243  fontsLoaded = ( fontsLoaded || loaded );
244  QgsDebugMsg( QString( "Test font '%1' %2 from filesystem [%3]" )
245  .arg( familyStyle ).arg( loaded ? "loaded" : "FAILED to load" ).arg( fontPath ) );
246  }
247  else
248  {
249  QFile fontResource( ":/testdata/font/" + fontpath );
250  if ( fontResource.open( QIODevice::ReadOnly ) )
251  {
252  int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
253  loaded = ( fontID != -1 );
254  fontsLoaded = ( fontsLoaded || loaded );
255  }
256  QgsDebugMsg( QString( "Test font '%1' %2 from testdata.qrc" )
257  .arg( familyStyle ).arg( loaded ? "loaded" : "FAILED to load" ) );
258  }
259  }
260  }
261 
262  return fontsLoaded;
263 }
264 
265 QFont QgsFontUtils::getStandardTestFont( const QString& style, int pointsize )
266 {
267  QFontDatabase fontDB;
268  if ( ! fontFamilyHasStyle( standardTestFontFamily(), style ) )
269  {
270  loadStandardTestFonts( QStringList() << style );
271  }
272 
273  QFont f = fontDB.font( standardTestFontFamily(), style, pointsize );
274  // in case above statement fails to set style
275  f.setBold( style.contains( "Bold" ) );
276  f.setItalic( style.contains( "Oblique" ) );
277 
278  return f;
279 }
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static bool isRunningFromBuildDir()
Indicates whether running from build directory (not installed)
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
static QFont getStandardTestFont(const QString &style="Roman", int pointsize=12)
Get standard test font with specific style.
static QString standardTestFontFamily()
Get standard test font family.
static QString buildSourcePath()
Returns path to the source directory.
static bool loadStandardTestFonts(QStringList loadstyles)
Loads standard test fonts from filesystem or qrc resource.
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 fontFamilyMatchOnSystem(const QString &family, QString *chosen=0, bool *match=0)
Check whether font family is on system.