Skip to content
qwt_text_engine.cpp 8.59 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- 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
#include "qwt_text_engine.h"
#include "qwt_math.h"
#include "qwt_painter.h"
pixhawk's avatar
pixhawk committed
#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qmap.h>
#include <qwidget.h>
Bryant's avatar
Bryant committed
#include <qtextobject.h>
#include <qtextdocument.h>
#include <qabstracttextdocumentlayout.h>
pixhawk's avatar
pixhawk committed

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

    // By default QSimpleRichText is Qt::AlignLeft
Bryant's avatar
Bryant committed
    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
    }

    return richText;
}

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

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

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

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

        QTextFrame *root = rootFrame();
        QTextFrameFormat fm = root->frameFormat();
Bryant's avatar
Bryant committed
        fm.setBorder( 0 );
        fm.setMargin( 0 );
        fm.setPadding( 0 );
        fm.setBottomMargin( 0 );
        fm.setLeftMargin( 0 );
        root->setFrameFormat( fm );
pixhawk's avatar
pixhawk committed

        adjustSize();
    }
};

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

        QMap<QString, int>::const_iterator it =
Bryant's avatar
Bryant committed
            d_ascentCache.find( fontKey );
        if ( it == d_ascentCache.end() )
        {
            int ascent = findAscent( font );
            it = d_ascentCache.insert( fontKey, ascent );
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        return ( *it );
pixhawk's avatar
pixhawk committed
    }

private:
Bryant's avatar
Bryant committed
    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
        p.end();

        const QImage img = pm.toImage();

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

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

        return fm.ascent();
pixhawk's avatar
pixhawk committed

    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
   \param width Width
pixhawk's avatar
pixhawk committed

   \return Calculated height
*/
Bryant's avatar
Bryant committed
double QwtPlainTextEngine::heightForWidth( const QFont& font, int flags,
        const QString& text, double width ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QFontMetricsF fm( font );
    const QRectF rect = fm.boundingRect(
        QRectF( 0, 0, width, QWIDGETSIZE_MAX ), flags, text );
pixhawk's avatar
pixhawk committed

    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
QSizeF QwtPlainTextEngine::textSize( const QFont &font,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QFontMetricsF fm( font );
    const QRectF rect = fm.boundingRect(
        QRectF( 0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ), flags, text );
pixhawk's avatar
pixhawk committed

    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
void QwtPlainTextEngine::textMargins( const QFont &font, const QString &,
    double &left, double &right, double &top, double &bottom ) const
pixhawk's avatar
pixhawk committed
{
    left = right = top = 0;

Bryant's avatar
Bryant committed
    const QFontMetricsF fm( font );
    top = fm.ascent() - d_data->effectiveAscent( font );
    bottom = fm.descent();
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Draw the text in a clipping rectangle
pixhawk's avatar
pixhawk committed
  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
void QwtPlainTextEngine::draw( QPainter *painter, const QRectF &rect,
    int flags, const QString& text ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtPainter::drawText( painter, rect, flags, text );
pixhawk's avatar
pixhawk committed
}

pixhawk's avatar
pixhawk committed
  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
bool QwtPlainTextEngine::mightRender( const QString & ) const
pixhawk's avatar
pixhawk committed
{
    return true;
}

#ifndef QT_NO_RICHTEXT

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

/*!
   Find the height for a given width
pixhawk's avatar
pixhawk committed
   \param font Font of the text
Bryant's avatar
Bryant committed
   \param flags Bitwise OR of the flags used like in QPainter::drawText()
pixhawk's avatar
pixhawk committed
   \param text Text to be rendered
   \param width Width
pixhawk's avatar
pixhawk committed

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

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

/*!
  Returns the size, that is needed to render text
pixhawk's avatar
pixhawk committed
  \param font Font of the text
Bryant's avatar
Bryant committed
  \param flags Bitwise OR of the flags used like in QPainter::drawText()
pixhawk's avatar
pixhawk committed
  \param text Text to be rendered

  \return Caluclated size
*/

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

    QTextOption option = doc.defaultTextOption();
Bryant's avatar
Bryant committed
    if ( option.wrapMode() != QTextOption::NoWrap )
    {
        option.setWrapMode( QTextOption::NoWrap );
        doc.setDefaultTextOption( option );
pixhawk's avatar
pixhawk committed
        doc.adjustSize();
    }

Bryant's avatar
Bryant committed
    return doc.size();
pixhawk's avatar
pixhawk committed
}

/*!
  Draw the text in a clipping rectangle

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

pixhawk's avatar
pixhawk committed
   Wrap text into <div align=...> </div> tags according flags

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

   \return Tagged text
*/
Bryant's avatar
Bryant committed
QString QwtRichTextEngine::taggedText( const QString &text, int flags ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return taggedRichText( text, flags );
pixhawk's avatar
pixhawk committed
}

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

  \param text Text to be tested
Bryant's avatar
Bryant committed
  \return Qt::mightBeRichText(text);
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtRichTextEngine::mightRender( const QString &text ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return Qt::mightBeRichText( text );
pixhawk's avatar
pixhawk committed
}

/*!
  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
void QwtRichTextEngine::textMargins( const QFont &, const QString &,
    double &left, double &right, double &top, double &bottom ) const
pixhawk's avatar
pixhawk committed
{
    left = right = top = bottom = 0;
}

#endif // !QT_NO_RICHTEXT