Subversion Repositories Scribus

Rev

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

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