src/gui/text/qfont.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qfont.h"
00025 #include "qpaintdevice.h"
00026 #include "qfontdatabase.h"
00027 #include "qfontmetrics.h"
00028 #include "qfontinfo.h"
00029 #include "qpainter.h"
00030 #include "qhash.h"
00031 #include "qdatastream.h"
00032 #include "qapplication.h"
00033 #include "qstringlist.h"
00034 
00035 #include "qthread.h"
00036 
00037 #include <private/qunicodetables_p.h>
00038 #include "qfont_p.h"
00039 #include <private/qfontengine_p.h>
00040 #include <private/qpainter_p.h>
00041 #include <private/qtextengine_p.h>
00042 
00043 #ifdef Q_WS_X11
00044 #include "qx11info_x11.h"
00045 extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
00046 #endif
00047 #ifdef Q_WS_QWS
00048 #include "qscreen_qws.h"
00049 #endif
00050 
00051 // #define QFONTCACHE_DEBUG
00052 #ifdef QFONTCACHE_DEBUG
00053 #  define FC_DEBUG qDebug
00054 #else
00055 #  define FC_DEBUG if (false) qDebug
00056 #endif
00057 
00058 
00059 bool QFontDef::exactMatch(const QFontDef &other) const
00060 {
00061     /*
00062       QFontDef comparison is more complicated than just simple
00063       per-member comparisons.
00064 
00065       When comparing point/pixel sizes, either point or pixelsize
00066       could be -1.  in This case we have to compare the non negative
00067       size value.
00068 
00069       This test will fail if the point-sizes differ by 1/2 point or
00070       more or they do not round to the same value.  We have to do this
00071       since our API still uses 'int' point-sizes in the API, but store
00072       deci-point-sizes internally.
00073 
00074       To compare the family members, we need to parse the font names
00075       and compare the family/foundry strings separately.  This allows
00076       us to compare e.g. "Helvetica" and "Helvetica [Adobe]" with
00077       positive results.
00078     */
00079     if (pixelSize != -1 && other.pixelSize != -1) {
00080         if (pixelSize != other.pixelSize)
00081             return false;
00082     } else if (pointSize != -1 && other.pointSize != -1) {
00083         if (pointSize != other.pointSize)
00084             return false;
00085     } else {
00086         return false;
00087     }
00088 
00089     if (!ignorePitch && !other.ignorePitch && fixedPitch != other.fixedPitch)
00090         return false;
00091 
00092     if (stretch != 0 && other.stretch != 0 && stretch != other.stretch)
00093         return false;
00094 
00095     QString this_family, this_foundry, other_family, other_foundry;
00096     QFontDatabase::parseFontName(family, this_foundry, this_family);
00097     QFontDatabase::parseFontName(other.family, other_foundry, other_family);
00098 
00099     return (styleHint     == other.styleHint
00100             && styleStrategy == other.styleStrategy
00101             && weight        == other.weight
00102             && style        == other.style
00103             && this_family   == other_family
00104             && (this_foundry.isEmpty()
00105                 || other_foundry.isEmpty()
00106                 || this_foundry == other_foundry)
00107 #ifdef Q_WS_X11
00108             && addStyle == other.addStyle
00109 #endif // Q_WS_X11
00110        );
00111 }
00112 
00113 #ifdef Q_WS_WIN
00114 extern HDC shared_dc;
00115 #endif
00116 
00117 extern bool qt_is_gui_used;
00118 
00119 Q_GUI_EXPORT int qt_defaultDpi()
00120 {
00121     if (!qt_is_gui_used)
00122         return 75;
00123 
00124     int dpi;
00125 #ifdef Q_WS_X11
00126     dpi = QX11Info::appDpiY();
00127 #elif defined(Q_WS_WIN)
00128     dpi = GetDeviceCaps(shared_dc,LOGPIXELSY);
00129 #elif defined(Q_WS_MAC)
00130     short hr;
00131     short mdpi;
00132     ScreenRes(&hr, &mdpi);
00133     dpi = int(mdpi);
00134 #elif defined(Q_WS_QWS)
00135     if (!qt_screen)
00136         return 72;
00137     QScreen *screen = qt_screen;
00138     const QList<QScreen*> subScreens = qt_screen->subScreens();
00139     if (!subScreens.isEmpty())
00140         screen = subScreens.at(0);
00141     dpi = qRound(screen->height() / double(screen->physicalHeight() / 25.4));
00142 #endif // Q_WS_X11
00143 
00144     return dpi;
00145 }
00146 
00147 QFontPrivate::QFontPrivate()
00148     : engineData(0), dpi(qt_defaultDpi()), screen(0),
00149       rawMode(false), underline(false), overline(false), strikeOut(false), kerning(true)
00150 {
00151     ref = 1;
00152 #ifdef Q_WS_X11
00153     screen = QX11Info::appScreen();
00154 #endif
00155 #ifdef Q_WS_WIN
00156     hdc = 0;
00157 #endif
00158 }
00159 
00160 QFontPrivate::QFontPrivate(const QFontPrivate &other)
00161     : request(other.request), engineData(0), dpi(other.dpi), screen(other.screen),
00162       rawMode(other.rawMode), underline(other.underline), overline(other.overline),
00163       strikeOut(other.strikeOut), kerning(other.kerning)
00164 {
00165     ref = 1;
00166 #ifdef Q_WS_WIN
00167     hdc = other.hdc;
00168 #endif
00169 }
00170 
00171 QFontPrivate::~QFontPrivate()
00172 {
00173     if (engineData)
00174         engineData->ref.deref();
00175     engineData = 0;
00176 }
00177 
00178 void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
00179 {
00180     Q_ASSERT(other != 0);
00181 
00182     dpi = other->dpi;
00183 
00184     if ((mask & Complete) == Complete) return;
00185 
00186     // assign the unset-bits with the set-bits of the other font def
00187     if (! (mask & Family))
00188         request.family = other->request.family;
00189 
00190     if (! (mask & Size)) {
00191         request.pointSize = other->request.pointSize;
00192         request.pixelSize = other->request.pixelSize;
00193     }
00194 
00195     if (! (mask & StyleHint))
00196         request.styleHint = other->request.styleHint;
00197 
00198     if (! (mask & StyleStrategy))
00199         request.styleStrategy = other->request.styleStrategy;
00200 
00201     if (! (mask & Weight))
00202         request.weight = other->request.weight;
00203 
00204     if (! (mask & Style))
00205         request.style = other->request.style;
00206 
00207     if (! (mask & FixedPitch))
00208         request.fixedPitch = other->request.fixedPitch;
00209 
00210     if (! (mask & Stretch))
00211         request.stretch = other->request.stretch;
00212 
00213     if (! (mask & Underline))
00214         underline = other->underline;
00215 
00216     if (! (mask & Overline))
00217         overline = other->overline;
00218 
00219     if (! (mask & StrikeOut))
00220         strikeOut = other->strikeOut;
00221 
00222     if (! (mask & Kerning))
00223         kerning = other->kerning;
00224 }
00225 
00226 
00227 
00228 
00229 QFontEngineData::QFontEngineData()
00230 {
00231     ref = 1;
00232 #if defined(Q_WS_X11) || defined(Q_WS_WIN)
00233     memset(engines, 0, QUnicodeTables::ScriptCount * sizeof(QFontEngine *));
00234 #else
00235     engine = 0;
00236 #endif // Q_WS_X11 || Q_WS_WIN
00237 }
00238 
00239 QFontEngineData::~QFontEngineData()
00240 {
00241 #if defined(Q_WS_X11) || defined(Q_WS_WIN)
00242     for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
00243         if (engines[i])
00244             engines[i]->ref.deref();
00245         engines[i] = 0;
00246     }
00247 #else
00248     if (engine)
00249         engine->ref.deref();
00250     engine = 0;
00251 #endif // Q_WS_X11 || Q_WS_WIN
00252 }
00253 
00254 
00255 
00256 
00435 void qt_font_tread_test()
00436 {
00437     if (QApplication::instance() && QThread::currentThread() != QApplication::instance()->thread())
00438         qWarning("QFont: It is not safe to use text and fonts outside the GUI thread");
00439 }
00440 
00444 QFont::QFont(const QFont &font, QPaintDevice *pd)
00445     : resolve_mask(font.resolve_mask)
00446 {
00447     Q_ASSERT(pd != 0);
00448     int dpi = pd->logicalDpiY();
00449 #ifdef Q_WS_X11
00450     const QX11Info *info = qt_x11Info(pd);
00451     int screen = info ? info->screen() : 0;
00452 #else
00453     const int screen = 0;
00454 #endif
00455     if (font.d->dpi != dpi || font.d->screen != screen ) {
00456         d = new QFontPrivate(*font.d);
00457         d->dpi = dpi;
00458         d->screen = screen;
00459     } else {
00460         d = font.d;
00461         d->ref.ref();
00462     }
00463 #ifdef Q_WS_WIN
00464     if (pd->devType() == QInternal::Printer && pd->getDC())
00465         d->hdc = pd->getDC();
00466 #endif
00467 }
00468 
00472 QFont::QFont(QFontPrivate *data)
00473     : resolve_mask(QFontPrivate::Complete)
00474 {
00475     qt_font_tread_test();
00476 
00477     d = data;
00478     d->ref.ref();
00479 }
00480 
00484 void QFont::detach()
00485 {
00486     if (d->ref == 1) {
00487         if (d->engineData)
00488             d->engineData->ref.deref();
00489         d->engineData = 0;
00490         return;
00491     }
00492 
00493     qAtomicDetach(d);
00494 }
00495 
00501 QFont::QFont()
00502     :d(QApplication::font().d), resolve_mask(0)
00503 {
00504     d->ref.ref();
00505 }
00506 
00523 QFont::QFont(const QString &family, int pointSize, int weight, bool italic)
00524     :d(new QFontPrivate)
00525 {
00526     qt_font_tread_test();
00527 
00528     resolve_mask = QFontPrivate::Family;
00529 
00530     if (pointSize <= 0) {
00531         pointSize = 12;
00532     } else {
00533         resolve_mask |= QFontPrivate::Size;
00534     }
00535 
00536     if (weight < 0) {
00537         weight = Normal;
00538     } else {
00539         resolve_mask |= QFontPrivate::Weight | QFontPrivate::Style;
00540     }
00541 
00542     d->request.family = family;
00543     d->request.pointSize = qreal(pointSize);
00544     d->request.pixelSize = -1;
00545     d->request.weight = weight;
00546     d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
00547 }
00548 
00552 QFont::QFont(const QFont &font)
00553 {
00554     d = font.d;
00555     d->ref.ref();
00556     resolve_mask = font.resolve_mask;
00557 }
00558 
00562 QFont::~QFont()
00563 {
00564     if (!d->ref.deref())
00565         delete d;
00566 }
00567 
00571 QFont &QFont::operator=(const QFont &font)
00572 {
00573     qAtomicAssign(d, font.d);
00574     resolve_mask = font.resolve_mask;
00575     return *this;
00576 }
00577 
00584 QString QFont::family() const
00585 {
00586     return d->request.family;
00587 }
00588 
00602 void QFont::setFamily(const QString &family)
00603 {
00604     detach();
00605 
00606     d->request.family = family;
00607 #if defined(Q_WS_X11)
00608     d->request.addStyle.clear();
00609 #endif // Q_WS_X11
00610 
00611     resolve_mask |= QFontPrivate::Family;
00612 }
00613 
00620 int QFont::pointSize() const
00621 {
00622     return qRound(d->request.pointSize);
00623 }
00624 
00631 void QFont::setPointSize(int pointSize)
00632 {
00633     Q_ASSERT_X (pointSize > 0, "QFont::setPointSize", "point size must be greater than 0");
00634 
00635     detach();
00636 
00637     d->request.pointSize = qreal(pointSize);
00638     d->request.pixelSize = -1;
00639 
00640     resolve_mask |= QFontPrivate::Size;
00641 }
00642 
00650 void QFont::setPointSizeF(qreal pointSize)
00651 {
00652     Q_ASSERT_X(pointSize > 0.0, "QFont::setPointSizeF", "point size must be greater than 0");
00653 
00654     detach();
00655 
00656     d->request.pointSize = pointSize;
00657     d->request.pixelSize = -1;
00658 
00659     resolve_mask |= QFontPrivate::Size;
00660 }
00661 
00668 qreal QFont::pointSizeF() const
00669 {
00670     return d->request.pointSize;
00671 }
00672 
00682 void QFont::setPixelSize(int pixelSize)
00683 {
00684     if (pixelSize <= 0) {
00685         qWarning("QFont::setPixelSize: Pixel size <= 0 (%d)", pixelSize);
00686         return;
00687     }
00688 
00689     detach();
00690 
00691     d->request.pixelSize = pixelSize;
00692     d->request.pointSize = -1;
00693 
00694     resolve_mask |= QFontPrivate::Size;
00695 }
00696 
00704 int QFont::pixelSize() const
00705 {
00706     return d->request.pixelSize;
00707 }
00708 
00709 #ifdef QT3_SUPPORT
00710 
00715 void QFont::setPixelSizeFloat(qreal pixelSize)
00716 {
00717     setPixelSize((int)pixelSize);
00718 }
00719 #endif
00720 
00743 QFont::Style QFont::style() const
00744 {
00745     return (QFont::Style)d->request.style;
00746 }
00747 
00748 
00754 void QFont::setStyle(Style style)
00755 {
00756     detach();
00757 
00758     d->request.style = style;
00759     resolve_mask |= QFontPrivate::Style;
00760 }
00761 
00768 int QFont::weight() const
00769 {
00770     return d->request.weight;
00771 }
00772 
00795 void QFont::setWeight(int weight)
00796 {
00797     Q_ASSERT_X(weight >= 0 && weight <= 99, "QFont::setWeight", "Weight must be between 0 and 99");
00798 
00799     detach();
00800 
00801     d->request.weight = weight;
00802     resolve_mask |= QFontPrivate::Weight;
00803 }
00804 
00831 bool QFont::underline() const
00832 {
00833     return d->underline;
00834 }
00835 
00842 void QFont::setUnderline(bool enable)
00843 {
00844     detach();
00845 
00846     d->underline = enable;
00847     resolve_mask |= QFontPrivate::Underline;
00848 }
00849 
00855 bool QFont::overline() const
00856 {
00857     return d->overline;
00858 }
00859 
00865 void QFont::setOverline(bool enable)
00866 {
00867     detach();
00868 
00869     d->overline = enable;
00870     resolve_mask |= QFontPrivate::Overline;
00871 }
00872 
00878 bool QFont::strikeOut() const
00879 {
00880     return d->strikeOut;
00881 }
00882 
00889 void QFont::setStrikeOut(bool enable)
00890 {
00891     detach();
00892 
00893     d->strikeOut = enable;
00894     resolve_mask |= QFontPrivate::StrikeOut;
00895 }
00896 
00902 bool QFont::fixedPitch() const
00903 {
00904     return d->request.fixedPitch;
00905 }
00906 
00913 void QFont::setFixedPitch(bool enable)
00914 {
00915     detach();
00916 
00917     d->request.fixedPitch = enable;
00918     d->request.ignorePitch = false;
00919     resolve_mask |= QFontPrivate::FixedPitch;
00920 }
00921 
00927 bool QFont::kerning() const
00928 {
00929     return d->kerning;
00930 }
00931 
00943 void QFont::setKerning(bool enable)
00944 {
00945     detach();
00946     d->kerning = enable;
00947     resolve_mask |= QFontPrivate::Kerning;
00948 }
00949 
00958 QFont::StyleStrategy QFont::styleStrategy() const
00959 {
00960     return (StyleStrategy) d->request.styleStrategy;
00961 }
00962 
00971 QFont::StyleHint QFont::styleHint() const
00972 {
00973     return (StyleHint) d->request.styleHint;
00974 }
00975 
01046 void QFont::setStyleHint(StyleHint hint, StyleStrategy strategy)
01047 {
01048     detach();
01049 
01050     if ((resolve_mask & (QFontPrivate::StyleHint | QFontPrivate::StyleStrategy)) &&
01051          (StyleHint) d->request.styleHint == hint &&
01052          (StyleStrategy) d->request.styleStrategy == strategy)
01053         return;
01054 
01055     d->request.styleHint = hint;
01056     d->request.styleStrategy = strategy;
01057     resolve_mask |= QFontPrivate::StyleHint;
01058     resolve_mask |= QFontPrivate::StyleStrategy;
01059 
01060 #if defined(Q_WS_X11)
01061     d->request.addStyle.clear();
01062 #endif // Q_WS_X11
01063 }
01064 
01070 void QFont::setStyleStrategy(StyleStrategy s)
01071 {
01072     detach();
01073 
01074     if ((resolve_mask & QFontPrivate::StyleStrategy) &&
01075          s == (StyleStrategy)d->request.styleStrategy)
01076         return;
01077 
01078     d->request.styleStrategy = s;
01079     resolve_mask |= QFontPrivate::StyleStrategy;
01080 }
01081 
01082 
01107 int QFont::stretch() const
01108 {
01109     return d->request.stretch;
01110 }
01111 
01130 void QFont::setStretch(int factor)
01131 {
01132     if (factor < 1 || factor > 4000) {
01133         qWarning("QFont::setStretch: Parameter '%d' out of range", factor);
01134         return;
01135     }
01136 
01137     detach();
01138 
01139     if ((resolve_mask & QFontPrivate::Stretch) &&
01140          d->request.stretch == (uint)factor)
01141         return;
01142 
01143     d->request.stretch = (uint)factor;
01144     resolve_mask |= QFontPrivate::Stretch;
01145 }
01146 
01164 void QFont::setRawMode(bool enable)
01165 {
01166     detach();
01167 
01168     if ((bool) d->rawMode == enable) return;
01169 
01170     d->rawMode = enable;
01171 }
01172 
01179 bool QFont::exactMatch() const
01180 {
01181     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01182     Q_ASSERT(engine != 0);
01183     return (d->rawMode
01184             ? engine->type() != QFontEngine::Box
01185             : d->request.exactMatch(engine->fontDef));
01186 }
01187 
01198 bool QFont::operator==(const QFont &f) const
01199 {
01200     return (f.d == d
01201             || (f.d->request   == d->request
01202                 && f.d->request.pointSize == d->request.pointSize
01203                 && f.d->underline == d->underline
01204                 && f.d->overline  == d->overline
01205                 && f.d->strikeOut == d->strikeOut
01206                 && f.d->kerning == d->kerning));
01207 }
01208 
01209 
01221 bool QFont::operator<(const QFont &f) const
01222 {
01223     if (f.d == d) return false;
01224     // the < operator for fontdefs ignores point sizes.
01225     QFontDef &r1 = f.d->request;
01226     QFontDef &r2 = d->request;
01227     if (r1.pointSize != r2.pointSize) return r1.pointSize < r2.pointSize;
01228     if (r1.pixelSize != r2.pixelSize) return r1.pixelSize < r2.pixelSize;
01229     if (r1.weight != r2.weight) return r1.weight < r2.weight;
01230     if (r1.style != r2.style) return r1.style < r2.style;
01231     if (r1.stretch != r2.stretch) return r1.stretch < r2.stretch;
01232     if (r1.styleHint != r2.styleHint) return r1.styleHint < r2.styleHint;
01233     if (r1.styleStrategy != r2.styleStrategy) return r1.styleStrategy < r2.styleStrategy;
01234     if (r1.family != r2.family) return r1.family < r2.family;
01235 #ifdef Q_WS_X11
01236     if (r1.addStyle != r2.addStyle) return r1.addStyle < r2.addStyle;
01237 #endif // Q_WS_X11
01238 
01239     int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
01240     int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
01241     return f1attrs < f2attrs;
01242 }
01243 
01244 
01255 bool QFont::operator!=(const QFont &f) const
01256 {
01257     return !(operator==(f));
01258 }
01259 
01263 QFont::operator QVariant() const
01264 {
01265     return QVariant(QVariant::Font, this);
01266 }
01267 
01275 bool QFont::isCopyOf(const QFont & f) const
01276 {
01277     return d == f.d;
01278 }
01279 
01286 bool QFont::rawMode() const
01287 {
01288     return d->rawMode;
01289 }
01290 
01295 QFont QFont::resolve(const QFont &other) const
01296 {
01297     if (*this == other
01298         && (resolve_mask == other.resolve_mask || resolve_mask == 0)
01299         && d->dpi == other.d->dpi) {
01300         QFont o = other;
01301         o.resolve_mask = resolve_mask;
01302         return o;
01303     }
01304 
01305     QFont font(*this);
01306     font.detach();
01307     font.d->resolve(resolve_mask, other.d);
01308 
01309     return font;
01310 }
01311 
01322 #ifdef QT3_SUPPORT
01323 
01328 QFont QFont::defaultFont()
01329 {
01330     return QApplication::font();
01331 }
01332 
01337 void QFont::setDefaultFont(const QFont &f)
01338 {
01339     QApplication::setFont(f);
01340 }
01341 
01355 #endif
01356 
01357 
01358 
01359 
01360 /*****************************************************************************
01361   QFont substitution management
01362  *****************************************************************************/
01363 
01364 typedef QHash<QString, QStringList> QFontSubst;
01365 Q_GLOBAL_STATIC(QFontSubst, globalFontSubst)
01366 
01367 // create substitution dict
01368 static void initFontSubst()
01369 {
01370     // default substitutions
01371     static const char *initTbl[] = {
01372 
01373 #if defined(Q_WS_X11)
01374         "arial",        "helvetica",
01375         "times new roman", "times",
01376         "courier new",  "courier",
01377         "sans serif",   "helvetica",
01378 #elif defined(Q_WS_WIN)
01379         "times",        "times new roman",
01380         "courier",      "courier new",
01381         "helvetica",    "arial",
01382         "sans serif",   "arial",
01383 #endif
01384 
01385         0,              0
01386     };
01387 
01388     QFontSubst *fontSubst = globalFontSubst();
01389     Q_ASSERT(fontSubst != 0);
01390     if (!fontSubst->isEmpty())
01391         return;
01392 
01393     for (int i=0; initTbl[i] != 0; i += 2) {
01394         QStringList &list = (*fontSubst)[QString::fromLatin1(initTbl[i])];
01395         list.append(QString::fromLatin1(initTbl[i+1]));
01396     }
01397 }
01398 
01399 
01411 QString QFont::substitute(const QString &familyName)
01412 {
01413     initFontSubst();
01414 
01415     QFontSubst *fontSubst = globalFontSubst();
01416     Q_ASSERT(fontSubst != 0);
01417     QFontSubst::ConstIterator it = fontSubst->constFind(familyName.toLower());
01418     if (it != fontSubst->constEnd() && !(*it).isEmpty())
01419         return (*it).first();
01420 
01421     return familyName;
01422 }
01423 
01424 
01434 QStringList QFont::substitutes(const QString &familyName)
01435 {
01436     initFontSubst();
01437 
01438     QFontSubst *fontSubst = globalFontSubst();
01439     Q_ASSERT(fontSubst != 0);
01440     return fontSubst->value(familyName.toLower(), QStringList());
01441 }
01442 
01443 
01450 void QFont::insertSubstitution(const QString &familyName,
01451                                const QString &substituteName)
01452 {
01453     initFontSubst();
01454 
01455     QFontSubst *fontSubst = globalFontSubst();
01456     Q_ASSERT(fontSubst != 0);
01457     QStringList &list = (*fontSubst)[familyName.toLower()];
01458     QString s = substituteName.toLower();
01459     if (!list.contains(s))
01460         list.append(s);
01461 }
01462 
01463 
01470 void QFont::insertSubstitutions(const QString &familyName,
01471                                 const QStringList &substituteNames)
01472 {
01473     initFontSubst();
01474 
01475     QFontSubst *fontSubst = globalFontSubst();
01476     Q_ASSERT(fontSubst != 0);
01477     QStringList &list = (*fontSubst)[familyName.toLower()];
01478     QStringList::ConstIterator it = substituteNames.constBegin();
01479     while (it != substituteNames.constEnd()) {
01480         QString s = (*it).toLower();
01481         if (!list.contains(s))
01482             list.append(s);
01483         it++;
01484     }
01485 }
01486 
01487 // ### mark: should be called removeSubstitutions()
01493 void QFont::removeSubstitution(const QString &familyName)
01494 { // ### function name should be removeSubstitutions() or
01495   // ### removeSubstitutionList()
01496     initFontSubst();
01497 
01498     QFontSubst *fontSubst = globalFontSubst();
01499     Q_ASSERT(fontSubst != 0);
01500     fontSubst->remove(familyName.toLower());
01501 }
01502 
01503 
01509 QStringList QFont::substitutions()
01510 {
01511     initFontSubst();
01512 
01513     QFontSubst *fontSubst = globalFontSubst();
01514     Q_ASSERT(fontSubst != 0);
01515     QStringList ret;
01516     QFontSubst::ConstIterator it = fontSubst->constBegin();
01517 
01518     while (it != fontSubst->constEnd()) {
01519         ret.append(it.key());
01520         ++it;
01521     }
01522 
01523     ret.sort();
01524     return ret;
01525 }
01526 
01527 
01528 /*  \internal
01529     Internal function. Converts boolean font settings to an unsigned
01530     8-bit number. Used for serialization etc.
01531 */
01532 static quint8 get_font_bits(int version, const QFontPrivate *f)
01533 {
01534     Q_ASSERT(f != 0);
01535     quint8 bits = 0;
01536     if (f->request.style)
01537         bits |= 0x01;
01538     if (f->underline)
01539         bits |= 0x02;
01540     if (f->overline)
01541         bits |= 0x40;
01542     if (f->strikeOut)
01543         bits |= 0x04;
01544     if (f->request.fixedPitch)
01545         bits |= 0x08;
01546     // if (f.hintSetByUser)
01547     // bits |= 0x10;
01548     if (f->rawMode)
01549         bits |= 0x20;
01550     if (version >= QDataStream::Qt_4_0) {
01551         if (f->kerning)
01552             bits |= 0x10;
01553     }
01554     if (f->request.style == QFont::StyleOblique)
01555         bits |= 0x80;
01556     return bits;
01557 }
01558 
01559 
01560 #ifndef QT_NO_DATASTREAM
01561 
01562 /*  \internal
01563     Internal function. Sets boolean font settings from an unsigned
01564     8-bit number. Used for serialization etc.
01565 */
01566 static void set_font_bits(int version, quint8 bits, QFontPrivate *f)
01567 {
01568     Q_ASSERT(f != 0);
01569     f->request.style         = (bits & 0x01) != 0 ? QFont::StyleItalic : QFont::StyleNormal;
01570     f->underline             = (bits & 0x02) != 0;
01571     f->overline              = (bits & 0x40) != 0;
01572     f->strikeOut             = (bits & 0x04) != 0;
01573     f->request.fixedPitch    = (bits & 0x08) != 0;
01574     // f->hintSetByUser      = (bits & 0x10) != 0;
01575     f->rawMode               = (bits & 0x20) != 0;
01576     if (version >= QDataStream::Qt_4_0)
01577         f->kerning               = (bits & 0x10) != 0;
01578     if ((bits & 0x80) != 0)
01579         f->request.style         = QFont::StyleOblique;
01580 }
01581 
01582 #endif
01583 
01584 
01591 QString QFont::key() const
01592 {
01593     return toString();
01594 }
01595 
01603 QString QFont::toString() const
01604 {
01605     const QChar comma(QLatin1Char(','));
01606     return family() + comma +
01607         QString::number(     pointSizeF()) + comma +
01608         QString::number(      pixelSize()) + comma +
01609         QString::number((int) styleHint()) + comma +
01610         QString::number(         weight()) + comma +
01611         QString::number((int)     style()) + comma +
01612         QString::number((int) underline()) + comma +
01613         QString::number((int) strikeOut()) + comma +
01614         QString::number((int)fixedPitch()) + comma +
01615         QString::number((int)   rawMode());
01616 }
01617 
01618 
01626 bool QFont::fromString(const QString &descrip)
01627 {
01628     QStringList l(descrip.split(QLatin1Char(',')));
01629 
01630     int count = l.count();
01631     if (!count || (count > 2 && count < 9) || count > 11) {
01632         qWarning("QFont::fromString: Invalid description '%s'",
01633                  descrip.isEmpty() ? "(empty)" : descrip.toLatin1().data());
01634         return false;
01635     }
01636 
01637     setFamily(l[0]);
01638     if (count > 1 && l[1].toDouble() > 0.0)
01639         setPointSizeF(l[1].toDouble());
01640     if (count == 9) {
01641         setStyleHint((StyleHint) l[2].toInt());
01642         setWeight(qMax(qMin(99, l[3].toInt()), 0));
01643         setItalic(l[4].toInt());
01644         setUnderline(l[5].toInt());
01645         setStrikeOut(l[6].toInt());
01646         setFixedPitch(l[7].toInt());
01647         setRawMode(l[8].toInt());
01648     } else if (count == 10) {
01649         if (l[2].toInt() > 0)
01650             setPixelSize(l[2].toInt());
01651         setStyleHint((StyleHint) l[3].toInt());
01652         setWeight(qMax(qMin(99, l[4].toInt()), 0));
01653         setStyle((QFont::Style)l[5].toInt());
01654         setUnderline(l[6].toInt());
01655         setStrikeOut(l[7].toInt());
01656         setFixedPitch(l[8].toInt());
01657         setRawMode(l[9].toInt());
01658     }
01659 
01660     return true;
01661 }
01662 
01663 #if !defined(Q_WS_QWS)
01664 
01668 void QFont::cacheStatistics()
01669 {
01670 
01671 
01672 }
01673 #endif // !Q_WS_QWS
01674 
01675 
01676 
01677 /*****************************************************************************
01678   QFont stream functions
01679  *****************************************************************************/
01680 #ifndef QT_NO_DATASTREAM
01681 
01690 QDataStream &operator<<(QDataStream &s, const QFont &font)
01691 {
01692     if (s.version() == 1) {
01693         s << font.d->request.family.toLatin1();
01694     } else {
01695         s << font.d->request.family;
01696     }
01697 
01698     if (s.version() >= QDataStream::Qt_4_0) {
01699         // 4.0
01700         double pointSize = font.d->request.pointSize;
01701         qint32 pixelSize = font.d->request.pixelSize;
01702         s << pointSize;
01703         s << pixelSize;
01704     } else if (s.version() <= 3) {
01705         qint16 pointSize = (qint16) (font.d->request.pointSize * 10);
01706         if (pointSize < 0) {
01707 #ifdef Q_WS_X11
01708             pointSize = (qint16)(font.d->request.pixelSize*720/QX11Info::appDpiY());
01709 #else
01710             pointSize = (qint16)QFontInfo(font).pointSize() * 10;
01711 #endif
01712         }
01713         s << pointSize;
01714     } else {
01715         s << (qint16) (font.d->request.pointSize * 10);
01716         s << (qint16) font.d->request.pixelSize;
01717     }
01718 
01719     s << (quint8) font.d->request.styleHint;
01720     if (s.version() >= 5)
01721         s << (quint8) font.d->request.styleStrategy;
01722     return s << (quint8) 0
01723              << (quint8) font.d->request.weight
01724              << get_font_bits(s.version(), font.d);
01725 }
01726 
01727 
01736 QDataStream &operator>>(QDataStream &s, QFont &font)
01737 {
01738     if (!font.d->ref.deref())
01739         delete font.d;
01740 
01741     font.d = new QFontPrivate;
01742     font.resolve_mask = QFontPrivate::Complete;
01743 
01744     quint8 styleHint, styleStrategy = QFont::PreferDefault, charSet, weight, bits;
01745 
01746     if (s.version() == 1) {
01747         QByteArray fam;
01748         s >> fam;
01749         font.d->request.family = QString::fromLatin1(fam);
01750     } else {
01751         s >> font.d->request.family;
01752     }
01753 
01754     if (s.version() >= QDataStream::Qt_4_0) {
01755         // 4.0
01756         double pointSize;
01757         qint32 pixelSize;
01758         s >> pointSize;
01759         s >> pixelSize;
01760         font.d->request.pointSize = qreal(pointSize);
01761         font.d->request.pixelSize = pixelSize;
01762     } else {
01763         qint16 pointSize, pixelSize = -1;
01764         s >> pointSize;
01765         if (s.version() >= 4)
01766             s >> pixelSize;
01767         font.d->request.pointSize = qreal(pointSize / 10.);
01768         font.d->request.pixelSize = pixelSize;
01769     }
01770     s >> styleHint;
01771     if (s.version() >= 5)
01772         s >> styleStrategy;
01773     s >> charSet;
01774     s >> weight;
01775     s >> bits;
01776 
01777     font.d->request.styleHint = styleHint;
01778     font.d->request.styleStrategy = styleStrategy;
01779     font.d->request.weight = weight;
01780 
01781     set_font_bits(s.version(), bits, font.d);
01782 
01783     return s;
01784 }
01785 
01786 #endif // QT_NO_DATASTREAM
01787 
01788 
01789 /*****************************************************************************
01790   QFontInfo member functions
01791  *****************************************************************************/
01792 
01853 QFontInfo::QFontInfo(const QFont &font)
01854     : d(font.d)
01855 { d->ref.ref(); }
01856 
01860 QFontInfo::QFontInfo(const QFontInfo &fi)
01861     : d(fi.d)
01862 { d->ref.ref(); }
01863 
01867 QFontInfo::~QFontInfo()
01868 {
01869     if (!d->ref.deref())
01870         delete d;
01871 }
01872 
01876 QFontInfo &QFontInfo::operator=(const QFontInfo &fi)
01877 {
01878     qAtomicAssign(d, fi.d);
01879     return *this;
01880 }
01881 
01887 QString QFontInfo::family() const
01888 {
01889     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01890     Q_ASSERT(engine != 0);
01891     return engine->fontDef.family;
01892 }
01893 
01899 int QFontInfo::pointSize() const
01900 {
01901     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01902     Q_ASSERT(engine != 0);
01903     return qRound(engine->fontDef.pointSize);
01904 }
01905 
01911 qreal QFontInfo::pointSizeF() const
01912 {
01913     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01914     Q_ASSERT(engine != 0);
01915     return engine->fontDef.pointSize;
01916 }
01917 
01923 int QFontInfo::pixelSize() const
01924 {
01925     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01926     Q_ASSERT(engine != 0);
01927     return engine->fontDef.pixelSize;
01928 }
01929 
01935 bool QFontInfo::italic() const
01936 {
01937     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01938     Q_ASSERT(engine != 0);
01939     return engine->fontDef.style != QFont::StyleNormal;
01940 }
01941 
01947 QFont::Style QFontInfo::style() const
01948 {
01949     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01950     Q_ASSERT(engine != 0);
01951     return (QFont::Style)engine->fontDef.style;
01952 }
01953 
01959 int QFontInfo::weight() const
01960 {
01961     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
01962     Q_ASSERT(engine != 0);
01963     return engine->fontDef.weight;
01964 
01965 }
01966 
01986 bool QFontInfo::underline() const
01987 {
01988     return d->underline;
01989 }
01990 
02001 bool QFontInfo::overline() const
02002 {
02003     return d->overline;
02004 }
02005 
02014 bool QFontInfo::strikeOut() const
02015 {
02016     return d->strikeOut;
02017 }
02018 
02024 bool QFontInfo::fixedPitch() const
02025 {
02026     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
02027     Q_ASSERT(engine != 0);
02028 #ifdef Q_OS_MAC
02029     if (!engine->fontDef.fixedPitchComputed) {
02030         QChar ch[2] = { QChar('i'), QChar('m') };
02031         QGlyphLayout g[2];
02032         int l = 2;
02033         engine->stringToCMap(ch, 2, g, &l, 0);
02034         engine->fontDef.fixedPitch = g[0].advance.x == g[1].advance.x;
02035         engine->fontDef.fixedPitchComputed = true;
02036     }
02037 #endif
02038     return engine->fontDef.fixedPitch;
02039 }
02040 
02048 QFont::StyleHint QFontInfo::styleHint() const
02049 {
02050     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
02051     Q_ASSERT(engine != 0);
02052     return (QFont::StyleHint) engine->fontDef.styleHint;
02053 }
02054 
02065 bool QFontInfo::rawMode() const
02066 {
02067     return d->rawMode;
02068 }
02069 
02076 bool QFontInfo::exactMatch() const
02077 {
02078     QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
02079     Q_ASSERT(engine != 0);
02080     return (d->rawMode
02081             ? engine->type() != QFontEngine::Box
02082             : d->request.exactMatch(engine->fontDef));
02083 }
02084 
02085 
02086 
02087 
02088 // **********************************************************************
02089 // QFontCache
02090 // **********************************************************************
02091 
02092 #ifdef QFONTCACHE_DEBUG
02093 // fast timeouts for debugging
02094 static const int fast_timeout =   1000;  // 1s
02095 static const int slow_timeout =   5000;  // 5s
02096 #else
02097 static const int fast_timeout =  10000; // 10s
02098 static const int slow_timeout = 300000; //  5m
02099 #endif // QFONTCACHE_DEBUG
02100 
02101 QFontCache *QFontCache::instance = 0;
02102 const uint QFontCache::min_cost = 4*1024; // 4mb
02103 
02104 
02105 QFontCache::QFontCache()
02106     : QObject(qApp), total_cost(0), max_cost(min_cost),
02107       current_timestamp(0), fast(false), timer_id(-1)
02108 {
02109     Q_ASSERT(instance == 0);
02110     instance = this;
02111 }
02112 
02113 QFontCache::~QFontCache()
02114 {
02115     {
02116         EngineDataCache::Iterator it = engineDataCache.begin(),
02117                                  end = engineDataCache.end();
02118         while (it != end) {
02119             if (it.value()->ref == 0)
02120                 delete it.value();
02121             else
02122                 FC_DEBUG("QFontCache::~QFontCache: engineData %p still has refcount %d",
02123                          it.value(), it.value()->ref.atomic);
02124             ++it;
02125         }
02126     }
02127     EngineCache::Iterator it = engineCache.begin(),
02128                          end = engineCache.end();
02129     while (it != end) {
02130         if (--it.value().data->cache_count == 0) {
02131             if (it.value().data->ref == 0) {
02132                 FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %d %d %d %d)",
02133                          it.value().data, it.key().script, it.key().def.pointSize,
02134                          it.key().def.pixelSize, it.key().def.weight, it.key().def.style,
02135                          it.key().def.fixedPitch);
02136 
02137                 delete it.value().data;
02138             } else {
02139                 FC_DEBUG("QFontCache::~QFontCache: engine = %p still has refcount %d",
02140                          it.value().data, it.value().data->ref.atomic);
02141             }
02142         }
02143         ++it;
02144     }
02145     instance = 0;
02146 }
02147 
02148 void QFontCache::clear()
02149 {
02150     {
02151         EngineDataCache::Iterator it = engineDataCache.begin(),
02152                                  end = engineDataCache.end();
02153         while (it != end) {
02154             QFontEngineData *data = it.value();
02155 #if defined(Q_WS_X11) || defined(Q_WS_WIN)
02156             for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
02157                 if (data->engines[i]) {
02158                     data->engines[i]->ref.deref();
02159                     data->engines[i] = 0;
02160                 }
02161             }
02162 #else
02163             if (data->engine) {
02164                 data->engine->ref.deref();
02165                 data->engine = 0;
02166             }
02167 #endif
02168             ++it;
02169         }
02170     }
02171 
02172     for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
02173          it != end; ++it) {
02174         if (it->data->ref == 0) {
02175             delete it->data;
02176             it->data = 0;
02177         }
02178     }
02179 
02180     for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
02181          it != end; ++it) {
02182         if (it->data && it->data->ref == 0) {
02183             delete it->data;
02184             it->data = 0;
02185         }
02186     }
02187 
02188     engineCache.clear();
02189 }
02190 
02191 QFontEngineData *QFontCache::findEngineData(const Key &key) const
02192 {
02193     EngineDataCache::ConstIterator it = engineDataCache.find(key),
02194                                   end = engineDataCache.end();
02195     if (it == end) return 0;
02196 
02197     // found
02198     return it.value();
02199 }
02200 
02201 void QFontCache::insertEngineData(const Key &key, QFontEngineData *engineData)
02202 {
02203     FC_DEBUG("QFontCache: inserting new engine data %p", engineData);
02204 
02205     engineDataCache.insert(key, engineData);
02206     increaseCost(sizeof(QFontEngineData));
02207 }
02208 
02209 QFontEngine *QFontCache::findEngine(const Key &key)
02210 {
02211     EngineCache::Iterator it = engineCache.find(key),
02212                          end = engineCache.end();
02213     if (it == end) return 0;
02214 
02215     // found... update the hitcount and timestamp
02216     it.value().hits++;
02217     it.value().timestamp = ++current_timestamp;
02218 
02219     FC_DEBUG("QFontCache: found font engine\n"
02220             "  %p: timestamp %4u hits %3u ref %2d/%2d, type '%s'",
02221             it.value().data, it.value().timestamp, it.value().hits,
02222             it.value().data->ref.atomic, it.value().data->cache_count,
02223             it.value().data->name());
02224 
02225     return it.value().data;
02226 }
02227 
02228 void QFontCache::insertEngine(const Key &key, QFontEngine *engine)
02229 {
02230     FC_DEBUG("QFontCache: inserting new engine %p", engine);
02231 
02232     Engine data(engine);
02233     data.timestamp = ++current_timestamp;
02234 
02235     engineCache.insert(key, data);
02236 
02237     // only increase the cost if this is the first time we insert the engine
02238     if (engine->cache_count == 0)
02239         increaseCost(engine->cache_cost);
02240 
02241     ++engine->cache_count;
02242 }
02243 
02244 void QFontCache::increaseCost(uint cost)
02245 {
02246     cost = (cost + 512) / 1024; // store cost in kb
02247     cost = cost > 0 ? cost : 1;
02248     total_cost += cost;
02249 
02250     FC_DEBUG("  COST: increased %u kb, total_cost %u kb, max_cost %u kb",
02251             cost, total_cost, max_cost);
02252 
02253     if (total_cost > max_cost) {
02254         max_cost = total_cost;
02255 
02256         if (timer_id == -1 || ! fast) {
02257             FC_DEBUG("  TIMER: starting fast timer (%d ms)", fast_timeout);
02258 
02259             if (timer_id != -1) killTimer(timer_id);
02260             timer_id = startTimer(fast_timeout);
02261             fast = true;
02262         }
02263     }
02264 }
02265 
02266 void QFontCache::decreaseCost(uint cost)
02267 {
02268     cost = (cost + 512) / 1024; // cost is stored in kb
02269     cost = cost > 0 ? cost : 1;
02270     Q_ASSERT(cost <= total_cost);
02271     total_cost -= cost;
02272 
02273     FC_DEBUG("  COST: decreased %u kb, total_cost %u kb, max_cost %u kb",
02274             cost, total_cost, max_cost);
02275 }
02276 
02277 #if defined(Q_WS_WIN) || defined (Q_WS_QWS)
02278 void QFontCache::cleanupPrinterFonts()
02279 {
02280     FC_DEBUG("QFontCache::cleanupPrinterFonts");
02281 
02282     {
02283         FC_DEBUG("  CLEAN engine data:");
02284 
02285         // clean out all unused engine data
02286         EngineDataCache::Iterator it = engineDataCache.begin(),
02287                                  end = engineDataCache.end();
02288         while (it != end) {
02289             if (it.key().screen == 0) {
02290                 ++it;
02291                 continue;
02292             }
02293 
02294             if(it.value()->ref != 0) {
02295 #ifdef Q_WS_WIN
02296                 for(int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
02297                     if(it.value()->engines[i]) {
02298                         it.value()->engines[i]->ref.deref();
02299                         it.value()->engines[i] = 0;
02300                     }
02301                 }
02302 #else
02303                 if (it.value()->engine) {
02304                     it.value()->engine->ref.deref();
02305                     it.value()->engine = 0;
02306                 }
02307 #endif
02308                 ++it;
02309             } else {
02310 
02311                 EngineDataCache::Iterator rem = it++;
02312 
02313                 decreaseCost(sizeof(QFontEngineData));
02314 
02315                 FC_DEBUG("    %p", rem.value());
02316 
02317                 delete rem.value();
02318                 engineDataCache.erase(rem);
02319             }
02320         }
02321     }
02322 
02323     EngineCache::Iterator it = engineCache.begin(),
02324                          end = engineCache.end();
02325     while(it != end) {
02326         if (it.value().data->ref != 0 || it.key().screen == 0) {
02327             ++it;
02328             continue;
02329         }
02330 
02331         FC_DEBUG("    %p: timestamp %4u hits %2u ref %2d/%2d, type '%s'",
02332                   it.value().data, it.value().timestamp, it.value().hits,
02333                   it.value().data->ref.atomic, it.value().data->cache_count,
02334                   it.value().data->name());
02335 
02336         if (--it.value().data->cache_count == 0) {
02337             FC_DEBUG("    DELETE: last occurrence in cache");
02338 
02339             decreaseCost(it.value().data->cache_cost);
02340             delete it.value().data;
02341         }
02342 
02343         engineCache.erase(it++);
02344     }
02345 }
02346 #endif
02347 
02348 void QFontCache::timerEvent(QTimerEvent *)
02349 {
02350     FC_DEBUG("QFontCache::timerEvent: performing cache maintenance (timestamp %u)",
02351               current_timestamp);
02352 
02353     if (total_cost <= max_cost && max_cost <= min_cost) {
02354         FC_DEBUG("  cache redused sufficiently, stopping timer");
02355 
02356         killTimer(timer_id);
02357         timer_id = -1;
02358         fast = false;
02359 
02360         return;
02361     }
02362 
02363     // go through the cache and count up everything in use
02364     uint in_use_cost = 0;
02365 
02366     {
02367         FC_DEBUG("  SWEEP engine data:");
02368 
02369         // make sure the cost of each engine data is at least 1kb
02370         const uint engine_data_cost =
02371             sizeof(QFontEngineData) > 1024 ? sizeof(QFontEngineData) : 1024;
02372 
02373         EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
02374                                       end = engineDataCache.constEnd();
02375         for (; it != end; ++it) {
02376 #ifdef QFONTCACHE_DEBUG
02377             FC_DEBUG("    %p: ref %2d", it.value(), int(it.value()->ref));
02378 
02379 #  if defined(Q_WS_X11) || defined(Q_WS_WIN)
02380             // print out all engines
02381             for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
02382                 if (! it.value()->engines[i])
02383                     continue;
02384                 FC_DEBUG("      contains %p", it.value()->engines[i]);
02385             }
02386 #  endif // Q_WS_X11 || Q_WS_WIN
02387 #endif // QFONTCACHE_DEBUG
02388 
02389             if (it.value()->ref != 0)
02390                 in_use_cost += engine_data_cost;
02391         }
02392     }
02393 
02394     {
02395         FC_DEBUG("  SWEEP engine:");
02396 
02397         EngineCache::ConstIterator it = engineCache.constBegin(),
02398                                   end = engineCache.constEnd();
02399         for (; it != end; ++it) {
02400             FC_DEBUG("    %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
02401                       it.value().data, it.value().timestamp, it.value().hits,
02402                       it.value().data->ref.atomic, it.value().data->cache_count,
02403                       it.value().data->cache_cost);
02404 
02405             if (it.value().data->ref != 0)
02406                 in_use_cost += it.value().data->cache_cost / it.value().data->cache_count;
02407         }
02408 
02409         // attempt to make up for rounding errors
02410         in_use_cost += engineCache.size();
02411     }
02412 
02413     in_use_cost = (in_use_cost + 512) / 1024; // cost is stored in kb
02414 
02415     /*
02416       calculate the new maximum cost for the cache
02417 
02418       NOTE: in_use_cost is *not* correct due to rounding errors in the
02419       above algorithm.  instead of worrying about getting the
02420       calculation correct, we are more interested in speed, and use
02421       in_use_cost as a floor for new_max_cost
02422     */
02423     uint new_max_cost = qMax(qMax(max_cost / 2, in_use_cost), min_cost);
02424 
02425     FC_DEBUG("  after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
02426               in_use_cost, total_cost, max_cost, new_max_cost);
02427 
02428     if (new_max_cost == max_cost) {
02429         if (fast) {
02430             FC_DEBUG("  cannot shrink cache, slowing timer");
02431 
02432             killTimer(timer_id);
02433             timer_id = startTimer(slow_timeout);
02434             fast = false;
02435         }
02436 
02437         return;
02438     } else if (! fast) {
02439         FC_DEBUG("  dropping into passing gear");
02440 
02441         killTimer(timer_id);
02442         timer_id = startTimer(fast_timeout);
02443         fast = true;
02444     }
02445 
02446     max_cost = new_max_cost;
02447 
02448     {
02449         FC_DEBUG("  CLEAN engine data:");
02450 
02451         // clean out all unused engine data
02452         EngineDataCache::Iterator it = engineDataCache.begin(),
02453                                  end = engineDataCache.end();
02454         while (it != end) {
02455             if (it.value()->ref != 0) {
02456                 ++it;
02457                 continue;
02458             }
02459 
02460             EngineDataCache::Iterator rem = it++;
02461 
02462             decreaseCost(sizeof(QFontEngineData));
02463 
02464             FC_DEBUG("    %p", rem.value());
02465 
02466             delete rem.value();
02467             engineDataCache.erase(rem);
02468         }
02469     }
02470 
02471     // clean out the engine cache just enough to get below our new max cost
02472     uint current_cost;
02473     do {
02474         current_cost = total_cost;
02475 
02476         EngineCache::Iterator it = engineCache.begin(),
02477                              end = engineCache.end();
02478         // determine the oldest and least popular of the unused engines
02479         uint oldest = ~0u;
02480         uint least_popular = ~0u;
02481 
02482         for (; it != end; ++it) {
02483             if (it.value().data->ref != 0)
02484                 continue;
02485 
02486             if (it.value().timestamp < oldest &&
02487                  it.value().hits <= least_popular) {
02488                 oldest = it.value().timestamp;
02489                 least_popular = it.value().hits;
02490             }
02491         }
02492 
02493         FC_DEBUG("    oldest %u least popular %u", oldest, least_popular);
02494 
02495         for (it = engineCache.begin(); it != end; ++it) {
02496             if (it.value().data->ref == 0 &&
02497                  it.value().timestamp == oldest &&
02498                  it.value().hits == least_popular)
02499                 break;
02500         }
02501 
02502         if (it != end) {
02503             FC_DEBUG("    %p: timestamp %4u hits %2u ref %2d/%2d, type '%s'",
02504                       it.value().data, it.value().timestamp, it.value().hits,
02505                       it.value().data->ref.atomic, it.value().data->cache_count,
02506                       it.value().data->name());
02507 
02508             if (--it.value().data->cache_count == 0) {
02509                 FC_DEBUG("    DELETE: last occurrence in cache");
02510 
02511                 decreaseCost(it.value().data->cache_cost);
02512                 delete it.value().data;
02513             } else {
02514                 /*
02515                   this particular font engine is in the cache multiple
02516                   times...  set current_cost to zero, so that we can
02517                   keep looping to get rid of all occurrences
02518                 */
02519                 current_cost = 0;
02520             }
02521 
02522             engineCache.erase(it);
02523         }
02524     } while (current_cost != total_cost && total_cost > max_cost);
02525 }

Generated on Thu Mar 15 11:55:52 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1