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