Ignore:
Timestamp:
Oct 14, 2009, 2:32:13 AM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Changed to using the Freetype2 library for populating the fonts. Implemented font matching against populated fonts.

File:
1 edited

Legend:

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

    r220 r222  
    4646#include "qfontengine_pm_p.h"
    4747
     48#include <ft2build.h>
     49#include FT_FREETYPE_H
     50#include FT_TYPES_H
     51#include FT_TRUETYPE_TABLES_H
     52
    4853QT_BEGIN_NAMESPACE
    4954
     
    6671    const QString foundryName;
    6772
    68     int hps = qt_display_ps();
    69 
    70     LONG cFonts = 0;
    71     cFonts = GpiQueryFonts(hps, QF_PUBLIC, NULL, &cFonts, 0, NULL);
    72     PFONTMETRICS afm = new FONTMETRICS[cFonts];
    73     GpiQueryFonts(hps, QF_PUBLIC, NULL, &cFonts, sizeof(FONTMETRICS), afm);
    74 
    75     LONG info[2];
    76     DevQueryCaps(GpiQueryDevice(hps), CAPS_HORIZONTAL_FONT_RES, 2, info);
    77     LONG xRes = info[0], yRes = info[1];
    78 
    79     for (PFONTMETRICS fm = afm ; cFonts ; cFonts--, fm++) {
    80         // ignore the default (lMatch = 0) GPI font, since it is always
    81         // present with non-zero lMatch in the list
    82         if (!fm->lMatch)
     73    QList<QByteArray> fontFiles;
     74
     75    ULONG bufSize = 0;
     76    BOOL ok = PrfQueryProfileSize(HINI_USERPROFILE, "PM_Fonts", 0, &bufSize);
     77    Q_ASSERT(ok);
     78    if (ok) {
     79        char *buffer = new char[bufSize + 1 /*terminating NULL*/];
     80        Q_ASSERT(buffer);
     81        if (buffer) {
     82            ULONG bufLen = PrfQueryProfileString(HINI_USERPROFILE, "PM_Fonts", 0, 0,
     83                                                 buffer, bufSize);
     84            if (bufLen) {
     85                char *key = buffer;
     86                while (*key) {
     87                    ULONG keySize = 0;
     88                    ok = PrfQueryProfileSize(HINI_USERPROFILE, "PM_Fonts", key,
     89                                             &keySize);
     90                    if (ok) {
     91                        QByteArray file(keySize, 0);
     92                        ULONG keyLen =
     93                            PrfQueryProfileString(HINI_USERPROFILE, "PM_Fonts", key, 0,
     94                                                  file.data(), file.size());
     95                        file.truncate(keyLen - 1 /*terminating NULL*/);
     96                        if (!file.isEmpty()) {
     97                            // FreeType doesn't understand .OFM but understands .PFB
     98                            if (file.toUpper().endsWith(".OFM")) {
     99                                file.chop(4);
     100                                file.append(".PFB");
     101                            }
     102                            fontFiles << file;
     103                        }
     104                    }
     105                    key += strlen(key) + 1;
     106                }
     107            }
     108            delete buffer;
     109        }
     110    }
     111
     112    extern FT_Library qt_getFreetype(); // qfontengine_ft.cpp
     113    FT_Library lib = qt_getFreetype();
     114
     115    foreach(const QByteArray &file, fontFiles) {
     116        FT_Face face;
     117        FT_Error rc = FT_New_Face(lib, file, -1, &face);
     118
     119        FD_DEBUG("populateDatabase: Font file %s: FT error %d, has %ld faces",
     120                 file.constData(), (int) rc, rc ? -1 : face->num_faces);
     121        if (rc != 0)
    83122            continue;
    84123
    85         QString familyName = QString::fromLocal8Bit(fm->szFamilyname);
    86         bool italic = fm->fsSelection & FM_SEL_ITALIC;
    87         bool fixed = fm->fsType & FM_TYPE_FIXED;
    88         bool scalable = fm->fsDefn & FM_DEFN_OUTLINE;
    89         USHORT weight = fm->usWeightClass;
    90         USHORT width = fm->usWidthClass;
    91 
    92         // ignore bitmap fonts that do not match the current device resolution
    93         if (!scalable && (fm->sXDeviceRes != xRes || fm->sYDeviceRes != yRes))
    94             continue;
    95 
    96         // the "@family" fonts are just the same as "family". Ignore them.
    97         if (familyName[0] == '@')
    98             continue;
    99 
    100         QtFontStyle::Key styleKey;
    101         styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
    102 
    103         if (weight < 4)
    104             styleKey.weight = QFont::Light;
    105         else if (weight < 6)
    106             styleKey.weight = QFont::Normal;
    107         else if (weight < 7)
    108             styleKey.weight = QFont::DemiBold;
    109         else if (weight < 8)
    110             styleKey.weight = QFont::Bold;
    111         else
    112             styleKey.weight = QFont::Black;
    113 
    114         switch (width) {
    115             case 1: styleKey.stretch = QFont::UltraCondensed; break;
    116             case 2: styleKey.stretch = QFont::ExtraCondensed; break;
    117             case 3: styleKey.stretch = QFont::Condensed; break;
    118             case 4: styleKey.stretch = QFont::SemiCondensed; break;
    119             case 5: styleKey.stretch = QFont::Unstretched; break;
    120             case 6: styleKey.stretch = QFont::SemiExpanded; break;
    121             case 7: styleKey.stretch = QFont::Expanded; break;
    122             case 8: styleKey.stretch = QFont::ExtraExpanded; break;
    123             case 9: styleKey.stretch = QFont::UltraExpanded; break;
    124             default: styleKey.stretch = QFont::Unstretched; break;
    125         }
    126 
    127         // @todo why?
    128         familyName.replace('-', ' ');
    129 
    130         QtFontFamily *family = privateDb()->family(familyName, true);
    131         // @todo is it possible that the same family is both fixed and not?
    132         family->fixedPitch = fixed;
    133 
    134         QtFontFoundry *foundry = family->foundry(foundryName, true);
    135         QtFontStyle *style = foundry->style(styleKey, true);
    136 
    137         // add new scalable style only if it hasn't been already added --
    138         // the first one of two duplicate (in Qt terms) non-bitmap font
    139         // styles wins.
    140         if (scalable && style->smoothScalable)
    141             continue;
    142 
    143         if (style->faceName[0]) {
    144             // if a duplicate (having the same style in Qt terms) bitmap
    145             // font is encountered but it has the different facename, ignore
    146             // it (we have only one facename field per style -- it should be
    147             // the same for all sizes otherwise we will not be able to create
    148             // a font with some sizes later).
    149             if (strcmp(style->faceName, fm->szFacename))
     124        FT_Long numFaces = face->num_faces;
     125        FT_Done_Face(face);
     126
     127        // go throuhg each face
     128        for (FT_Long idx = 0; idx < numFaces; ++idx) {
     129            rc = FT_New_Face(lib, file, idx, &face);
     130            if (rc != 0)
    150131                continue;
    151         } else {
    152             strcpy(style->faceName, fm->szFacename);
    153         }
    154 
    155         if (scalable) {
    156             style->smoothScalable = TRUE;
    157             QtFontSize *size =
    158                 style->pixelSize(SMOOTH_SCALABLE, TRUE, fm->sNominalPointSize);
    159             size->lMatch = fm->lMatch;
    160         } else {
    161             QtFontSize *size =
    162                 style->pixelSize(fm->lEmHeight, TRUE, fm->sNominalPointSize);
    163             // the first bitmap style with a given pixel and point size wins
    164             if (size->lMatch)
    165                 continue;
    166             size->lMatch = fm->lMatch;
    167         }
    168     }
    169 
    170     delete[] afm;
     132
     133            QString familyName = QString::fromLatin1(face->family_name);
     134
     135            QtFontStyle::Key styleKey;
     136
     137            styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ?
     138                QFont::StyleItalic : QFont::StyleNormal;
     139
     140            TT_OS2 *os2_table = 0;
     141            if (face->face_flags & FT_FACE_FLAG_SFNT) {
     142                os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
     143            }
     144            if (os2_table) {
     145                // map weight and width values
     146                if (os2_table->usWeightClass < 400)
     147                    styleKey.weight = QFont::Light;
     148                else if (os2_table->usWeightClass < 600)
     149                    styleKey.weight = QFont::Normal;
     150                else if (os2_table->usWeightClass < 700)
     151                    styleKey.weight = QFont::DemiBold;
     152                else if (os2_table->usWeightClass < 800)
     153                    styleKey.weight = QFont::Bold;
     154                else
     155                    styleKey.weight = QFont::Black;
     156
     157                switch (os2_table->usWidthClass) {
     158                    case 1: styleKey.stretch = QFont::UltraCondensed; break;
     159                    case 2: styleKey.stretch = QFont::ExtraCondensed; break;
     160                    case 3: styleKey.stretch = QFont::Condensed; break;
     161                    case 4: styleKey.stretch = QFont::SemiCondensed; break;
     162                    case 5: styleKey.stretch = QFont::Unstretched; break;
     163                    case 6: styleKey.stretch = QFont::SemiExpanded; break;
     164                    case 7: styleKey.stretch = QFont::Expanded; break;
     165                    case 8: styleKey.stretch = QFont::ExtraExpanded; break;
     166                    case 9: styleKey.stretch = QFont::UltraExpanded; break;
     167                    default: styleKey.stretch = QFont::Unstretched; break;
     168                }
     169            } else {
     170                // we've only got simple weight information and no stretch
     171                styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ?
     172                    QFont::Bold : QFont::Normal;
     173                styleKey.stretch = QFont::Unstretched;
     174            }
     175
     176            QtFontFamily *family = privateDb()->family(familyName, true);
     177
     178            // @todo is it possible that the same family is both fixed and not?
     179            Q_ASSERT(!family->fixedPitch || face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
     180            family->fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
     181
     182            // writing systems aren't used ATM, let's support just Any
     183            family->writingSystems[QFontDatabase::Any] = QtFontFamily::Supported;
     184
     185            QtFontFoundry *foundry = family->foundry(foundryName, true);
     186            QtFontStyle *style = foundry->style(styleKey, true);
     187
     188            // so far, all recognized fonts are antialiased
     189            style->antialiased = true;
     190
     191            if ((face->face_flags & FT_FACE_FLAG_SCALABLE) &&
     192                !style->smoothScalable) {
     193                // add new scalable style only if it hasn't been already added --
     194                // the first one of two duplicate (in Qt terms) non-bitmap font
     195                // styles wins.
     196                style->smoothScalable = true;
     197                QtFontSize *size =
     198                    style->pixelSize(SMOOTH_SCALABLE, true);
     199                size->fileName = file;
     200                size->fileIndex = idx;
     201            }
     202
     203            // the font may both be scalable and contain fixed size bitmaps
     204            if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) {
     205                for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) {
     206                    QtFontSize *size =
     207                        style->pixelSize(face->available_sizes[i].height, true);
     208                    // the first bitmap style with a given pixel and point size wins
     209                    if (!size->fileName.isEmpty())
     210                        continue;
     211                    size->fileName = file;
     212                    size->fileIndex = idx;
     213                }
     214            }
     215
     216            FT_Done_Face(face);
     217        }
     218    }
    171219}
    172220
     
    181229#ifdef QFONTDATABASE_DEBUG
    182230    // print the database
     231    qDebug("initializeDb:");
    183232    for (int f = 0; f < db->count; f++) {
    184233        QtFontFamily *family = db->families[f];
    185234        qDebug("    %s: %p", qPrintable(family->name), family);
    186235        populateDatabase(family->name);
    187 
    188236#if 1
    189237        qDebug("        scripts supported:");
     
    193241        for (int fd = 0; fd < family->count; fd++) {
    194242            QtFontFoundry *foundry = family->foundries[fd];
    195             qDebug("        %s", qPrintable(foundry->name));
     243            qDebug("        %s", foundry->name.isEmpty() ? "(empty foundry)" :
     244                   qPrintable(foundry->name));
    196245            for (int s = 0; s < foundry->count; s++) {
    197246                QtFontStyle *style = foundry->styles[s];
    198247                qDebug("            style: style=%d weight=%d smooth=%d",  style->key.style,
    199248                       style->key.weight, style->smoothScalable);
    200                 if(!style->smoothScalable) {
    201                     for(int i = 0; i < style->count; ++i) {
    202                         qDebug("                %d", style->pixelSizes[i].pixelSize);
    203                     }
     249                for(int i = 0; i < style->count; ++i) {
     250                    if (style->pixelSizes[i].pixelSize == SMOOTH_SCALABLE)
     251                        qDebug("                smooth %s:%d",
     252                               style->pixelSizes[i].fileName.constData(),
     253                               style->pixelSizes[i].fileIndex);
     254                    else
     255                        qDebug("                %d px %s:%d", style->pixelSizes[i].pixelSize,
     256                               style->pixelSizes[i].fileName.constData(),
     257                               style->pixelSizes[i].fileIndex);
    204258                }
    205259            }
     
    220274}
    221275
     276static const char *styleHint(const QFontDef &request)
     277{
     278    const char *stylehint = 0;
     279    switch (request.styleHint) {
     280    case QFont::SansSerif:
     281        stylehint = "Helvetica";
     282        break;
     283    case QFont::Serif:
     284        stylehint = "Roman";
     285        break;
     286    case QFont::TypeWriter:
     287        stylehint = "Courier";
     288        break;
     289    default:
     290        if (request.fixedPitch)
     291            stylehint = "Courier";
     292        else
     293            stylehint = "Helvetica";
     294        break;
     295    }
     296    return stylehint;
     297}
     298
     299static QFontDef fontDescToFontDef(const QFontDef &req, const QtFontDesc &desc)
     300{
     301    static LONG dpi = -1;
     302    if (dpi == -1) {
     303        // PM cannot change resolutions on the fly so cache it
     304        int hps = qt_display_ps();
     305        DevQueryCaps(GpiQueryDevice(hps), CAPS_HORIZONTAL_FONT_RES, 1, &dpi);
     306    }
     307
     308    QFontDef fontDef;
     309
     310    fontDef.family = desc.family->name;
     311
     312    if (desc.size->pixelSize == SMOOTH_SCALABLE) {
     313        // scalable font matched, calculate the missing size (points or pixels)
     314        fontDef.pointSize = req.pointSize;
     315        fontDef.pixelSize = req.pixelSize;
     316        if (req.pointSize < 0) {
     317            fontDef.pointSize = req.pixelSize * 72. / dpi;
     318        } else if (req.pixelSize == -1) {
     319            fontDef.pixelSize = qRound(req.pointSize * dpi / 72.);
     320        }
     321    } else {
     322        // non-scalable font matched, calculate both point and pixel size
     323        fontDef.pixelSize = desc.size->pixelSize;
     324        fontDef.pointSize = desc.size->pixelSize * 72. / dpi;
     325    }
     326
     327    fontDef.styleStrategy = req.styleStrategy;
     328    fontDef.styleHint = req.styleHint;
     329
     330    fontDef.weight = desc.style->key.weight;
     331    fontDef.fixedPitch = desc.family->fixedPitch;
     332    fontDef.style = desc.style->key.style;
     333    fontDef.stretch = desc.style->key.stretch;
     334
     335    return fontDef;
     336}
     337
     338static QFontEngine *loadEngine(const QFontDef &req, const QtFontDesc &desc)
     339{
     340    QFontEngineFT *fe = new QFontEnginePMFT(fontDescToFontDef(req, desc));
     341    Q_ASSERT(fe);
     342    if (fe) {
     343        QFontEngine::FaceId faceId;
     344        faceId.filename = desc.size->fileName;
     345        faceId.index = desc.size->fileIndex;
     346        fe->init(faceId, desc.style->antialiased);
     347        if (fe->invalid()) {
     348            FM_DEBUG("   --> invalid!\n");
     349            delete fe;
     350            fe = 0;
     351        }
     352    }
     353    return fe;
     354}
     355
    222356static QFontEngine *loadPM(const QFontPrivate *d, int script, const QFontDef &req)
    223357{
    224     // @todo initializeDb() and stuff, get the engine
    225     QFontDef fd = req;
    226     QFontEngine *fe = new QFontEnginePMFT(fd);
     358    // list of families to try
     359    QStringList families = familyList(req);
     360
     361    const char *stylehint = styleHint(d->request);
     362    if (stylehint)
     363        families << QLatin1String(stylehint);
     364
     365    // add the default family
     366    QString defaultFamily = QApplication::font().family();
     367    if (! families.contains(defaultFamily))
     368        families << defaultFamily;
     369
     370    // add QFont::defaultFamily() to the list, for compatibility with
     371    // previous versions
     372    families << QApplication::font().defaultFamily();
     373
     374    // null family means find the first font matching the specified script
     375    families << QString();
     376
     377    QtFontDesc desc;
     378    QFontEngine *fe = 0;
     379    QList<int> blacklistedFamilies;
     380
     381    while (!fe) {
     382        for (int i = 0; i < families.size(); ++i) {
     383            QString family, foundry;
     384            parseFontName(families.at(i), foundry, family);
     385            FM_DEBUG("loadPM: >>>>>>>>>>>>>> trying to match '%s'", qPrintable(family));
     386            QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies);
     387            if (desc.family)
     388                break;
     389        }
     390        if (!desc.family)
     391            break;
     392        fe = loadEngine(req, desc);
     393        if (!fe)
     394            blacklistedFamilies.append(desc.familyIndex);
     395    }
    227396    return fe;
    228397}
Note: See TracChangeset for help on using the changeset viewer.