Subversion Repositories Scribus

Rev

Rev 22237 | Rev 24096 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 paul 1
/*
4430 cbradney 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
/*
3 paul 8
        Scribus font handling code - copyright (C) 2001 by
9
        Alastair M. Robinson
10
 
220 Franz 11
        Based on code by Christian Tpp and Franz Schmid.
3 paul 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
 
10152 cbradney 18
#include <QApplication>
10181 cbradney 19
#include <QDir>
10152 cbradney 20
#include <QDomDocument>
21
#include <QFile>
22
#include <QFileInfo>
10181 cbradney 23
#include <QFont>
10152 cbradney 24
#include <QGlobalStatic>
25
#include <QMap>
26
#include <QRegExp>
10181 cbradney 27
#include <QString>
1669 craig 28
 
10181 cbradney 29
 
3 paul 30
#include <cstdlib>
31
#include "scfonts.h"
5980 avox 32
#include "fonts/ftface.h"
33
#include "fonts/scface_ps.h"
34
#include "fonts/scface_ttf.h"
35
#include "fonts/scfontmetrics.h"
494 fschmid 36
 
2856 cbradney 37
#include "prefsmanager.h"
2510 craig 38
#include "prefsfile.h"
39
#include "prefscontext.h"
40
#include "prefstable.h"
3566 fschmid 41
#include "scribus.h"
5243 cbradney 42
#include "scribuscore.h"
2529 craig 43
#ifdef Q_WS_X11
44
#include <X11/X.h>
45
#include <X11/Xlib.h>
46
#endif
47
 
494 fschmid 48
#ifdef HAVE_FONTCONFIG
49
#include <fontconfig/fontconfig.h>
50
#endif
51
 
2295 cbradney 52
#include FT_TRUETYPE_TAGS_H
53
#include FT_TRUETYPE_TABLES_H
54
 
5700 avox 55
 
5980 avox 56
#include "scpaths.h"
57
#include "util.h"
5283 avox 58
 
5700 avox 59
 
3 paul 60
 
61
/***************************************************************************/
62
 
8594 jghali 63
SCFonts::SCFonts() : QMap<QString,ScFace>(), FontPath()
3559 fschmid 64
{
11256 fschmid 65
//      insert("", ScFace::none()); // Wtf why inserting an empty entry here ????
3559 fschmid 66
        showFontInformation=false;
67
        checkedFonts.clear();
68
}
69
 
3 paul 70
SCFonts::~SCFonts()
71
{
72
}
73
 
2508 fschmid 74
void SCFonts::updateFontMap()
75
{
76
        fontMap.clear();
77
        SCFontsIterator it( *this );
5980 avox 78
        for ( ; it.hasNext(); it.next())
2508 fschmid 79
        {
5980 avox 80
                if (it.current().usable())
2508 fschmid 81
                {
5980 avox 82
                        if (fontMap.contains(it.current().family()))
2508 fschmid 83
                        {
5980 avox 84
                                if (!fontMap[it.current().family()].contains(it.current().style()))
2508 fschmid 85
                                {
5980 avox 86
                                        fontMap[it.current().family()].append(it.current().style());
2508 fschmid 87
                                }
88
                        }
89
                        else
90
                        {
5980 avox 91
                                QStringList styles;
92
                                styles.append(it.current().style());
93
                                fontMap.insert(it.current().family(), styles);
2508 fschmid 94
                        }
95
                }
96
        }
97
}
98
 
3 paul 99
/* Add a path to the list of fontpaths, ensuring that
100
   a trailing slash is present.
101
   Checks to make sure this path is not already present
102
   before adding.
103
*/
104
void SCFonts::AddPath(QString p)
105
{
106
        if(p.right(1) != "/")
107
          p += "/";
8594 jghali 108
        if(!FontPath.contains(p))
3 paul 109
                FontPath.insert(FontPath.count(),p);
110
}
111
 
494 fschmid 112
void SCFonts::AddScalableFonts(const QString &path, QString DocName)
3 paul 113
{
3831 cbradney 114
        //Make sure this is not empty or we will scan the whole drive on *nix
115
        //QString::null+/ is / of course.
116
        if (path.isEmpty())
117
                return;
494 fschmid 118
        FT_Library library = NULL;
12812 jghali 119
        QString pathfile, fullpath;
68 Franz 120
        bool error;
3566 fschmid 121
        error = FT_Init_FreeType( &library );
3831 cbradney 122
        QString pathname(path);
3830 cbradney 123
        if ( !pathname.endsWith("/") )
124
                pathname += "/";
12812 jghali 125
        pathname=QDir::toNativeSeparators(pathname);
3830 cbradney 126
        QDir d(pathname, "*", QDir::Name, QDir::Dirs | QDir::Files | QDir::Readable);
68 Franz 127
        if ((d.exists()) && (d.count() != 0))
494 fschmid 128
        {
129
                for (uint dc = 0; dc < d.count(); ++dc)
3 paul 130
                {
2919 craig 131
                        // readdir may return . or .., which we don't want to recurse
132
                        // over. Skip 'em.
133
                        if (d[dc] == "." || d[dc] == "..")
134
                                continue;
12812 jghali 135
                        fullpath = pathname+d[dc];
136
                        QFileInfo fi(fullpath);
84 Franz 137
                        if (!fi.exists())      // Sanity check for broken Symlinks
138
                                continue;
2295 cbradney 139
 
140
                        qApp->processEvents();
141
 
12812 jghali 142
                        bool symlink = fi.isSymLink();
143
                        if (symlink)
494 fschmid 144
                        {
84 Franz 145
                                QFileInfo fi3(fi.readLink());
146
                                if (fi3.isRelative())
3830 cbradney 147
                                        pathfile = pathname+fi.readLink();
84 Franz 148
                                else
10561 fschmid 149
                                        pathfile = fi3.absoluteFilePath();
494 fschmid 150
                        }
84 Franz 151
                        else
12812 jghali 152
                                pathfile = fullpath;
84 Franz 153
                        QFileInfo fi2(pathfile);
2295 cbradney 154
                        if (fi2.isDir())
155
                        {
12812 jghali 156
                                if (symlink)
157
                                {
158
                                        // Check if symlink points to a parent directory
159
                                        // in order to avoid infinite recursion
160
                                        QString fullpath2 = fullpath, pathfile2 = pathfile;
161
                                        if (ScCore->isWinGUI())
162
                                        {
163
                                                // Ensure both path use same separators on Windows
164
                                                fullpath2 = QDir::toNativeSeparators(fullpath2.toLower());
165
                                                pathfile2 = QDir::toNativeSeparators(pathfile2.toLower());
166
                                        }
167
                                        if (fullpath2.startsWith(pathfile2))
168
                                                continue;
169
                                }
2877 cbradney 170
                                if (DocName.isEmpty())
2295 cbradney 171
                                        AddScalableFonts(pathfile);
172
                                continue;
173
                        }
10561 fschmid 174
                        QString ext = fi.suffix().toLower();
175
                        QString ext2 = fi2.suffix().toLower();
2877 cbradney 176
                        if ((ext != ext2) && (ext.isEmpty()))
177
                                ext = ext2;
2295 cbradney 178
                        if ((ext == "ttc") || (ext == "dfont") || (ext == "pfa") || (ext == "pfb") || (ext == "ttf") || (ext == "otf"))
414 Franz 179
                        {
2295 cbradney 180
                                error = AddScalableFont(pathfile, library, DocName);
414 Franz 181
                        }
5980 avox 182
#ifdef Q_OS_MAC
2877 cbradney 183
                        else if (ext.isEmpty() && DocName.isEmpty())
2295 cbradney 184
                        {
185
                                error = AddScalableFont(pathfile, library, DocName);
186
                                if (error)
8380 avox 187
                                        error = AddScalableFont(pathfile + "/..namedfork/rsrc",library, DocName);
2295 cbradney 188
                        }
189
#endif                          
3 paul 190
                }
494 fschmid 191
        }
68 Franz 192
        FT_Done_FreeType( library );
3 paul 193
}
194
 
5700 avox 195
 
196
/*****
197
   What to do with font files:
198
   - note mod. date
199
   - in FontCache?  => load faces from cache
200
   - load via FT    => or broken
201
   - for all faces:
202
       load face
203
       get fontinfo (type, names, styles, global metrics)
204
       check encoding(s)
205
       (calc. hash sum)
206
       create cache entry
207
 *****/
208
 
209
 
2295 cbradney 210
/**
211
 * tests magic words to determine the fontformat and preliminary fonttype
212
 */
5980 avox 213
void getFontFormat(FT_Face face, ScFace::FontFormat & fmt, ScFace::FontType & type)
494 fschmid 214
{
2295 cbradney 215
        static const char* T42_HEAD      = "%!PS-TrueTypeFont";
216
        static const char* T1_HEAD      = "%!FontType1";
217
        static const char* T1_ADOBE_HEAD = "%!PS-AdobeFont-1";
218
        static const char* PSFONT_ADOBE2_HEAD  = "%!PS-Adobe-2.0 Font";
219
        static const char* PSFONT_ADOBE21_HEAD  = "%!PS-Adobe-2.1 Font";
220
        static const char* PSFONT_ADOBE3_HEAD  = "%!PS-Adobe-3.0 Resource-Font";
2730 cbradney 221
        static const int   FONT_NO_ERROR = 0;
2295 cbradney 222
 
223
        const FT_Stream fts = face->stream;
224
        char buf[128];
225
 
5980 avox 226
        fmt = ScFace::UNKNOWN_FORMAT;
227
        type = ScFace::UNKNOWN_TYPE;
5318 avox 228
        if (ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(buf), 128) == FONT_NO_ERROR)
494 fschmid 229
        {
2295 cbradney 230
                if(strncmp(buf,T42_HEAD,strlen(T42_HEAD)) == 0)
1321 fschmid 231
                {
5980 avox 232
                        fmt = ScFace::TYPE42;
233
                        type = ScFace::TTF;
1321 fschmid 234
                }
2295 cbradney 235
                else if(strncmp(buf,T1_HEAD,strlen(T1_HEAD)) == 0 ||
236
                            strncmp(buf,T1_ADOBE_HEAD,strlen(T1_ADOBE_HEAD)) == 0)
1321 fschmid 237
                {
5980 avox 238
                        fmt = ScFace::PFA;
239
                        type = ScFace::TYPE1;
1321 fschmid 240
                }
2295 cbradney 241
                else if(strncmp(buf,PSFONT_ADOBE2_HEAD,strlen(PSFONT_ADOBE2_HEAD)) == 0 ||
242
                            strncmp(buf,PSFONT_ADOBE21_HEAD,strlen(PSFONT_ADOBE21_HEAD)) == 0 ||
243
                            strncmp(buf,PSFONT_ADOBE3_HEAD,strlen(PSFONT_ADOBE3_HEAD)) ==0)
1321 fschmid 244
                {
2295 cbradney 245
                        // Type2(CFF), Type0(Composite/CID), Type 3, Type 14 etc would end here
5980 avox 246
                        fmt = ScFace::PFA;
247
                        type = ScFace::UNKNOWN_TYPE;
1321 fschmid 248
                }
2295 cbradney 249
                else if(buf[0] == '\200' && buf[1] == '\1')
1321 fschmid 250
                {
5980 avox 251
                        fmt = ScFace::PFB;
252
                        type = ScFace::TYPE1;
1321 fschmid 253
                }
2295 cbradney 254
                else if(buf[0] == '\0' && buf[1] == '\1'
255
                                && buf[2] == '\0' && buf[3] == '\0')
494 fschmid 256
                {
5980 avox 257
                        fmt = ScFace::SFNT;
258
                        type = ScFace::TTF;
2295 cbradney 259
                }
260
                else if(strncmp(buf,"true",4) == 0)
261
                {
5980 avox 262
                        fmt = ScFace::SFNT;
263
                        type = ScFace::TTF;
2295 cbradney 264
                }
265
                else if(strncmp(buf,"ttcf",4) == 0)
266
                {
5980 avox 267
                        fmt = ScFace::TTCF;
268
                        type = ScFace::OTF;
2295 cbradney 269
                }
270
                else if(strncmp(buf,"OTTO",4) == 0)
271
                {
5980 avox 272
                        fmt = ScFace::SFNT;
273
                        type = ScFace::OTF;
2295 cbradney 274
                }
275
                // missing: raw CFF
276
        }
277
}
278
 
279
/**
280
 * Checks face, which must be sfnt based, for the subtype.
281
 * Possible values: TTF, CFF, OTF
282
 */
5980 avox 283
void getSFontType(FT_Face face, ScFace::FontType & type)
2295 cbradney 284
{
285
        if (FT_IS_SFNT(face))
286
        {
287
                FT_ULong ret = 0;
288
                bool hasGlyph = ! FT_Load_Sfnt_Table(face, TTAG_glyf, 0, NULL,  &ret);
289
                hasGlyph &= ret > 0;
290
                bool hasCFF = ! FT_Load_Sfnt_Table(face, TTAG_CFF, 0, NULL,  &ret);
291
                hasCFF &= ret > 0;
292
                if (hasGlyph)
5980 avox 293
                        type = ScFace::TTF;
2295 cbradney 294
                else if (hasCFF)
5980 avox 295
                        type = ScFace::OTF;
2295 cbradney 296
                // TODO: CID or no
297
        }
298
}
299
 
300
// Load a single font into the library from the passed filename. Returns true on error.
301
bool SCFonts::AddScalableFont(QString filename, FT_Library &library, QString DocName)
302
{
3566 fschmid 303
        static bool firstRun;
3633 fschmid 304
        bool Subset = false;
305
        char *buf[50];
306
        QString glyName = "";
5980 avox 307
        ScFace::FontFormat format;
308
        ScFace::FontType   type;
2295 cbradney 309
        FT_Face         face = NULL;
3559 fschmid 310
        struct testCache foCache;
311
        QFileInfo fic(filename);
14487 jghali 312
        QDateTime lastMod = fic.lastModified();
313
        QTime lastModTime = lastMod.time();
314
        if (lastModTime.msec() != 0)  //Sometime file time is stored with precision up to msecs
315
        {
316
                lastModTime.setHMS(lastModTime.hour(), lastModTime.minute(), lastModTime.second());
317
                lastMod.setTime(lastModTime);
318
        }
3559 fschmid 319
        foCache.isOK = false;
320
        foCache.isChecked = true;
14487 jghali 321
        foCache.lastMod = lastMod;
3566 fschmid 322
        if (checkedFonts.count() == 0)
323
        {
324
                firstRun = true;
5243 cbradney 325
                ScCore->setSplashStatus( QObject::tr("Creating Font Cache") );
3566 fschmid 326
        }
5980 avox 327
        bool error = FT_New_Face( library, QFile::encodeName(filename), 0, &face );
2295 cbradney 328
        if (error)
329
        {
330
                if (face != NULL)
331
                        FT_Done_Face( face );
3559 fschmid 332
                checkedFonts.insert(filename, foCache);
5018 avox 333
                if (showFontInformation)
334
                        sDebug(QObject::tr("Font %1 is broken, discarding it").arg(filename));
2295 cbradney 335
                return true;
336
        }
337
        getFontFormat(face, format, type);
5980 avox 338
        if (format == ScFace::UNKNOWN_FORMAT)
2295 cbradney 339
        {
3474 cbradney 340
                if (showFontInformation)
341
                        sDebug(QObject::tr("Failed to load font %1 - font type unknown").arg(filename));
22237 jghali 342
                FT_Done_Face(face);
3559 fschmid 343
                checkedFonts.insert(filename, foCache);
22237 jghali 344
                return true;
2295 cbradney 345
        }
22236 jghali 346
        // Some fonts such as Noto ColorEmoji are in fact bitmap fonts
347
        // and do not provide a valid value for units_per_EM
348
        if (face->units_per_EM == 0)
349
        {
350
                if (showFontInformation)
351
                        sDebug(QObject::tr("Failed to load font %1 - font is not scalable").arg(filename));
22237 jghali 352
                FT_Done_Face(face);
22236 jghali 353
                checkedFonts.insert(filename, foCache);
22237 jghali 354
                return true;
22236 jghali 355
        }
3633 fschmid 356
        bool HasNames = FT_HAS_GLYPH_NAMES(face);
22237 jghali 357
 
358
        if (!checkedFonts.contains(filename))
3469 fschmid 359
        {
22237 jghali 360
                if (!firstRun)
361
                        ScCore->setSplashStatus( QObject::tr("New Font found, checking...") );
362
                FT_UInt gindex = 0;
363
                FT_ULong charcode = FT_Get_First_Char( face, &gindex );
364
                while ( gindex != 0 )
3469 fschmid 365
                {
22237 jghali 366
                        error = FT_Load_Glyph( face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
367
                        if (error)
368
                        {
369
                                if (showFontInformation)
370
                                        sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(filename).arg(gindex).arg(charcode));
371
                                if (face != NULL)
372
                                        FT_Done_Face( face );
373
                                checkedFonts.insert(filename, foCache);
374
                                return true;
375
                        }
376
                        FT_Get_Glyph_Name(face, gindex, buf, 50);
377
                        QString newName = QString(reinterpret_cast<char*>(buf));
378
                        if (newName == glyName)
379
                        {
380
                                HasNames = false;
381
                                Subset = true;
382
                        }
383
                        glyName = newName;
384
                        charcode = FT_Get_Next_Char( face, charcode, &gindex );
385
                }
386
                foCache.isOK = true;
387
                checkedFonts.insert(filename, foCache);
388
        }
389
        else
390
        {
391
                if (!checkedFonts[filename].isOK)
392
                {
393
                        checkedFonts[filename].isChecked = true;
394
                        if (face != NULL)
395
                                FT_Done_Face( face );
396
                        return true;
397
                }
398
                if (checkedFonts[filename].lastMod != foCache.lastMod)
399
                {
400
                        ScCore->setSplashStatus( QObject::tr("Modified Font found, checking...") );
3559 fschmid 401
                        FT_UInt gindex = 0;
402
                        FT_ULong charcode = FT_Get_First_Char( face, &gindex );
403
                        while ( gindex != 0 )
3469 fschmid 404
                        {
3559 fschmid 405
                                error = FT_Load_Glyph( face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
406
                                if (error)
407
                                {
408
                                        if (showFontInformation)
409
                                                sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(filename).arg(gindex).arg(charcode));
410
                                        if (face != NULL)
411
                                                FT_Done_Face( face );
412
                                        checkedFonts.insert(filename, foCache);
413
                                        return true;
414
                                }
3633 fschmid 415
                                FT_Get_Glyph_Name(face, gindex, buf, 50);
416
                                QString newName = QString(reinterpret_cast<char*>(buf));
417
                                if (newName == glyName)
418
                                {
419
                                        HasNames = false;
420
                                        Subset = true;
421
                                }
422
                                glyName = newName;
3559 fschmid 423
                                charcode = FT_Get_Next_Char( face, charcode, &gindex );
424
                        }
425
                        foCache.isOK = true;
22237 jghali 426
                        checkedFonts[filename] = foCache;
3559 fschmid 427
                }
428
                else
429
                {
22237 jghali 430
                        checkedFonts[filename].isOK = true;
431
                        checkedFonts[filename].isChecked = true;
3469 fschmid 432
                }
433
        }
22237 jghali 434
 
3469 fschmid 435
        int faceindex=0;
2295 cbradney 436
        while (!error)
437
        {
4343 cbradney 438
                QString fam(face->family_name);
439
                QString sty(face->style_name);
3460 fschmid 440
                if (sty == "Regular")
441
                {
442
                        switch (face->style_flags)
443
                        {
444
                                case 0:
445
                                        break;
446
                                case 1:
447
                                        sty = "Italic";
448
                                        break;
449
                                case 2:
450
                                        sty = "Bold";
451
                                        break;
452
                                case 3:
453
                                        sty = "Bold Italic";
454
                                        break;
455
                                default:
456
                                        break;
457
                        }
458
                }
4343 cbradney 459
                QString ts(fam + " " + sty);
460
                QString alt("");
3460 fschmid 461
                const char* psName = FT_Get_Postscript_Name(face);
462
                QString qpsName;
463
                if (psName)
464
                        qpsName = QString(psName);
465
                else
466
                        qpsName = ts;
5980 avox 467
                ScFace t;
468
                if (contains(ts))
3460 fschmid 469
                {
5980 avox 470
                        t = (*this)[ts];
471
                        if (t.psName() != qpsName)
3460 fschmid 472
                        {
3544 avox 473
                                alt = " ("+qpsName+")";
474
                                ts += alt;
475
                                sty += alt;
3460 fschmid 476
                        }
477
                }
5980 avox 478
                t = (*this)[ts];
479
                if (t.isNone())
2295 cbradney 480
                {
481
                        switch (format)
482
                        {
5980 avox 483
                                case ScFace::PFA:
484
                                        t = ScFace(new ScFace_pfa(fam, sty, "", ts, qpsName, filename, faceindex));
485
                                        t.subset(Subset);
2295 cbradney 486
                                        break;
5980 avox 487
                                case ScFace::PFB:
488
                                        t = ScFace(new ScFace_pfb(fam, sty, "", ts, qpsName, filename, faceindex));
489
                                        t.subset(Subset);
2295 cbradney 490
                                        break;
5980 avox 491
                                case ScFace::SFNT:
492
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
493
                                        getSFontType(face, t.m->typeCode);
494
                                        if (t.type() == ScFace::OTF)
2295 cbradney 495
                                        {
5980 avox 496
                                                t.subset(true);
2295 cbradney 497
                                        }
3633 fschmid 498
                                        else
5980 avox 499
                                                t.subset(Subset);
2295 cbradney 500
                                        break;
5980 avox 501
                                case ScFace::TTCF:
502
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
503
                                        t.m->formatCode = ScFace::TTCF;
504
                                        t.m->typeCode = ScFace::TTF;
505
                                        //getSFontType(face, t.m->typeCode);
506
                                        if (t.type() == ScFace::OTF)
2295 cbradney 507
                                        {
5980 avox 508
                                                t.subset(true);
2295 cbradney 509
                                        }
3633 fschmid 510
                                        else
5980 avox 511
                                                t.subset(Subset);
2295 cbradney 512
                                        break;
5980 avox 513
                                case ScFace::TYPE42:
514
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
515
                                        getSFontType(face, t.m->typeCode);
516
                                        if (t.type() == ScFace::OTF)
2295 cbradney 517
                                        {
5980 avox 518
                                                t.subset(true);
2295 cbradney 519
                                        }
3633 fschmid 520
                                        else
5980 avox 521
                                                t.subset(Subset);
2295 cbradney 522
                                        break;
4166 fschmid 523
                                default:
524
                                /* catching any types not handled above to silence compiler */
525
                                        break;
2295 cbradney 526
                        }
494 fschmid 527
                        insert(ts,t);
5980 avox 528
                        t.m->hasNames = HasNames;
529
                        t.embedPs(true);
530
                        t.usable(true);
6983 fschmid 531
                        t.m->status = ScFace::UNKNOWN;
532
                        if (face->num_glyphs > 2048)
533
                                t.subset(true);
5980 avox 534
                        t.m->forDocument = DocName;
2633 cbradney 535
                        //setBestEncoding(face); //AV
5980 avox 536
/*                      switch (face->charmap? face->charmap->encoding : -1)
2295 cbradney 537
                        {
538
                                case     FT_ENCODING_ADOBE_STANDARD:
539
                                        t->FontEnc = QString("StandardEncoding");
540
                                        break;
541
                                case     FT_ENCODING_APPLE_ROMAN:
542
                                        t->FontEnc = QString("MacRomanEncoding");
543
                                        break;
544
                                case     FT_ENCODING_ADOBE_EXPERT:
545
                                        t->FontEnc = QString("MacExpertEncoding");
546
                                        break;
547
                                case     FT_ENCODING_ADOBE_LATIN_1:
548
                                        t->FontEnc = QString("WinAnsiEncoding");
549
                                        break;
550
                                case     FT_ENCODING_UNICODE:
551
                                        t->FontEnc = QString("Unicode");
552
                                        break;
553
                                default:
554
                                        t->FontEnc = QString();
555
                        }
5980 avox 556
*/                      if (showFontInformation)
557
                                sDebug(QObject::tr("Font %1 loaded from %2(%3)").arg(t.psName()).arg(filename).arg(faceindex+1));
2295 cbradney 558
 
559
/*
560
//debug
561
                        QByteArray bb;
562
                        t->RawData(bb);
563
                        QFile dump(QString("/tmp/fonts/%1-%2").arg(ts).arg(psName));
564
                        dump.open(IO_WriteOnly);
565
                        QDataStream os(&dump);
566
                        os.writeRawBytes(bb.data(), bb.size());
567
                        dump.close();
568
*/
494 fschmid 569
                }
2295 cbradney 570
                else
571
                {
572
                        if (showFontInformation)
5980 avox 573
                                sDebug(QObject::tr("Font %1(%2) is duplicate of %3").arg(filename).arg(faceindex+1).arg(t.fontPath()));
2295 cbradney 574
                        // this is needed since eg. AppleSymbols will happily return a face for *any* face_index
5018 avox 575
                        if (faceindex > 0) {
576
                                break;
577
                        }
2295 cbradney 578
                }
18537 jghali 579
                if ((++faceindex) >= face->num_faces)
580
                        break;
5018 avox 581
                FT_Done_Face(face);
18537 jghali 582
                face = NULL;
583
                error = FT_New_Face(library, QFile::encodeName(filename), faceindex, &face);
2295 cbradney 584
        } //while
585
 
494 fschmid 586
        if (face != 0)
18537 jghali 587
                FT_Done_Face(face);
4668 avox 588
        return error && faceindex == 0;
494 fschmid 589
}
590
 
591
void SCFonts::removeFont(QString name)
592
{
593
        remove(name);
2508 fschmid 594
        updateFontMap();
494 fschmid 595
}
596
 
8775 cbradney 597
const ScFace& SCFonts::findFont(const QString& fontname, ScribusDoc *doc)
598
{
599
        if (fontname.isEmpty())
600
                return ScFace::none();
601
 
602
        PrefsManager* prefsManager = PrefsManager::instance();
603
 
604
        if (!contains(fontname) || !(*this)[fontname].usable())
605
        {
606
                QString replFont;
607
                if ((!prefsManager->appPrefs.GFontSub.contains(fontname)) || (!(*this)[prefsManager->appPrefs.GFontSub[fontname]].usable()))
608
                {
609
                        replFont = doc ? doc->toolSettings.defFont : prefsManager->appPrefs.toolSettings.defFont;
610
                }
611
                else
612
                        replFont = prefsManager->appPrefs.GFontSub[fontname];
613
                ScFace repl = (*this)[replFont].mkReplacementFor(fontname, doc ? doc->DocName : QString(""));
614
                insert(fontname, repl);
615
        }
616
        else if ( doc && !doc->UsedFonts.contains(fontname) )
617
        {
618
                doc->AddFont(fontname, qRound(doc->toolSettings.defSize / 10.0));
619
        }
620
        return (*this)[fontname];      
621
}
622
 
623
 
9803 fschmid 624
QMap<QString,QString> SCFonts::getSubstitutions(const QList<QString> skip) const
8775 cbradney 625
{
626
        QMap<QString,QString> result;
627
        QMap<QString,ScFace>::ConstIterator it;
628
        for (it = begin(); it != end(); ++it)
629
        {
10516 cbradney 630
                if (it.value().isReplacement() && !skip.contains(it.key()))
631
                        result[it.key()] = it.value().replacementName();
8775 cbradney 632
        }
633
        return result;
634
}
635
 
636
 
637
void SCFonts::setSubstitutions(const QMap<QString,QString>& substitutes, ScribusDoc* doc)
638
{
639
        QMap<QString,QString>::ConstIterator it;
640
        for (it = substitutes.begin(); it != substitutes.end(); ++it)
641
        {
10516 cbradney 642
                if (it.key() == it.value())
8775 cbradney 643
                        continue;
644
                ScFace& font(const_cast<ScFace&>(findFont(it.key(), doc)));
645
                if (font.isReplacement())
646
                {
10516 cbradney 647
                        font.chReplacementTo(const_cast<ScFace&>(findFont(it.value(), doc)), doc->DocName);
8775 cbradney 648
                }
649
        }
650
}
651
 
652
 
776 fschmid 653
#ifdef HAVE_FONTCONFIG
654
// Use Fontconfig to locate and load fonts.
655
void SCFonts::AddFontconfigFonts()
656
{
2295 cbradney 657
        // All-in-one library setup. Perhaps this should be in
658
        // the SCFonts constructor.
659
        FcConfig* FcInitLoadConfigAndFonts();
660
        // The pattern controls what fonts to match. In this case we want to
661
        // match all scalable fonts, but ignore bitmap fonts.
662
        FcPattern* pat = FcPatternBuild(NULL,
663
                                                                        FC_SCALABLE, FcTypeBool, true,
664
                                                                        NULL);
665
        // The ObjectSet tells FontConfig what information about each match to return.
666
        // We currently just need FC_FILE, but other info like font family and style
10749 cbradney 667
        // is available - see "man fontconfig".
2295 cbradney 668
        FcObjectSet* os = FcObjectSetBuild (FC_FILE, (char *) 0);
669
        // Now ask fontconfig to retrieve info as specified in 'os' about fonts
670
        // matching pattern 'pat'.
671
        FcFontSet* fs = FcFontList(0, pat, os);
672
        FcObjectSetDestroy(os);
673
        FcPatternDestroy(pat);
674
        // Create the Freetype library
675
        bool error;
676
        FT_Library library = NULL;
677
        error = FT_Init_FreeType( &library );
678
        // Now iterate over the font files and load them
679
        int i;
680
        for (i = 0; i < fs->nfont; i++)
681
        {
682
                FcChar8 *file = NULL;
683
                if (FcPatternGetString (fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch)
684
                {
685
                        if (showFontInformation)
3240 fschmid 686
                                sDebug(QObject::tr("Loading font %1 (found using fontconfig)").arg(QString((char*)file)));
2295 cbradney 687
                        error = AddScalableFont(QString((char*)file), library, "");
688
                }
689
                else
690
                        if (showFontInformation)
3240 fschmid 691
                                sDebug(QObject::tr("Failed to load a font - freetype2 couldn't find the font file"));
2295 cbradney 692
        }
693
        FT_Done_FreeType( library );
776 fschmid 694
}
695
 
10531 avox 696
#elif defined(Q_WS_X11)
776 fschmid 697
 
3 paul 698
void SCFonts::AddXFontPath()
699
{
700
        int pathcount,i;
701
        Display *display=XOpenDisplay(NULL);
702
        char **fontpath=XGetFontPath(display,&pathcount);
703
        for(i=0; i<pathcount; ++i)
704
                AddPath(fontpath[i]);
705
        XFreeFontPath(fontpath);
706
}
707
 
708
/* replacement for AddXFontServerPath() for correctly parsing
709
 * RedHad-Style /etc/X11/fs/config files */
710
 
711
void SCFonts::AddXFontServerPath()
712
{
713
        QFile fs("/etc/X11/fs/config");
714
        if(!(fs.exists()))
715
        {
716
                fs.setName("/usr/X11R6/lib/X11/fs/config");
717
                if(!(fs.exists()))
718
                {
719
                        fs.setName("/usr/X11/lib/X11/fs/config");
720
                        if(!(fs.exists()))
721
                                return;
722
                }
723
        }
724
 
8501 cbradney 725
        if (fs.open(QIODevice::ReadOnly))
3 paul 726
        {
727
                QString fsconfig,paths,tmp;
10004 fschmid 728
                QTextStream tsx(&fs);
729
                fsconfig = tsx.readAll();
3 paul 730
                fs.close();
731
 
732
                int pos = fsconfig.find("catalogue");
733
                paths = fsconfig.mid(pos);
734
                paths = paths.mid(paths.find("=")+1);
735
 
736
                pos=0;
737
                do {
738
                        pos = paths.find("\n",pos+1);
739
                } while (pos > -1 && paths.mid(pos-1, 1) == ",");
740
 
741
                if(pos<0) pos=paths.length();
742
                paths = paths.left(pos);
10394 cbradney 743
                paths = paths.simplified();
3 paul 744
                paths.replace(QRegExp(" "), "");
745
                paths.replace(QRegExp(","), "\n");
746
                paths += "\n";
10394 cbradney 747
                paths = paths.trimmed();
3 paul 748
 
749
                pos=-1;
750
                do {
751
                        int pos2;
752
                        pos2 = paths.find("\n",pos+1);
753
                        tmp = paths.mid(pos+1,(pos2-pos)-1);
754
                        pos=pos2;
755
 
756
                        AddPath(tmp);
757
 
758
                } while (pos > -1);
759
        }
760
}
776 fschmid 761
#endif
3 paul 762
 
2510 craig 763
/* Add an extra path to our font search path,
764
 * allowing a user to have extra fonts installed
765
 * only for this user. Can also be used also as an emergency
766
 * fallback if no suitable fonts are found elsewere */
3240 fschmid 767
void SCFonts::AddUserPath(QString )
3 paul 768
{
2856 cbradney 769
        PrefsContext *pc = PrefsManager::instance()->prefsFile->getContext("Fonts");
2510 craig 770
        PrefsTable *extraDirs = pc->getTable("ExtraFontDirs");
771
        for (int i = 0; i < extraDirs->getRowCount(); ++i)
772
                AddPath(extraDirs->get(i, 0));
3 paul 773
}
774
 
3559 fschmid 775
void SCFonts::ReadCacheList(QString pf)
776
{
3633 fschmid 777
        QFile fr(pf + "/cfonts.xml");
778
        QFileInfo fir(fr);
779
        if (fir.exists())
780
                fr.remove();
3559 fschmid 781
        checkedFonts.clear();
782
        struct testCache foCache;
783
        QDomDocument docu("fontcacherc");
3633 fschmid 784
        QFile f(pf + "/checkfonts.xml");
8501 cbradney 785
        if(!f.open(QIODevice::ReadOnly))
3559 fschmid 786
                return;
5243 cbradney 787
        ScCore->setSplashStatus( QObject::tr("Reading Font Cache") );
10004 fschmid 788
        QTextStream ts(&f);
789
        ts.setCodec("UTF-8");
3559 fschmid 790
        QString errorMsg;
791
        int errorLine = 0, errorColumn = 0;
10004 fschmid 792
        if( !docu.setContent(ts.readAll(), &errorMsg, &errorLine, &errorColumn) )
3559 fschmid 793
        {
794
                f.close();
795
                return;
796
        }
797
        f.close();
798
        QDomElement elem = docu.documentElement();
799
        if (elem.tagName() != "CachedFonts")
800
                return;
801
        QDomNode DOC = elem.firstChild();
802
        while(!DOC.isNull())
803
        {
804
                QDomElement dc = DOC.toElement();
805
                if (dc.tagName()=="Font")
806
                {
807
                        foCache.isChecked = false;
4026 craig 808
                        foCache.isOK = static_cast<bool>(dc.attribute("Status", "1").toInt());
3559 fschmid 809
                        foCache.lastMod = QDateTime::fromString(dc.attribute("Modified"), Qt::ISODate);
810
                        checkedFonts.insert(dc.attribute("File"), foCache);
811
                }
812
                DOC = DOC.nextSibling();
813
        }
814
}
815
 
816
void SCFonts::WriteCacheList(QString pf)
817
{
818
        QDomDocument docu("fontcacherc");
819
        QString st="<CachedFonts></CachedFonts>";
23468 jghali 820
 
3559 fschmid 821
        docu.setContent(st);
822
        QDomElement elem = docu.documentElement();
823
        QMap<QString, testCache>::Iterator it;
824
        for (it = checkedFonts.begin(); it != checkedFonts.end(); ++it)
825
        {
23468 jghali 826
                const testCache& checkedFont = it.value();
827
 
828
                bool saveItem = checkedFont.isChecked;
829
                if (!checkedFont.isChecked) // Font might be located in another local Scribus font folder
830
                        saveItem = QFile::exists(it.key());
831
                if (!saveItem)
832
                        continue;
833
 
834
                QDomElement fosu = docu.createElement("Font");
835
                fosu.setAttribute("File",it.key());
836
                fosu.setAttribute("Status", static_cast<int>(it.value().isOK));
837
                fosu.setAttribute("Modified", it.value().lastMod.toString(Qt::ISODate));
838
                elem.appendChild(fosu);
3559 fschmid 839
        }
23468 jghali 840
 
5243 cbradney 841
        ScCore->setSplashStatus( QObject::tr("Writing updated Font Cache") );
23468 jghali 842
 
3633 fschmid 843
        QFile f(pf + "/checkfonts.xml");
23468 jghali 844
        if (f.open(QIODevice::WriteOnly))
3559 fschmid 845
        {
10004 fschmid 846
                QTextStream s(&f);
847
                s.setCodec("UTF-8");
3559 fschmid 848
                s<<docu.toString();
849
                f.close();
850
        }
851
        checkedFonts.clear();
852
}
853
 
2295 cbradney 854
void SCFonts::GetFonts(QString pf, bool showFontInfo)
3 paul 855
{
2295 cbradney 856
        showFontInformation=showFontInfo;
3 paul 857
        FontPath.clear();
3559 fschmid 858
        ReadCacheList(pf);
5243 cbradney 859
        ScCore->setSplashStatus( QObject::tr("Searching for Fonts") );
3 paul 860
        AddUserPath(pf);
3794 cbradney 861
        // Search the system paths
862
        QStringList ftDirs = ScPaths::getSystemFontDirs();
8594 jghali 863
        for (int i = 0; i < ftDirs.count(); i++)
3794 cbradney 864
                AddScalableFonts( ftDirs[i] );
865
        // Search Scribus font path
3831 cbradney 866
        if (!ScPaths::instance().fontDir().isEmpty() && QDir(ScPaths::instance().fontDir()).exists())
3794 cbradney 867
                AddScalableFonts( ScPaths::instance().fontDir() );
2295 cbradney 868
// if fontconfig is there, it does all the work
869
#if HAVE_FONTCONFIG
3794 cbradney 870
        // Search fontconfig paths
8594 jghali 871
        QStringList::iterator fpi, fpend = FontPath.end();
872
        for(fpi = FontPath.begin() ; fpi != fpend; ++fpi)
873
                AddScalableFonts(*fpi);
776 fschmid 874
        AddFontconfigFonts();
875
#else
2295 cbradney 876
// on X11 look there:
877
#ifdef Q_WS_X11
3 paul 878
        AddXFontPath();
879
        AddXFontServerPath();
2295 cbradney 880
#endif
881
// add user and X11 fonts:
8594 jghali 882
        QStringList::iterator fpi, fpend = FontPath.end();
883
        for(fpi = FontPath.begin() ; fpi != fpend; ++fpi)
884
                AddScalableFonts(*fpi);
494 fschmid 885
#endif
2508 fschmid 886
        updateFontMap();
3559 fschmid 887
        WriteCacheList(pf);
494 fschmid 888
}