PDA

View Full Version : How draw a rotated text in a QPainterPath?



iw2nhl
14th August 2007, 02:18
Hi,
I need to draw a rotated text as part of a QPainterPath to be drawn in a QGraphicsScene (I use a custom subclass of QGraphicsItem).
I can draw the text with QPainterPath::addText(), but it accepts only the starting point.
Really I have 2 problems:
1) I need the text to stay inside a specified rectangle
2) the rectangle may be rotated

For 1) I thought about this solution: a while() loop which reduces font size until QFontMetricsF::boundingRect() is small enough to fit in the requested rectangle.
Is there a better way?

For 2) I don't have a solution at the moment.

I don't know how to use the QTextLayout and QTextLine classes, may them help me in some way?

Any help/suggestion is really appreciated!

Thanks,
Alessandro

rajesh
14th August 2007, 06:49
in QPainterPath you can draw any type of data.
I dont understand your question.
is your question is to display rotated text in QGraphicsView?
for your 1st question: you can create own GraphicsTextItem by drive QGraphicTextItem and inside boundingRect() you can create rect.


class GraphicsTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
GraphicsTextItem(const QString & text, QGraphicsItem * parent = 0, QGraphicsScene * scene = 0 );
virtual QSize sizeHint() const{return szHint;}
virtual QSize minimumSizeHint() const{return minSzHint;}
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
QSize szHint, minSzHint;
QString m_text;
};

GraphicsTextItem::GraphicsTextItem(const QString & text, QGraphicsItem * parent , QGraphicsScene * scene )
:QGraphicsTextItem( text,parent,scene )
{
szHint = QSize(10,10);
minSzHint = QSize(10, 10);
adjustSize ();
m_text = text;
}

QRectF GraphicsTextItem::boundingRect() const
{
qreal penWidth = 1;
return QRectF(4 - penWidth / 2, 0 - penWidth / 2,
30 + penWidth / 2, PIN_HEIGHT + penWidth / 2);
}

void GraphicsTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->drawRect(boundingRect());
painter->drawText(boundingRect(),Qt::AlignCenter,m_text.lef t(5));
}

iw2nhl
15th August 2007, 03:25
Thank you rajesh for the reply.
For now I can use your suggestion.

Anyway my full problem is how to draw a text in a rotated rectangle with QPainterPath.
The idea is that I don't want to rotate all the QGraphicsItem, but I want to draw a rotated text inside a path with other elements in it. The result will go inside a QGraphicsPathItem.
May be there is a better way to obtain this effect.

Example:

QPainterPath path(0, 0);
path.lineTo(10, 0); // Horizontal line
path.lineTo(20, 10); // 45° line
path.addText(QPointF(20,10), m_font, "Hello");

The last line should specify the rectangle (= text size) in which to draw the text and the angle (in this example 45°), so that the text fits the rectangle and follows the previous line direction.

darksaga
15th August 2007, 05:07
don't know exactly, coz i've not yet worked with QPainterPath,

but you should take a look @ the QMatrix class

you could do something like:



QMatrix mat;
mat.rotate(myAngle);


and then you should use


//QPainterPath map ( const QPainterPath & path ) const
//e.g.:
QPainterPath newPath = mat.map(oldPath);


this shold get the job done, I think

iw2nhl
16th August 2007, 15:07
Thank you very much darksaga!!!
This was exactly what I was looking for!

Now the rotated rectangle is no more a problem.
Probably I can use the same way to make the text fit the rectangle: I could draw a text in a generic size (say 10 pts), than I can scale it's painter path in both x and y to fill the rectangle.
I'll post if it works or not :-)

iw2nhl
17th August 2007, 18:47
Thank you again because I solved the other problem too, in fact I could scale and rotate the text perfectly!
I just draw the text with the default size and then I scale it depending on the rectangle size.

Just I changed the rectangle mapping from a QPainterPath to a QPolygonF, because I think it is faster (QPainterPath handles curves too, while QPolygonF handles just points). I converted it to a QPainterPath after the mapping.

Here is the code (may be it can be useful to someone else):


{
// Rectangle mapping

QLineF rectBase = QLineF(begin, end);
qreal angle = rectBase.angle(QLineF(QPointF(0, 0), QPointF(1, 0)));
QMatrix rotationMatrix;
rotationMatrix.translate(begin.x(), begin.y());
rotationMatrix.rotate(angle);
path.addPolygon(rotationMatrix.map(QPolygonF(textR ectangle)));

// Text mapping

QFont font = QFont("courier");
// Take text size
QFontMetricsF fm(font);
QSizeF textSize = fm.size(0, text);
// Calculate how much to scale the text to fit the rectangle
qreal scaleX = textRectangle.width() / textSize.width();
qreal scaleY = textRectangle.height() / textSize.height();
// Apply the scale factors
rotationMatrix.scale(scaleX, -scaleY);
// Draw the text
QPainterPath textPath;
textPath.addText(QPointF(0, -fm.descent()), font, text);
path.addPath(rotationMatrix.map(textPath));
}

iw2nhl
17th August 2007, 18:55
I also added a little border to not have the text touching the rectangle:


{
// [...]

// Leave a 1 pixel wide border around the text
const qreal borderSize = 1;
const qreal doubleBorder = borderSize * 2;
// Calculate how much to scale the text to fit the rectangle
qreal scaleX = textRectangle.width() / (textSize.width() + doubleBorder);
qreal scaleY = textRectangle.height() / (textSize.height() + doubleBorder);
// Apply the scale factors
rotationMatrix.scale(scaleX, -scaleY);
// Center the text (because of the border)
qreal offsetX = borderSize;
qreal offsetY = borderSize;
// Draw the text
QPainterPath textPath;
textPath.addText(QPointF(offsetX, -(offsetY + fm.descent())), font, text);

// [...]
}


Now I have troubles in getting exact font size because QFontMetricsF seems to be very inaccurate and often the text goes outside the rectangle because it is bigger than what is reported.
Probably I'll make another post for this problem :D, anyway I think it is not easily solvable (Qt or X11 or KDE Bug?).