Subversion Repositories Scribus

Rev

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

Rev Author Line No. Line
3 paul 1
/*
4430 cbradney 2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
6
*/
7
/*
3 paul 8
        Scribus font handling code - copyright (C) 2001 by
9
        Alastair M. Robinson
10
 
220 Franz 11
        Based on code by Christian Tpp and Franz Schmid.
3 paul 12
 
13
        Contributed to the Scribus project under the terms of
14
        the GNU General Public License version 2, or any later
15
        version, at your discretion.
16
*/
17
 
10152 cbradney 18
#include <QApplication>
10181 cbradney 19
#include <QDir>
10152 cbradney 20
#include <QDomDocument>
21
#include <QFile>
22
#include <QFileInfo>
10181 cbradney 23
#include <QFont>
10152 cbradney 24
#include <QGlobalStatic>
25
#include <QMap>
26
#include <QRegExp>
10181 cbradney 27
#include <QString>
1669 craig 28
 
10181 cbradney 29
 
3 paul 30
#include <cstdlib>
31
#include "scfonts.h"
5980 avox 32
#include "fonts/ftface.h"
33
#include "fonts/scface_ps.h"
34
#include "fonts/scface_ttf.h"
35
#include "fonts/scfontmetrics.h"
494 fschmid 36
 
2856 cbradney 37
#include "prefsmanager.h"
2510 craig 38
#include "prefsfile.h"
39
#include "prefscontext.h"
40
#include "prefstable.h"
3566 fschmid 41
#include "scribus.h"
5243 cbradney 42
#include "scribuscore.h"
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"
63
#include "util.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
 
306
// Load a single font into the library from the passed filename. Returns true on error.
307
bool SCFonts::AddScalableFont(QString filename, FT_Library &library, QString DocName)
308
{
3566 fschmid 309
        static bool firstRun;
3633 fschmid 310
        bool Subset = false;
311
        char *buf[50];
312
        QString glyName = "";
5980 avox 313
        ScFace::FontFormat format;
314
        ScFace::FontType   type;
2295 cbradney 315
        FT_Face         face = NULL;
3559 fschmid 316
        struct testCache foCache;
317
        QFileInfo fic(filename);
14488 jghali 318
        QDateTime lastMod = fic.lastModified();
319
        QTime lastModTime = lastMod.time();
320
        if (lastModTime.msec() != 0)  //Sometime file time is stored with precision up to msecs
321
        {
322
                lastModTime.setHMS(lastModTime.hour(), lastModTime.minute(), lastModTime.second());
323
                lastMod.setTime(lastModTime);
324
        }
3559 fschmid 325
        foCache.isOK = false;
326
        foCache.isChecked = true;
14488 jghali 327
        foCache.lastMod = lastMod;
3566 fschmid 328
        if (checkedFonts.count() == 0)
329
        {
330
                firstRun = true;
5243 cbradney 331
                ScCore->setSplashStatus( QObject::tr("Creating Font Cache") );
3566 fschmid 332
        }
5980 avox 333
        bool error = FT_New_Face( library, QFile::encodeName(filename), 0, &face );
2295 cbradney 334
        if (error)
335
        {
336
                if (face != NULL)
337
                        FT_Done_Face( face );
3559 fschmid 338
                checkedFonts.insert(filename, foCache);
5018 avox 339
                if (showFontInformation)
340
                        sDebug(QObject::tr("Font %1 is broken, discarding it").arg(filename));
2295 cbradney 341
                return true;
342
        }
343
        getFontFormat(face, format, type);
5980 avox 344
        if (format == ScFace::UNKNOWN_FORMAT)
2295 cbradney 345
        {
3474 cbradney 346
                if (showFontInformation)
347
                        sDebug(QObject::tr("Failed to load font %1 - font type unknown").arg(filename));
3559 fschmid 348
                checkedFonts.insert(filename, foCache);
2295 cbradney 349
                error = true;
350
        }
3633 fschmid 351
        bool HasNames = FT_HAS_GLYPH_NAMES(face);
3469 fschmid 352
        if (!error)
353
        {
3559 fschmid 354
                if (!checkedFonts.contains(filename))
3469 fschmid 355
                {
3566 fschmid 356
                        if (!firstRun)
5243 cbradney 357
                                ScCore->setSplashStatus( QObject::tr("New Font found, checking...") );
3559 fschmid 358
                        FT_UInt gindex = 0;
359
                        FT_ULong charcode = FT_Get_First_Char( face, &gindex );
360
                        while ( gindex != 0 )
3469 fschmid 361
                        {
3559 fschmid 362
                                error = FT_Load_Glyph( face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
363
                                if (error)
364
                                {
365
                                        if (showFontInformation)
366
                                                sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(filename).arg(gindex).arg(charcode));
367
                                        if (face != NULL)
368
                                                FT_Done_Face( face );
369
                                        checkedFonts.insert(filename, foCache);
370
                                        return true;
371
                                }
3633 fschmid 372
                                FT_Get_Glyph_Name(face, gindex, buf, 50);
373
                                QString newName = QString(reinterpret_cast<char*>(buf));
374
                                if (newName == glyName)
375
                                {
376
                                        HasNames = false;
377
                                        Subset = true;
378
                                }
379
                                glyName = newName;
3559 fschmid 380
                                charcode = FT_Get_Next_Char( face, charcode, &gindex );
381
                        }
382
                        foCache.isOK = true;
383
                        checkedFonts.insert(filename, foCache);
384
                }
385
                else
386
                {
387
                        if (!checkedFonts[filename].isOK)
388
                        {
389
                                checkedFonts[filename].isChecked = true;
3469 fschmid 390
                                return true;
391
                        }
14488 jghali 392
                        if (checkedFonts[filename].lastMod != foCache.lastMod)
3559 fschmid 393
                        {
5243 cbradney 394
                                ScCore->setSplashStatus( QObject::tr("Modified Font found, checking...") );
3559 fschmid 395
                                FT_UInt gindex = 0;
396
                                FT_ULong charcode = FT_Get_First_Char( face, &gindex );
397
                                while ( gindex != 0 )
398
                                {
399
                                        error = FT_Load_Glyph( face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
400
                                        if (error)
401
                                        {
402
                                                if (showFontInformation)
403
                                                        sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(filename).arg(gindex).arg(charcode));
404
                                                if (face != NULL)
405
                                                        FT_Done_Face( face );
406
                                                checkedFonts.insert(filename, foCache);
407
                                                return true;
408
                                        }
3633 fschmid 409
                                        FT_Get_Glyph_Name(face, gindex, buf, 50);
410
                                        QString newName = QString(reinterpret_cast<char*>(buf));
411
                                        if (newName == glyName)
412
                                        {
413
                                                HasNames = false;
414
                                                Subset = true;
415
                                        }
416
                                        glyName = newName;
3559 fschmid 417
                                        charcode = FT_Get_Next_Char( face, charcode, &gindex );
418
                                }
419
                                foCache.isOK = true;
420
                                checkedFonts[filename] = foCache;
421
                        }
422
                        else
423
                        {
424
                                checkedFonts[filename].isOK = true;
425
                                checkedFonts[filename].isChecked = true;
426
                        }
3469 fschmid 427
                }
428
        }
429
        int faceindex=0;
2295 cbradney 430
        while (!error)
431
        {
4343 cbradney 432
                QString fam(face->family_name);
433
                QString sty(face->style_name);
3460 fschmid 434
                if (sty == "Regular")
435
                {
436
                        switch (face->style_flags)
437
                        {
438
                                case 0:
439
                                        break;
440
                                case 1:
441
                                        sty = "Italic";
442
                                        break;
443
                                case 2:
444
                                        sty = "Bold";
445
                                        break;
446
                                case 3:
447
                                        sty = "Bold Italic";
448
                                        break;
449
                                default:
450
                                        break;
451
                        }
452
                }
4343 cbradney 453
                QString ts(fam + " " + sty);
454
                QString alt("");
3460 fschmid 455
                const char* psName = FT_Get_Postscript_Name(face);
456
                QString qpsName;
457
                if (psName)
458
                        qpsName = QString(psName);
459
                else
460
                        qpsName = ts;
5980 avox 461
                ScFace t;
462
                if (contains(ts))
3460 fschmid 463
                {
5980 avox 464
                        t = (*this)[ts];
465
                        if (t.psName() != qpsName)
3460 fschmid 466
                        {
3544 avox 467
                                alt = " ("+qpsName+")";
468
                                ts += alt;
469
                                sty += alt;
3460 fschmid 470
                        }
471
                }
5980 avox 472
                t = (*this)[ts];
473
                if (t.isNone())
2295 cbradney 474
                {
475
                        switch (format)
476
                        {
5980 avox 477
                                case ScFace::PFA:
478
                                        t = ScFace(new ScFace_pfa(fam, sty, "", ts, qpsName, filename, faceindex));
479
                                        t.subset(Subset);
2295 cbradney 480
                                        break;
5980 avox 481
                                case ScFace::PFB:
482
                                        t = ScFace(new ScFace_pfb(fam, sty, "", ts, qpsName, filename, faceindex));
483
                                        t.subset(Subset);
2295 cbradney 484
                                        break;
5980 avox 485
                                case ScFace::SFNT:
486
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
487
                                        getSFontType(face, t.m->typeCode);
488
                                        if (t.type() == ScFace::OTF)
2295 cbradney 489
                                        {
5980 avox 490
                                                t.subset(true);
2295 cbradney 491
                                        }
3633 fschmid 492
                                        else
5980 avox 493
                                                t.subset(Subset);
2295 cbradney 494
                                        break;
5980 avox 495
                                case ScFace::TTCF:
496
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
497
                                        t.m->formatCode = ScFace::TTCF;
498
                                        t.m->typeCode = ScFace::TTF;
499
                                        //getSFontType(face, t.m->typeCode);
500
                                        if (t.type() == ScFace::OTF)
2295 cbradney 501
                                        {
5980 avox 502
                                                t.subset(true);
2295 cbradney 503
                                        }
3633 fschmid 504
                                        else
5980 avox 505
                                                t.subset(Subset);
2295 cbradney 506
                                        break;
5980 avox 507
                                case ScFace::TYPE42:
508
                                        t = ScFace(new ScFace_ttf(fam, sty, "", ts, qpsName, filename, faceindex));
509
                                        getSFontType(face, t.m->typeCode);
510
                                        if (t.type() == ScFace::OTF)
2295 cbradney 511
                                        {
5980 avox 512
                                                t.subset(true);
2295 cbradney 513
                                        }
3633 fschmid 514
                                        else
5980 avox 515
                                                t.subset(Subset);
2295 cbradney 516
                                        break;
4166 fschmid 517
                                default:
518
                                /* catching any types not handled above to silence compiler */
519
                                        break;
2295 cbradney 520
                        }
494 fschmid 521
                        insert(ts,t);
5980 avox 522
                        t.m->hasNames = HasNames;
523
                        t.embedPs(true);
524
                        t.usable(true);
6983 fschmid 525
                        t.m->status = ScFace::UNKNOWN;
526
                        if (face->num_glyphs > 2048)
527
                                t.subset(true);
5980 avox 528
                        t.m->forDocument = DocName;
2633 cbradney 529
                        //setBestEncoding(face); //AV
5980 avox 530
/*                      switch (face->charmap? face->charmap->encoding : -1)
2295 cbradney 531
                        {
532
                                case     FT_ENCODING_ADOBE_STANDARD:
533
                                        t->FontEnc = QString("StandardEncoding");
534
                                        break;
535
                                case     FT_ENCODING_APPLE_ROMAN:
536
                                        t->FontEnc = QString("MacRomanEncoding");
537
                                        break;
538
                                case     FT_ENCODING_ADOBE_EXPERT:
539
                                        t->FontEnc = QString("MacExpertEncoding");
540
                                        break;
541
                                case     FT_ENCODING_ADOBE_LATIN_1:
542
                                        t->FontEnc = QString("WinAnsiEncoding");
543
                                        break;
544
                                case     FT_ENCODING_UNICODE:
545
                                        t->FontEnc = QString("Unicode");
546
                                        break;
547
                                default:
548
                                        t->FontEnc = QString();
549
                        }
5980 avox 550
*/                      if (showFontInformation)
551
                                sDebug(QObject::tr("Font %1 loaded from %2(%3)").arg(t.psName()).arg(filename).arg(faceindex+1));
2295 cbradney 552
 
553
/*
554
//debug
555
                        QByteArray bb;
556
                        t->RawData(bb);
557
                        QFile dump(QString("/tmp/fonts/%1-%2").arg(ts).arg(psName));
558
                        dump.open(IO_WriteOnly);
559
                        QDataStream os(&dump);
560
                        os.writeRawBytes(bb.data(), bb.size());
561
                        dump.close();
562
*/
494 fschmid 563
                }
2295 cbradney 564
                else
565
                {
566
                        if (showFontInformation)
5980 avox 567
                                sDebug(QObject::tr("Font %1(%2) is duplicate of %3").arg(filename).arg(faceindex+1).arg(t.fontPath()));
2295 cbradney 568
                        // this is needed since eg. AppleSymbols will happily return a face for *any* face_index
5018 avox 569
                        if (faceindex > 0) {
570
                                break;
571
                        }
2295 cbradney 572
                }
5018 avox 573
                FT_Done_Face(face);
574
                face=NULL;
575
                ++faceindex;
5980 avox 576
                error = FT_New_Face( library, QFile::encodeName(filename), faceindex, &face );
2295 cbradney 577
        } //while
578
 
494 fschmid 579
        if (face != 0)
580
                FT_Done_Face( face );
4668 avox 581
        return error && faceindex == 0;
494 fschmid 582
}
583
 
584
void SCFonts::removeFont(QString name)
585
{
586
        remove(name);
2508 fschmid 587
        updateFontMap();
494 fschmid 588
}
589
 
8775 cbradney 590
const ScFace& SCFonts::findFont(const QString& fontname, ScribusDoc *doc)
591
{
592
        if (fontname.isEmpty())
593
                return ScFace::none();
594
 
595
        PrefsManager* prefsManager = PrefsManager::instance();
596
 
597
        if (!contains(fontname) || !(*this)[fontname].usable())
598
        {
599
                QString replFont;
13967 cbradney 600
                if ((!prefsManager->appPrefs.fontPrefs.GFontSub.contains(fontname)) || (!(*this)[prefsManager->appPrefs.fontPrefs.GFontSub[fontname]].usable()))
8775 cbradney 601
                {
14932 cbradney 602
                        replFont = doc ? doc->itemToolPrefs().textFont : prefsManager->appPrefs.itemToolPrefs.textFont;
8775 cbradney 603
                }
604
                else
13967 cbradney 605
                        replFont = prefsManager->appPrefs.fontPrefs.GFontSub[fontname];
8775 cbradney 606
                ScFace repl = (*this)[replFont].mkReplacementFor(fontname, doc ? doc->DocName : QString(""));
607
                insert(fontname, repl);
608
        }
609
        else if ( doc && !doc->UsedFonts.contains(fontname) )
610
        {
14932 cbradney 611
                doc->AddFont(fontname, qRound(doc->itemToolPrefs().textSize / 10.0));
8775 cbradney 612
        }
613
        return (*this)[fontname];      
614
}
615
 
616
 
9803 fschmid 617
QMap<QString,QString> SCFonts::getSubstitutions(const QList<QString> skip) const
8775 cbradney 618
{
619
        QMap<QString,QString> result;
620
        QMap<QString,ScFace>::ConstIterator it;
621
        for (it = begin(); it != end(); ++it)
622
        {
10516 cbradney 623
                if (it.value().isReplacement() && !skip.contains(it.key()))
624
                        result[it.key()] = it.value().replacementName();
8775 cbradney 625
        }
626
        return result;
627
}
628
 
629
 
630
void SCFonts::setSubstitutions(const QMap<QString,QString>& substitutes, ScribusDoc* doc)
631
{
632
        QMap<QString,QString>::ConstIterator it;
633
        for (it = substitutes.begin(); it != substitutes.end(); ++it)
634
        {
10516 cbradney 635
                if (it.key() == it.value())
8775 cbradney 636
                        continue;
637
                ScFace& font(const_cast<ScFace&>(findFont(it.key(), doc)));
638
                if (font.isReplacement())
639
                {
10516 cbradney 640
                        font.chReplacementTo(const_cast<ScFace&>(findFont(it.value(), doc)), doc->DocName);
8775 cbradney 641
                }
642
        }
643
}
644
 
645
 
776 fschmid 646
#ifdef HAVE_FONTCONFIG
647
// Use Fontconfig to locate and load fonts.
648
void SCFonts::AddFontconfigFonts()
649
{
2295 cbradney 650
        // All-in-one library setup. Perhaps this should be in
651
        // the SCFonts constructor.
652
        FcConfig* FcInitLoadConfigAndFonts();
653
        // The pattern controls what fonts to match. In this case we want to
654
        // match all scalable fonts, but ignore bitmap fonts.
655
        FcPattern* pat = FcPatternBuild(NULL,
656
                                                                        FC_SCALABLE, FcTypeBool, true,
657
                                                                        NULL);
658
        // The ObjectSet tells FontConfig what information about each match to return.
659
        // We currently just need FC_FILE, but other info like font family and style
10749 cbradney 660
        // is available - see "man fontconfig".
2295 cbradney 661
        FcObjectSet* os = FcObjectSetBuild (FC_FILE, (char *) 0);
662
        // Now ask fontconfig to retrieve info as specified in 'os' about fonts
663
        // matching pattern 'pat'.
664
        FcFontSet* fs = FcFontList(0, pat, os);
665
        FcObjectSetDestroy(os);
666
        FcPatternDestroy(pat);
667
        // Create the Freetype library
668
        bool error;
669
        FT_Library library = NULL;
670
        error = FT_Init_FreeType( &library );
671
        // Now iterate over the font files and load them
672
        int i;
673
        for (i = 0; i < fs->nfont; i++)
674
        {
675
                FcChar8 *file = NULL;
676
                if (FcPatternGetString (fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch)
677
                {
678
                        if (showFontInformation)
3240 fschmid 679
                                sDebug(QObject::tr("Loading font %1 (found using fontconfig)").arg(QString((char*)file)));
2295 cbradney 680
                        error = AddScalableFont(QString((char*)file), library, "");
681
                }
682
                else
683
                        if (showFontInformation)
3240 fschmid 684
                                sDebug(QObject::tr("Failed to load a font - freetype2 couldn't find the font file"));
2295 cbradney 685
        }
686
        FT_Done_FreeType( library );
776 fschmid 687
}
688
 
18204 fschmid 689
#elif defined(Q_OS_LINUX)
776 fschmid 690
 
3 paul 691
void SCFonts::AddXFontPath()
692
{
693
        int pathcount,i;
694
        Display *display=XOpenDisplay(NULL);
695
        char **fontpath=XGetFontPath(display,&pathcount);
696
        for(i=0; i<pathcount; ++i)
697
                AddPath(fontpath[i]);
698
        XFreeFontPath(fontpath);
699
}
700
 
701
/* replacement for AddXFontServerPath() for correctly parsing
702
 * RedHad-Style /etc/X11/fs/config files */
703
 
704
void SCFonts::AddXFontServerPath()
705
{
706
        QFile fs("/etc/X11/fs/config");
707
        if(!(fs.exists()))
708
        {
709
                fs.setName("/usr/X11R6/lib/X11/fs/config");
710
                if(!(fs.exists()))
711
                {
712
                        fs.setName("/usr/X11/lib/X11/fs/config");
713
                        if(!(fs.exists()))
714
                                return;
715
                }
716
        }
717
 
8501 cbradney 718
        if (fs.open(QIODevice::ReadOnly))
3 paul 719
        {
720
                QString fsconfig,paths,tmp;
10004 fschmid 721
                QTextStream tsx(&fs);
722
                fsconfig = tsx.readAll();
3 paul 723
                fs.close();
724
 
725
                int pos = fsconfig.find("catalogue");
726
                paths = fsconfig.mid(pos);
727
                paths = paths.mid(paths.find("=")+1);
728
 
729
                pos=0;
730
                do {
731
                        pos = paths.find("\n",pos+1);
732
                } while (pos > -1 && paths.mid(pos-1, 1) == ",");
733
 
734
                if(pos<0) pos=paths.length();
735
                paths = paths.left(pos);
10394 cbradney 736
                paths = paths.simplified();
3 paul 737
                paths.replace(QRegExp(" "), "");
738
                paths.replace(QRegExp(","), "\n");
739
                paths += "\n";
10394 cbradney 740
                paths = paths.trimmed();
3 paul 741
 
742
                pos=-1;
743
                do {
744
                        int pos2;
745
                        pos2 = paths.find("\n",pos+1);
746
                        tmp = paths.mid(pos+1,(pos2-pos)-1);
747
                        pos=pos2;
748
 
749
                        AddPath(tmp);
750
 
751
                } while (pos > -1);
752
        }
753
}
776 fschmid 754
#endif
3 paul 755
 
2510 craig 756
/* Add an extra path to our font search path,
757
 * allowing a user to have extra fonts installed
758
 * only for this user. Can also be used also as an emergency
759
 * fallback if no suitable fonts are found elsewere */
3240 fschmid 760
void SCFonts::AddUserPath(QString )
3 paul 761
{
2856 cbradney 762
        PrefsContext *pc = PrefsManager::instance()->prefsFile->getContext("Fonts");
2510 craig 763
        PrefsTable *extraDirs = pc->getTable("ExtraFontDirs");
764
        for (int i = 0; i < extraDirs->getRowCount(); ++i)
765
                AddPath(extraDirs->get(i, 0));
3 paul 766
}
767
 
3559 fschmid 768
void SCFonts::ReadCacheList(QString pf)
769
{
3633 fschmid 770
        QFile fr(pf + "/cfonts.xml");
771
        QFileInfo fir(fr);
772
        if (fir.exists())
773
                fr.remove();
3559 fschmid 774
        checkedFonts.clear();
775
        struct testCache foCache;
776
        QDomDocument docu("fontcacherc");
3633 fschmid 777
        QFile f(pf + "/checkfonts.xml");
8501 cbradney 778
        if(!f.open(QIODevice::ReadOnly))
3559 fschmid 779
                return;
5243 cbradney 780
        ScCore->setSplashStatus( QObject::tr("Reading Font Cache") );
10004 fschmid 781
        QTextStream ts(&f);
782
        ts.setCodec("UTF-8");
3559 fschmid 783
        QString errorMsg;
784
        int errorLine = 0, errorColumn = 0;
10004 fschmid 785
        if( !docu.setContent(ts.readAll(), &errorMsg, &errorLine, &errorColumn) )
3559 fschmid 786
        {
787
                f.close();
788
                return;
789
        }
790
        f.close();
791
        QDomElement elem = docu.documentElement();
792
        if (elem.tagName() != "CachedFonts")
793
                return;
794
        QDomNode DOC = elem.firstChild();
795
        while(!DOC.isNull())
796
        {
797
                QDomElement dc = DOC.toElement();
798
                if (dc.tagName()=="Font")
799
                {
800
                        foCache.isChecked = false;
4026 craig 801
                        foCache.isOK = static_cast<bool>(dc.attribute("Status", "1").toInt());
3559 fschmid 802
                        foCache.lastMod = QDateTime::fromString(dc.attribute("Modified"), Qt::ISODate);
803
                        checkedFonts.insert(dc.attribute("File"), foCache);
804
                }
805
                DOC = DOC.nextSibling();
806
        }
807
}
808
 
809
void SCFonts::WriteCacheList(QString pf)
810
{
811
        QDomDocument docu("fontcacherc");
812
        QString st="<CachedFonts></CachedFonts>";
813
        docu.setContent(st);
814
        QDomElement elem = docu.documentElement();
815
        QMap<QString, testCache>::Iterator it;
816
        for (it = checkedFonts.begin(); it != checkedFonts.end(); ++it)
817
        {
10516 cbradney 818
                if (it.value().isChecked)
3559 fschmid 819
                {
820
                        QDomElement fosu = docu.createElement("Font");
821
                        fosu.setAttribute("File",it.key());
10516 cbradney 822
                        fosu.setAttribute("Status",static_cast<int>(it.value().isOK));
823
                        fosu.setAttribute("Modified",it.value().lastMod.toString(Qt::ISODate));
3559 fschmid 824
                        elem.appendChild(fosu);
825
                }
826
        }
5243 cbradney 827
        ScCore->setSplashStatus( QObject::tr("Writing updated Font Cache") );
3633 fschmid 828
        QFile f(pf + "/checkfonts.xml");
8501 cbradney 829
        if(f.open(QIODevice::WriteOnly))
3559 fschmid 830
        {
10004 fschmid 831
                QTextStream s(&f);
832
                s.setCodec("UTF-8");
3559 fschmid 833
                s<<docu.toString();
834
                f.close();
835
        }
836
        checkedFonts.clear();
837
}
838
 
2295 cbradney 839
void SCFonts::GetFonts(QString pf, bool showFontInfo)
3 paul 840
{
2295 cbradney 841
        showFontInformation=showFontInfo;
3 paul 842
        FontPath.clear();
3559 fschmid 843
        ReadCacheList(pf);
5243 cbradney 844
        ScCore->setSplashStatus( QObject::tr("Searching for Fonts") );
3 paul 845
        AddUserPath(pf);
3794 cbradney 846
        // Search the system paths
847
        QStringList ftDirs = ScPaths::getSystemFontDirs();
8594 jghali 848
        for (int i = 0; i < ftDirs.count(); i++)
3794 cbradney 849
                AddScalableFonts( ftDirs[i] );
850
        // Search Scribus font path
3831 cbradney 851
        if (!ScPaths::instance().fontDir().isEmpty() && QDir(ScPaths::instance().fontDir()).exists())
3794 cbradney 852
                AddScalableFonts( ScPaths::instance().fontDir() );
2295 cbradney 853
// if fontconfig is there, it does all the work
854
#if HAVE_FONTCONFIG
3794 cbradney 855
        // Search fontconfig paths
8594 jghali 856
        QStringList::iterator fpi, fpend = FontPath.end();
857
        for(fpi = FontPath.begin() ; fpi != fpend; ++fpi)
858
                AddScalableFonts(*fpi);
776 fschmid 859
        AddFontconfigFonts();
860
#else
2295 cbradney 861
// on X11 look there:
18204 fschmid 862
#ifdef Q_OS_LINUX
3 paul 863
        AddXFontPath();
864
        AddXFontServerPath();
2295 cbradney 865
#endif
866
// add user and X11 fonts:
8594 jghali 867
        QStringList::iterator fpi, fpend = FontPath.end();
868
        for(fpi = FontPath.begin() ; fpi != fpend; ++fpi)
869
                AddScalableFonts(*fpi);
494 fschmid 870
#endif
2508 fschmid 871
        updateFontMap();
3559 fschmid 872
        WriteCacheList(pf);
494 fschmid 873
}