Subversion Repositories Scribus

Rev

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