Пример #1
0
void PieDiagram::shuffleLabels( QRectF* textBoundingRect )
{
    // things that could be improved here:
    // - use a variable number (chosen using angle information) of neighbors to check
    // - try harder to arrange the labels to look nice

    // ideas:
    // - leave labels that don't collide alone (only if they their offset is zero)
    // - use a graphics view for collision detection

    LabelPaintCache& lpc = d->labelPaintCache;
    const int n = lpc.paintReplay.size();
    bool modified = false;
    qreal direction = 5.0;
    QVector< qreal > offsets;
    offsets.fill( 0.0, n );

    for ( bool lastRoundModified = true; lastRoundModified; ) {
        lastRoundModified = false;

        for ( int i = 0; i < n; i++ ) {
            const int neighborsToCheck = qMax( 10, lpc.paintReplay.size() - 1 );
            const int minComp = wraparound( i - neighborsToCheck / 2, n );
            const int maxComp = wraparound( i + ( neighborsToCheck + 1 ) / 2, n );

            QPainterPath& path = lpc.paintReplay[ i ].labelArea;

            for ( int j = minComp; j != maxComp; j = wraparound( j + 1, n ) ) {
                if ( i == j ) {
                    continue;
                }
                QPainterPath& otherPath = lpc.paintReplay[ j ].labelArea;

                while ( ( offsets[ i ] + direction > 0 ) && otherPath.intersects( path ) ) {
#ifdef SHUFFLE_DEBUG
                    qDebug() << "collision involving" << j << "and" << i << " -- n =" << n;
                    TextAttributes ta = lpc.paintReplay[ i ].attrs.textAttributes();
                    ta.setPen( QPen( Qt::white ) );
                    lpc.paintReplay[ i ].attrs.setTextAttributes( ta );
#endif
                    uint slice = lpc.paintReplay[ i ].index.column();
                    qreal angle = DEGTORAD( d->startAngles[ slice ] + d->angleLens[ slice ] / 2.0 );
                    qreal dx = cos( angle ) * direction;
                    qreal dy = -sin( angle ) * direction;
                    offsets[ i ] += direction;
                    path.translate( dx, dy );
                    lastRoundModified = true;
                }
            }
        }
        direction *= -1.07; // this can "overshoot", but avoids getting trapped in local minimums
        modified = modified || lastRoundModified;
    }

    if ( modified ) {
        for ( int i = 0; i < lpc.paintReplay.size(); i++ ) {
            *textBoundingRect |= lpc.paintReplay[ i ].labelArea.boundingRect();
        }
    }
}
void TernaryAxis::updatePrerenderedLabels()
{
    TextAttributes attributes = titleTextAttributes();
    double axisLabelAngle = 0.0;
    double fiftyMarkAngle = 0.0;
    QPointF axisLabelPosition;
    QPointF fiftyMarkPosition;
    KDChartEnums::PositionValue fiftyMarkReferencePoint = KDChartEnums::PositionUnknown;

    switch( position().value() ) {
    case KDChartEnums::PositionSouth:
        // this is the axis on the other side of A
        axisLabelAngle = 0.0;
        fiftyMarkAngle = 0.0;
        axisLabelPosition = TriangleTop;
        fiftyMarkPosition = 0.5 * AxisVector_B_C - RelMarkerLength * Norm_B_C;
        fiftyMarkReferencePoint = KDChartEnums::PositionNorth;
        break;
    case KDChartEnums::PositionEast:
        // this is the axis on the other side of B
        axisLabelAngle = 240.0;
        fiftyMarkAngle = 60;
        axisLabelPosition = TriangleBottomLeft;
        fiftyMarkPosition = AxisVector_B_C + 0.5 * AxisVector_C_A - RelMarkerLength * Norm_C_A;
        fiftyMarkReferencePoint = KDChartEnums::PositionSouth;
        break;
    case KDChartEnums::PositionWest:
        // this is the axis on the other side of C
        axisLabelAngle = 120.0;
        fiftyMarkAngle = 300.0;
        axisLabelPosition = TriangleBottomRight;
        fiftyMarkPosition = 0.5 * AxisVector_B_A + RelMarkerLength * Norm_B_A;
        fiftyMarkReferencePoint = KDChartEnums::PositionSouth;
        break;
    case KDChartEnums::PositionUnknown:
        break; // initial value
    default:
        qDebug() << "TernaryAxis::updatePrerenderedLabel: unknown location";
    };

    m_label->setFont( attributes.font() );
    // m_label->setText( titleText() ); // done by setTitleText()
    m_label->setAngle( axisLabelAngle );
    m_label->setPosition( axisLabelPosition );
    m_label->setReferencePoint( KDChartEnums::PositionSouth );
    QFont font = attributes.font();
    font.setPointSizeF( 0.85 * font.pointSizeF() );
    m_fifty->setFont( font );
    m_fifty->setAngle( fiftyMarkAngle );
    m_fifty->setPosition( fiftyMarkPosition );
    m_fifty->setReferencePoint( fiftyMarkReferencePoint );
}
Пример #3
0
static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta )
{
    QFont f = font;
    const qreal origResult = f.pointSizeF();
    qreal result = origResult;
    const QSizeF mySize = geometry.size();
    if ( mySize.isNull() )
        return result;

    const QString t = text;
    QFontMetrics fm( f );
    while ( true )
    {
        const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size();

        if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() )
            return result;

        result -= 0.5;
        if ( result <= 0.0 )
            return origResult;
        f.setPointSizeF( result );
        fm = QFontMetrics( f );
    }
}
Пример #4
0
void MainWindow::on_paintValuesCB_toggled( bool checked )
{
    const int colCount = m_lines->model()->columnCount();
    for ( int iColumn = 0; iColumn<colCount; ++iColumn ) {
        DataValueAttributes a = m_lines->dataValueAttributes( iColumn );
        a.setVisible( true );

        TextAttributes ta = a.textAttributes();
        ta.setRotation( 0 );
        ta.setFont( QFont( "Comic", 10 ) );
        ta.setPen( m_lines->brush( iColumn ).color() );
        ta.setVisible( checked );

        a.setTextAttributes( ta );
        m_lines->setDataValueAttributes( iColumn, a);
    }
}
Пример #5
0
/**
  \brief Use this to specify the text attributes to be used for this item.

  \sa textAttributes
*/
void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a )
{
    mAttributes = a;
    cachedFont = a.font();
    cachedSizeHint = QSize(); // invalidate size hint
    sizeHint();
    if( mParent )
        mParent->update();
}
Пример #6
0
MainWindow::MainWindow( QWidget *parent )
    : QWidget( parent )
    , m_chart( new Chart() )
    , m_diagram( m_chart )
{
    setupUi( this );

    m_HLCModel.loadFromCSV( ":/HLC" );
    m_OHLCModel.loadFromCSV( ":/OHLC" );

    m_diagram.setType( StockDiagram::HighLowClose );
    m_diagram.setModel( &m_HLCModel );
    m_chart->coordinatePlane()->replaceDiagram( &m_diagram );
    KDChart::Legend* legend  = new KDChart::Legend( &m_diagram, m_chart );
    m_chart->addLegend( legend );

    QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame );
    chartLayout->addWidget( m_chart );

    // Abscissa
    CartesianAxis *leftAxis = new CartesianAxis( &m_diagram );
    // Ordinate
    CartesianAxis *bottomAxis = new CartesianAxis( &m_diagram );

    leftAxis->setPosition( CartesianAxis::Left );

    TextAttributes attributes = bottomAxis->textAttributes();
    attributes.setRotation( 90 );
    attributes.setFontSize( Measure( 7.0, KDChartEnums::MeasureCalculationModeAbsolute ) );
    bottomAxis->setTextAttributes( attributes );
    bottomAxis->setPosition( CartesianAxis::Bottom );
    m_diagram.addAxis( leftAxis );
    m_diagram.addAxis( bottomAxis );
    m_diagram.addAxis( bottomAxis );
    applyColor( QColor( "chartreuse" ) );
    const bool connected = connect( colorChooser, SIGNAL( clicked() ), SLOT( chooseColor() ) );
    Q_ASSERT( connected );
    Q_UNUSED( connected );

    // Initialize all values for the stock chart to sane defaults
    initValues();
}
Пример #7
0
bool TextAttributes::operator==( const TextAttributes& r ) const
{
    // the following works around a bug in gcc 4.3.2
    // causing StyleHint to be set to Zero when copying a QFont
    const QFont myFont( font() );
    QFont r_font( r.font() );
    r_font.setStyleHint( myFont.styleHint(), myFont.styleStrategy() );
    return ( isVisible() == r.isVisible() &&
             myFont == r_font &&
             fontSize() == r.fontSize() &&
             minimalFontSize() == r.minimalFontSize() &&
             autoRotate() == r.autoRotate() &&
             autoShrink() == r.autoShrink() &&
             rotation() == r.rotation() &&
             pen() == r.pen() &&
             textDocument() == r.textDocument() );
}
Пример #8
0
void markPeak(Plotter* p, const QModelIndex& peak, quint64 cost, const KColorScheme& scheme)
{
    QBrush brush = p->model()->data(peak, DatasetBrushRole).value<QBrush>();

    QColor outline = brush.color();
    QColor foreground = scheme.foreground().color();
    QBrush background = scheme.background();

    DataValueAttributes dataAttributes = p->dataValueAttributes(peak);
    dataAttributes.setDataLabel(prettyCost(cost));
    dataAttributes.setVisible(true);
    dataAttributes.setShowRepetitiveDataLabels(true);
    dataAttributes.setShowOverlappingDataLabels(false);

    FrameAttributes frameAttrs = dataAttributes.frameAttributes();
    QPen framePen(outline);
    framePen.setWidth(2);
    frameAttrs.setPen(framePen);
    frameAttrs.setVisible(true);
    dataAttributes.setFrameAttributes(frameAttrs);

    MarkerAttributes a = dataAttributes.markerAttributes();
    a.setMarkerSize(QSizeF(7, 7));
    a.setPen(outline);
    a.setMarkerStyle(KChart::MarkerAttributes::MarkerDiamond);
    a.setVisible(true);
    dataAttributes.setMarkerAttributes(a);

    TextAttributes txtAttrs = dataAttributes.textAttributes();
    txtAttrs.setPen(foreground);
    txtAttrs.setFontSize(Measure(12));
    dataAttributes.setTextAttributes(txtAttrs);

    BackgroundAttributes bkgAtt = dataAttributes.backgroundAttributes();

    bkgAtt.setBrush(background);
    bkgAtt.setVisible(true);
    dataAttributes.setBackgroundAttributes(bkgAtt);

    p->setDataValueAttributes(peak, dataAttributes);
}
Пример #9
0
AttributedString BaseTextShadowNode::getAttributedString(
  const TextAttributes &textAttributes,
  const SharedShadowNodeSharedList &childNodes
) const {
  // TODO: Implement caching.

  AttributedString attributedString;

  for (auto &&childNode : *childNodes) {
    // RawShadowNode
    SharedRawTextShadowNode rawTextShadowNode = std::dynamic_pointer_cast<const RawTextShadowNode>(childNode);
    if (rawTextShadowNode) {
      AttributedString::Fragment fragment;
      fragment.string = rawTextShadowNode->getProps()->text;
      fragment.textAttributes = textAttributes;
      attributedString.appendFragment(fragment);
      continue;
    }

    // TextShadowNode
    SharedTextShadowNode textShadowNode = std::dynamic_pointer_cast<const TextShadowNode>(childNode);
    if (textShadowNode) {
      TextAttributes localTextAttributes = textAttributes;
      localTextAttributes.apply(textShadowNode->getProps()->textAttributes);
      attributedString.appendAttributedString(textShadowNode->getAttributedString(localTextAttributes, textShadowNode->getChildren()));
      continue;
    }

    // Any other kind of ShadowNode
    AttributedString::Fragment fragment;
    fragment.shadowNode = childNode;
    fragment.textAttributes = textAttributes;
    attributedString.appendFragment(fragment);
  }

  return attributedString;
}
Пример #10
0
MainWindow::MainWindow( QWidget* parent ) :
    QWidget( parent )
{
    setupUi( this );

    QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame );
    m_chart = new Chart();
    m_chart->setGlobalLeading( 10,  10,  10,  10 );
    chartLayout->addWidget( m_chart );
    hSBar->setVisible( false );
    vSBar->setVisible( false );

    m_model.loadFromCSV( ":/data" );

    // Set up the diagram
    m_lines = new BarDiagram();
    m_lines->setModel( &m_model );

    // create and position axis
    CartesianAxis *topAxis = new CartesianAxis( m_lines );
    CartesianAxis *leftAxis = new CartesianAxis ( m_lines );
    RulerAttributes rulerAttr = topAxis->rulerAttributes();
    rulerAttr.setTickMarkPen( 0.9999999, QPen( Qt::red ) );
    rulerAttr.setTickMarkPen( 2.0, QPen( Qt::green ) );
    rulerAttr.setTickMarkPen( 3.0, QPen( Qt::blue ) );
    rulerAttr.setShowMinorTickMarks(true);
    //rulerAttr.setShowMajorTickMarks(false);
    topAxis->setRulerAttributes( rulerAttr );
    CartesianAxis *rightAxis = new CartesianAxis ( m_lines );
    CartesianAxis *bottomAxis = new CartesianAxis ( m_lines );
    topAxis->setPosition ( CartesianAxis::Top );
    leftAxis->setPosition ( CartesianAxis::Left );
    rightAxis->setPosition ( CartesianAxis::Right );
    bottomAxis->setPosition ( CartesianAxis::Bottom );

// set the margin that should be used between the displayed labels and the ticks to zero
#if 0
    RulerAttributes ra = bottomAxis->rulerAttributes();
    ra.setLabelMargin(0);
    bottomAxis->setRulerAttributes( ra );
#endif

// show a red frame around the bottom axis
#if 0
    FrameAttributes fa( bottomAxis->frameAttributes() );
    fa.setPen( QPen(QBrush(QColor("#ff0000")),1.0) );
    fa.setVisible( true );
    bottomAxis->setFrameAttributes( fa );
#endif
    
    // set axis titles
    topAxis->setTitleText ( "Abscissa color configured top position" );
    leftAxis->setTitleText ( "left Ordinate: fonts configured" );
    rightAxis->setTitleText ( "right Ordinate: default settings" );
    bottomAxis->setTitleText ( "Abscissa Bottom" );
    topAxis->setTitleSize(1.1);
    topAxis->setTitleSpace(.4);

    // configure titles text attributes
    TextAttributes taTop ( topAxis->titleTextAttributes () );
    taTop.setPen( QPen( Qt::red ) );
    topAxis->setTitleTextAttributes ( taTop );

    TextAttributes taLeft ( leftAxis->titleTextAttributes () );
    taLeft.setRotation( 180 );
    Measure me( taLeft.fontSize() );
    me.setValue( me.value() * 0.8 );
    taLeft.setFontSize( me );

// Set the following to 1, to hide the left axis title
//  - no matter if a title text is set or not
#if 0
    taLeft.setVisible( false );
#endif
    leftAxis->setTitleTextAttributes ( taLeft );

    TextAttributes taBottom ( bottomAxis->titleTextAttributes () );
    taBottom.setPen(  QPen( Qt::blue ) );
    bottomAxis->setTitleTextAttributes ( taBottom );

    // configure labels text attributes
    TextAttributes taLabels( topAxis->textAttributes() );
    taLabels.setPen(  QPen( Qt::darkGreen ) );
    taLabels.setRotation( 90 );
    topAxis->setTextAttributes( taLabels );
    leftAxis->setTextAttributes( taLabels );
    bottomAxis->setTextAttributes( taLabels );


// Set the following to 0, to see the default Abscissa labels
// (== X headers, as read from the data file)
#if 1
    // configure labels and their shortened versions
    QStringList daysOfWeek;
    daysOfWeek << "M O N D A Y" << "Tuesday" << "Wednesday"
               << "Thursday" << "Friday" ;
    topAxis->setLabels( daysOfWeek );

    QStringList shortDays;
    shortDays << "MON" << "Tue" << "Wed"
              << "Thu" << "Fri";
    topAxis->setShortLabels( shortDays );

    QStringList bottomLabels;
    bottomLabels << "Team A" << "Team B" << "Team C";
    bottomAxis->setLabels( bottomLabels );

    QStringList shortBottomLabels;
    shortBottomLabels << "A" << "B";
    bottomAxis->setShortLabels( shortBottomLabels );
#endif

    // add axis
    m_lines->addAxis( topAxis );
    m_lines->addAxis( leftAxis );
    m_lines->addAxis( rightAxis );
    m_lines->addAxis( bottomAxis );

    // assign diagram to chart view
    m_chart->coordinatePlane()->replaceDiagram( m_lines );
}
Пример #11
0
void RadarDiagram::paint( PaintContext* ctx,
                          bool calculateListAndReturnScale,
                          qreal& newZoomX, qreal& newZoomY )
{
    // note: Not having any data model assigned is no bug
    //       but we can not draw a diagram then either.
    if ( !checkInvariants(true) )
        return;
    d->reverseMapper.clear();

    const int rowCount = model()->rowCount( rootIndex() );
    const int colCount = model()->columnCount( rootIndex() );

    int iRow, iCol;

    const qreal min = dataBoundaries().first.y();
    const qreal r = qAbs( min ) + dataBoundaries().second.y();
    const qreal step = ( r - qAbs( min ) ) / ( numberOfGridRings() );
    
    RadarCoordinatePlane* plane = dynamic_cast<RadarCoordinatePlane*>(ctx->coordinatePlane());
    TextAttributes ta = plane->textAttributes();
    QRectF fontRect = ctx->rectangle();    
    fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) );
    const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta );
    QFont labelFont = ta.font();
    ctx->painter()->setPen( ta.pen() );
    labelFont.setPointSizeF( labelFontSize );
    const QFontMetricsF metric( labelFont );
    const qreal labelHeight = metric.height();
    QPointF offset;
    QRectF destRect = ctx->rectangle();    
    if ( ta.isVisible() )
    {        
        destRect.setY( destRect.y() + 2 * labelHeight );
        destRect.setHeight( destRect.height() - 4 * labelHeight );
    }
    
    if ( calculateListAndReturnScale ) {
        ctx->painter()->save();
        // Check if all of the data value texts / data comments will fit
        // into the available space:
        d->labelPaintCache.clear();
        ctx->painter()->save();
        for ( iCol=0; iCol < colCount; ++iCol ) {
            for ( iRow=0; iRow < rowCount; ++iRow ) {
                QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
                const qreal value = model()->data( index ).toReal();
                QPointF point = scaleToRealPosition( QPointF( value, iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
                d->addLabel( &d->labelPaintCache, index, 0, PositionPoints( point ),
                             Position::Center, Position::Center, value );
            }
        }
        ctx->painter()->restore();
        const qreal oldZoomX = coordinatePlane()->zoomFactorX();
        const qreal oldZoomY = coordinatePlane()->zoomFactorY();
        newZoomX = oldZoomX;
        newZoomY = oldZoomY;
        if ( d->labelPaintCache.paintReplay.count() ) {
            QRectF txtRectF;
            d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF );
            const QRect txtRect = txtRectF.toRect();
            const QRect curRect = coordinatePlane()->geometry();
            const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right()  - txtRect.right() );
            const qreal gapY = qMin( txtRect.top()  - curRect.top(),  curRect.bottom() - txtRect.bottom() );
            newZoomX = oldZoomX;
            newZoomY = oldZoomY;
            if ( gapX < 0.0 )
                newZoomX *= 1.0 + (gapX-1.0) / curRect.width();
            if ( gapY < 0.0 )
                newZoomY *= 1.0 + (gapY-1.0) / curRect.height();
        }
        ctx->painter()->restore();

    } else {
        // Iterate through data sets and create a list of polygons out of them.
        QList<Polygon> polygons;
        for ( iCol=0; iCol < colCount; ++iCol ) {
            //TODO(khz): As of yet RadarDiagram can not show per-segment line attributes
            //           but it draws every polyline in one go - using one color.
            //           This needs to be enhanced to allow for cell-specific settings
            //           in the same way as LineDiagram does it.
            QPolygonF polygon;
            QPointF point0;
            for ( iRow=0; iRow < rowCount; ++iRow ) {
                QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
                const qreal value = model()->data( index ).toReal();
                QPointF point = scaleToRealPosition( QPointF( value, d->reverseData ? ( rowCount - iRow ) : iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
                polygon.append( point );
                if ( ! iRow )
                    point0= point;
            }
            if ( closeDatasets() && rowCount )
                polygon.append( point0 );

            QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value<QBrush>();
            QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >();
            if ( p.style() != Qt::NoPen )
            {
                polygons.append( Polygon( polygon, brush, PrintingParameters::scalePen( p ) ) );
            }
        }

        // first fill the areas with the brush-color and the defined alpha-value.
        if (d->fillAlpha > 0.0) {
            Q_FOREACH(const Polygon& p, polygons) {
                PainterSaver painterSaver( ctx->painter() );
                ctx->painter()->setRenderHint ( QPainter::Antialiasing );
                QBrush br = p.brush;
                QColor c = br.color();
                c.setAlphaF(d->fillAlpha);
                br.setColor(c);
                ctx->painter()->setBrush( br );
                ctx->painter()->setPen( p.pen );
                ctx->painter()->drawPolygon( p.polygon );
            }
        }
Пример #12
0
void AbstractDiagram::Private::addLabel(
    LabelPaintCache* cache,
    const QModelIndex& index,
    const CartesianDiagramDataCompressor::CachePosition* position,
    const PositionPoints& points,
    const Position& autoPositionPositive, const Position& autoPositionNegative,
    const qreal value, qreal favoriteAngle /* = 0.0 */ )
{
    CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs(
        aggregatedAttrs( index, position ) );

    QMap<QModelIndex, DataValueAttributes>::const_iterator it;
    for ( it = allAttrs.constBegin(); it != allAttrs.constEnd(); ++it ) {
        DataValueAttributes dva = it.value();
        if ( !dva.isVisible() ) {
            continue;
        }

        const bool isPositive = ( value >= 0.0 );

        RelativePosition relPos( dva.position( isPositive ) );
        relPos.setReferencePoints( points );
        if ( relPos.referencePosition().isUnknown() ) {
            relPos.setReferencePosition( isPositive ? autoPositionPositive : autoPositionNegative );
        }

        // Rotate the label position (not the label itself) if the diagram is rotated so that the defaults still work
        if ( isTransposed() ) {
            KChartEnums::PositionValue posValue = relPos.referencePosition().value();
            if ( posValue >= KChartEnums::PositionNorthWest && posValue <= KChartEnums::PositionWest ) {
                // rotate 90 degrees clockwise
                posValue = static_cast< KChartEnums::PositionValue >( posValue + 2 );
                if ( posValue > KChartEnums::PositionWest ) {
                    // wraparound
                    posValue = static_cast< KChartEnums::PositionValue >( posValue -
                                ( KChartEnums::PositionWest - KChartEnums::PositionNorthWest ) );
                }
                relPos.setReferencePosition( Position( posValue ) );
            }
        }

        const QPointF referencePoint = relPos.referencePoint();
        if ( !diagram->coordinatePlane()->isVisiblePoint( referencePoint ) ) {
            continue;
        }

        const qreal fontHeight = cachedFontMetrics( dva.textAttributes().
                calculatedFont( plane, KChartEnums::MeasureOrientationMinimum ), diagram )->height();

        // Note: When printing data value texts and padding's Measure is using automatic reference area
        //       detection, the font height is used as reference size for both horizontal and vertical
        //       padding.
        QSizeF relativeMeasureSize( fontHeight, fontHeight );

        if ( !dva.textAttributes().hasRotation() ) {
            TextAttributes ta = dva.textAttributes();
            ta.setRotation( favoriteAngle );
            dva.setTextAttributes( ta );
        }

        // get the size of the label text using a subset of the information going into the final layout
        const QString text = formatDataValueText( dva, index, value );
        QTextDocument doc;
        doc.setDocumentMargin( 0 );
        if ( Qt::mightBeRichText( text ) ) {
            doc.setHtml( text );
        } else {
            doc.setPlainText( text );
        }
        const QFont calculatedFont( dva.textAttributes()
                                    .calculatedFont( plane, KChartEnums::MeasureOrientationMinimum ) );
        doc.setDefaultFont( calculatedFont );

        const QRectF plainRect = doc.documentLayout()->frameBoundingRect( doc.rootFrame() );

        /**
        * A few hints on how the positioning of the text frame is done:
        *
        * Let's assume we have a bar chart, a text for a positive value
        * to be drawn, and "North" as attrs.positivePosition().
        *
        * The reference point (pos) is then set to the top center point
        * of a bar. The offset now depends on the alignment:
        *
        *    Top: text is centered horizontally to the bar, bottom of
        *         text frame starts at top of bar
        *
        *    Bottom: text is centered horizontally to the bar, top of
        *            text frame starts at top of bar
        *
        *    Center: text is centered horizontally to the bar, center
        *            line of text frame is same as top of bar
        *
        *    TopLeft: right edge of text frame is horizontal center of
        *             bar, bottom of text frame is top of bar.
        *
        *    ...
        *
        * Positive and negative value labels are treated equally, "North"
        * also refers to the top of a negative bar, and *not* to the bottom.
        *
        *
        * "NorthEast" likewise refers to the top right edge of the bar,
        * "NorthWest" to the top left edge of the bar, and so on.
        *
        * In other words, attrs.positivePosition() always refers to a
        * position of the *bar*, and relPos.alignment() always refers
        * to an alignment of the text frame relative to this position.
        */

        QTransform transform;
        {
            // move to the general area where the label should be
            QPointF calcPoint = relPos.calculatedPoint( relativeMeasureSize );
            transform.translate( calcPoint.x(), calcPoint.y() );
            // align the text rect; find out by how many half-widths / half-heights to move.
            int dx = -1;
            if ( relPos.alignment() & Qt::AlignLeft ) {
                dx -= 1;
            } else if ( relPos.alignment() & Qt::AlignRight ) {
                 dx += 1;
            }

            int dy = -1;
            if ( relPos.alignment() & Qt::AlignTop ) {
                dy -= 1;
            } else if ( relPos.alignment() & Qt::AlignBottom ) {
                dy += 1;
            }
            transform.translate( qreal( dx ) * plainRect.width() * 0.5,
                                 qreal( dy ) * plainRect.height() * 0.5 );

            // rotate the text rect around its center
            transform.translate( plainRect.center().x(), plainRect.center().y() );
            int rotation = dva.textAttributes().rotation();
            if ( !isPositive && dva.mirrorNegativeValueTextRotation() ) {
                rotation *= -1;
            }
            transform.rotate( rotation );
            transform.translate( -plainRect.center().x(), -plainRect.center().y() );
        }

        QPainterPath labelArea;
        //labelArea.addPolygon( transform.mapToPolygon( plainRect.toRect() ) );
        //labelArea.closeSubpath();
        // Not doing that because QTransform has a special case for 180° that gives a different than
        // usual ordering of the points in the polygon returned by mapToPolygon( const QRect & ).
        // We expect a particular ordering in paintDataValueTextsAndMarkers() by using elementAt( 0 ),
        // and similar things might happen elsewhere.
        labelArea.addPolygon( transform.map( QPolygon( plainRect.toRect(), true ) ) );

        // store the label geometry and auxiliary data
        cache->paintReplay.append( LabelPaintInfo( it.key(), dva, labelArea,
                                                   referencePoint, value >= 0.0, text ) );
    }
}