1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 /*
8 Scribus font handling code - copyright (C) 2001 by
9 Alastair M. Robinson
10
11 Based on code by Christian Tpp and Franz Schmid.
12
13 Contributed to the Scribus project under the terms of
14 the GNU General Public License version 2, or any later
15 version, at your discretion.
16 */
17
18 #include <QApplication>
19 #include <QDir>
20 #include <QDomDocument>
21 #include <QFile>
22 #include <QFileInfo>
23 #include <QFont>
24 #include <QGlobalStatic>
25 #include <QHash>
26 #include <QMap>
27 #include <QRegExp>
28 #include <QRawFont>
29 #ifdef Q_OS_WIN32
30 #include <QSet>
31 #include <QSettings>
32 #include <QStandardPaths>
33 #endif
34 #include <QString>
35 #include <QTextCodec>
36
37 #include <cstdlib>
38 #include <vector>
39
40 #include "scfonts.h"
41 #include "fonts/ftface.h"
42 #include "fonts/scface_ps.h"
43 #include "fonts/scface_ttf.h"
44 #include "fonts/scfontmetrics.h"
45
46 #include "prefsmanager.h"
47 #include "prefsfile.h"
48 #include "prefscontext.h"
49 #include "prefstable.h"
50
51 #include "scribuscore.h"
52 #include "scribusdoc.h"
53 #ifdef Q_OS_LINUX
54 #include <X11/X.h>
55 #include <X11/Xlib.h>
56 #endif
57
58 #ifdef HAVE_FONTCONFIG
59 #include <fontconfig/fontconfig.h>
60 #endif
61
62 #include <ft2build.h>
63 #include FT_FREETYPE_H
64 #include FT_OUTLINE_H
65 #include FT_GLYPH_H
66 #include FT_SFNT_NAMES_H
67 #include FT_TRUETYPE_IDS_H
68 #include FT_TRUETYPE_TAGS_H
69 #include FT_TRUETYPE_TABLES_H
70
71 #include <harfbuzz/hb.h>
72 #include <harfbuzz/hb-ot.h>
73 #include <harfbuzz/hb-ft.h>
74
75 #include "scpaths.h"
76 #include "util_debug.h"
77
78
79
80 /***************************************************************************/
81
SCFonts()82 SCFonts::SCFonts()
83 {
84 // insert("", ScFace::none()); // Wtf why inserting an empty entry here ????
85 m_showFontInfo = false;
86 m_checkedFonts.clear();
87 }
88
~SCFonts()89 SCFonts::~SCFonts()
90 {
91 }
92
updateFontMap()93 void SCFonts::updateFontMap()
94 {
95 fontMap.clear();
96 SCFontsIterator it( *this );
97 for ( ; it.hasNext(); it.next())
98 {
99 if (it.current().usable())
100 {
101 if (fontMap.contains(it.current().family()))
102 {
103 if (!fontMap[it.current().family()].contains(it.current().style()))
104 {
105 fontMap[it.current().family()].append(it.current().style());
106 }
107 }
108 else
109 {
110 QStringList styles;
111 styles.append(it.current().style());
112 fontMap.insert(it.current().family(), styles);
113 }
114 }
115 }
116 }
117
118 /* Add a path to the list of fontpaths, ensuring that
119 a trailing slash is present.
120 Checks to make sure this path is not already present
121 before adding.
122 */
addPath(QString p)123 void SCFonts::addPath(QString p)
124 {
125 if (p.right(1) != "/")
126 p += "/";
127 if (!m_fontPaths.contains(p))
128 m_fontPaths.insert(m_fontPaths.count(), p);
129 }
130
addScalableFonts(const QString & path,const QString & DocName)131 void SCFonts::addScalableFonts(const QString &path, const QString& DocName)
132 {
133 //Make sure this is not empty or we will scan the whole drive on *nix
134 //QString()+/ is / of course.
135 if (path.isEmpty())
136 return;
137 FT_Library library = nullptr;
138 QString pathfile, fullpath;
139 // bool error;
140 // error =
141 FT_Init_FreeType( &library );
142 QString pathname(path);
143 if ( !pathname.endsWith("/") )
144 pathname += "/";
145 pathname = QDir::toNativeSeparators(pathname);
146 QDir d(pathname, "*", QDir::Name, QDir::Dirs | QDir::Files | QDir::Readable);
147 if ((d.exists()) && (d.count() != 0))
148 {
149 for (uint i = 0; i < d.count(); ++i)
150 {
151 // readdir may return . or .., which we don't want to recurse
152 // over. Skip 'em.
153 if (d[i] == "." || d[i] == "..")
154 continue;
155 fullpath = pathname + d[i];
156 QFileInfo fi(fullpath);
157 if (!fi.exists()) // Sanity check for broken Symlinks
158 continue;
159
160 qApp->processEvents();
161
162 bool symlink = fi.isSymLink();
163 if (symlink)
164 {
165 QFileInfo fi3(fi.symLinkTarget());
166 if (fi3.isRelative())
167 pathfile = pathname + fi.symLinkTarget();
168 else
169 pathfile = fi3.absoluteFilePath();
170 }
171 else
172 pathfile = fullpath;
173 QFileInfo fi2(pathfile);
174 if (fi2.isDir())
175 {
176 if (symlink)
177 {
178 // Check if symlink points to a parent directory
179 // in order to avoid infinite recursion
180 QString fullpath2 = fullpath, pathfile2 = pathfile;
181 if (ScCore->isWinGUI())
182 {
183 // Ensure both path use same separators on Windows
184 fullpath2 = QDir::toNativeSeparators(fullpath2.toLower());
185 pathfile2 = QDir::toNativeSeparators(pathfile2.toLower());
186 }
187 if (fullpath2.startsWith(pathfile2))
188 continue;
189 }
190 if (DocName.isEmpty())
191 addScalableFonts(pathfile);
192 continue;
193 }
194 QString ext = fi.suffix().toLower();
195 QString ext2 = fi2.suffix().toLower();
196 if ((ext != ext2) && (ext.isEmpty()))
197 ext = ext2;
198 if ((ext == "ttc") || (ext == "dfont") || (ext == "pfa") || (ext == "pfb") || (ext == "ttf") || (ext == "otf"))
199 {
200 addScalableFont(pathfile, library, DocName);
201 }
202 #ifdef Q_OS_MAC
203 else if (ext.isEmpty() && DocName.isEmpty())
204 {
205 bool error = addScalableFont(pathfile, library, DocName);
206 if (error)
207 error = addScalableFont(pathfile + "/..namedfork/rsrc",library, DocName);
208 }
209 #endif
210 }
211 }
212 FT_Done_FreeType(library);
213 }
214
215
216 /*****
217 What to do with font files:
218 - note mod. date
219 - in FontCache? => load faces from cache
220 - load via FT => or broken
221 - for all faces:
222 load face
223 get fontinfo (type, names, styles, global metrics)
224 check encoding(s)
225 (calc. hash sum)
226 create cache entry
227 *****/
228
229
230 /**
231 * tests magic words to determine the fontformat and preliminary fonttype
232 */
getFontFormat(FT_Face face,ScFace::FontFormat & fmt,ScFace::FontType & type)233 void getFontFormat(FT_Face face, ScFace::FontFormat & fmt, ScFace::FontType & type)
234 {
235 static const char* T42_HEAD = "%!PS-TrueTypeFont";
236 static const char* T1_HEAD = "%!FontType1";
237 static const char* T1_ADOBE_HEAD = "%!PS-AdobeFont-1";
238 static const char* PSFONT_ADOBE2_HEAD = "%!PS-Adobe-2.0 Font";
239 static const char* PSFONT_ADOBE21_HEAD = "%!PS-Adobe-2.1 Font";
240 static const char* PSFONT_ADOBE3_HEAD = "%!PS-Adobe-3.0 Resource-Font";
241 static const int FONT_NO_ERROR = 0;
242
243 const FT_Stream fts = face->stream;
244 char buf[128];
245
246 fmt = ScFace::UNKNOWN_FORMAT;
247 type = ScFace::UNKNOWN_TYPE;
248 if (ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(buf), 128) == FONT_NO_ERROR)
249 {
250 if (strncmp(buf, T42_HEAD, strlen(T42_HEAD)) == 0)
251 {
252 fmt = ScFace::TYPE42;
253 type = ScFace::TTF;
254 }
255 else if (strncmp(buf, T1_HEAD, strlen(T1_HEAD)) == 0 ||
256 strncmp(buf, T1_ADOBE_HEAD, strlen(T1_ADOBE_HEAD)) == 0)
257 {
258 fmt = ScFace::PFA;
259 type = ScFace::TYPE1;
260 }
261 else if (strncmp(buf, PSFONT_ADOBE2_HEAD, strlen(PSFONT_ADOBE2_HEAD)) == 0 ||
262 strncmp(buf, PSFONT_ADOBE21_HEAD, strlen(PSFONT_ADOBE21_HEAD)) == 0 ||
263 strncmp(buf, PSFONT_ADOBE3_HEAD, strlen(PSFONT_ADOBE3_HEAD)) == 0)
264 {
265 // Type2(CFF), Type0(Composite/CID), Type 3, Type 14 etc would end here
266 fmt = ScFace::PFA;
267 type = ScFace::UNKNOWN_TYPE;
268 }
269 else if (buf[0] == '\200' && buf[1] == '\1')
270 {
271 fmt = ScFace::PFB;
272 type = ScFace::TYPE1;
273 }
274 else if (buf[0] == '\0' && buf[1] == '\1'
275 && buf[2] == '\0' && buf[3] == '\0')
276 {
277 fmt = ScFace::SFNT;
278 type = ScFace::TTF;
279 }
280 else if (strncmp(buf, "true", 4) == 0)
281 {
282 fmt = ScFace::SFNT;
283 type = ScFace::TTF;
284 }
285 else if (strncmp(buf, "ttcf", 4) == 0)
286 {
287 fmt = ScFace::TTCF;
288 type = ScFace::OTF;
289 }
290 else if (strncmp(buf, "OTTO", 4) == 0)
291 {
292 fmt = ScFace::SFNT;
293 type = ScFace::OTF;
294 }
295 // missing: raw CFF
296 }
297 }
298
299 /**
300 * Checks face, which must be sfnt based, for the subtype.
301 * Possible values: TTF, CFF, OTF
302 */
getSubFontType(FT_Face face,ScFace::FontType & type)303 void getSubFontType(FT_Face face, ScFace::FontType & type)
304 {
305 if (FT_IS_SFNT(face))
306 {
307 FT_ULong ret = 0;
308 bool hasGlyph = ! FT_Load_Sfnt_Table(face, TTAG_glyf, 0, nullptr, &ret);
309 hasGlyph &= ret > 0;
310 bool hasCFF = ! FT_Load_Sfnt_Table(face, TTAG_CFF, 0, nullptr, &ret);
311 hasCFF &= ret > 0;
312 if (hasGlyph)
313 type = ScFace::TTF;
314 else if (hasCFF)
315 type = ScFace::OTF;
316 // TODO: CID or no
317 }
318 }
319
320 // sort name records so the preferred ones come first
nameComp(const FT_SfntName a,const FT_SfntName b)321 static bool nameComp(const FT_SfntName a, const FT_SfntName b)
322 {
323 // sort preferred family first
324 if (a.name_id != b.name_id)
325 {
326 if (a.name_id == TT_NAME_ID_PREFERRED_FAMILY)
327 return true;
328 if (b.name_id == TT_NAME_ID_PREFERRED_FAMILY)
329 return false;
330 }
331
332 // sort Unicode platforms first
333 // preferring MS platform as it is more likely to
334 // not have bogus entries, being the more tested one.
335 if (a.platform_id != b.platform_id)
336 {
337 if (a.platform_id == TT_PLATFORM_MICROSOFT)
338 return true;
339 if (b.platform_id == TT_PLATFORM_MICROSOFT)
340 return false;
341 if (a.platform_id == TT_PLATFORM_APPLE_UNICODE)
342 return true;
343 if (b.platform_id == TT_PLATFORM_APPLE_UNICODE)
344 return false;
345 }
346
347 // sort Unicode encodings first
348 if (a.encoding_id != b.encoding_id)
349 {
350 if (a.platform_id == TT_PLATFORM_MICROSOFT)
351 {
352 if (a.encoding_id == TT_MS_ID_UCS_4)
353 return true;
354 if (b.encoding_id == TT_MS_ID_UCS_4)
355 return false;
356 if (a.encoding_id == TT_MS_ID_UNICODE_CS)
357 return true;
358 if (b.encoding_id == TT_MS_ID_UNICODE_CS)
359 return false;
360 }
361 }
362
363 // sort English names first
364 if (a.language_id != b.language_id)
365 {
366 if (a.platform_id == TT_PLATFORM_MICROSOFT)
367 {
368 if (a.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)
369 return true;
370 if (b.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)
371 return false;
372 }
373 }
374
375 // the rest is all the same for us
376 return false;
377 }
378
decodeNameRecord(FT_SfntName name)379 static QString decodeNameRecord(FT_SfntName name)
380 {
381 if (!name.string || name.string_len == 0)
382 return QString();
383
384 QByteArray encoding;
385 if (name.platform_id == TT_PLATFORM_APPLE_UNICODE)
386 {
387 encoding = "UTF-16BE";
388 }
389 else if (name.platform_id == TT_PLATFORM_MICROSOFT)
390 {
391 switch (name.encoding_id) {
392 case TT_MS_ID_SYMBOL_CS:
393 case TT_MS_ID_UNICODE_CS:
394 case TT_MS_ID_UCS_4:
395 encoding = "UTF-16BE";
396 break;
397 case TT_MS_ID_SJIS:
398 encoding = "Shift-JIS";
399 break;
400 case TT_MS_ID_GB2312:
401 encoding = "GB18030";
402 break;
403 case TT_MS_ID_BIG_5:
404 encoding = "Big5";
405 break;
406 default:
407 break;
408 }
409 }
410
411 if (encoding.isEmpty())
412 return QString();
413
414 QTextCodec* codec = QTextCodec::codecForName(encoding);
415 if (!codec)
416 return QString();
417
418 QByteArray bytes((const char*) name.string, name.string_len);
419 if (name.encoding_id == TT_MS_ID_GB2312 && bytes.startsWith('\0'))
420 {
421 // 16 bytes chars appears to be used as transport type for 8 bytes values.
422 // We have to fix this so that Qt GB18030 codec process strings correctly
423 for (int i = 0; i < (int) name.string_len / 2; ++i)
424 bytes[i] = bytes[2 * i + 1];
425 bytes.resize(name.string_len / 2);
426 }
427
428 QString string = codec->toUnicode(bytes);
429 return string;
430 }
431
getFamilyName(const FT_Face face)432 static QString getFamilyName(const FT_Face face)
433 {
434 QString familyName(face->family_name);
435
436 QVector<FT_SfntName> names;
437
438 for (FT_UInt i = 0; i < FT_Get_Sfnt_Name_Count(face); i++)
439 {
440 FT_SfntName name;
441 if (!FT_Get_Sfnt_Name(face, i, &name))
442 {
443 switch (name.name_id) {
444 case TT_NAME_ID_FONT_FAMILY:
445 case TT_NAME_ID_PREFERRED_FAMILY:
446 if (name.platform_id != TT_PLATFORM_MACINTOSH)
447 names.append(name);
448 break;
449 default:
450 break;
451 }
452 }
453 }
454
455 if (!names.isEmpty())
456 {
457 std::sort(names.begin(), names.end(), nameComp);
458 for (const FT_SfntName& name : qAsConst(names))
459 {
460 QString string(decodeNameRecord(name));
461 if (!string.isEmpty())
462 {
463 familyName = string;
464 break;
465 }
466 }
467 }
468
469 return familyName;
470 }
471
getFontFeaturesFromTable(hb_tag_t table,hb_face_t * hb_face)472 static QStringList getFontFeaturesFromTable(hb_tag_t table, hb_face_t *hb_face)
473 {
474 QStringList fontFeaturesList;
475 //get all supported Opentype Features
476 unsigned count = hb_ot_layout_table_get_feature_tags(hb_face, table, 0, nullptr, nullptr);
477 std::vector<hb_tag_t> features(count);
478 hb_ot_layout_table_get_feature_tags(hb_face, table, 0, &count, features.data());
479 for (unsigned i = 0; i < count; ++i)
480 {
481 char feature[4] = {0};
482 hb_tag_to_string(features[i], feature);
483 std::string strFeature(feature, 4);
484 fontFeaturesList.append(QString::fromStdString(strFeature));
485 }
486 fontFeaturesList.removeDuplicates();
487 fontFeaturesList.sort();
488 return fontFeaturesList;
489 }
490
getFontFeatures(const FT_Face face)491 static QStringList getFontFeatures(const FT_Face face)
492 {
493 // Create hb-ft font and get hb_face from it
494 hb_font_t *hb_font;
495 hb_font = hb_ft_font_create(face, nullptr);
496 hb_face_t *hb_face = hb_font_get_face(hb_font);
497 //find Opentype Font Features in GSUB table
498 QStringList featuresGSUB = getFontFeaturesFromTable(HB_OT_TAG_GSUB, hb_face);
499 // find Opentype Font Features in GPOS table
500 QStringList featuresGPOS = getFontFeaturesFromTable(HB_OT_TAG_GPOS, hb_face);
501
502 hb_font_destroy(hb_font);
503
504 QStringList fontFeatures = featuresGSUB + featuresGPOS;
505 fontFeatures.removeDuplicates();
506 fontFeatures.sort();
507 return fontFeatures;
508 }
509
loadScalableFont(const QString & filename)510 ScFace SCFonts::loadScalableFont(const QString &filename)
511 {
512 ScFace t;
513 if (filename.isEmpty())
514 return t;
515 FT_Library library = nullptr;
516 bool error;
517 error = FT_Init_FreeType( &library );
518 QFileInfo fi(filename);
519 if (!fi.exists())
520 return t;
521 bool Subset = false;
522 char *buf[50];
523 QString glyName = "";
524 ScFace::FontFormat format;
525 ScFace::FontType type;
526 FT_Face face = nullptr;
527 error = FT_New_Face(library, QFile::encodeName(filename), 0, &face);
528 if (error || (face == nullptr))
529 {
530 if (face != nullptr)
531 FT_Done_Face(face);
532 FT_Done_FreeType(library);
533 return t;
534 }
535 getFontFormat(face, format, type);
536 if (format == ScFace::UNKNOWN_FORMAT)
537 {
538 FT_Done_Face(face);
539 FT_Done_FreeType(library);
540 return t;
541 }
542 bool HasNames = FT_HAS_GLYPH_NAMES(face);
543
544 FT_UInt gindex = 0;
545 FT_ULong charcode = FT_Get_First_Char(face, &gindex);
546 while (gindex != 0)
547 {
548 error = FT_Load_Glyph(face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
549 if (error)
550 {
551 FT_Done_Face(face);
552 FT_Done_FreeType(library);
553 return t;
554 }
555 FT_Get_Glyph_Name(face, gindex, buf, 50);
556 QString newName = QString(reinterpret_cast<char*>(buf));
557 if (newName == glyName)
558 {
559 HasNames = false;
560 Subset = true;
561 }
562 glyName = newName;
563 charcode = FT_Get_Next_Char(face, charcode, &gindex);
564 }
565
566 // Warning: code below is also present in addScalableFont, so if you do
567 // any modification here, think also about modifying code in addScalableFont
568 int faceIndex = 0;
569 QString fam(getFamilyName(face));
570 QStringList features(getFontFeatures(face));
571 QString sty(face->style_name);
572 if ((sty == "Regular" && face->style_flags != 0) || sty.isEmpty())
573 {
574 switch (face->style_flags)
575 {
576 case 0:
577 sty = "Regular";
578 break;
579 case 1:
580 sty = "Italic";
581 break;
582 case 2:
583 sty = "Bold";
584 break;
585 case 3:
586 sty = "Bold Italic";
587 break;
588 default:
589 break;
590 }
591 }
592 QString fullName(fam);
593 if (!sty.isEmpty())
594 fullName += " " + sty;
595 const char* psName = FT_Get_Postscript_Name(face);
596 QString qpsName;
597 if (psName)
598 qpsName = QString(psName);
599 else
600 qpsName = fullName;
601 if (t.isNone())
602 {
603 switch (format)
604 {
605 case ScFace::PFA:
606 t = ScFace(new ScFace_PFA(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
607 t.subset(Subset);
608 break;
609 case ScFace::PFB:
610 t = ScFace(new ScFace_PFB(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
611 t.subset(Subset);
612 break;
613 case ScFace::SFNT:
614 case ScFace::TTCF:
615 case ScFace::TYPE42:
616 t = ScFace(new ScFace_ttf(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
617 getSubFontType(face, t.m_m->typeCode);
618 t.subset(Subset);
619 break;
620 default:
621 /* catching any types not handled above to silence compiler */
622 break;
623 }
624 t.m_m->hasGlyphNames = HasNames;
625 t.embedPs(true);
626 t.usable(true);
627 t.m_m->status = ScFace::UNKNOWN;
628 if (face->num_glyphs > 2048)
629 t.subset(true);
630 }
631 setBestEncoding(face);
632 FT_Done_Face(face);
633 FT_Done_FreeType(library);
634 return t;
635 }
636
getFtError(int code)637 static QString getFtError(int code)
638 {
639 #undef FTERRORS_H_
640 #define FT_ERRORDEF(e, v, s) ftErrors[e] = s;
641 static QHash<int, QString> ftErrors;
642 #include FT_ERRORS_H
643 #undef FT_ERRORDEF
644
645 if (ftErrors.contains(code))
646 return ftErrors.value(code);
647 return QString();
648 }
649
650 // Load a single font into the library from the passed filename. Returns true on error.
addScalableFont(const QString & filename,FT_Library & library,const QString & DocName)651 bool SCFonts::addScalableFont(const QString& filename, FT_Library &library, const QString& DocName)
652 {
653 static bool firstRun;
654 bool Subset = false;
655 char *buf[50];
656 QString glyName = "";
657 ScFace::FontFormat format;
658 ScFace::FontType type;
659 FT_Face face = nullptr;
660 struct testCache foCache;
661 QFileInfo fic(filename);
662 QDateTime lastMod = fic.lastModified();
663 QTime lastModTime = lastMod.time();
664 if (lastModTime.msec() != 0) //Sometime file time is stored with precision up to msecs
665 {
666 lastModTime.setHMS(lastModTime.hour(), lastModTime.minute(), lastModTime.second());
667 lastMod.setTime(lastModTime);
668 }
669 foCache.isOK = false;
670 foCache.isChecked = true;
671 foCache.lastMod = lastMod;
672 if (m_checkedFonts.count() == 0)
673 {
674 firstRun = true;
675 ScCore->setSplashStatus( QObject::tr("Creating Font Cache") );
676 }
677 FT_Error error = FT_New_Face( library, QFile::encodeName(filename), 0, &face );
678 if (error || (face == nullptr))
679 {
680 if (face != nullptr)
681 FT_Done_Face(face);
682 m_checkedFonts.insert(filename, foCache);
683 addRejectedFont(filename, QObject::tr("Font is broken: \"%1\"").arg(getFtError(error)));
684 if (m_showFontInfo)
685 sDebug(QObject::tr("Font %1 is broken, discarding it. Error message: \"%2\"").arg(filename, getFtError(error)));
686 return true;
687 }
688 if (face->family_name == nullptr)
689 {
690 addRejectedFont(filename, QObject::tr("Failed to load font: font family unspecified"));
691 if (m_showFontInfo)
692 sDebug(QObject::tr("Failed to load font %1 - font family unspecified").arg(filename));
693 FT_Done_Face(face);
694 m_checkedFonts.insert(filename, foCache);
695 return true;
696 }
697 getFontFormat(face, format, type);
698 if (format == ScFace::UNKNOWN_FORMAT)
699 {
700 addRejectedFont(filename, QObject::tr("Failed to load font: font type unknown"));
701 if (m_showFontInfo)
702 sDebug(QObject::tr("Failed to load font %1 - font type unknown").arg(filename));
703 FT_Done_Face(face);
704 m_checkedFonts.insert(filename, foCache);
705 return true;
706 }
707 // Some fonts such as Noto ColorEmoji are in fact bitmap fonts
708 // and do not provide a valid value for units_per_EM
709 if (face->units_per_EM == 0)
710 {
711 addRejectedFont(filename, QObject::tr("Failed to load font: font is not scalable"));
712 if (m_showFontInfo)
713 sDebug(QObject::tr("Failed to load font %1 - font is not scalable").arg(filename));
714 FT_Done_Face(face);
715 m_checkedFonts.insert(filename, foCache);
716 return true;
717 }
718 bool HasNames = FT_HAS_GLYPH_NAMES(face);
719
720 if (!m_checkedFonts.contains(filename))
721 {
722 if (!firstRun)
723 ScCore->setSplashStatus( QObject::tr("New Font found, checking...") );
724 FT_UInt gindex = 0;
725 FT_ULong charcode = FT_Get_First_Char( face, &gindex );
726 while ( gindex != 0 )
727 {
728 error = FT_Load_Glyph(face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
729 if (error)
730 {
731 auto errorMessage = QObject::tr("Font %1 has broken glyph %2 (charcode U+%3). Error message: \"%4\"")
732 .arg(filename)
733 .arg(gindex)
734 .arg(charcode, 4, 16, QChar('0'))
735 .arg(getFtError(error));
736 addRejectedFont(filename, errorMessage);
737 if (m_showFontInfo)
738 sDebug(errorMessage);
739 FT_Done_Face(face);
740 m_checkedFonts.insert(filename, foCache);
741 return true;
742 }
743 FT_Get_Glyph_Name(face, gindex, buf, 50);
744 QString newName = QString(reinterpret_cast<char*>(buf));
745 if (newName == glyName)
746 {
747 HasNames = false;
748 Subset = true;
749 }
750 glyName = newName;
751 charcode = FT_Get_Next_Char( face, charcode, &gindex );
752 }
753 foCache.isOK = true;
754 m_checkedFonts.insert(filename, foCache);
755 }
756 else
757 {
758 auto& checkedFont = m_checkedFonts[filename];
759 if (!checkedFont.isOK)
760 {
761 checkedFont.isChecked = true;
762 FT_Done_Face(face);
763 return true;
764 }
765 if (checkedFont.lastMod != foCache.lastMod)
766 {
767 ScCore->setSplashStatus( QObject::tr("Modified Font found, checking...") );
768 FT_UInt gindex = 0;
769 FT_ULong charcode = FT_Get_First_Char( face, &gindex );
770 while ( gindex != 0 )
771 {
772 error = FT_Load_Glyph(face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
773 if (error)
774 {
775 auto errorMessage = QObject::tr("Font %1 has broken glyph %2 (charcode U+%3). Error message: \"%4\"")
776 .arg(filename)
777 .arg(gindex)
778 .arg(charcode, 4, 16, QChar('0'))
779 .arg(getFtError(error));
780 addRejectedFont(filename, errorMessage);
781 if (m_showFontInfo)
782 sDebug(errorMessage);
783 FT_Done_Face(face);
784 m_checkedFonts.insert(filename, foCache);
785 return true;
786 }
787 FT_Get_Glyph_Name(face, gindex, buf, 50);
788 QString newName = QString(reinterpret_cast<char*>(buf));
789 if (newName == glyName)
790 {
791 HasNames = false;
792 Subset = true;
793 }
794 glyName = newName;
795 charcode = FT_Get_Next_Char( face, charcode, &gindex );
796 }
797 foCache.isOK = true;
798 checkedFont = foCache;
799 }
800 else
801 {
802 checkedFont.isOK = true;
803 checkedFont.isChecked = true;
804 }
805 }
806
807 // Warning: code below is also present in loadScalableFont, so if you do
808 // any modification here, think also about modifying code in loadScalableFont
809 int faceIndex = 0;
810 while (!error)
811 {
812 QString fam(getFamilyName(face));
813 QStringList features(getFontFeatures(face));
814 QString sty(face->style_name);
815 if ((sty == "Regular" && face->style_flags != 0) || sty.isEmpty())
816 {
817 switch (face->style_flags)
818 {
819 case 0:
820 sty = "Regular";
821 break;
822 case 1:
823 sty = "Italic";
824 break;
825 case 2:
826 sty = "Bold";
827 break;
828 case 3:
829 sty = "Bold Italic";
830 break;
831 default:
832 break;
833 }
834 }
835 QString fullName(fam);
836 if (!sty.isEmpty())
837 fullName += " " + sty;
838 const char* psName = FT_Get_Postscript_Name(face);
839 QString qpsName;
840 if (psName)
841 qpsName = QString(psName);
842 else
843 qpsName = fullName;
844 ScFace t;
845 if (contains(fullName))
846 {
847 t = (*this)[fullName];
848 if (t.psName() != qpsName)
849 {
850 QString alt = " (" + qpsName + ")";
851 fullName += alt;
852 sty += alt;
853 }
854 }
855 t = (*this)[fullName];
856 if (t.isNone())
857 {
858 switch (format)
859 {
860 case ScFace::PFA:
861 t = ScFace(new ScFace_PFA(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
862 t.subset(Subset);
863 break;
864 case ScFace::PFB:
865 t = ScFace(new ScFace_PFB(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
866 t.subset(Subset);
867 break;
868 case ScFace::SFNT:
869 t = ScFace(new ScFace_ttf(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
870 getSubFontType(face, t.m_m->typeCode);
871 t.subset(Subset);
872 break;
873 case ScFace::TTCF:
874 t = ScFace(new ScFace_ttf(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
875 t.m_m->formatCode = ScFace::TTCF;
876 t.m_m->typeCode = ScFace::TTF;
877 getSubFontType(face, t.m_m->typeCode);
878 t.subset(Subset);
879 break;
880 case ScFace::TYPE42:
881 t = ScFace(new ScFace_ttf(fam, sty, "", fullName, qpsName, filename, faceIndex, features));
882 getSubFontType(face, t.m_m->typeCode);
883 t.subset(Subset);
884 break;
885 default:
886 /* catching any types not handled above to silence compiler */
887 break;
888 }
889 insert(fullName, t);
890 t.m_m->hasGlyphNames = HasNames;
891 t.embedPs(true);
892 t.usable(true);
893 t.m_m->status = ScFace::UNKNOWN;
894 if (face->num_glyphs > 2048)
895 t.subset(true);
896 t.m_m->forDocument = DocName;
897 //setBestEncoding(face); //AV
898 if (m_showFontInfo)
899 sDebug(QObject::tr("Font %1 loaded from %2(%3)").arg(t.psName(), filename).arg(faceIndex + 1));
900
901 /*
902 //debug
903 QByteArray bb;
904 t->rawData(bb);
905 QFile dump(QString("/tmp/fonts/%1-%2").arg(fullName, psName));
906 dump.open(IO_WriteOnly);
907 QDataStream os(&dump);
908 os.writeRawBytes(bb.data(), bb.size());
909 dump.close();
910 */
911 }
912 else
913 {
914 if (m_showFontInfo)
915 sDebug(QObject::tr("Font %1(%2) is duplicate of %3").arg(filename).arg(faceIndex + 1).arg(t.fontPath()));
916 // this is needed since eg. AppleSymbols will happily return a face for *any* face_index
917 if (faceIndex > 0) {
918 break;
919 }
920 }
921 if ((++faceIndex) >= face->num_faces)
922 break;
923 FT_Done_Face(face);
924 face = nullptr;
925 error = FT_New_Face(library, QFile::encodeName(filename), faceIndex, &face);
926 } //while
927
928 if (face != nullptr)
929 FT_Done_Face(face);
930 return error && faceIndex == 0;
931 }
932
removeFont(const QString & name)933 void SCFonts::removeFont(const QString& name)
934 {
935 remove(name);
936 updateFontMap();
937 }
938
findFont(const QString & fontname,ScribusDoc * doc)939 const ScFace& SCFonts::findFont(const QString& fontname, ScribusDoc *doc)
940 {
941 if (fontname.isEmpty())
942 return ScFace::none();
943
944 PrefsManager& prefsManager = PrefsManager::instance();
945
946 if (!contains(fontname) || !(*this)[fontname].usable())
947 {
948 QString replFont;
949 if ((!prefsManager.appPrefs.fontPrefs.GFontSub.contains(fontname)) || (!(*this)[prefsManager.appPrefs.fontPrefs.GFontSub[fontname]].usable()))
950 {
951 replFont = doc ? doc->itemToolPrefs().textFont : prefsManager.appPrefs.itemToolPrefs.textFont;
952 }
953 else
954 replFont = prefsManager.appPrefs.fontPrefs.GFontSub[fontname];
955 ScFace repl = (*this)[replFont].mkReplacementFor(fontname, doc ? doc->documentFileName() : QString());
956 insert(fontname, repl);
957 }
958 else if ( doc && !doc->UsedFonts.contains(fontname) )
959 {
960 doc->AddFont(fontname, qRound(doc->itemToolPrefs().textSize / 10.0));
961 }
962 return (*this)[fontname];
963 }
964
965
findFont(const QString & fontFamily,const QString & fontStyle,ScribusDoc * doc)966 const ScFace& SCFonts::findFont(const QString& fontFamily, const QString& fontStyle, ScribusDoc* doc)
967 {
968 if (fontStyle.isEmpty())
969 return findFont(fontFamily + " Regular", doc);
970 return findFont(fontFamily + " " + fontStyle, doc);
971 }
972
getSubstitutions(const QList<QString> & skip) const973 QMap<QString,QString> SCFonts::getSubstitutions(const QList<QString>& skip) const
974 {
975 QMap<QString,QString> result;
976 QMap<QString,ScFace>::ConstIterator it;
977 for (it = begin(); it != end(); ++it)
978 {
979 if (it.value().isReplacement() && !skip.contains(it.key()))
980 result[it.key()] = it.value().replacementName();
981 }
982 return result;
983 }
984
985
setSubstitutions(const QMap<QString,QString> & substitutes,ScribusDoc * doc)986 void SCFonts::setSubstitutions(const QMap<QString,QString>& substitutes, ScribusDoc* doc)
987 {
988 QMap<QString,QString>::ConstIterator it;
989 for (it = substitutes.begin(); it != substitutes.end(); ++it)
990 {
991 if (it.key() == it.value())
992 continue;
993 ScFace& font(const_cast<ScFace&>(findFont(it.key(), doc)));
994 if (font.isReplacement())
995 {
996 font.chReplacementTo(const_cast<ScFace&>(findFont(it.value(), doc)), doc->documentFileName());
997 }
998 }
999 }
1000
1001
1002 #ifdef HAVE_FONTCONFIG
1003 // Use Fontconfig to locate and load fonts.
addFontconfigFonts()1004 void SCFonts::addFontconfigFonts()
1005 {
1006 // All-in-one library setup. Perhaps this should be in
1007 // the SCFonts constructor.
1008 FcConfig* config = FcInitLoadConfigAndFonts();
1009 // The pattern controls what fonts to match. In this case we want to
1010 // match all scalable fonts, but ignore bitmap fonts.
1011 FcPattern* pat = FcPatternBuild(nullptr,
1012 FC_SCALABLE, FcTypeBool, true,
1013 nullptr);
1014 // The ObjectSet tells FontConfig what information about each match to return.
1015 // We currently just need FC_FILE, but other info like font family and style
1016 // is available - see "man fontconfig".
1017 FcObjectSet* os = FcObjectSetBuild (FC_FILE, (char *) nullptr);
1018 if (!os)
1019 {
1020 qFatal("SCFonts::addFontconfigFonts() FcObjectSet* os failed to build object set");
1021 return;
1022 }
1023 // Now ask fontconfig to retrieve info as specified in 'os' about fonts
1024 // matching pattern 'pat'.
1025 FcFontSet* fs = FcFontList(config, pat, os);
1026 if (!fs)
1027 {
1028 qFatal("SCFonts::addFontconfigFonts() FcFontSet* fs failed to create font list");
1029 return;
1030 }
1031 FcConfigDestroy(config);
1032 FcObjectSetDestroy(os);
1033 FcPatternDestroy(pat);
1034 // Create the Freetype library
1035 // bool error;
1036 FT_Library library = nullptr;
1037 FT_Init_FreeType( &library );
1038 // Now iterate over the font files and load them
1039 for (int i = 0; i < fs->nfont; i++)
1040 {
1041 FcChar8 *file = nullptr;
1042 if (FcPatternGetString (fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch)
1043 {
1044 if (m_showFontInfo)
1045 sDebug(QObject::tr("Loading font %1 (found using fontconfig)").arg(QString((char*)file)));
1046 addScalableFont(QString((char*)file), library, "");
1047 }
1048 else
1049 if (m_showFontInfo)
1050 {
1051 auto errorMessage = QObject::tr("Failed to load a font - freetype2 couldn't find the font file");
1052 addRejectedFont(QString((char*)file), errorMessage);
1053 sDebug(errorMessage);
1054 }
1055 }
1056 FT_Done_FreeType(library);
1057 FcFontSetDestroy(fs);
1058 }
1059
1060 #elif defined(Q_OS_WIN32)
1061
addRegistryFonts()1062 void SCFonts::addRegistryFonts()
1063 {
1064 const QStringList keys = { QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"),
1065 QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"),
1066 #if defined(Q_OS_WIN64)
1067 QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"),
1068 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"),
1069 #endif
1070 };
1071 QSet<QString> foundFonts;
1072
1073 FT_Library library = nullptr;
1074 FT_Init_FreeType( &library );
1075
1076 for (const auto key : keys)
1077 {
1078 const QSettings fontRegistry(key, QSettings::NativeFormat);
1079 const QStringList allKeys = fontRegistry.allKeys();
1080
1081 int size = allKeys.size();
1082 for (int i = 0; i < size; ++i)
1083 {
1084 const QString &fontKey = allKeys.at(i);
1085 const QString fileName = fontRegistry.value(fontKey).toString();
1086
1087 QFileInfo fontInfo(fileName);
1088 if (fontInfo.isRelative())
1089 continue; // Font located in system font directory, nothing to do
1090 if (!fontInfo.exists())
1091 continue; // Sanity check for broken Symlinks
1092 QString fontPath = fontInfo.absoluteFilePath();
1093 QString pathName = fontInfo.absolutePath();
1094
1095 qApp->processEvents();
1096
1097 bool isSymlink = fontInfo.isSymLink();
1098 if (isSymlink)
1099 {
1100 QFileInfo symlinkInfo(fontInfo.symLinkTarget());
1101 if (symlinkInfo.isRelative())
1102 fontPath = pathName + fontInfo.symLinkTarget();
1103 else
1104 fontPath = symlinkInfo.absoluteFilePath();
1105 }
1106
1107 fontPath = QDir::toNativeSeparators(fontPath);
1108 if (foundFonts.contains(fontPath))
1109 continue;
1110
1111 QFileInfo fontInfo2(fontPath);
1112 QString ext = fontInfo.suffix().toLower();
1113 QString ext2 = fontInfo2.suffix().toLower();
1114 if ((ext != ext2) && (ext.isEmpty()))
1115 ext = ext2;
1116 if ((ext == "ttc") || (ext == "dfont") || (ext == "pfa") || (ext == "pfb") || (ext == "ttf") || (ext == "otf"))
1117 {
1118 foundFonts.insert(fontPath);
1119 addScalableFont(fontPath, library, QString());
1120 }
1121 }
1122 }
1123
1124 FT_Done_FreeType(library);
1125 }
1126
addType1RegistryFonts()1127 void SCFonts::addType1RegistryFonts()
1128 {
1129 const QStringList keys = { QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Type 1 Installer\\Type 1 Fonts"),
1130 QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Type 1 Installer\\Type 1 Fonts"),
1131 #if defined(Q_OS_WIN64)
1132 QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Type 1 Installer\\Type 1 Fonts"),
1133 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Type 1 Installer\\Type 1 Fonts"),
1134 #endif
1135 };
1136 QSet<QString> foundFonts;
1137
1138 FT_Library library = nullptr;
1139 FT_Init_FreeType( &library );
1140
1141 for (const auto key : keys)
1142 {
1143 const QSettings fontRegistry(key, QSettings::NativeFormat);
1144 const QStringList allKeys = fontRegistry.allKeys();
1145
1146 int size = allKeys.size();
1147 for (int i = 0; i < size; ++i)
1148 {
1149 const QString &fontKey = allKeys.at(i);
1150 const QStringList fileNames = fontRegistry.value(fontKey).toStringList();
1151 if (fileNames.size() <= 0)
1152 continue;
1153 if (fileNames.at(0) != "T")
1154 continue;
1155
1156 for (int j = 1; j < fileNames.count(); ++j)
1157 {
1158 QString fileName = fileNames.at(j);
1159
1160 QFileInfo fontInfo(fileName);
1161 if (fontInfo.isRelative())
1162 continue; // Font located in system font directory, nothing to do
1163 if (!fontInfo.exists())
1164 continue; // Sanity check for broken Symlinks
1165
1166 QString ext = fontInfo.suffix().toLower();
1167 if ((ext != "pfa") && (ext != "pfb"))
1168 continue;
1169
1170 QString fontPath = fontInfo.absoluteFilePath();
1171 QString pathName = fontInfo.absolutePath();
1172
1173 qApp->processEvents();
1174
1175 bool isSymlink = fontInfo.isSymLink();
1176 if (isSymlink)
1177 {
1178 QFileInfo symlinkInfo(fontInfo.symLinkTarget());
1179 if (symlinkInfo.isRelative())
1180 fontPath = pathName + fontInfo.symLinkTarget();
1181 else
1182 fontPath = symlinkInfo.absoluteFilePath();
1183 }
1184
1185 fontPath = QDir::toNativeSeparators(fontPath);
1186 if (foundFonts.contains(fontPath))
1187 continue;
1188
1189 QFileInfo fontInfo2(fontPath);
1190 QString ext2 = fontInfo2.suffix().toLower();
1191 if ((ext != ext2) && (ext.isEmpty()))
1192 ext = ext2;
1193 if ((ext == "pfa") || (ext == "pfb"))
1194 {
1195 foundFonts.insert(fontPath);
1196 addScalableFont(fontPath, library, QString());
1197 break;
1198 }
1199 }
1200 }
1201 }
1202
1203 FT_Done_FreeType(library);
1204 }
1205
1206 #endif
1207
1208 /* Add an extra path to our font search path,
1209 * allowing a user to have extra fonts installed
1210 * only for this user. Can also be used also as an emergency
1211 * fallback if no suitable fonts are found elsewere */
addUserPath(const QString & pf)1212 void SCFonts::addUserPath(const QString& pf)
1213 {
1214 PrefsContext *pc = PrefsManager::instance().prefsFile->getContext("Fonts");
1215 PrefsTable *extraDirs = pc->getTable("ExtraFontDirs");
1216 for (int i = 0; i < extraDirs->getRowCount(); ++i)
1217 addPath(extraDirs->get(i, 0));
1218 }
1219
readFontCache(const QString & pf)1220 void SCFonts::readFontCache(const QString& pf)
1221 {
1222 QFile fr(pf + "/cfonts.xml");
1223 QFileInfo fir(fr);
1224 if (fir.exists())
1225 fr.remove();
1226 m_checkedFonts.clear();
1227 struct testCache foCache;
1228 QDomDocument docu("fontcacherc");
1229 QFile f(pf + "/checkfonts150.xml");
1230 if (!f.open(QIODevice::ReadOnly))
1231 return;
1232 ScCore->setSplashStatus( QObject::tr("Reading Font Cache") );
1233 QTextStream ts(&f);
1234 ts.setCodec("UTF-8");
1235 QString errorMsg;
1236 int errorLine = 0, errorColumn = 0;
1237 if ( !docu.setContent(ts.readAll(), &errorMsg, &errorLine, &errorColumn) )
1238 {
1239 f.close();
1240 return;
1241 }
1242 f.close();
1243 QDomElement elem = docu.documentElement();
1244 if (elem.tagName() != "CachedFonts")
1245 return;
1246 QDomNode DOC = elem.firstChild();
1247 while (!DOC.isNull())
1248 {
1249 QDomElement dc = DOC.toElement();
1250 if (dc.tagName()=="Font")
1251 {
1252 foCache.isChecked = false;
1253 foCache.isOK = static_cast<bool>(dc.attribute("Status", "1").toInt());
1254 foCache.lastMod = QDateTime::fromString(dc.attribute("Modified"), Qt::ISODate);
1255 m_checkedFonts.insert(dc.attribute("File"), foCache);
1256 }
1257 DOC = DOC.nextSibling();
1258 }
1259 }
1260
writeFontCache()1261 void SCFonts::writeFontCache()
1262 {
1263 QString prefsLocation = PrefsManager::instance().preferencesLocation();
1264 writeFontCache(prefsLocation);
1265 }
1266
writeFontCache(const QString & pf)1267 void SCFonts::writeFontCache(const QString& pf)
1268 {
1269 QDomDocument docu("fontcacherc");
1270 QString st="<CachedFonts></CachedFonts>";
1271
1272 docu.setContent(st);
1273 QDomElement elem = docu.documentElement();
1274 for (auto it = m_checkedFonts.cbegin(); it != m_checkedFonts.cend(); ++it)
1275 {
1276 const auto& checkedFont = it.value();
1277
1278 bool saveItem = checkedFont.isChecked;
1279 if (!checkedFont.isChecked) // Font might be located in another local Scribus font folder
1280 saveItem = QFile::exists(it.key());
1281 if (!saveItem)
1282 continue;
1283
1284 QDomElement fosu = docu.createElement("Font");
1285 fosu.setAttribute("File", it.key());
1286 fosu.setAttribute("Status", static_cast<int>(checkedFont.isOK));
1287 fosu.setAttribute("Modified", checkedFont.lastMod.toString(Qt::ISODate));
1288 elem.appendChild(fosu);
1289 }
1290
1291 ScCore->setSplashStatus( QObject::tr("Writing updated Font Cache") );
1292
1293 QFile file(pf + "/checkfonts150.xml");
1294 if (!file.open(QIODevice::WriteOnly))
1295 return;
1296 QTextStream s(&file);
1297 s.setCodec("UTF-8");
1298 s << docu.toString();
1299 file.close();
1300 }
1301
getFonts(const QString & pf,bool showFontInfo)1302 void SCFonts::getFonts(const QString& pf, bool showFontInfo)
1303 {
1304 m_showFontInfo=showFontInfo;
1305 m_fontPaths.clear();
1306 readFontCache(pf);
1307 ScCore->setSplashStatus( QObject::tr("Searching for Fonts") );
1308 addUserPath(pf);
1309
1310 // Search the system paths
1311 QStringList ftDirs = ScPaths::systemFontDirs();
1312 for (int i = 0; i < ftDirs.count(); i++)
1313 addScalableFonts( ftDirs[i] );
1314
1315 #ifdef Q_OS_WIN32
1316 // Search fonts outside system paths using Windows Registry
1317 addRegistryFonts();
1318 addType1RegistryFonts();
1319 #endif
1320
1321 // Search Scribus font path
1322 if (!ScPaths::instance().fontDir().isEmpty() && QDir(ScPaths::instance().fontDir()).exists())
1323 addScalableFonts( ScPaths::instance().fontDir() );
1324
1325 //Add downloaded user fonts
1326 QString userFontDir(ScPaths::instance().userFontDir(false));
1327 if (QDir(userFontDir).exists())
1328 addScalableFonts( userFontDir );
1329
1330 // if fontconfig is there, it does all the work
1331 #if HAVE_FONTCONFIG
1332 // Search fontconfig paths
1333 QStringList::iterator fpi, fpend = m_fontPaths.end();
1334 for (fpi = m_fontPaths.begin() ; fpi != fpend; ++fpi)
1335 addScalableFonts(*fpi);
1336 addFontconfigFonts();
1337 #else
1338 // add user and X11 fonts:
1339 QStringList::iterator fpi, fpend = m_fontPaths.end();
1340 for (fpi = m_fontPaths.begin() ; fpi != fpend; ++fpi)
1341 addScalableFonts(*fpi);
1342 #endif
1343 updateFontMap();
1344 writeFontCache(pf);
1345 }
1346
addRejectedFont(const QString & fontPath,const QString & message)1347 void SCFonts::addRejectedFont(const QString& fontPath, const QString& message)
1348 {
1349 rejectedFonts.insert(fontPath, message);
1350 }
1351