qwt_text_engine.cpp 8.59 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2003   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
10 11 12
#include "qwt_text_engine.h"
#include "qwt_math.h"
#include "qwt_painter.h"
pixhawk's avatar
pixhawk committed
13 14 15 16 17
#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qmap.h>
#include <qwidget.h>
Bryant's avatar
Bryant committed
18 19 20
#include <qtextobject.h>
#include <qtextdocument.h>
#include <qabstracttextdocumentlayout.h>
pixhawk's avatar
pixhawk committed
21

Bryant's avatar
Bryant committed
22
static QString taggedRichText( const QString &text, int flags )
pixhawk's avatar
pixhawk committed
23 24 25 26
{
    QString richText = text;

    // By default QSimpleRichText is Qt::AlignLeft
Bryant's avatar
Bryant committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40
    if ( flags & Qt::AlignJustify )
    {
        richText.prepend( QString::fromLatin1( "<div align=\"justify\">" ) );
        richText.append( QString::fromLatin1( "</div>" ) );
    }
    else if ( flags & Qt::AlignRight )
    {
        richText.prepend( QString::fromLatin1( "<div align=\"right\">" ) );
        richText.append( QString::fromLatin1( "</div>" ) );
    }
    else if ( flags & Qt::AlignHCenter )
    {
        richText.prepend( QString::fromLatin1( "<div align=\"center\">" ) );
        richText.append( QString::fromLatin1( "</div>" ) );
pixhawk's avatar
pixhawk committed
41 42 43 44 45 46 47 48
    }

    return richText;
}

class QwtRichTextDocument: public QTextDocument
{
public:
Bryant's avatar
Bryant committed
49 50 51 52 53
    QwtRichTextDocument( const QString &text, int flags, const QFont &font )
    {
        setUndoRedoEnabled( false );
        setDefaultFont( font );
        setHtml( text );
pixhawk's avatar
pixhawk committed
54 55

        // make sure we have a document layout
Bryant's avatar
Bryant committed
56
        ( void )documentLayout();
pixhawk's avatar
pixhawk committed
57 58 59

        QTextOption option = defaultTextOption();
        if ( flags & Qt::TextWordWrap )
Bryant's avatar
Bryant committed
60
            option.setWrapMode( QTextOption::WordWrap );
pixhawk's avatar
pixhawk committed
61
        else
Bryant's avatar
Bryant committed
62
            option.setWrapMode( QTextOption::NoWrap );
pixhawk's avatar
pixhawk committed
63

Bryant's avatar
Bryant committed
64 65
        option.setAlignment( static_cast<Qt::Alignment>( flags ) );
        setDefaultTextOption( option );
pixhawk's avatar
pixhawk committed
66 67 68

        QTextFrame *root = rootFrame();
        QTextFrameFormat fm = root->frameFormat();
Bryant's avatar
Bryant committed
69 70 71 72 73 74
        fm.setBorder( 0 );
        fm.setMargin( 0 );
        fm.setPadding( 0 );
        fm.setBottomMargin( 0 );
        fm.setLeftMargin( 0 );
        root->setFrameFormat( fm );
pixhawk's avatar
pixhawk committed
75 76 77 78 79 80 81 82

        adjustSize();
    }
};

class QwtPlainTextEngine::PrivateData
{
public:
Bryant's avatar
Bryant committed
83 84
    int effectiveAscent( const QFont &font ) const
    {
pixhawk's avatar
pixhawk committed
85 86
        const QString fontKey = font.key();

87
        QMap<QString, int>::const_iterator it =
Bryant's avatar
Bryant committed
88 89 90 91 92
            d_ascentCache.find( fontKey );
        if ( it == d_ascentCache.end() )
        {
            int ascent = findAscent( font );
            it = d_ascentCache.insert( fontKey, ascent );
pixhawk's avatar
pixhawk committed
93 94
        }

Bryant's avatar
Bryant committed
95
        return ( *it );
pixhawk's avatar
pixhawk committed
96 97 98
    }

private:
Bryant's avatar
Bryant committed
99 100 101 102 103 104 105 106 107 108 109 110
    int findAscent( const QFont &font ) const
    {
        static const QString dummy( "E" );
        static const QColor white( Qt::white );

        const QFontMetrics fm( font );
        QPixmap pm( fm.width( dummy ), fm.height() );
        pm.fill( white );

        QPainter p( &pm );
        p.setFont( font );
        p.drawText( 0, 0,  pm.width(), pm.height(), 0, dummy );
pixhawk's avatar
pixhawk committed
111 112 113 114 115
        p.end();

        const QImage img = pm.toImage();

        int row = 0;
Bryant's avatar
Bryant committed
116 117 118 119
        for ( row = 0; row < img.height(); row++ )
        {
            const QRgb *line = reinterpret_cast<const QRgb *>( 
                img.scanLine( row ) );
pixhawk's avatar
pixhawk committed
120 121

            const int w = pm.width();
Bryant's avatar
Bryant committed
122 123
            for ( int col = 0; col < w; col++ )
            {
pixhawk's avatar
pixhawk committed
124 125 126 127 128 129
                if ( line[col] != white.rgb() )
                    return fm.ascent() - row + 1;
            }
        }

        return fm.ascent();
130
    }
pixhawk's avatar
pixhawk committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

    mutable QMap<QString, int> d_ascentCache;
};

//! Constructor
QwtTextEngine::QwtTextEngine()
{
}

//! Destructor
QwtTextEngine::~QwtTextEngine()
{
}

//! Constructor
QwtPlainTextEngine::QwtPlainTextEngine()
{
    d_data = new PrivateData;
}

//! Destructor
QwtPlainTextEngine::~QwtPlainTextEngine()
{
    delete d_data;
}

/*!
   Find the height for a given width

   \param font Font of the text
   \param flags Bitwise OR of the flags used like in QPainter::drawText
   \param text Text to be rendered
163
   \param width Width
pixhawk's avatar
pixhawk committed
164 165 166

   \return Calculated height
*/
Bryant's avatar
Bryant committed
167 168
double QwtPlainTextEngine::heightForWidth( const QFont& font, int flags,
        const QString& text, double width ) const
pixhawk's avatar
pixhawk committed
169
{
Bryant's avatar
Bryant committed
170 171 172
    const QFontMetricsF fm( font );
    const QRectF rect = fm.boundingRect(
        QRectF( 0, 0, width, QWIDGETSIZE_MAX ), flags, text );
pixhawk's avatar
pixhawk committed
173 174 175 176 177 178 179 180 181 182 183 184 185

    return rect.height();
}

/*!
  Returns the size, that is needed to render text

  \param font Font of the text
  \param flags Bitwise OR of the flags used like in QPainter::drawText
  \param text Text to be rendered

  \return Caluclated size
*/
Bryant's avatar
Bryant committed
186 187
QSizeF QwtPlainTextEngine::textSize( const QFont &font,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
188
{
Bryant's avatar
Bryant committed
189 190 191
    const QFontMetricsF fm( font );
    const QRectF rect = fm.boundingRect(
        QRectF( 0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ), flags, text );
pixhawk's avatar
pixhawk committed
192 193 194 195 196 197 198 199 200 201 202 203 204

    return rect.size();
}

/*!
  Return margins around the texts

  \param font Font of the text
  \param left Return 0
  \param right Return 0
  \param top Return value for the top margin
  \param bottom Return value for the bottom margin
*/
Bryant's avatar
Bryant committed
205 206
void QwtPlainTextEngine::textMargins( const QFont &font, const QString &,
    double &left, double &right, double &top, double &bottom ) const
pixhawk's avatar
pixhawk committed
207 208 209
{
    left = right = top = 0;

Bryant's avatar
Bryant committed
210 211 212
    const QFontMetricsF fm( font );
    top = fm.ascent() - d_data->effectiveAscent( font );
    bottom = fm.descent();
pixhawk's avatar
pixhawk committed
213 214 215 216
}

/*!
  \brief Draw the text in a clipping rectangle
217

pixhawk's avatar
pixhawk committed
218 219 220 221 222 223 224
  A wrapper for QPainter::drawText.

  \param painter Painter
  \param rect Clipping rectangle
  \param flags Bitwise OR of the flags used like in QPainter::drawText
  \param text Text to be rendered
*/
Bryant's avatar
Bryant committed
225 226
void QwtPlainTextEngine::draw( QPainter *painter, const QRectF &rect,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
227
{
Bryant's avatar
Bryant committed
228
    QwtPainter::drawText( painter, rect, flags, text );
pixhawk's avatar
pixhawk committed
229 230
}

231
/*!
pixhawk's avatar
pixhawk committed
232 233 234
  Test if a string can be rendered by this text engine.
  \return Always true. All texts can be rendered by QwtPlainTextEngine
*/
Bryant's avatar
Bryant committed
235
bool QwtPlainTextEngine::mightRender( const QString & ) const
pixhawk's avatar
pixhawk committed
236 237 238 239 240 241 242 243 244 245 246 247 248
{
    return true;
}

#ifndef QT_NO_RICHTEXT

//! Constructor
QwtRichTextEngine::QwtRichTextEngine()
{
}

/*!
   Find the height for a given width
249

pixhawk's avatar
pixhawk committed
250
   \param font Font of the text
Bryant's avatar
Bryant committed
251
   \param flags Bitwise OR of the flags used like in QPainter::drawText()
pixhawk's avatar
pixhawk committed
252
   \param text Text to be rendered
253
   \param width Width
pixhawk's avatar
pixhawk committed
254 255 256

   \return Calculated height
*/
Bryant's avatar
Bryant committed
257 258
double QwtRichTextEngine::heightForWidth( const QFont& font, int flags,
        const QString& text, double width ) const
pixhawk's avatar
pixhawk committed
259
{
Bryant's avatar
Bryant committed
260 261 262 263
    QwtRichTextDocument doc( text, flags, font );

    doc.setPageSize( QSizeF( width, QWIDGETSIZE_MAX ) );
    return doc.documentLayout()->documentSize().height();
pixhawk's avatar
pixhawk committed
264 265 266 267
}

/*!
  Returns the size, that is needed to render text
268

pixhawk's avatar
pixhawk committed
269
  \param font Font of the text
Bryant's avatar
Bryant committed
270
  \param flags Bitwise OR of the flags used like in QPainter::drawText()
pixhawk's avatar
pixhawk committed
271 272 273 274 275
  \param text Text to be rendered

  \return Caluclated size
*/

Bryant's avatar
Bryant committed
276 277
QSizeF QwtRichTextEngine::textSize( const QFont &font,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
278
{
Bryant's avatar
Bryant committed
279
    QwtRichTextDocument doc( text, flags, font );
pixhawk's avatar
pixhawk committed
280 281

    QTextOption option = doc.defaultTextOption();
Bryant's avatar
Bryant committed
282 283 284 285
    if ( option.wrapMode() != QTextOption::NoWrap )
    {
        option.setWrapMode( QTextOption::NoWrap );
        doc.setDefaultTextOption( option );
pixhawk's avatar
pixhawk committed
286 287 288
        doc.adjustSize();
    }

Bryant's avatar
Bryant committed
289
    return doc.size();
pixhawk's avatar
pixhawk committed
290 291 292 293 294 295 296
}

/*!
  Draw the text in a clipping rectangle

  \param painter Painter
  \param rect Clipping rectangle
Bryant's avatar
Bryant committed
297
  \param flags Bitwise OR of the flags like in for QPainter::drawText()
pixhawk's avatar
pixhawk committed
298 299
  \param text Text to be rendered
*/
Bryant's avatar
Bryant committed
300 301
void QwtRichTextEngine::draw( QPainter *painter, const QRectF &rect,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
302
{
Bryant's avatar
Bryant committed
303 304
    QwtRichTextDocument doc( text, flags, painter->font() );
    QwtPainter::drawSimpleRichText( painter, rect, flags, doc );
pixhawk's avatar
pixhawk committed
305 306
}

307
/*!
pixhawk's avatar
pixhawk committed
308 309 310
   Wrap text into <div align=...> </div> tags according flags

   \param text Text
Bryant's avatar
Bryant committed
311
   \param flags Bitwise OR of the flags like in for QPainter::drawText()
pixhawk's avatar
pixhawk committed
312 313 314

   \return Tagged text
*/
Bryant's avatar
Bryant committed
315
QString QwtRichTextEngine::taggedText( const QString &text, int flags ) const
pixhawk's avatar
pixhawk committed
316
{
Bryant's avatar
Bryant committed
317
    return taggedRichText( text, flags );
pixhawk's avatar
pixhawk committed
318 319 320 321 322 323
}

/*!
  Test if a string can be rendered by this text engine

  \param text Text to be tested
Bryant's avatar
Bryant committed
324
  \return Qt::mightBeRichText(text);
pixhawk's avatar
pixhawk committed
325
*/
Bryant's avatar
Bryant committed
326
bool QwtRichTextEngine::mightRender( const QString &text ) const
pixhawk's avatar
pixhawk committed
327
{
Bryant's avatar
Bryant committed
328
    return Qt::mightBeRichText( text );
pixhawk's avatar
pixhawk committed
329 330 331 332 333 334 335 336 337 338
}

/*!
  Return margins around the texts

  \param left Return 0
  \param right Return 0
  \param top Return 0
  \param bottom Return 0
*/
Bryant's avatar
Bryant committed
339 340
void QwtRichTextEngine::textMargins( const QFont &, const QString &,
    double &left, double &right, double &top, double &bottom ) const
pixhawk's avatar
pixhawk committed
341 342 343 344 345
{
    left = right = top = bottom = 0;
}

#endif // !QT_NO_RICHTEXT