QGIS API Documentation 3.27.0-Master (9c08adf5ef)
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 Mono" ),
424 QStringLiteral( "Fira Sans" ),
425 QStringLiteral( "Fjalla One" ),
426 QStringLiteral( "Fjord One" ),
427 QStringLiteral( "Flamenco" ),
428 QStringLiteral( "Flavors" ),
429 QStringLiteral( "Fondamento" ),
430 QStringLiteral( "Fontdiner Swanky" ),
431 QStringLiteral( "Forum" ),
432 QStringLiteral( "Francois One" ),
433 QStringLiteral( "Freckle Face" ),
434 QStringLiteral( "Fredericka the Great" ),
435 QStringLiteral( "Fredoka One" ),
436 QStringLiteral( "Freehand" ),
437 QStringLiteral( "Fresca" ),
438 QStringLiteral( "Frijole" ),
439 QStringLiteral( "Fruktur" ),
440 QStringLiteral( "Fugaz One" ),
441 QStringLiteral( "GFS Didot" ),
442 QStringLiteral( "GFS Neohellenic" ),
443 QStringLiteral( "Gabriela" ),
444 QStringLiteral( "Gafata" ),
445 QStringLiteral( "Galdeano" ),
446 QStringLiteral( "Galindo" ),
447 QStringLiteral( "Gentium Basic" ),
448 QStringLiteral( "Gentium Book Basic" ),
449 QStringLiteral( "Geo" ),
450 QStringLiteral( "Geostar" ),
451 QStringLiteral( "Geostar Fill" ),
452 QStringLiteral( "Germania One" ),
453 QStringLiteral( "Gidugu" ),
454 QStringLiteral( "Gilda Display" ),
455 QStringLiteral( "Give You Glory" ),
456 QStringLiteral( "Glass Antiqua" ),
457 QStringLiteral( "Glegoo" ),
458 QStringLiteral( "Gloria Hallelujah" ),
459 QStringLiteral( "Goblin One" ),
460 QStringLiteral( "Gochi Hand" ),
461 QStringLiteral( "Gorditas" ),
462 QStringLiteral( "Goudy Bookletter 1911" ),
463 QStringLiteral( "Graduate" ),
464 QStringLiteral( "Grand Hotel" ),
465 QStringLiteral( "Gravitas One" ),
466 QStringLiteral( "Great Vibes" ),
467 QStringLiteral( "Griffy" ),
468 QStringLiteral( "Gruppo" ),
469 QStringLiteral( "Gudea" ),
470 QStringLiteral( "Gurajada" ),
471 QStringLiteral( "Habibi" ),
472 QStringLiteral( "Halant" ),
473 QStringLiteral( "Hammersmith One" ),
474 QStringLiteral( "Hanalei" ),
475 QStringLiteral( "Hanalei Fill" ),
476 QStringLiteral( "Handlee" ),
477 QStringLiteral( "Hanuman" ),
478 QStringLiteral( "Happy Monkey" ),
479 QStringLiteral( "Headland One" ),
480 QStringLiteral( "Henny Penny" ),
481 QStringLiteral( "Herr Von Muellerhoff" ),
482 QStringLiteral( "Hind" ),
483 QStringLiteral( "Holtwood One SC" ),
484 QStringLiteral( "Homemade Apple" ),
485 QStringLiteral( "Homenaje" ),
486 QStringLiteral( "IM Fell DW Pica" ),
487 QStringLiteral( "IM Fell DW Pica SC" ),
488 QStringLiteral( "IM Fell Double Pica" ),
489 QStringLiteral( "IM Fell Double Pica SC" ),
490 QStringLiteral( "IM Fell English" ),
491 QStringLiteral( "IM Fell English SC" ),
492 QStringLiteral( "IM Fell French Canon" ),
493 QStringLiteral( "IM Fell French Canon SC" ),
494 QStringLiteral( "IM Fell Great Primer" ),
495 QStringLiteral( "IM Fell Great Primer SC" ),
496 QStringLiteral( "Iceberg" ),
497 QStringLiteral( "Iceland" ),
498 QStringLiteral( "Imprima" ),
499 QStringLiteral( "Inconsolata" ),
500 QStringLiteral( "Inder" ),
501 QStringLiteral( "Indie Flower" ),
502 QStringLiteral( "Inika" ),
503 QStringLiteral( "Irish Grover" ),
504 QStringLiteral( "Istok Web" ),
505 QStringLiteral( "Italiana" ),
506 QStringLiteral( "Italianno" ),
507 QStringLiteral( "Jacques Francois" ),
508 QStringLiteral( "Jacques Francois Shadow" ),
509 QStringLiteral( "Jaldi" ),
510 QStringLiteral( "Jim Nightshade" ),
511 QStringLiteral( "Jockey One" ),
512 QStringLiteral( "Jolly Lodger" ),
513 QStringLiteral( "Josefin Sans" ),
514 QStringLiteral( "Josefin Slab" ),
515 QStringLiteral( "Joti One" ),
516 QStringLiteral( "Judson" ),
517 QStringLiteral( "Julee" ),
518 QStringLiteral( "Julius Sans One" ),
519 QStringLiteral( "Junge" ),
520 QStringLiteral( "Jura" ),
521 QStringLiteral( "Just Another Hand" ),
522 QStringLiteral( "Just Me Again Down Here" ),
523 QStringLiteral( "Kalam" ),
524 QStringLiteral( "Kameron" ),
525 QStringLiteral( "Kantumruy" ),
526 QStringLiteral( "Karla" ),
527 QStringLiteral( "Karma" ),
528 QStringLiteral( "Kaushan Script" ),
529 QStringLiteral( "Kavoon" ),
530 QStringLiteral( "Kdam Thmor" ),
531 QStringLiteral( "Keania One" ),
532 QStringLiteral( "Kelly Slab" ),
533 QStringLiteral( "Kenia" ),
534 QStringLiteral( "Khand" ),
535 QStringLiteral( "Khmer" ),
536 QStringLiteral( "Khula" ),
537 QStringLiteral( "Kite One" ),
538 QStringLiteral( "Knewave" ),
539 QStringLiteral( "Kotta One" ),
540 QStringLiteral( "Koulen" ),
541 QStringLiteral( "Kranky" ),
542 QStringLiteral( "Kreon" ),
543 QStringLiteral( "Kristi" ),
544 QStringLiteral( "Krona One" ),
545 QStringLiteral( "La Belle Aurore" ),
546 QStringLiteral( "Laila" ),
547 QStringLiteral( "Lakki Reddy" ),
548 QStringLiteral( "Lancelot" ),
549 QStringLiteral( "Lateef" ),
550 QStringLiteral( "Lato" ),
551 QStringLiteral( "League Script" ),
552 QStringLiteral( "Leckerli One" ),
553 QStringLiteral( "Ledger" ),
554 QStringLiteral( "Lekton" ),
555 QStringLiteral( "Lemon" ),
556 QStringLiteral( "Libre Baskerville" ),
557 QStringLiteral( "Life Savers" ),
558 QStringLiteral( "Lilita One" ),
559 QStringLiteral( "Lily Script One" ),
560 QStringLiteral( "Limelight" ),
561 QStringLiteral( "Linden Hill" ),
562 QStringLiteral( "Lobster" ),
563 QStringLiteral( "Lobster Two" ),
564 QStringLiteral( "Londrina Outline" ),
565 QStringLiteral( "Londrina Shadow" ),
566 QStringLiteral( "Londrina Sketch" ),
567 QStringLiteral( "Londrina Solid" ),
568 QStringLiteral( "Lora" ),
569 QStringLiteral( "Love Ya Like A Sister" ),
570 QStringLiteral( "Loved by the King" ),
571 QStringLiteral( "Lovers Quarrel" ),
572 QStringLiteral( "Luckiest Guy" ),
573 QStringLiteral( "Lusitana" ),
574 QStringLiteral( "Lustria" ),
575 QStringLiteral( "Macondo" ),
576 QStringLiteral( "Macondo Swash Caps" ),
577 QStringLiteral( "Magra" ),
578 QStringLiteral( "Maiden Orange" ),
579 QStringLiteral( "Mako" ),
580 QStringLiteral( "Mallanna" ),
581 QStringLiteral( "Mandali" ),
582 QStringLiteral( "Marcellus" ),
583 QStringLiteral( "Marcellus SC" ),
584 QStringLiteral( "Marck Script" ),
585 QStringLiteral( "Margarine" ),
586 QStringLiteral( "Marko One" ),
587 QStringLiteral( "Marmelad" ),
588 QStringLiteral( "Martel" ),
589 QStringLiteral( "Martel Sans" ),
590 QStringLiteral( "Marvel" ),
591 QStringLiteral( "Mate" ),
592 QStringLiteral( "Mate SC" ),
593 QStringLiteral( "Maven Pro" ),
594 QStringLiteral( "McLaren" ),
595 QStringLiteral( "Meddon" ),
596 QStringLiteral( "MedievalSharp" ),
597 QStringLiteral( "Medula One" ),
598 QStringLiteral( "Megrim" ),
599 QStringLiteral( "Meie Script" ),
600 QStringLiteral( "Merienda" ),
601 QStringLiteral( "Merienda One" ),
602 QStringLiteral( "Merriweather" ),
603 QStringLiteral( "Merriweather Sans" ),
604 QStringLiteral( "Metal" ),
605 QStringLiteral( "Metal Mania" ),
606 QStringLiteral( "Metamorphous" ),
607 QStringLiteral( "Metrophobic" ),
608 QStringLiteral( "Michroma" ),
609 QStringLiteral( "Milonga" ),
610 QStringLiteral( "Miltonian" ),
611 QStringLiteral( "Miltonian Tattoo" ),
612 QStringLiteral( "Miniver" ),
613 QStringLiteral( "Miss Fajardose" ),
614 QStringLiteral( "Modak" ),
615 QStringLiteral( "Modern Antiqua" ),
616 QStringLiteral( "Molengo" ),
617 QStringLiteral( "Molle" ),
618 QStringLiteral( "Monda" ),
619 QStringLiteral( "Monofett" ),
620 QStringLiteral( "Monoton" ),
621 QStringLiteral( "Monsieur La Doulaise" ),
622 QStringLiteral( "Montaga" ),
623 QStringLiteral( "Montez" ),
624 QStringLiteral( "Montserrat" ),
625 QStringLiteral( "Montserrat Alternates" ),
626 QStringLiteral( "Montserrat Subrayada" ),
627 QStringLiteral( "Moul" ),
628 QStringLiteral( "Moulpali" ),
629 QStringLiteral( "Mountains of Christmas" ),
630 QStringLiteral( "Mouse Memoirs" ),
631 QStringLiteral( "Mr Bedfort" ),
632 QStringLiteral( "Mr Dafoe" ),
633 QStringLiteral( "Mr De Haviland" ),
634 QStringLiteral( "Mrs Saint Delafield" ),
635 QStringLiteral( "Mrs Sheppards" ),
636 QStringLiteral( "Muli" ),
637 QStringLiteral( "Mystery Quest" ),
638 QStringLiteral( "NTR" ),
639 QStringLiteral( "Neucha" ),
640 QStringLiteral( "Neuton" ),
641 QStringLiteral( "New Rocker" ),
642 QStringLiteral( "News Cycle" ),
643 QStringLiteral( "Niconne" ),
644 QStringLiteral( "Nixie One" ),
645 QStringLiteral( "Nobile" ),
646 QStringLiteral( "Nokora" ),
647 QStringLiteral( "Norican" ),
648 QStringLiteral( "Nosifer" ),
649 QStringLiteral( "Nothing You Could Do" ),
650 QStringLiteral( "Noticia Text" ),
651 QStringLiteral( "Noto Sans" ),
652 QStringLiteral( "Noto Serif" ),
653 QStringLiteral( "Nova Cut" ),
654 QStringLiteral( "Nova Flat" ),
655 QStringLiteral( "Nova Mono" ),
656 QStringLiteral( "Nova Oval" ),
657 QStringLiteral( "Nova Round" ),
658 QStringLiteral( "Nova Script" ),
659 QStringLiteral( "Nova Slim" ),
660 QStringLiteral( "Nova Square" ),
661 QStringLiteral( "Numans" ),
662 QStringLiteral( "Nunito" ),
663 QStringLiteral( "Odor Mean Chey" ),
664 QStringLiteral( "Offside" ),
665 QStringLiteral( "Old Standard TT" ),
666 QStringLiteral( "Oldenburg" ),
667 QStringLiteral( "Oleo Script" ),
668 QStringLiteral( "Oleo Script Swash Caps" ),
669 QStringLiteral( "Open Sans" ),
670 QStringLiteral( "Open Sans Condensed" ),
671 QStringLiteral( "Oranienbaum" ),
672 QStringLiteral( "Orbitron" ),
673 QStringLiteral( "Oregano" ),
674 QStringLiteral( "Orienta" ),
675 QStringLiteral( "Original Surfer" ),
676 QStringLiteral( "Oswald" ),
677 QStringLiteral( "Over the Rainbow" ),
678 QStringLiteral( "Overlock" ),
679 QStringLiteral( "Overlock SC" ),
680 QStringLiteral( "Ovo" ),
681 QStringLiteral( "Oxygen" ),
682 QStringLiteral( "Oxygen Mono" ),
683 QStringLiteral( "PT Mono" ),
684 QStringLiteral( "PT Sans" ),
685 QStringLiteral( "PT Sans Caption" ),
686 QStringLiteral( "PT Sans Narrow" ),
687 QStringLiteral( "PT Serif" ),
688 QStringLiteral( "PT Serif Caption" ),
689 QStringLiteral( "Pacifico" ),
690 QStringLiteral( "Palanquin" ),
691 QStringLiteral( "Palanquin Dark" ),
692 QStringLiteral( "Paprika" ),
693 QStringLiteral( "Parisienne" ),
694 QStringLiteral( "Passero One" ),
695 QStringLiteral( "Passion One" ),
696 QStringLiteral( "Pathway Gothic One" ),
697 QStringLiteral( "Patrick Hand" ),
698 QStringLiteral( "Patrick Hand SC" ),
699 QStringLiteral( "Patua One" ),
700 QStringLiteral( "Paytone One" ),
701 QStringLiteral( "Peddana" ),
702 QStringLiteral( "Peralta" ),
703 QStringLiteral( "Permanent Marker" ),
704 QStringLiteral( "Petit Formal Script" ),
705 QStringLiteral( "Petrona" ),
706 QStringLiteral( "Philosopher" ),
707 QStringLiteral( "Piedra" ),
708 QStringLiteral( "Pinyon Script" ),
709 QStringLiteral( "Pirata One" ),
710 QStringLiteral( "Plaster" ),
711 QStringLiteral( "Play" ),
712 QStringLiteral( "Playball" ),
713 QStringLiteral( "Playfair Display" ),
714 QStringLiteral( "Playfair Display SC" ),
715 QStringLiteral( "Podkova" ),
716 QStringLiteral( "Poiret One" ),
717 QStringLiteral( "Poller One" ),
718 QStringLiteral( "Poly" ),
719 QStringLiteral( "Pompiere" ),
720 QStringLiteral( "Pontano Sans" ),
721 QStringLiteral( "Poppins" ),
722 QStringLiteral( "Port Lligat Sans" ),
723 QStringLiteral( "Port Lligat Slab" ),
724 QStringLiteral( "Pragati Narrow" ),
725 QStringLiteral( "Prata" ),
726 QStringLiteral( "Preahvihear" ),
727 QStringLiteral( "Press Start 2P" ),
728 QStringLiteral( "Princess Sofia" ),
729 QStringLiteral( "Prociono" ),
730 QStringLiteral( "Prosto One" ),
731 QStringLiteral( "Puritan" ),
732 QStringLiteral( "Purple Purse" ),
733 QStringLiteral( "Quando" ),
734 QStringLiteral( "Quantico" ),
735 QStringLiteral( "Quattrocento" ),
736 QStringLiteral( "Quattrocento Sans" ),
737 QStringLiteral( "Questrial" ),
738 QStringLiteral( "Quicksand" ),
739 QStringLiteral( "Quintessential" ),
740 QStringLiteral( "Qwigley" ),
741 QStringLiteral( "Racing Sans One" ),
742 QStringLiteral( "Radley" ),
743 QStringLiteral( "Rajdhani" ),
744 QStringLiteral( "Raleway" ),
745 QStringLiteral( "Raleway Dots" ),
746 QStringLiteral( "Ramabhadra" ),
747 QStringLiteral( "Ramaraja" ),
748 QStringLiteral( "Rambla" ),
749 QStringLiteral( "Rammetto One" ),
750 QStringLiteral( "Ranchers" ),
751 QStringLiteral( "Rancho" ),
752 QStringLiteral( "Ranga" ),
753 QStringLiteral( "Rationale" ),
754 QStringLiteral( "Ravi Prakash" ),
755 QStringLiteral( "Redressed" ),
756 QStringLiteral( "Reenie Beanie" ),
757 QStringLiteral( "Revalia" ),
758 QStringLiteral( "Ribeye" ),
759 QStringLiteral( "Ribeye Marrow" ),
760 QStringLiteral( "Righteous" ),
761 QStringLiteral( "Risque" ),
762 QStringLiteral( "Roboto" ),
763 QStringLiteral( "Roboto Condensed" ),
764 QStringLiteral( "Roboto Slab" ),
765 QStringLiteral( "Rochester" ),
766 QStringLiteral( "Rock Salt" ),
767 QStringLiteral( "Rokkitt" ),
768 QStringLiteral( "Romanesco" ),
769 QStringLiteral( "Ropa Sans" ),
770 QStringLiteral( "Rosario" ),
771 QStringLiteral( "Rosarivo" ),
772 QStringLiteral( "Rouge Script" ),
773 QStringLiteral( "Rozha One" ),
774 QStringLiteral( "Rubik Mono One" ),
775 QStringLiteral( "Rubik One" ),
776 QStringLiteral( "Ruda" ),
777 QStringLiteral( "Rufina" ),
778 QStringLiteral( "Ruge Boogie" ),
779 QStringLiteral( "Ruluko" ),
780 QStringLiteral( "Rum Raisin" ),
781 QStringLiteral( "Ruslan Display" ),
782 QStringLiteral( "Russo One" ),
783 QStringLiteral( "Ruthie" ),
784 QStringLiteral( "Rye" ),
785 QStringLiteral( "Sacramento" ),
786 QStringLiteral( "Sail" ),
787 QStringLiteral( "Salsa" ),
788 QStringLiteral( "Sanchez" ),
789 QStringLiteral( "Sancreek" ),
790 QStringLiteral( "Sansita One" ),
791 QStringLiteral( "Sarabun" ),
792 QStringLiteral( "Sarina" ),
793 QStringLiteral( "Sarpanch" ),
794 QStringLiteral( "Satisfy" ),
795 QStringLiteral( "Scada" ),
796 QStringLiteral( "Scheherazade" ),
797 QStringLiteral( "Schoolbell" ),
798 QStringLiteral( "Seaweed Script" ),
799 QStringLiteral( "Sevillana" ),
800 QStringLiteral( "Seymour One" ),
801 QStringLiteral( "Shadows Into Light" ),
802 QStringLiteral( "Shadows Into Light Two" ),
803 QStringLiteral( "Shanti" ),
804 QStringLiteral( "Share" ),
805 QStringLiteral( "Share Tech" ),
806 QStringLiteral( "Share Tech Mono" ),
807 QStringLiteral( "Shojumaru" ),
808 QStringLiteral( "Short Stack" ),
809 QStringLiteral( "Siemreap" ),
810 QStringLiteral( "Sigmar One" ),
811 QStringLiteral( "Signika" ),
812 QStringLiteral( "Signika Negative" ),
813 QStringLiteral( "Simonetta" ),
814 QStringLiteral( "Sintony" ),
815 QStringLiteral( "Sirin Stencil" ),
816 QStringLiteral( "Six Caps" ),
817 QStringLiteral( "Skranji" ),
818 QStringLiteral( "Slabo 13px" ),
819 QStringLiteral( "Slabo 27px" ),
820 QStringLiteral( "Slackey" ),
821 QStringLiteral( "Smokum" ),
822 QStringLiteral( "Smythe" ),
823 QStringLiteral( "Sniglet" ),
824 QStringLiteral( "Snippet" ),
825 QStringLiteral( "Snowburst One" ),
826 QStringLiteral( "Sofadi One" ),
827 QStringLiteral( "Sofia" ),
828 QStringLiteral( "Sonsie One" ),
829 QStringLiteral( "Sorts Mill Goudy" ),
830 QStringLiteral( "Source Code Pro" ),
831 QStringLiteral( "Source Sans Pro" ),
832 QStringLiteral( "Source Serif Pro" ),
833 QStringLiteral( "Special Elite" ),
834 QStringLiteral( "Spicy Rice" ),
835 QStringLiteral( "Spinnaker" ),
836 QStringLiteral( "Spirax" ),
837 QStringLiteral( "Squada One" ),
838 QStringLiteral( "Sree Krushnadevaraya" ),
839 QStringLiteral( "Stalemate" ),
840 QStringLiteral( "Stalinist One" ),
841 QStringLiteral( "Stardos Stencil" ),
842 QStringLiteral( "Stint Ultra Condensed" ),
843 QStringLiteral( "Stint Ultra Expanded" ),
844 QStringLiteral( "Stoke" ),
845 QStringLiteral( "Strait" ),
846 QStringLiteral( "Sue Ellen Francisco" ),
847 QStringLiteral( "Sunshiney" ),
848 QStringLiteral( "Supermercado One" ),
849 QStringLiteral( "Suranna" ),
850 QStringLiteral( "Suravaram" ),
851 QStringLiteral( "Suwannaphum" ),
852 QStringLiteral( "Swanky and Moo Moo" ),
853 QStringLiteral( "Syncopate" ),
854 QStringLiteral( "Tangerine" ),
855 QStringLiteral( "Taprom" ),
856 QStringLiteral( "Tauri" ),
857 QStringLiteral( "Teko" ),
858 QStringLiteral( "Telex" ),
859 QStringLiteral( "Tenali Ramakrishna" ),
860 QStringLiteral( "Tenor Sans" ),
861 QStringLiteral( "Text Me One" ),
862 QStringLiteral( "The Girl Next Door" ),
863 QStringLiteral( "Tienne" ),
864 QStringLiteral( "Timmana" ),
865 QStringLiteral( "Tinos" ),
866 QStringLiteral( "Titan One" ),
867 QStringLiteral( "Titillium Web" ),
868 QStringLiteral( "Trade Winds" ),
869 QStringLiteral( "Trocchi" ),
870 QStringLiteral( "Trochut" ),
871 QStringLiteral( "Trykker" ),
872 QStringLiteral( "Tulpen One" ),
873 QStringLiteral( "Ubuntu" ),
874 QStringLiteral( "Ubuntu Condensed" ),
875 QStringLiteral( "Ubuntu Mono" ),
876 QStringLiteral( "Ultra" ),
877 QStringLiteral( "Uncial Antiqua" ),
878 QStringLiteral( "Underdog" ),
879 QStringLiteral( "Unica One" ),
880 QStringLiteral( "UnifrakturCook" ),
881 QStringLiteral( "UnifrakturMaguntia" ),
882 QStringLiteral( "Unkempt" ),
883 QStringLiteral( "Unlock" ),
884 QStringLiteral( "Unna" ),
885 QStringLiteral( "VT323" ),
886 QStringLiteral( "Vampiro One" ),
887 QStringLiteral( "Varela" ),
888 QStringLiteral( "Varela Round" ),
889 QStringLiteral( "Vast Shadow" ),
890 QStringLiteral( "Vesper Libre" ),
891 QStringLiteral( "Vibur" ),
892 QStringLiteral( "Vidaloka" ),
893 QStringLiteral( "Viga" ),
894 QStringLiteral( "Voces" ),
895 QStringLiteral( "Volkhov" ),
896 QStringLiteral( "Vollkorn" ),
897 QStringLiteral( "Voltaire" ),
898 QStringLiteral( "Waiting for the Sunrise" ),
899 QStringLiteral( "Wallpoet" ),
900 QStringLiteral( "Walter Turncoat" ),
901 QStringLiteral( "Warnes" ),
902 QStringLiteral( "Wellfleet" ),
903 QStringLiteral( "Wendy One" ),
904 QStringLiteral( "Wire One" ),
905 QStringLiteral( "Yanone Kaffeesatz" ),
906 QStringLiteral( "Yellowtail" ),
907 QStringLiteral( "Yeseva One" ),
908 QStringLiteral( "Yesteryear" ),
909 QStringLiteral( "Zeyada" ),
910 };
911
912 auto cleanFontFamily = []( const QString & family ) -> QString
913 {
914 const thread_local QRegularExpression charsToRemove( QStringLiteral( "[^a-z]" ) );
915 const thread_local QRegularExpression styleNames( QStringLiteral( "(?:normal|regular|light|bold|black|demi|italic|oblique|medium|thin)" ) );
916
917 QString processed = family.toLower();
918 processed.replace( styleNames, QString() );
919 return processed.replace( charsToRemove, QString() );
920 };
921
922 matchedFamily.clear();
923 const QString cleanedFamily = cleanFontFamily( family );
924 for ( const QString &candidate : sGoogleFonts )
925 {
926 if ( cleanFontFamily( candidate ) == cleanedFamily )
927 {
928 QString paramName = candidate;
929 paramName.replace( ' ', '+' );
930 matchedFamily = candidate;
931 return QStringLiteral( "https://fonts.google.com/download?family=%1" ).arg( paramName );
932 }
933 }
934 return QString();
935}
936
937void QgsFontManager::downloadAndInstallFont( const QUrl &url, const QString &identifier )
938{
939 QString description;
940 if ( identifier.isEmpty() )
941 {
942 description = tr( "Installing fonts from %1" ).arg( url.toString() );
943 }
944 else
945 {
946 description = tr( "Installing %1" ).arg( identifier );
947 }
948
949 QgsNetworkContentFetcherTask *task = new QgsNetworkContentFetcherTask( url, QString(), QgsTask::CanCancel, description );
950 connect( task, &QgsNetworkContentFetcherTask::fetched, this, [ = ]
951 {
952 if ( task->reply()->error() != QNetworkReply::NoError )
953 {
954 emit fontDownloadErrorOccurred( url, identifier, task->reply()->errorString() );
955 }
956 else
957 {
958 QString errorMessage;
959 QStringList families;
960 QString licenseDetails;
961 if ( installFontsFromData( task->reply()->readAll(), errorMessage, families, licenseDetails, task->contentDispositionFilename() ) )
962 {
963 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
964 mPendingFontDownloads.remove( identifier );
965 locker.unlock();
966
967 emit fontDownloaded( families, licenseDetails );
968 }
969 else
970 {
971 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
972 mPendingFontDownloads.remove( identifier );
973 locker.unlock();
974
975 emit fontDownloadErrorOccurred( url, identifier, errorMessage );
976 }
977 }
978 } );
980}
981
982bool QgsFontManager::installFontsFromData( const QByteArray &data, QString &errorMessage, QStringList &families, QString &licenseDetails, const QString &filename )
983{
984 errorMessage.clear();
985 families.clear();
986 licenseDetails.clear();
987
988 QTemporaryFile tempFile;
989 QTemporaryDir tempDir;
990
991 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
992 const QString userFontsDir = mUserFontDirectories.empty() ? ( QgsApplication::qgisSettingsDirPath() + "fonts" ) : mUserFontDirectories.at( 0 );
993 locker.unlock();
994
995 const QDir fontsDir( userFontsDir );
996
997 if ( !tempFile.open() )
998 {
999 errorMessage = tr( "Could not write font data to a temporary file" );
1000 return false;
1001 }
1002
1003 tempFile.write( data );
1004 tempFile.close();
1005
1006 QString sourcePath = tempFile.fileName();
1007
1008 //try to install the data directly as a font
1009 int id = QFontDatabase::addApplicationFont( sourcePath );
1010 if ( id != -1 )
1011 {
1012 // successfully loaded data as a font
1013 const QStringList foundFamilies = QFontDatabase::applicationFontFamilies( id );
1014 // remove the application font, as we'll copy it to the final destination and re-add from there
1015 QFontDatabase::removeApplicationFont( id );
1016
1017 if ( foundFamilies.empty() )
1018 {
1019 errorMessage = tr( "Could not find any families in font" );
1020 return false;
1021 }
1022
1023 QgsDebugMsgLevel( QStringLiteral( "Found fonts %1" ).arg( foundFamilies.join( ',' ) ), 2 );
1024 families = foundFamilies;
1025 // guess a good name for the file, by taking the first family name from the font
1026 const QString family = families.at( 0 );
1027 const QString destPath = fontsDir.filePath( filename.isEmpty() ? family : filename );
1028 if ( !QFile::copy( sourcePath, destPath ) )
1029 {
1030 errorMessage = tr( "Could not copy font to %1" ).arg( destPath );
1031 return false;
1032 }
1033
1034 id = QFontDatabase::addApplicationFont( destPath );
1035 if ( id == -1 )
1036 {
1037 errorMessage = tr( "Could not install font from %1" ).arg( destPath );
1038 return false;
1039 }
1040 else
1041 {
1043 mUserFontToFamilyMap.insert( destPath, foundFamilies );
1044 mUserFontToIdMap.insert( destPath, id );
1045 }
1046 return true;
1047 }
1048 else
1049 {
1050 // font install failed, but maybe it's a zip file
1051 QStringList files;
1052 if ( QgsZipUtils::unzip( tempFile.fileName(), tempDir.path(), files ) )
1053 {
1055 for ( const QString &file : std::as_const( files ) )
1056 {
1057 const QFileInfo fi( file );
1058 if ( fi.fileName().compare( QLatin1String( "OFL.txt" ), Qt::CaseInsensitive ) == 0
1059 || fi.fileName().compare( QLatin1String( "LICENSE.txt" ), Qt::CaseInsensitive ) == 0 )
1060 {
1061 QFile licenseFile( file );
1062 if ( licenseFile.open( QIODevice::ReadOnly ) )
1063 {
1064 QTextStream in( &licenseFile );
1065 const QString license = in.readAll();
1066 licenseDetails.append( license );
1067 }
1068 }
1069 else if ( fi.suffix().compare( QLatin1String( "ttf" ), Qt::CaseInsensitive ) == 0 ||
1070 fi.suffix().compare( QLatin1String( "otf" ), Qt::CaseInsensitive ) == 0 )
1071 {
1072 sourcePath = file;
1073 id = QFontDatabase::addApplicationFont( sourcePath );
1074 if ( id != -1 )
1075 {
1076 QFontDatabase::removeApplicationFont( id );
1077 const QString destPath = fontsDir.filePath( fi.fileName() );
1078 // dest path may already exist for zip files -- e.g if a single zip contains a number of font variants
1079 if ( !QFile::exists( destPath ) && !QFile::copy( sourcePath, destPath ) )
1080 {
1081 errorMessage = tr( "Could not copy font to %1" ).arg( destPath );
1082 return false;
1083 }
1084 id = QFontDatabase::addApplicationFont( destPath );
1085 if ( id == -1 )
1086 {
1087 errorMessage = tr( "Could not install font from %1" ).arg( destPath );
1088 return false;
1089 }
1090 const QStringList foundFamilies = QFontDatabase::applicationFontFamilies( id );
1091 mUserFontToFamilyMap.insert( destPath, foundFamilies );
1092 mUserFontToIdMap.insert( destPath, id );
1093 for ( const QString &found : foundFamilies )
1094 {
1095 if ( !families.contains( found ) )
1096 families << found;
1097 }
1098 }
1099 }
1100 }
1101 return true;
1102 }
1103 }
1104
1105 errorMessage = tr( "Could not read fonts from data" );
1106 return false;
1107}
1108
1109void QgsFontManager::addUserFontDirectory( const QString &directory )
1110{
1111 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
1112 if ( mUserFontDirectories.contains( directory ) )
1113 return;
1114
1116 mUserFontDirectories.append( directory );
1117 locker.unlock();
1118
1119 if ( !QFile::exists( directory ) && !QDir().mkpath( directory ) )
1120 {
1121 QgsDebugMsg( QStringLiteral( "Cannot create local fonts dir: %1" ).arg( directory ) );
1122 return;
1123 }
1124
1125 installFontsFromDirectory( directory );
1126}
1127
1128QMap<QString, QStringList> QgsFontManager::userFontToFamilyMap() const
1129{
1130 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Read );
1131 return mUserFontToFamilyMap;
1132}
1133
1134bool QgsFontManager::removeUserFont( const QString &path )
1135{
1136 QgsReadWriteLocker locker( mReplacementLock, QgsReadWriteLocker::Write );
1137 const int id = mUserFontToIdMap.value( path, -1 );
1138 if ( id != -1 )
1139 QFontDatabase::removeApplicationFont( id );
1140 QFile::remove( path );
1141 mUserFontToIdMap.remove( path );
1142 mUserFontToFamilyMap.remove( path );
1143 return true;
1144}
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