QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsfontmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfontmanager.cpp
3 ------------------
4 Date : June 2022
5 Copyright : (C) 2022 Nyall Dawson
6 Email : nyall dot dawson at gmail 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 "qgsfontmanager.h"
17#include "qgsreadwritelocker.h"
18#include "qgsapplication.h"
20#include "qgsziputils.h"
22#include "qgssettingstree.h"
23
24#include <QRegularExpression>
25#include <QRegularExpressionMatch>
26#include <QDir>
27#include <QFontDatabase>
28#include <QTemporaryFile>
29#include <QTemporaryDir>
30
31const QgsSettingsEntryStringList *QgsFontManager::settingsFontFamilyReplacements = new QgsSettingsEntryStringList( QStringLiteral( "fontFamilyReplacements" ), QgsSettingsTree::sTreeFonts, QStringList(), QStringLiteral( "Automatic font family replacements" ) );
32
33const QgsSettingsEntryBool *QgsFontManager::settingsDownloadMissingFonts = new QgsSettingsEntryBool( QStringLiteral( "downloadMissingFonts" ), QgsSettingsTree::sTreeFonts, true, QStringLiteral( "Automatically download missing fonts whenever possible" ) );
34
36 : QObject( parent )
37{
38 const QStringList replacements = settingsFontFamilyReplacements->value();
39 for ( const QString &replacement : replacements )
40 {
41 const thread_local QRegularExpression rxReplacement( QStringLiteral( "(.*?):(.*)" ) );
42 const QRegularExpressionMatch match = rxReplacement.match( replacement );
43 if ( match.hasMatch() )
44 {
45 mFamilyReplacements.insert( match.captured( 1 ), match.captured( 2 ) );
46 mLowerCaseFamilyReplacements.insert( match.captured( 1 ).toLower(), match.captured( 2 ) );
47 }
48 }
49}
50
51QMap<QString, QString> QgsFontManager::fontFamilyReplacements() const
52{
53 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
54 return mFamilyReplacements;
55}
56
57void QgsFontManager::addFontFamilyReplacement( const QString &original, const QString &replacement )
58{
59 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
60 if ( !replacement.isEmpty() )
61 {
62 mFamilyReplacements.insert( original, replacement );
63 mLowerCaseFamilyReplacements.insert( original.toLower(), replacement );
64 }
65 else
66 {
67 mFamilyReplacements.remove( original );
68 mLowerCaseFamilyReplacements.remove( original.toLower() );
69 }
70 storeFamilyReplacements();
71}
72
73void QgsFontManager::setFontFamilyReplacements( const QMap<QString, QString> &replacements )
74{
75 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
76 mFamilyReplacements = replacements;
77 mLowerCaseFamilyReplacements.clear();
78 for ( auto it = mFamilyReplacements.constBegin(); it != mFamilyReplacements.constEnd(); ++it )
79 mLowerCaseFamilyReplacements.insert( it.key().toLower(), it.value() );
80
81 storeFamilyReplacements();
82}
83
84QString QgsFontManager::processFontFamilyName( const QString &name ) const
85{
86 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
87 auto it = mLowerCaseFamilyReplacements.constFind( name.toLower() );
88 if ( it != mLowerCaseFamilyReplacements.constEnd() )
89 return it.value();
90 else
91 return name;
92}
93
94void QgsFontManager::storeFamilyReplacements()
95{
96 QStringList replacements;
97 for ( auto it = mFamilyReplacements.constBegin(); it != mFamilyReplacements.constEnd(); ++it )
98 replacements << QStringLiteral( "%1:%2" ).arg( it.key(), it.value() );
100}
101
103{
104 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
105 const QString userProfileFontsDir = QgsApplication::qgisSettingsDirPath() + "fonts";
106 QStringList fontDirs { userProfileFontsDir };
107
108 fontDirs.append( mUserFontDirectories );
109
110 for ( const QString &dir : std::as_const( fontDirs ) )
111 {
112 if ( !QFile::exists( dir ) && !QDir().mkpath( dir ) )
113 {
114 QgsDebugMsg( QStringLiteral( "Cannot create local fonts dir: %1" ).arg( dir ) );
115 return;
116 }
117
118 installFontsFromDirectory( dir );
119 }
120}
121
122void QgsFontManager::installFontsFromDirectory( const QString &dir )
123{
124 const QFileInfoList fileInfoList = QDir( dir ).entryInfoList( QStringList( QStringLiteral( "*" ) ), QDir::Files );
125 QFileInfoList::const_iterator infoIt = fileInfoList.constBegin();
126 for ( ; infoIt != fileInfoList.constEnd(); ++infoIt )
127 {
128 const int id = QFontDatabase::addApplicationFont( infoIt->filePath() );
129 if ( id == -1 )
130 {
131 QgsDebugMsg( QStringLiteral( "The user font %1 could not be installed" ).arg( infoIt->filePath() ) );
132 mUserFontToFamilyMap.remove( infoIt->filePath() );
133 mUserFontToIdMap.remove( infoIt->filePath() );
134 }
135 else
136 {
137 mUserFontToFamilyMap.insert( infoIt->filePath(), QFontDatabase::applicationFontFamilies( id ) );
138 mUserFontToIdMap.insert( infoIt->filePath(), id );
139 }
140 }
141}
142
143bool QgsFontManager::tryToDownloadFontFamily( const QString &family, QString &matchedFamily )
144{
145 matchedFamily.clear();
147 return false;
148
149 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
150 auto it = mPendingFontDownloads.constFind( family );
151 if ( it != mPendingFontDownloads.constEnd() )
152 {
153 matchedFamily = it.value();
154 return true;
155 }
156 locker.unlock();
157
158 const QString url = urlForFontDownload( family, matchedFamily );
159 if ( url.isEmpty() )
160 return false;
161
162 // It's possible that the font family laundering applied in urlForFontDownload has cleaned up the font
163 // family to a valid font which already exists on the system. In this case we shouldn't try to download
164 // the font again.
165 const QFont testFont( matchedFamily );
166 if ( testFont.exactMatch() )
167 return true;
168
170 mPendingFontDownloads.insert( family, matchedFamily );
171 if ( !mEnableFontDownloads )
172 {
173 mDeferredFontDownloads.insert( url, family );
174 }
175 else
176 {
177 locker.unlock();
178 downloadAndInstallFont( QUrl( url ), family );
179 }
180 return true;
181}
182
184{
185 if ( mEnableFontDownloads )
186 return;
187
188 mEnableFontDownloads = true;
189 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
190 if ( !mDeferredFontDownloads.isEmpty() )
191 {
193 for ( auto it = mDeferredFontDownloads.constBegin(); it != mDeferredFontDownloads.constEnd(); ++it )
194 {
195 downloadAndInstallFont( QUrl( it.key() ), it.value() );
196 }
197 mDeferredFontDownloads.clear();
198 }
199}
200
201QString QgsFontManager::urlForFontDownload( const QString &family, QString &matchedFamily ) const
202{
203 const thread_local QStringList sGoogleFonts
204 {
205 QStringLiteral( "ABeeZee" ),
206 QStringLiteral( "Abel" ),
207 QStringLiteral( "Abril Fatface" ),
208 QStringLiteral( "Aclonica" ),
209 QStringLiteral( "Acme" ),
210 QStringLiteral( "Actor" ),
211 QStringLiteral( "Adamina" ),
212 QStringLiteral( "Advent Pro" ),
213 QStringLiteral( "Aguafina Script" ),
214 QStringLiteral( "Akronim" ),
215 QStringLiteral( "Aladin" ),
216 QStringLiteral( "Aldrich" ),
217 QStringLiteral( "Alef" ),
218 QStringLiteral( "Alegreya" ),
219 QStringLiteral( "Alegreya SC" ),
220 QStringLiteral( "Alegreya Sans" ),
221 QStringLiteral( "Alegreya Sans SC" ),
222 QStringLiteral( "Alex Brush" ),
223 QStringLiteral( "Alfa Slab One" ),
224 QStringLiteral( "Alice" ),
225 QStringLiteral( "Alike" ),
226 QStringLiteral( "Alike Angular" ),
227 QStringLiteral( "Allan" ),
228 QStringLiteral( "Allerta" ),
229 QStringLiteral( "Allerta Stencil" ),
230 QStringLiteral( "Allura" ),
231 QStringLiteral( "Almendra" ),
232 QStringLiteral( "Almendra Display" ),
233 QStringLiteral( "Almendra SC" ),
234 QStringLiteral( "Amarante" ),
235 QStringLiteral( "Amaranth" ),
236 QStringLiteral( "Amatic SC" ),
237 QStringLiteral( "Amethysta" ),
238 QStringLiteral( "Amiri" ),
239 QStringLiteral( "Anaheim" ),
240 QStringLiteral( "Andada" ),
241 QStringLiteral( "Andika" ),
242 QStringLiteral( "Angkor" ),
243 QStringLiteral( "Annie Use Your Telescope" ),
244 QStringLiteral( "Anonymous Pro" ),
245 QStringLiteral( "Antic" ),
246 QStringLiteral( "Antic Didone" ),
247 QStringLiteral( "Antic Slab" ),
248 QStringLiteral( "Anton" ),
249 QStringLiteral( "Arapey" ),
250 QStringLiteral( "Arbutus" ),
251 QStringLiteral( "Arbutus Slab" ),
252 QStringLiteral( "Architects Daughter" ),
253 QStringLiteral( "Archivo" ),
254 QStringLiteral( "Archivo Black" ),
255 QStringLiteral( "Archivo Narrow" ),
256 QStringLiteral( "Arimo" ),
257 QStringLiteral( "Arizonia" ),
258 QStringLiteral( "Armata" ),
259 QStringLiteral( "Artifika" ),
260 QStringLiteral( "Arvo" ),
261 QStringLiteral( "Asap" ),
262 QStringLiteral( "Asset" ),
263 QStringLiteral( "Astloch" ),
264 QStringLiteral( "Asul" ),
265 QStringLiteral( "Atomic Age" ),
266 QStringLiteral( "Aubrey" ),
267 QStringLiteral( "Audiowide" ),
268 QStringLiteral( "Autour One" ),
269 QStringLiteral( "Average" ),
270 QStringLiteral( "Average Sans" ),
271 QStringLiteral( "Averia Gruesa Libre" ),
272 QStringLiteral( "Averia Libre" ),
273 QStringLiteral( "Averia Sans Libre" ),
274 QStringLiteral( "Averia Serif Libre" ),
275 QStringLiteral( "Bad Script" ),
276 QStringLiteral( "Balthazar" ),
277 QStringLiteral( "Bangers" ),
278 QStringLiteral( "Barlow" ),
279 QStringLiteral( "Basic" ),
280 QStringLiteral( "Battambang" ),
281 QStringLiteral( "Baumans" ),
282 QStringLiteral( "Bayon" ),
283 QStringLiteral( "Belgrano" ),
284 QStringLiteral( "Belleza" ),
285 QStringLiteral( "BenchNine" ),
286 QStringLiteral( "Bentham" ),
287 QStringLiteral( "Berkshire Swash" ),
288 QStringLiteral( "Bevan" ),
289 QStringLiteral( "Bigelow Rules" ),
290 QStringLiteral( "Bigshot One" ),
291 QStringLiteral( "Bilbo" ),
292 QStringLiteral( "Bilbo Swash Caps" ),
293 QStringLiteral( "Biryani" ),
294 QStringLiteral( "Bitter" ),
295 QStringLiteral( "Black Ops One" ),
296 QStringLiteral( "Bokor" ),
297 QStringLiteral( "Bonbon" ),
298 QStringLiteral( "Boogaloo" ),
299 QStringLiteral( "Bowlby One" ),
300 QStringLiteral( "Bowlby One SC" ),
301 QStringLiteral( "Brawler" ),
302 QStringLiteral( "Bree Serif" ),
303 QStringLiteral( "Bubblegum Sans" ),
304 QStringLiteral( "Bubbler One" ),
305 QStringLiteral( "Buda" ),
306 QStringLiteral( "Buenard" ),
307 QStringLiteral( "Butcherman" ),
308 QStringLiteral( "Butterfly Kids" ),
309 QStringLiteral( "Cabin" ),
310 QStringLiteral( "Cabin Condensed" ),
311 QStringLiteral( "Cabin Sketch" ),
312 QStringLiteral( "Caesar Dressing" ),
313 QStringLiteral( "Cagliostro" ),
314 QStringLiteral( "Calligraffitti" ),
315 QStringLiteral( "Cambay" ),
316 QStringLiteral( "Cambo" ),
317 QStringLiteral( "Candal" ),
318 QStringLiteral( "Cantarell" ),
319 QStringLiteral( "Cantata One" ),
320 QStringLiteral( "Cantora One" ),
321 QStringLiteral( "Capriola" ),
322 QStringLiteral( "Cardo" ),
323 QStringLiteral( "Carme" ),
324 QStringLiteral( "Carrois Gothic" ),
325 QStringLiteral( "Carrois Gothic SC" ),
326 QStringLiteral( "Carter One" ),
327 QStringLiteral( "Caudex" ),
328 QStringLiteral( "Cedarville Cursive" ),
329 QStringLiteral( "Ceviche One" ),
330 QStringLiteral( "Changa One" ),
331 QStringLiteral( "Chango" ),
332 QStringLiteral( "Chau Philomene One" ),
333 QStringLiteral( "Chela One" ),
334 QStringLiteral( "Chelsea Market" ),
335 QStringLiteral( "Chenla" ),
336 QStringLiteral( "Cherry Cream Soda" ),
337 QStringLiteral( "Cherry Swash" ),
338 QStringLiteral( "Chewy" ),
339 QStringLiteral( "Chicle" ),
340 QStringLiteral( "Chivo" ),
341 QStringLiteral( "Cinzel" ),
342 QStringLiteral( "Cinzel Decorative" ),
343 QStringLiteral( "Clicker Script" ),
344 QStringLiteral( "Coda" ),
345 QStringLiteral( "Coda Caption" ),
346 QStringLiteral( "Codystar" ),
347 QStringLiteral( "Combo" ),
348 QStringLiteral( "Comfortaa" ),
349 QStringLiteral( "Coming Soon" ),
350 QStringLiteral( "Concert One" ),
351 QStringLiteral( "Condiment" ),
352 QStringLiteral( "Content" ),
353 QStringLiteral( "Contrail One" ),
354 QStringLiteral( "Convergence" ),
355 QStringLiteral( "Cookie" ),
356 QStringLiteral( "Copse" ),
357 QStringLiteral( "Corben" ),
358 QStringLiteral( "Courgette" ),
359 QStringLiteral( "Cousine" ),
360 QStringLiteral( "Coustard" ),
361 QStringLiteral( "Covered By Your Grace" ),
362 QStringLiteral( "Crafty Girls" ),
363 QStringLiteral( "Creepster" ),
364 QStringLiteral( "Crete Round" ),
365 QStringLiteral( "Crimson Text" ),
366 QStringLiteral( "Croissant One" ),
367 QStringLiteral( "Crushed" ),
368 QStringLiteral( "Cuprum" ),
369 QStringLiteral( "Cutive" ),
370 QStringLiteral( "Cutive Mono" ),
371 QStringLiteral( "Damion" ),
372 QStringLiteral( "Dancing Script" ),
373 QStringLiteral( "Dangrek" ),
374 QStringLiteral( "Dawning of a New Day" ),
375 QStringLiteral( "Days One" ),
376 QStringLiteral( "Dekko" ),
377 QStringLiteral( "Delius" ),
378 QStringLiteral( "Delius Swash Caps" ),
379 QStringLiteral( "Delius Unicase" ),
380 QStringLiteral( "Della Respira" ),
381 QStringLiteral( "Denk One" ),
382 QStringLiteral( "Devonshire" ),
383 QStringLiteral( "Dhurjati" ),
384 QStringLiteral( "Didact Gothic" ),
385 QStringLiteral( "Diplomata" ),
386 QStringLiteral( "Diplomata SC" ),
387 QStringLiteral( "Domine" ),
388 QStringLiteral( "Donegal One" ),
389 QStringLiteral( "Doppio One" ),
390 QStringLiteral( "Dorsa" ),
391 QStringLiteral( "Dosis" ),
392 QStringLiteral( "Dr Sugiyama" ),
393 QStringLiteral( "Droid Sans" ),
394 QStringLiteral( "Droid Sans Mono" ),
395 QStringLiteral( "Droid Serif" ),
396 QStringLiteral( "Duru Sans" ),
397 QStringLiteral( "Dynalight" ),
398 QStringLiteral( "EB Garamond" ),
399 QStringLiteral( "Eagle Lake" ),
400 QStringLiteral( "Eater" ),
401 QStringLiteral( "Economica" ),
402 QStringLiteral( "Ek Mukta" ),
403 QStringLiteral( "Electrolize" ),
404 QStringLiteral( "Elsie" ),
405 QStringLiteral( "Elsie Swash Caps" ),
406 QStringLiteral( "Emblema One" ),
407 QStringLiteral( "Emilys Candy" ),
408 QStringLiteral( "Engagement" ),
409 QStringLiteral( "Englebert" ),
410 QStringLiteral( "Enriqueta" ),
411 QStringLiteral( "Erica One" ),
412 QStringLiteral( "Esteban" ),
413 QStringLiteral( "Euphoria Script" ),
414 QStringLiteral( "Ewert" ),
415 QStringLiteral( "Exo" ),
416 QStringLiteral( "Exo 2" ),
417 QStringLiteral( "Expletus Sans" ),
418 QStringLiteral( "Fanwood Text" ),
419 QStringLiteral( "Fascinate" ),
420 QStringLiteral( "Fascinate Inline" ),
421 QStringLiteral( "Faster One" ),
422 QStringLiteral( "Fasthand" ),
423 QStringLiteral( "Fauna One" ),
424 QStringLiteral( "Federant" ),
425 QStringLiteral( "Federo" ),
426 QStringLiteral( "Felipa" ),
427 QStringLiteral( "Fenix" ),
428 QStringLiteral( "Finger Paint" ),
429 QStringLiteral( "Fira Code" ),
430 QStringLiteral( "Fira Mono" ),
431 QStringLiteral( "Fira Sans" ),
432 QStringLiteral( "Fira Sans Condensed" ),
433 QStringLiteral( "Fira Sans Extra Condensed " ),
434 QStringLiteral( "Fjalla One" ),
435 QStringLiteral( "Fjord One" ),
436 QStringLiteral( "Flamenco" ),
437 QStringLiteral( "Flavors" ),
438 QStringLiteral( "Fondamento" ),
439 QStringLiteral( "Fontdiner Swanky" ),
440 QStringLiteral( "Forum" ),
441 QStringLiteral( "Francois One" ),
442 QStringLiteral( "Freckle Face" ),
443 QStringLiteral( "Fredericka the Great" ),
444 QStringLiteral( "Fredoka One" ),
445 QStringLiteral( "Freehand" ),
446 QStringLiteral( "Fresca" ),
447 QStringLiteral( "Frijole" ),
448 QStringLiteral( "Fruktur" ),
449 QStringLiteral( "Fugaz One" ),
450 QStringLiteral( "GFS Didot" ),
451 QStringLiteral( "GFS Neohellenic" ),
452 QStringLiteral( "Gabriela" ),
453 QStringLiteral( "Gafata" ),
454 QStringLiteral( "Galdeano" ),
455 QStringLiteral( "Galindo" ),
456 QStringLiteral( "Gentium Basic" ),
457 QStringLiteral( "Gentium Book Basic" ),
458 QStringLiteral( "Geo" ),
459 QStringLiteral( "Geostar" ),
460 QStringLiteral( "Geostar Fill" ),
461 QStringLiteral( "Germania One" ),
462 QStringLiteral( "Gidugu" ),
463 QStringLiteral( "Gilda Display" ),
464 QStringLiteral( "Give You Glory" ),
465 QStringLiteral( "Glass Antiqua" ),
466 QStringLiteral( "Glegoo" ),
467 QStringLiteral( "Gloria Hallelujah" ),
468 QStringLiteral( "Goblin One" ),
469 QStringLiteral( "Gochi Hand" ),
470 QStringLiteral( "Gorditas" ),
471 QStringLiteral( "Goudy Bookletter 1911" ),
472 QStringLiteral( "Graduate" ),
473 QStringLiteral( "Grand Hotel" ),
474 QStringLiteral( "Gravitas One" ),
475 QStringLiteral( "Great Vibes" ),
476 QStringLiteral( "Griffy" ),
477 QStringLiteral( "Gruppo" ),
478 QStringLiteral( "Gudea" ),
479 QStringLiteral( "Gurajada" ),
480 QStringLiteral( "Habibi" ),
481 QStringLiteral( "Halant" ),
482 QStringLiteral( "Hammersmith One" ),
483 QStringLiteral( "Hanalei" ),
484 QStringLiteral( "Hanalei Fill" ),
485 QStringLiteral( "Handlee" ),
486 QStringLiteral( "Hanuman" ),
487 QStringLiteral( "Happy Monkey" ),
488 QStringLiteral( "Headland One" ),
489 QStringLiteral( "Henny Penny" ),
490 QStringLiteral( "Herr Von Muellerhoff" ),
491 QStringLiteral( "Hind" ),
492 QStringLiteral( "Holtwood One SC" ),
493 QStringLiteral( "Homemade Apple" ),
494 QStringLiteral( "Homenaje" ),
495 QStringLiteral( "IM Fell DW Pica" ),
496 QStringLiteral( "IM Fell DW Pica SC" ),
497 QStringLiteral( "IM Fell Double Pica" ),
498 QStringLiteral( "IM Fell Double Pica SC" ),
499 QStringLiteral( "IM Fell English" ),
500 QStringLiteral( "IM Fell English SC" ),
501 QStringLiteral( "IM Fell French Canon" ),
502 QStringLiteral( "IM Fell French Canon SC" ),
503 QStringLiteral( "IM Fell Great Primer" ),
504 QStringLiteral( "IM Fell Great Primer SC" ),
505 QStringLiteral( "Iceberg" ),
506 QStringLiteral( "Iceland" ),
507 QStringLiteral( "Imprima" ),
508 QStringLiteral( "Inconsolata" ),
509 QStringLiteral( "Inder" ),
510 QStringLiteral( "Indie Flower" ),
511 QStringLiteral( "Inika" ),
512 QStringLiteral( "Inter" ),
513 QStringLiteral( "Irish Grover" ),
514 QStringLiteral( "Istok Web" ),
515 QStringLiteral( "Italiana" ),
516 QStringLiteral( "Italianno" ),
517 QStringLiteral( "Jacques Francois" ),
518 QStringLiteral( "Jacques Francois Shadow" ),
519 QStringLiteral( "Jaldi" ),
520 QStringLiteral( "Jim Nightshade" ),
521 QStringLiteral( "Jockey One" ),
522 QStringLiteral( "Jolly Lodger" ),
523 QStringLiteral( "Josefin Sans" ),
524 QStringLiteral( "Josefin Slab" ),
525 QStringLiteral( "Joti One" ),
526 QStringLiteral( "Judson" ),
527 QStringLiteral( "Julee" ),
528 QStringLiteral( "Julius Sans One" ),
529 QStringLiteral( "Junge" ),
530 QStringLiteral( "Jura" ),
531 QStringLiteral( "Just Another Hand" ),
532 QStringLiteral( "Just Me Again Down Here" ),
533 QStringLiteral( "Kalam" ),
534 QStringLiteral( "Kameron" ),
535 QStringLiteral( "Kantumruy" ),
536 QStringLiteral( "Karla" ),
537 QStringLiteral( "Karma" ),
538 QStringLiteral( "Kaushan Script" ),
539 QStringLiteral( "Kavoon" ),
540 QStringLiteral( "Kdam Thmor" ),
541 QStringLiteral( "Keania One" ),
542 QStringLiteral( "Kelly Slab" ),
543 QStringLiteral( "Kenia" ),
544 QStringLiteral( "Khand" ),
545 QStringLiteral( "Khmer" ),
546 QStringLiteral( "Khula" ),
547 QStringLiteral( "Kite One" ),
548 QStringLiteral( "Knewave" ),
549 QStringLiteral( "Kotta One" ),
550 QStringLiteral( "Koulen" ),
551 QStringLiteral( "Kranky" ),
552 QStringLiteral( "Kreon" ),
553 QStringLiteral( "Kristi" ),
554 QStringLiteral( "Krona One" ),
555 QStringLiteral( "La Belle Aurore" ),
556 QStringLiteral( "Laila" ),
557 QStringLiteral( "Lakki Reddy" ),
558 QStringLiteral( "Lancelot" ),
559 QStringLiteral( "Lateef" ),
560 QStringLiteral( "Lato" ),
561 QStringLiteral( "League Script" ),
562 QStringLiteral( "Leckerli One" ),
563 QStringLiteral( "Ledger" ),
564 QStringLiteral( "Lekton" ),
565 QStringLiteral( "Lemon" ),
566 QStringLiteral( "Libre Baskerville" ),
567 QStringLiteral( "Life Savers" ),
568 QStringLiteral( "Lilita One" ),
569 QStringLiteral( "Lily Script One" ),
570 QStringLiteral( "Limelight" ),
571 QStringLiteral( "Linden Hill" ),
572 QStringLiteral( "Lobster" ),
573 QStringLiteral( "Lobster Two" ),
574 QStringLiteral( "Londrina Outline" ),
575 QStringLiteral( "Londrina Shadow" ),
576 QStringLiteral( "Londrina Sketch" ),
577 QStringLiteral( "Londrina Solid" ),
578 QStringLiteral( "Lora" ),
579 QStringLiteral( "Love Ya Like A Sister" ),
580 QStringLiteral( "Loved by the King" ),
581 QStringLiteral( "Lovers Quarrel" ),
582 QStringLiteral( "Luckiest Guy" ),
583 QStringLiteral( "Lusitana" ),
584 QStringLiteral( "Lustria" ),
585 QStringLiteral( "Macondo" ),
586 QStringLiteral( "Macondo Swash Caps" ),
587 QStringLiteral( "Magra" ),
588 QStringLiteral( "Maiden Orange" ),
589 QStringLiteral( "Mako" ),
590 QStringLiteral( "Mallanna" ),
591 QStringLiteral( "Mandali" ),
592 QStringLiteral( "Marcellus" ),
593 QStringLiteral( "Marcellus SC" ),
594 QStringLiteral( "Marck Script" ),
595 QStringLiteral( "Margarine" ),
596 QStringLiteral( "Marko One" ),
597 QStringLiteral( "Marmelad" ),
598 QStringLiteral( "Martel" ),
599 QStringLiteral( "Martel Sans" ),
600 QStringLiteral( "Marvel" ),
601 QStringLiteral( "Mate" ),
602 QStringLiteral( "Mate SC" ),
603 QStringLiteral( "Maven Pro" ),
604 QStringLiteral( "McLaren" ),
605 QStringLiteral( "Meddon" ),
606 QStringLiteral( "MedievalSharp" ),
607 QStringLiteral( "Medula One" ),
608 QStringLiteral( "Megrim" ),
609 QStringLiteral( "Meie Script" ),
610 QStringLiteral( "Merienda" ),
611 QStringLiteral( "Merienda One" ),
612 QStringLiteral( "Merriweather" ),
613 QStringLiteral( "Merriweather Sans" ),
614 QStringLiteral( "Metal" ),
615 QStringLiteral( "Metal Mania" ),
616 QStringLiteral( "Metamorphous" ),
617 QStringLiteral( "Metrophobic" ),
618 QStringLiteral( "Michroma" ),
619 QStringLiteral( "Milonga" ),
620 QStringLiteral( "Miltonian" ),
621 QStringLiteral( "Miltonian Tattoo" ),
622 QStringLiteral( "Miniver" ),
623 QStringLiteral( "Miss Fajardose" ),
624 QStringLiteral( "Modak" ),
625 QStringLiteral( "Modern Antiqua" ),
626 QStringLiteral( "Molengo" ),
627 QStringLiteral( "Molle" ),
628 QStringLiteral( "Monda" ),
629 QStringLiteral( "Monofett" ),
630 QStringLiteral( "Monoton" ),
631 QStringLiteral( "Monsieur La Doulaise" ),
632 QStringLiteral( "Montaga" ),
633 QStringLiteral( "Montez" ),
634 QStringLiteral( "Montserrat" ),
635 QStringLiteral( "Montserrat Alternates" ),
636 QStringLiteral( "Montserrat Subrayada" ),
637 QStringLiteral( "Moul" ),
638 QStringLiteral( "Moulpali" ),
639 QStringLiteral( "Mountains of Christmas" ),
640 QStringLiteral( "Mouse Memoirs" ),
641 QStringLiteral( "Mr Bedfort" ),
642 QStringLiteral( "Mr Dafoe" ),
643 QStringLiteral( "Mr De Haviland" ),
644 QStringLiteral( "Mrs Saint Delafield" ),
645 QStringLiteral( "Mrs Sheppards" ),
646 QStringLiteral( "Mukta" ),
647 QStringLiteral( "Muli" ),
648 QStringLiteral( "Mystery Quest" ),
649 QStringLiteral( "NTR" ),
650 QStringLiteral( "Neucha" ),
651 QStringLiteral( "Neuton" ),
652 QStringLiteral( "New Rocker" ),
653 QStringLiteral( "News Cycle" ),
654 QStringLiteral( "Niconne" ),
655 QStringLiteral( "Nixie One" ),
656 QStringLiteral( "Nobile" ),
657 QStringLiteral( "Nokora" ),
658 QStringLiteral( "Norican" ),
659 QStringLiteral( "Nosifer" ),
660 QStringLiteral( "Nothing You Could Do" ),
661 QStringLiteral( "Noticia Text" ),
662 QStringLiteral( "Noto Sans" ),
663 QStringLiteral( "Noto Sans Japanese" ),
664 QStringLiteral( "Noto Sans Korean" ),
665 QStringLiteral( "Noto Serif" ),
666 QStringLiteral( "Nova Cut" ),
667 QStringLiteral( "Nova Flat" ),
668 QStringLiteral( "Nova Mono" ),
669 QStringLiteral( "Nova Oval" ),
670 QStringLiteral( "Nova Round" ),
671 QStringLiteral( "Nova Script" ),
672 QStringLiteral( "Nova Slim" ),
673 QStringLiteral( "Nova Square" ),
674 QStringLiteral( "Numans" ),
675 QStringLiteral( "Nunito" ),
676 QStringLiteral( "Nunito Sans" ),
677 QStringLiteral( "Odor Mean Chey" ),
678 QStringLiteral( "Offside" ),
679 QStringLiteral( "Old Standard TT" ),
680 QStringLiteral( "Oldenburg" ),
681 QStringLiteral( "Oleo Script" ),
682 QStringLiteral( "Oleo Script Swash Caps" ),
683 QStringLiteral( "Open Sans" ),
684 QStringLiteral( "Open Sans Condensed" ),
685 QStringLiteral( "Oranienbaum" ),
686 QStringLiteral( "Orbitron" ),
687 QStringLiteral( "Oregano" ),
688 QStringLiteral( "Orienta" ),
689 QStringLiteral( "Original Surfer" ),
690 QStringLiteral( "Oswald" ),
691 QStringLiteral( "Over the Rainbow" ),
692 QStringLiteral( "Overlock" ),
693 QStringLiteral( "Overlock SC" ),
694 QStringLiteral( "Ovo" ),
695 QStringLiteral( "Oxygen" ),
696 QStringLiteral( "Oxygen Mono" ),
697 QStringLiteral( "PT Mono" ),
698 QStringLiteral( "PT Sans" ),
699 QStringLiteral( "PT Sans Caption" ),
700 QStringLiteral( "PT Sans Narrow" ),
701 QStringLiteral( "PT Serif" ),
702 QStringLiteral( "PT Serif Caption" ),
703 QStringLiteral( "Pacifico" ),
704 QStringLiteral( "Palanquin" ),
705 QStringLiteral( "Palanquin Dark" ),
706 QStringLiteral( "Paprika" ),
707 QStringLiteral( "Parisienne" ),
708 QStringLiteral( "Passero One" ),
709 QStringLiteral( "Passion One" ),
710 QStringLiteral( "Pathway Gothic One" ),
711 QStringLiteral( "Patrick Hand" ),
712 QStringLiteral( "Patrick Hand SC" ),
713 QStringLiteral( "Patua One" ),
714 QStringLiteral( "Paytone One" ),
715 QStringLiteral( "Peddana" ),
716 QStringLiteral( "Peralta" ),
717 QStringLiteral( "Permanent Marker" ),
718 QStringLiteral( "Petit Formal Script" ),
719 QStringLiteral( "Petrona" ),
720 QStringLiteral( "Philosopher" ),
721 QStringLiteral( "Piedra" ),
722 QStringLiteral( "Pinyon Script" ),
723 QStringLiteral( "Pirata One" ),
724 QStringLiteral( "Plaster" ),
725 QStringLiteral( "Play" ),
726 QStringLiteral( "Playball" ),
727 QStringLiteral( "Playfair Display" ),
728 QStringLiteral( "Playfair Display SC" ),
729 QStringLiteral( "Podkova" ),
730 QStringLiteral( "Poiret One" ),
731 QStringLiteral( "Poller One" ),
732 QStringLiteral( "Poly" ),
733 QStringLiteral( "Pompiere" ),
734 QStringLiteral( "Pontano Sans" ),
735 QStringLiteral( "Poppins" ),
736 QStringLiteral( "Port Lligat Sans" ),
737 QStringLiteral( "Port Lligat Slab" ),
738 QStringLiteral( "Pragati Narrow" ),
739 QStringLiteral( "Prata" ),
740 QStringLiteral( "Preahvihear" ),
741 QStringLiteral( "Press Start 2P" ),
742 QStringLiteral( "Princess Sofia" ),
743 QStringLiteral( "Prociono" ),
744 QStringLiteral( "Prosto One" ),
745 QStringLiteral( "Puritan" ),
746 QStringLiteral( "Purple Purse" ),
747 QStringLiteral( "Quando" ),
748 QStringLiteral( "Quantico" ),
749 QStringLiteral( "Quattrocento" ),
750 QStringLiteral( "Quattrocento Sans" ),
751 QStringLiteral( "Questrial" ),
752 QStringLiteral( "Quicksand" ),
753 QStringLiteral( "Quintessential" ),
754 QStringLiteral( "Qwigley" ),
755 QStringLiteral( "Racing Sans One" ),
756 QStringLiteral( "Radley" ),
757 QStringLiteral( "Rajdhani" ),
758 QStringLiteral( "Raleway" ),
759 QStringLiteral( "Raleway Dots" ),
760 QStringLiteral( "Ramabhadra" ),
761 QStringLiteral( "Ramaraja" ),
762 QStringLiteral( "Rambla" ),
763 QStringLiteral( "Rammetto One" ),
764 QStringLiteral( "Ranchers" ),
765 QStringLiteral( "Rancho" ),
766 QStringLiteral( "Ranga" ),
767 QStringLiteral( "Rationale" ),
768 QStringLiteral( "Ravi Prakash" ),
769 QStringLiteral( "Redressed" ),
770 QStringLiteral( "Reenie Beanie" ),
771 QStringLiteral( "Revalia" ),
772 QStringLiteral( "Ribeye" ),
773 QStringLiteral( "Ribeye Marrow" ),
774 QStringLiteral( "Righteous" ),
775 QStringLiteral( "Risque" ),
776 QStringLiteral( "Roboto" ),
777 QStringLiteral( "Roboto Condensed" ),
778 QStringLiteral( "Roboto Mono" ),
779 QStringLiteral( "Roboto Slab" ),
780 QStringLiteral( "Rochester" ),
781 QStringLiteral( "Rock Salt" ),
782 QStringLiteral( "Rokkitt" ),
783 QStringLiteral( "Romanesco" ),
784 QStringLiteral( "Ropa Sans" ),
785 QStringLiteral( "Rosario" ),
786 QStringLiteral( "Rosarivo" ),
787 QStringLiteral( "Rouge Script" ),
788 QStringLiteral( "Rozha One" ),
789 QStringLiteral( "Rubik" ),
790 QStringLiteral( "Rubik Mono One" ),
791 QStringLiteral( "Rubik One" ),
792 QStringLiteral( "Ruda" ),
793 QStringLiteral( "Rufina" ),
794 QStringLiteral( "Ruge Boogie" ),
795 QStringLiteral( "Ruluko" ),
796 QStringLiteral( "Rum Raisin" ),
797 QStringLiteral( "Ruslan Display" ),
798 QStringLiteral( "Russo One" ),
799 QStringLiteral( "Ruthie" ),
800 QStringLiteral( "Rye" ),
801 QStringLiteral( "Sacramento" ),
802 QStringLiteral( "Sail" ),
803 QStringLiteral( "Salsa" ),
804 QStringLiteral( "Sanchez" ),
805 QStringLiteral( "Sancreek" ),
806 QStringLiteral( "Sansita One" ),
807 QStringLiteral( "Sarabun" ),
808 QStringLiteral( "Sarina" ),
809 QStringLiteral( "Sarpanch" ),
810 QStringLiteral( "Satisfy" ),
811 QStringLiteral( "Scada" ),
812 QStringLiteral( "Scheherazade" ),
813 QStringLiteral( "Schoolbell" ),
814 QStringLiteral( "Seaweed Script" ),
815 QStringLiteral( "Sevillana" ),
816 QStringLiteral( "Seymour One" ),
817 QStringLiteral( "Shadows Into Light" ),
818 QStringLiteral( "Shadows Into Light Two" ),
819 QStringLiteral( "Shanti" ),
820 QStringLiteral( "Share" ),
821 QStringLiteral( "Share Tech" ),
822 QStringLiteral( "Share Tech Mono" ),
823 QStringLiteral( "Shojumaru" ),
824 QStringLiteral( "Short Stack" ),
825 QStringLiteral( "Siemreap" ),
826 QStringLiteral( "Sigmar One" ),
827 QStringLiteral( "Signika" ),
828 QStringLiteral( "Signika Negative" ),
829 QStringLiteral( "Simonetta" ),
830 QStringLiteral( "Sintony" ),
831 QStringLiteral( "Sirin Stencil" ),
832 QStringLiteral( "Six Caps" ),
833 QStringLiteral( "Skranji" ),
834 QStringLiteral( "Slabo 13px" ),
835 QStringLiteral( "Slabo 27px" ),
836 QStringLiteral( "Slackey" ),
837 QStringLiteral( "Smokum" ),
838 QStringLiteral( "Smythe" ),
839 QStringLiteral( "Sniglet" ),
840 QStringLiteral( "Snippet" ),
841 QStringLiteral( "Snowburst One" ),
842 QStringLiteral( "Sofadi One" ),
843 QStringLiteral( "Sofia" ),
844 QStringLiteral( "Sonsie One" ),
845 QStringLiteral( "Sorts Mill Goudy" ),
846 QStringLiteral( "Source Code Pro" ),
847 QStringLiteral( "Source Sans Pro" ),
848 QStringLiteral( "Source Serif Pro" ),
849 QStringLiteral( "Special Elite" ),
850 QStringLiteral( "Spicy Rice" ),
851 QStringLiteral( "Spinnaker" ),
852 QStringLiteral( "Spirax" ),
853 QStringLiteral( "Squada One" ),
854 QStringLiteral( "Sree Krushnadevaraya" ),
855 QStringLiteral( "Stalemate" ),
856 QStringLiteral( "Stalinist One" ),
857 QStringLiteral( "Stardos Stencil" ),
858 QStringLiteral( "Stint Ultra Condensed" ),
859 QStringLiteral( "Stint Ultra Expanded" ),
860 QStringLiteral( "Stoke" ),
861 QStringLiteral( "Strait" ),
862 QStringLiteral( "Sue Ellen Francisco" ),
863 QStringLiteral( "Sunshiney" ),
864 QStringLiteral( "Supermercado One" ),
865 QStringLiteral( "Suranna" ),
866 QStringLiteral( "Suravaram" ),
867 QStringLiteral( "Suwannaphum" ),
868 QStringLiteral( "Swanky and Moo Moo" ),
869 QStringLiteral( "Syncopate" ),
870 QStringLiteral( "Tangerine" ),
871 QStringLiteral( "Taprom" ),
872 QStringLiteral( "Tauri" ),
873 QStringLiteral( "Teko" ),
874 QStringLiteral( "Telex" ),
875 QStringLiteral( "Tenali Ramakrishna" ),
876 QStringLiteral( "Tenor Sans" ),
877 QStringLiteral( "Text Me One" ),
878 QStringLiteral( "The Girl Next Door" ),
879 QStringLiteral( "Tienne" ),
880 QStringLiteral( "Timmana" ),
881 QStringLiteral( "Tinos" ),
882 QStringLiteral( "Titan One" ),
883 QStringLiteral( "Titillium Web" ),
884 QStringLiteral( "Trade Winds" ),
885 QStringLiteral( "Trocchi" ),
886 QStringLiteral( "Trochut" ),
887 QStringLiteral( "Trykker" ),
888 QStringLiteral( "Tulpen One" ),
889 QStringLiteral( "Ubuntu" ),
890 QStringLiteral( "Ubuntu Condensed" ),
891 QStringLiteral( "Ubuntu Mono" ),
892 QStringLiteral( "Ultra" ),
893 QStringLiteral( "Uncial Antiqua" ),
894 QStringLiteral( "Underdog" ),
895 QStringLiteral( "Unica One" ),
896 QStringLiteral( "UnifrakturCook" ),
897 QStringLiteral( "UnifrakturMaguntia" ),
898 QStringLiteral( "Unkempt" ),
899 QStringLiteral( "Unlock" ),
900 QStringLiteral( "Unna" ),
901 QStringLiteral( "VT323" ),
902 QStringLiteral( "Vampiro One" ),
903 QStringLiteral( "Varela" ),
904 QStringLiteral( "Varela Round" ),
905 QStringLiteral( "Vast Shadow" ),
906 QStringLiteral( "Vesper Libre" ),
907 QStringLiteral( "Vibur" ),
908 QStringLiteral( "Vidaloka" ),
909 QStringLiteral( "Viga" ),
910 QStringLiteral( "Voces" ),
911 QStringLiteral( "Volkhov" ),
912 QStringLiteral( "Vollkorn" ),
913 QStringLiteral( "Voltaire" ),
914 QStringLiteral( "Waiting for the Sunrise" ),
915 QStringLiteral( "Wallpoet" ),
916 QStringLiteral( "Walter Turncoat" ),
917 QStringLiteral( "Warnes" ),
918 QStringLiteral( "Wellfleet" ),
919 QStringLiteral( "Wendy One" ),
920 QStringLiteral( "Wire One" ),
921 QStringLiteral( "Yanone Kaffeesatz" ),
922 QStringLiteral( "Yellowtail" ),
923 QStringLiteral( "Yeseva One" ),
924 QStringLiteral( "Yesteryear" ),
925 QStringLiteral( "Zeyada" ),
926 };
927
928 auto cleanFontFamily = []( const QString & family ) -> QString
929 {
930 const thread_local QRegularExpression charsToRemove( QStringLiteral( "[^a-z]" ) );
931 const thread_local QRegularExpression styleNames( QStringLiteral( "(?:normal|regular|light|bold|black|demi|italic|oblique|medium|thin)" ) );
932
933 QString processed = family.toLower();
934 processed.replace( styleNames, QString() );
935 return processed.replace( charsToRemove, QString() );
936 };
937
938 matchedFamily.clear();
939 const QString cleanedFamily = cleanFontFamily( family );
940 for ( const QString &candidate : sGoogleFonts )
941 {
942 if ( cleanFontFamily( candidate ) == cleanedFamily )
943 {
944 QString paramName = candidate;
945 paramName.replace( ' ', '+' );
946 matchedFamily = candidate;
947 return QStringLiteral( "https://fonts.google.com/download?family=%1" ).arg( paramName );
948 }
949 }
950 return QString();
951}
952
953void QgsFontManager::downloadAndInstallFont( const QUrl &url, const QString &identifier )
954{
955 QString description;
956 if ( identifier.isEmpty() )
957 {
958 description = tr( "Installing fonts from %1" ).arg( url.toString() );
959 }
960 else
961 {
962 description = tr( "Installing %1" ).arg( identifier );
963 }
964
965 QgsNetworkContentFetcherTask *task = new QgsNetworkContentFetcherTask( url, QString(), QgsTask::CanCancel, description );
966 connect( task, &QgsNetworkContentFetcherTask::fetched, this, [ = ]
967 {
968 if ( task->reply()->error() != QNetworkReply::NoError )
969 {
970 emit fontDownloadErrorOccurred( url, identifier, task->reply()->errorString() );
971 }
972 else
973 {
974 QString errorMessage;
975 QStringList families;
976 QString licenseDetails;
977 if ( installFontsFromData( task->reply()->readAll(), errorMessage, families, licenseDetails, task->contentDispositionFilename() ) )
978 {
979 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
980 mPendingFontDownloads.remove( identifier );
981 locker.unlock();
982
983 emit fontDownloaded( families, licenseDetails );
984 }
985 else
986 {
987 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
988 mPendingFontDownloads.remove( identifier );
989 locker.unlock();
990
991 emit fontDownloadErrorOccurred( url, identifier, errorMessage );
992 }
993 }
994 } );
996}
997
998bool QgsFontManager::installFontsFromData( const QByteArray &data, QString &errorMessage, QStringList &families, QString &licenseDetails, const QString &filename )
999{
1000 errorMessage.clear();
1001 families.clear();
1002 licenseDetails.clear();
1003
1004 QTemporaryFile tempFile;
1005 QTemporaryDir tempDir;
1006
1007 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
1008 const QString userFontsDir = mUserFontDirectories.empty() ? ( QgsApplication::qgisSettingsDirPath() + "fonts" ) : mUserFontDirectories.at( 0 );
1009 locker.unlock();
1010
1011 const QDir fontsDir( userFontsDir );
1012
1013 if ( !tempFile.open() )
1014 {
1015 errorMessage = tr( "Could not write font data to a temporary file" );
1016 return false;
1017 }
1018
1019 tempFile.write( data );
1020 tempFile.close();
1021
1022 QString sourcePath = tempFile.fileName();
1023
1024 //try to install the data directly as a font
1025 int id = QFontDatabase::addApplicationFont( sourcePath );
1026 if ( id != -1 )
1027 {
1028 // successfully loaded data as a font
1029 const QStringList foundFamilies = QFontDatabase::applicationFontFamilies( id );
1030 // remove the application font, as we'll copy it to the final destination and re-add from there
1031 QFontDatabase::removeApplicationFont( id );
1032
1033 if ( foundFamilies.empty() )
1034 {
1035 errorMessage = tr( "Could not find any families in font" );
1036 return false;
1037 }
1038
1039 QgsDebugMsgLevel( QStringLiteral( "Found fonts %1" ).arg( foundFamilies.join( ',' ) ), 2 );
1040 families = foundFamilies;
1041 // guess a good name for the file, by taking the first family name from the font
1042 const QString family = families.at( 0 );
1043 const QString destPath = fontsDir.filePath( filename.isEmpty() ? family : filename );
1044 if ( !QFile::copy( sourcePath, destPath ) )
1045 {
1046 errorMessage = tr( "Could not copy font to %1" ).arg( destPath );
1047 return false;
1048 }
1049
1050 id = QFontDatabase::addApplicationFont( destPath );
1051 if ( id == -1 )
1052 {
1053 errorMessage = tr( "Could not install font from %1" ).arg( destPath );
1054 return false;
1055 }
1056 else
1057 {
1059 mUserFontToFamilyMap.insert( destPath, foundFamilies );
1060 mUserFontToIdMap.insert( destPath, id );
1061 }
1062 return true;
1063 }
1064 else
1065 {
1066 // font install failed, but maybe it's a zip file
1067 QStringList files;
1068 if ( QgsZipUtils::unzip( tempFile.fileName(), tempDir.path(), files ) )
1069 {
1071 for ( const QString &file : std::as_const( files ) )
1072 {
1073 const QFileInfo fi( file );
1074 if ( fi.fileName().compare( QLatin1String( "OFL.txt" ), Qt::CaseInsensitive ) == 0
1075 || fi.fileName().compare( QLatin1String( "LICENSE.txt" ), Qt::CaseInsensitive ) == 0 )
1076 {
1077 QFile licenseFile( file );
1078 if ( licenseFile.open( QIODevice::ReadOnly ) )
1079 {
1080 QTextStream in( &licenseFile );
1081 const QString license = in.readAll();
1082 licenseDetails.append( license );
1083 }
1084 }
1085 else if ( fi.suffix().compare( QLatin1String( "ttf" ), Qt::CaseInsensitive ) == 0 ||
1086 fi.suffix().compare( QLatin1String( "otf" ), Qt::CaseInsensitive ) == 0 )
1087 {
1088 sourcePath = file;
1089 id = QFontDatabase::addApplicationFont( sourcePath );
1090 if ( id != -1 )
1091 {
1092 QFontDatabase::removeApplicationFont( id );
1093 const QString destPath = fontsDir.filePath( fi.fileName() );
1094 // dest path may already exist for zip files -- e.g if a single zip contains a number of font variants
1095 if ( !QFile::exists( destPath ) && !QFile::copy( sourcePath, destPath ) )
1096 {
1097 errorMessage = tr( "Could not copy font to %1" ).arg( destPath );
1098 return false;
1099 }
1100 id = QFontDatabase::addApplicationFont( destPath );
1101 if ( id == -1 )
1102 {
1103 errorMessage = tr( "Could not install font from %1" ).arg( destPath );
1104 return false;
1105 }
1106 const QStringList foundFamilies = QFontDatabase::applicationFontFamilies( id );
1107 mUserFontToFamilyMap.insert( destPath, foundFamilies );
1108 mUserFontToIdMap.insert( destPath, id );
1109 for ( const QString &found : foundFamilies )
1110 {
1111 if ( !families.contains( found ) )
1112 families << found;
1113 }
1114 }
1115 }
1116 }
1117 return true;
1118 }
1119 }
1120
1121 errorMessage = tr( "Could not read fonts from data" );
1122 return false;
1123}
1124
1125void QgsFontManager::addUserFontDirectory( const QString &directory )
1126{
1127 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
1128 if ( mUserFontDirectories.contains( directory ) )
1129 return;
1130
1132 mUserFontDirectories.append( directory );
1133 locker.unlock();
1134
1135 if ( !QFile::exists( directory ) && !QDir().mkpath( directory ) )
1136 {
1137 QgsDebugMsg( QStringLiteral( "Cannot create local fonts dir: %1" ).arg( directory ) );
1138 return;
1139 }
1140
1141 installFontsFromDirectory( directory );
1142}
1143
1144QMap<QString, QStringList> QgsFontManager::userFontToFamilyMap() const
1145{
1146 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
1147 return mUserFontToFamilyMap;
1148}
1149
1150bool QgsFontManager::removeUserFont( const QString &path )
1151{
1152 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
1153 const int id = mUserFontToIdMap.value( path, -1 );
1154 if ( id != -1 )
1155 QFontDatabase::removeApplicationFont( id );
1156 QFile::remove( path );
1157 mUserFontToIdMap.remove( path );
1158 mUserFontToFamilyMap.remove( path );
1159 return true;
1160}
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
void addFontFamilyReplacement(const QString &original, const QString &replacement)
Adds a new font replacement from the original font family to a replacement font family.
bool removeUserFont(const QString &path)
Removes the user font at the specified path.
bool installFontsFromData(const QByteArray &data, QString &errorMessage, QStringList &families, QString &licenseDetails, const QString &filename=QString())
Installs local user fonts from the specified raw data.
QMap< QString, QString > fontFamilyReplacements() const
Returns the map of automatic font family replacements.
void installUserFonts()
Installs user fonts from the profile/fonts directory as application fonts.
void addUserFontDirectory(const QString &directory)
Adds a directory to use for user fonts.
void setFontFamilyReplacements(const QMap< QString, QString > &replacements)
Sets the map of automatic font family replacements.
void downloadAndInstallFont(const QUrl &url, const QString &identifier=QString())
Downloads a font and installs in the user's profile/fonts directory as an application font.
void enableFontDownloadsForSession()
Enables font downloads the the current QGIS session.
QString urlForFontDownload(const QString &family, QString &matchedFamily) const
Returns the URL at which the font family can be downloaded.
static const QgsSettingsEntryStringList * settingsFontFamilyReplacements
Settings entry for font family replacements.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
QMap< QString, QStringList > userFontToFamilyMap() const
Returns the mapping of installed user fonts to font families.
QgsFontManager(QObject *parent=nullptr)
Constructor for QgsFontManager, with the specified parent object.
static const QgsSettingsEntryBool * settingsDownloadMissingFonts
Settings entry for font family replacements.
bool tryToDownloadFontFamily(const QString &family, QString &matchedFamily)
Tries to download and install the specified font family.
Handles HTTP network content fetching in a background task.
void fetched()
Emitted when the network content has been fetched, regardless of whether the fetch was successful or ...
QNetworkReply * reply()
Returns the network reply.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
@ Read
Lock for read.
void unlock()
Unlocks the lock.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A boolean settings entry.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
A string list settings entry.
static QgsSettingsTreeNode * sTreeFonts
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
@ CanCancel
Task can be canceled.
CORE_EXPORT bool unzip(const QString &zip, const QString &dir, QStringList &files)
Unzip a zip file in an output directory.
Definition: qgsziputils.cpp:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38