# qwt_text_engine.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2003   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 // vim: expandtab
    00011
    00012 #include <qpainter.h>
    00013 #include <qpixmap.h>
    00014 #include <qimage.h>
    00015 #include <qmap.h>
    00016 #include <qwidget.h>
    00017 #include "qwt_math.h"
    00018 #include "qwt_painter.h"
    00019 #include "qwt_text_engine.h"
    00020
    00021 #if QT_VERSION < 0x040000
    00022
    00023 #include <qsimplerichtext.h>
    00024 #include <qstylesheet.h>
    00025
    00026 class QwtRichTextDocument: public QSimpleRichText
    00027 {
    00028 public:
    00029     QwtRichTextDocument(const QString &text, const QFont &font):
    00030         QSimpleRichText(text, font)
    00031     {
    00032     }
    00033 };
    00034
    00035 #else // QT_VERSION >= 0x040000
    00036
    00037 #if QT_VERSION < 0x040200
    00038 #define USE_LABEL 1
    00039 #endif
    00040
    00041 #ifdef USE_LABEL
    00042 #include <qlabel.h>
    00043 #else
    00044 #include <qtextobject.h>
    00045 #endif
    00046 #include <qtextdocument.h>
    00047 #include <qabstracttextdocumentlayout.h>
    00048
    00049 class QwtRichTextDocument: public QTextDocument
    00050 {
    00051 public:
    00052     QwtRichTextDocument(const QString &text, const QFont &font)
    00053     {
    00054         setUndoRedoEnabled(false);
    00055         setDefaultFont(font);
    00056         setHtml(text);
    00057
    00058         // make sure we have a document layout
    00059         (void)documentLayout();
    00060     }
    00061 };
    00062
    00063 #endif
    00064
    00065 class QwtPlainTextEngine::PrivateData
    00066 {
    00067 public:
    00068     int effectiveAscent(const QFont &font) const
    00069     {
    00070         const QString fontKey = font.key();
    00071
    00072         QMap<QString, int>::const_iterator it =
    00073             d_ascentCache.find(fontKey);
    00074         if ( it == d_ascentCache.end() )
    00075         {
    00076             int ascent = findAscent(font);
    00077             it = d_ascentCache.insert(fontKey, ascent);
    00078         }
    00079
    00080         return (*it);
    00081     }
    00082
    00083 private:
    00084     int findAscent(const QFont &font) const
    00085     {
    00086         static const QString dummy("E");
    00087         static const QColor white(Qt::white);
    00088
    00089         const QFontMetrics fm(font);
    00090         QPixmap pm(fm.width(dummy), fm.height());
    00091         pm.fill(white);
    00092
    00093         QPainter p(&pm);
    00094         p.setFont(font);
    00095         p.drawText(0, 0,  pm.width(), pm.height(), 0, dummy);
    00096         p.end();
    00097
    00098     #if QT_VERSION < 0x040000
    00099         const QImage img = pm.convertToImage();
    00100     #else
    00101         const QImage img = pm.toImage();
    00102     #endif
    00103
    00104         int row = 0;
    00105         for ( row = 0; row < img.height(); row++ )
    00106         {
    00107             const QRgb *line = (const QRgb *)img.scanLine(row);
    00108
    00109             const int w = pm.width();
    00110             for ( int col = 0; col < w; col++ )
    00111             {
    00112                 if ( line[col] != white.rgb() )
    00113                     return fm.ascent() - row + 1;
    00114             }
    00115         }
    00116
    00117         return fm.ascent();
    00118     }
    00119
    00120     mutable QMap<QString, int> d_ascentCache;
    00121 };
    00122
    00124 QwtTextEngine::QwtTextEngine()
    00125 {
    00126 }
    00127
    00129 QwtTextEngine::~QwtTextEngine()
    00130 {
    00131 }
    00132
    00134 QwtPlainTextEngine::QwtPlainTextEngine()
    00135 {
    00136     d_data = new PrivateData;
    00137 }
    00138
    00140 QwtPlainTextEngine::~QwtPlainTextEngine()
    00141 {
    00142     delete d_data;
    00143 }
    00144
    00155 int QwtPlainTextEngine::heightForWidth(const QFont& font, int flags,
    00156         const QString& text, int width) const
    00157 {
    00158     const QFontMetrics fm(font);
    00159     const QRect rect = fm.boundingRect(
    00160         0, 0, width, QWIDGETSIZE_MAX, flags, text);
    00161
    00162     return rect.height();
    00163 }
    00164
    00174 QSize QwtPlainTextEngine::textSize(const QFont &font,
    00175     int flags, const QString& text) const
    00176 {
    00177     const QFontMetrics fm(font);
    00178     const QRect rect = fm.boundingRect(
    00179         0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX, flags, text);
    00180
    00181     return rect.size();
    00182 }
    00183
    00193 void QwtPlainTextEngine::textMargins(const QFont &font, const QString &,
    00194     int &left, int &right, int &top, int &bottom) const
    00195 {
    00196     left = right = top = 0;
    00197
    00198     const QFontMetrics fm(font);
    00199     top = fm.ascent() - d_data->effectiveAscent(font);
    00200     bottom = fm.descent() + 1;
    00201 }
    00202
    00213 void QwtPlainTextEngine::draw(QPainter *painter, const QRect &rect,
    00214     int flags, const QString& text) const
    00215 {
    00216     QwtPainter::drawText(painter, rect, flags, text);
    00217 }
    00218
    00223 bool QwtPlainTextEngine::mightRender(const QString &) const
    00224 {
    00225     return true;
    00226 }
    00227
    00228 #ifndef QT_NO_RICHTEXT
    00229
    00231 QwtRichTextEngine::QwtRichTextEngine()
    00232 {
    00233 }
    00234
    00245 int QwtRichTextEngine::heightForWidth(const QFont& font, int flags,
    00246         const QString& text, int width) const
    00247 {
    00248     QwtRichTextDocument doc(taggedText(text, flags), font);
    00249
    00250 #if QT_VERSION < 0x040000
    00251     doc.setWidth(width);
    00252     const int h = doc.height();
    00253 #else
    00254     doc.setPageSize(QSize(width, QWIDGETSIZE_MAX));
    00255     const int h = qRound(doc.documentLayout()->documentSize().height());
    00256 #endif
    00257     return h;
    00258 }
    00259
    00270 QSize QwtRichTextEngine::textSize(const QFont &font,
    00271     int flags, const QString& text) const
    00272 {
    00273     QwtRichTextDocument doc(taggedText(text, flags), font);
    00274
    00275 #if QT_VERSION < 0x040000
    00276     doc.setWidth(QWIDGETSIZE_MAX);
    00277
    00278     int w = doc.widthUsed();
    00279     int h = doc.height();
    00280 #else
    00281 #if USE_LABEL
    00282     /*
    00283       Unfortunately offering the bounding rect calculation in the
    00284       API of QTextDocument has been forgotten in Qt <= 4.1.x. It
    00285       is planned to come with Qt 4.2.x.
    00286       In the meantime we need a hack with a temporary QLabel,
    00287       to reengineer the internal calculations.
    00288      */
    00289
    00290     static int off = 0;
    00291     static QLabel *label = NULL;
    00292     if ( label == NULL )
    00293     {
    00294         label = new QLabel;
    00295         label->hide();
    00296
    00297         const char *s = "XXXXX";
    00298         label->setText(s);
    00299         int w1 = label->sizeHint().width();
    00300         const QFontMetrics fm(label->font());
    00301         int w2 = fm.width(s);
    00302         off = w1 - w2;
    00303     }
    00304     label->setFont(doc.defaultFont());
    00305     label->setText(text);
    00306
    00307     int w = qwtMax(label->sizeHint().width() - off, 0);
    00308     doc.setPageSize(QSize(w, QWIDGETSIZE_MAX));
    00309
    00310     int h = qRound(doc.documentLayout()->documentSize().height());
    00311 #else
    00312     QTextLayout *layout = doc.begin().layout();
    00313     layout->beginLayout();
    00314     for(qreal y = 0;;)
    00315     {
    00316         QTextLine line = layout->createLine();
    00317         if (!line.isValid())
    00318             break;
    00319         line.setPosition(QPointF(0, y));
    00320         y += line.height();
    00321     }
    00322     layout->endLayout();
    00323
    00324     int w = qRound(layout->maximumWidth());
    00325     int h = qRound(layout->boundingRect().height());
    00326
    00327     h += QFontMetrics(font).descent() + 4;
    00328     w += 2 * 4;
    00329 #endif
    00330 #endif
    00331
    00332     return QSize(w, h);
    00333 }
    00334
    00343 void QwtRichTextEngine::draw(QPainter *painter, const QRect &rect,
    00344     int flags, const QString& text) const
    00345 {
    00346     QwtRichTextDocument doc(taggedText(text, flags), painter->font());
    00347     QwtPainter::drawSimpleRichText(painter, rect, flags, doc);
    00348 }
    00349
    00358 QString QwtRichTextEngine::taggedText(const QString &text, int flags) const
    00359 {
    00360     QString richText = text;
    00361
    00362     // By default QSimpleRichText is Qt::AlignLeft
    00363     if (flags & Qt::AlignJustify)
    00364     {
    00365         richText.prepend(QString::fromLatin1("<div align=\"justify\">"));
    00366         richText.append(QString::fromLatin1("</div>"));
    00367     }
    00368     else if (flags & Qt::AlignRight)
    00369     {
    00370         richText.prepend(QString::fromLatin1("<div align=\"right\">"));
    00371         richText.append(QString::fromLatin1("</div>"));
    00372     }
    00373     else if (flags & Qt::AlignHCenter)
    00374     {
    00375         richText.prepend(QString::fromLatin1("<div align=\"center\">"));
    00376         richText.append(QString::fromLatin1("</div>"));
    00377     }
    00378
    00379     return richText;
    00380 }
    00381
    00388 bool QwtRichTextEngine::mightRender(const QString &text) const
    00389 {
    00390 #if QT_VERSION < 0x040000
    00391     return QStyleSheet::mightBeRichText(text);
    00392 #else
    00393     return Qt::mightBeRichText(text);
    00394 #endif
    00395 }
    00396
    00405 void QwtRichTextEngine::textMargins(const QFont &, const QString &,
    00406     int &left, int &right, int &top, int &bottom) const
    00407 {
    00408     left = right = top = bottom = 0;
    00409 }
    00410
    00411 #endif // !QT_NO_RICHTEXT