Subversion Repositories Scribus

Rev

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