Changeset 230


Ignore:
Timestamp:
Oct 16, 2009, 3:22:12 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Implemented simple font caching through the QSettings registry. This gets upto 10x faster font population times at application startup and when unknown families are requested.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/text/qfontdatabase_pm.cpp

    r228 r230  
    4646#include "qfontengine_pm_p.h"
    4747
     48
     49
     50
     51
     52
    4853#include <ft2build.h>
    4954#include FT_FREETYPE_H
     
    5358
    5459QT_BEGIN_NAMESPACE
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
     100
     101
     102
     103
     104
     105
     106
     107
     108
     109
     110
     111
     112
     113
     114
     115
     116
     117
     118
     119
     120
    55121
    56122static void populateDatabase(const QString& fam)
     
    72138    const QString foundryName;
    73139
    74     QList<QByteArray> fontFiles;
     140#ifdef QFONTDATABASE_DEBUG
     141    QTime timer;
     142    timer.start();
     143#endif
     144
     145    QSettings fontCache(QSettings::UserScope, QLatin1String("Trolltech"));
     146    fontCache.beginGroup(QLatin1String("Qt/Fonts/Cache 1.0"));
     147
     148    if (!knownFontFilesInitialized) {
     149        // get the initial list of know font files from the cache (necessary to
     150        // detect deleted font files)
     151        knownFontFilesInitialized = true;
     152        QStringList files = fontCache.childGroups();
     153        foreach(QString file, files) {
     154            file.replace("|", "/");
     155            knownFontFiles.insert(file, FileData());
     156            // note that QFileInfo is empty so the file will be considered as
     157            // NEW which is necessary for the font to get into the database
     158        }
     159    } else {
     160        // reset the 'seen' flag
     161        for (FontFileHash::iterator it = knownFontFiles.begin();
     162             it != knownFontFiles.end(); ++it)
     163            it.value().seen = false;
     164    }
     165
     166    QList<QFileInfo> fontFiles;
    75167
    76168    ULONG bufSize = 0;
     
    101193                                file.append(".PFB");
    102194                            }
    103                             fontFiles << file;
     195                            QFileInfo fileInfo(QFile::decodeName(file));
     196                            QString fileName = fileInfo.canonicalFilePath().toLower();
     197                            if (!fileName.isEmpty()) { // file may have been deleted
     198                                fileInfo.setFile(fileName);
     199                                // check the in-process file name cache
     200                                FileData &cached = knownFontFiles[fileName];
     201                                if (cached.fileInfo.filePath().isEmpty() ||
     202                                    cached.fileInfo.lastModified() != fileInfo.lastModified() ||
     203                                    cached.fileInfo.size() != fileInfo.size()) {
     204                                    // no cache entry or outdated, process it
     205                                    cached.fileInfo = fileInfo;
     206                                    cached.seen = true;
     207                                    fontFiles << fileInfo;
     208                                    FD_DEBUG("populateDatabase: NEW/UPDATED font file %s",
     209                                             qPrintable(fileName));
     210                                } else {
     211                                    // just set the 'seen' flag and skip this font
     212                                    // (it's already in the database)
     213                                    knownFontFiles[fileName].seen = true;
     214                                    FD_DEBUG("populateDatabase: UNCHANGED font file %s",
     215                                             qPrintable(fileName));
     216                                }
     217                            }
    104218                        }
    105219                    }
     
    114228    FT_Library lib = qt_getFreetype();
    115229
    116     foreach(const QByteArray &file, fontFiles) {
    117         FT_Face face;
    118         FT_Error rc = FT_New_Face(lib, file, -1, &face);
    119 
    120         FD_DEBUG("populateDatabase: Font file %s: FT error %d, has %ld faces",
    121                  file.constData(), (int) rc, rc ? -1 : face->num_faces);
    122         if (rc != 0)
    123             continue;
    124 
    125         FT_Long numFaces = face->num_faces;
    126         FT_Done_Face(face);
    127 
    128         // go throuhg each face
    129         for (FT_Long idx = 0; idx < numFaces; ++idx) {
    130             rc = FT_New_Face(lib, file, idx, &face);
    131             if (rc != 0)
    132                 continue;
    133 
    134             QString familyName = QString::fromLatin1(face->family_name);
    135 
    136             // familyName may contain extra spaces (at least this is true for
    137             // TNR.PFB that is reported as "Times New Roman ". Trim them.
    138             familyName = familyName.trimmed();
    139 
    140             QtFontStyle::Key styleKey;
    141 
    142             styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ?
    143                 QFont::StyleItalic : QFont::StyleNormal;
    144 
    145             QList<QFontDatabase::WritingSystem> systems;
    146 
    147             TT_OS2 *os2_table = 0;
    148             if (face->face_flags & FT_FACE_FLAG_SFNT) {
    149                 os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
    150             }
    151             if (os2_table) {
    152                 // map weight and width values
    153                 if (os2_table->usWeightClass < 400)
    154                     styleKey.weight = QFont::Light;
    155                 else if (os2_table->usWeightClass < 600)
    156                     styleKey.weight = QFont::Normal;
    157                 else if (os2_table->usWeightClass < 700)
    158                     styleKey.weight = QFont::DemiBold;
    159                 else if (os2_table->usWeightClass < 800)
    160                     styleKey.weight = QFont::Bold;
    161                 else
    162                     styleKey.weight = QFont::Black;
    163 
    164                 switch (os2_table->usWidthClass) {
    165                     case 1: styleKey.stretch = QFont::UltraCondensed; break;
    166                     case 2: styleKey.stretch = QFont::ExtraCondensed; break;
    167                     case 3: styleKey.stretch = QFont::Condensed; break;
    168                     case 4: styleKey.stretch = QFont::SemiCondensed; break;
    169                     case 5: styleKey.stretch = QFont::Unstretched; break;
    170                     case 6: styleKey.stretch = QFont::SemiExpanded; break;
    171                     case 7: styleKey.stretch = QFont::Expanded; break;
    172                     case 8: styleKey.stretch = QFont::ExtraExpanded; break;
    173                     case 9: styleKey.stretch = QFont::UltraExpanded; break;
    174                     default: styleKey.stretch = QFont::Unstretched; break;
     230    // go through each font file and get available faces
     231    foreach(const QFileInfo &fileInfo, fontFiles) {
     232        QString fileKey = fileInfo.absoluteFilePath().toLower();
     233        QByteArray file = QFile::encodeName(fileKey);
     234
     235        // QSettings uses / for splitting into groups, suppress it
     236        fileKey.replace("/", "|");
     237
     238        QList<FaceData> cachedFaces;
     239
     240        // first, look up the cached data
     241        fontCache.beginGroup(fileKey);
     242
     243        if (fontCache.value(QLatin1String("DateTime")).toDateTime() != fileInfo.lastModified() ||
     244            fontCache.value(QLatin1String("Size")).toUInt() != fileInfo.size()) {
     245            // the cache is outdated or doesn't exist, query the font file
     246
     247            FT_Long numFaces = 0;
     248            FT_Face face;
     249
     250            FT_Error rc = FT_New_Face(lib, file, -1, &face);
     251            if (rc == 0) {
     252                numFaces = face->num_faces;
     253                FT_Done_Face(face);
     254            } else {
     255                // invalid/unsupported font file, numFaces is left 0 so that
     256                // only DateTime and Size will be cached indicating that this
     257                // file is not recognized
     258            }
     259
     260            FD_DEBUG("populateDatabase: Font file %s: FT error %d, has %ld faces",
     261                     file.constData(), (int) rc, numFaces);
     262
     263            // go throuhg each face
     264            for (FT_Long idx = 0; idx < numFaces; ++idx) {
     265                rc = FT_New_Face(lib, file, idx, &face);
     266                if (rc != 0)
     267                    continue;
     268
     269                FaceData cached;
     270
     271                cached.index = idx;
     272                cached.familyName = QString::fromLatin1(face->family_name);
     273
     274                // familyName may contain extra spaces (at least this is true for
     275                // TNR.PFB that is reported as "Times New Roman ". Trim them.
     276                cached.familyName = cached.familyName.trimmed();
     277
     278                cached.styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ?
     279                    QFont::StyleItalic : QFont::StyleNormal;
     280
     281                TT_OS2 *os2_table = 0;
     282                if (face->face_flags & FT_FACE_FLAG_SFNT) {
     283                    os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
    175284                }
    176 
    177                 quint32 unicodeRange[4] = {
    178                     os2_table->ulUnicodeRange1, os2_table->ulUnicodeRange2,
    179                     os2_table->ulUnicodeRange3, os2_table->ulUnicodeRange4
    180                 };
    181                 quint32 codePageRange[2] = {
    182                     os2_table->ulCodePageRange1, os2_table->ulCodePageRange2
    183                 };
    184                 systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
    185             } else {
    186                 // we've only got simple weight information and no stretch
    187                 styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ?
    188                     QFont::Bold : QFont::Normal;
    189                 styleKey.stretch = QFont::Unstretched;
    190             }
    191 
    192             QtFontFamily *family = privateDb()->family(familyName, true);
     285                if (os2_table) {
     286                    // map weight and width values
     287                    if (os2_table->usWeightClass < 400)
     288                        cached.styleKey.weight = QFont::Light;
     289                    else if (os2_table->usWeightClass < 600)
     290                        cached.styleKey.weight = QFont::Normal;
     291                    else if (os2_table->usWeightClass < 700)
     292                        cached.styleKey.weight = QFont::DemiBold;
     293                    else if (os2_table->usWeightClass < 800)
     294                        cached.styleKey.weight = QFont::Bold;
     295                    else
     296                        cached.styleKey.weight = QFont::Black;
     297
     298                    switch (os2_table->usWidthClass) {
     299                        case 1: cached.styleKey.stretch = QFont::UltraCondensed; break;
     300                        case 2: cached.styleKey.stretch = QFont::ExtraCondensed; break;
     301                        case 3: cached.styleKey.stretch = QFont::Condensed; break;
     302                        case 4: cached.styleKey.stretch = QFont::SemiCondensed; break;
     303                        case 5: cached.styleKey.stretch = QFont::Unstretched; break;
     304                        case 6: cached.styleKey.stretch = QFont::SemiExpanded; break;
     305                        case 7: cached.styleKey.stretch = QFont::Expanded; break;
     306                        case 8: cached.styleKey.stretch = QFont::ExtraExpanded; break;
     307                        case 9: cached.styleKey.stretch = QFont::UltraExpanded; break;
     308                        default: cached.styleKey.stretch = QFont::Unstretched; break;
     309                    }
     310
     311                    quint32 unicodeRange[4] = {
     312                        os2_table->ulUnicodeRange1, os2_table->ulUnicodeRange2,
     313                        os2_table->ulUnicodeRange3, os2_table->ulUnicodeRange4
     314                    };
     315                    quint32 codePageRange[2] = {
     316                        os2_table->ulCodePageRange1, os2_table->ulCodePageRange2
     317                    };
     318                    cached.systems =
     319                        determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
     320                } else {
     321                    // we've only got simple weight information and no stretch
     322                    cached.styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ?
     323                        QFont::Bold : QFont::Normal;
     324                    cached.styleKey.stretch = QFont::Unstretched;
     325                }
     326
     327                cached.fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
     328
     329                cached.smoothScalable = face->face_flags & FT_FACE_FLAG_SCALABLE;
     330
     331                // the font may both be scalable and contain fixed size bitmaps
     332                if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) {
     333                    for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) {
     334                        cached.pixelSizes << face->available_sizes[i].height;
     335                    }
     336                }
     337
     338                cachedFaces << cached;
     339
     340                FT_Done_Face(face);
     341            }
     342
     343            // store the data into the cache
     344            fontCache.setValue(QLatin1String("DateTime"), fileInfo.lastModified());
     345            fontCache.setValue(QLatin1String("Size"), fileInfo.size());
     346            foreach(FaceData cached, cachedFaces) {
     347                QByteArray rawData;
     348                QDataStream data(&rawData, QIODevice::WriteOnly);
     349                data << cached;
     350
     351                QString face = QString::number(cached.index);
     352                fontCache.beginGroup(face);
     353                fontCache.setValue(QLatin1String("Info"), rawData);
     354                fontCache.endGroup();
     355            }
     356        } else {
     357            // take the face data from the cache
     358
     359            QStringList faces = fontCache.childGroups();
     360
     361            FD_DEBUG("populateDatabase: Font file %s: IN CACHE, has %d faces",
     362                     file.constData(), faces.count());
     363
     364            foreach(QString face, faces) {
     365                bool ok = false;
     366                FaceData cached;
     367                cached.index = face.toInt(&ok);
     368                if (!ok || cached.index < 0) // not a valid index
     369                    continue;
     370
     371                fontCache.beginGroup(face);
     372                QByteArray rawData =
     373                    fontCache.value(QLatin1String("Info")).toByteArray();
     374                QDataStream data(rawData);
     375                data >> cached;
     376                fontCache.endGroup();
     377
     378                cachedFaces << cached;
     379            }
     380        }
     381
     382        fontCache.endGroup();
     383
     384        // go throuhg each cached face and add it to the database
     385        foreach(FaceData cached, cachedFaces) {
     386
     387            QtFontFamily *family = privateDb()->family(cached.familyName, true);
    193388
    194389            // @todo is it possible that the same family is both fixed and not?
    195             Q_ASSERT(!family->fixedPitch || face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
    196             family->fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
    197 
    198             if (systems.isEmpty() || familyName.compare("Workplace Sans")) {
     390            Q_ASSERT(!family->fixedPitch || );
     391            family->fixedPitch = ;
     392
     393            if (familyName.compare("Workplace Sans")) {
    199394                // it was hard or impossible to determine the actual writing system
    200395                // of the font (as in case of OS/2 bitmap and PFB fonts for which it is
     
    210405                    family->writingSystems[ws] = QtFontFamily::Supported;
    211406            } else {
    212                 for (int i = 0; i < systems.count(); ++i)
    213                     family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
     407                for (int i = 0; i < systems.count(); ++i)
     408                    family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
    214409            }
    215410
    216411            QtFontFoundry *foundry = family->foundry(foundryName, true);
    217             QtFontStyle *style = foundry->style(styleKey, true);
     412            QtFontStyle *style = foundry->style(styleKey, true);
    218413
    219414            // so far, all recognized fonts are antialiased
    220415            style->antialiased = true;
    221416
    222             if ((face->face_flags & FT_FACE_FLAG_SCALABLE) &&
    223                 !style->smoothScalable) {
     417            if (cached.smoothScalable && !style->smoothScalable) {
    224418                // add new scalable style only if it hasn't been already added --
    225419                // the first one of two duplicate (in Qt terms) non-bitmap font
     
    229423                    style->pixelSize(SMOOTH_SCALABLE, true);
    230424                size->fileName = file;
    231                 size->fileIndex = idx;
    232             }
    233 
    234             // the font may both be scalable and contain fixed size bitmaps
    235             if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) {
    236                 for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) {
    237                     QtFontSize *size =
    238                         style->pixelSize(face->available_sizes[i].height, true);
    239                     // the first bitmap style with a given pixel and point size wins
    240                     if (!size->fileName.isEmpty())
    241                         continue;
    242                     size->fileName = file;
    243                     size->fileIndex = idx;
    244                 }
    245             }
    246 
    247             FT_Done_Face(face);
    248         }
    249     }
     425                size->fileIndex = cached.index;
     426            }
     427
     428            foreach(unsigned short pixelSize, cached.pixelSizes) {
     429                QtFontSize *size = style->pixelSize(pixelSize, true);
     430                // the first bitmap style with a given pixel and point size wins
     431                if (!size->fileName.isEmpty())
     432                    continue;
     433                size->fileName = file;
     434                size->fileIndex = cached.index;
     435            }
     436        }
     437    }
     438
     439    // go through the known file list to detect what files have been removed
     440    for (FontFileHash::iterator it = knownFontFiles.begin();
     441         it != knownFontFiles.end();) {
     442        if (!it.value().seen) {
     443            FD_DEBUG("populateDatabase: DELETED font file %s",
     444                     qPrintable(it.key()));
     445            // remove from the both caches
     446            QString fileKey = it.key();
     447            fileKey.replace("/", "|");
     448            fontCache.remove(fileKey);
     449            it = knownFontFiles.erase(it);
     450            // @todo should we remove all references to this file from the
     451            // font database? My concern is that this font may be in use by Qt
     452            // and its glyphs may be still cached when file deletion happens
     453        } else {
     454            ++it;
     455        }
     456    }
     457
     458#ifdef QFONTDATABASE_DEBUG
     459    FD_DEBUG("populateDatabase: took %d ms", timer.elapsed());
     460#endif
    250461}
    251462
Note: See TracChangeset for help on using the changeset viewer.