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