QGIS API Documentation  2.8.2-Wien
 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 }