Set QGraphicsTextItem text content to exact height and width

I need to create text elements with the exact width and height of the text content .
Text height is the most important requirement.
The position of the text should relate to the text itself.

I should also be able to place it on canvas in the exact place.

Assuming a (printed) canvas (on a larger QGraphicsScene ), say 5 inches wide and 1 inch high, my text should be able to stretch the upper lower left right corner and be placed on the canvas, and not partially.


I will subclass QGraphicsTextItem for my item type. I resize it using QTransform() to the required size in inches or mm or in pixels (72 * inch).
We also set the marker document() to 0, and everything inside (for example, QTextBlockFormat fields) is also equal to 0.

I implemented setItemSize(QSizeF sz) (with sz in pixels) which resizes the QGraphicsTextItem as needed.
Sz is initialized using the bounding box element.
Assuming no wrap, single-line text (multi-line can be resolved separately after fixing this problem).

When adding an element to the canvas, I still see the upper and lower margins - and this depends on the font choice.
I drew a rectangle around the element to see it.
The upper / lower distances depend on the font selection.


I tried using font metrics to determine these distances (in paint() I drew lines to try to determine the position and the rectangle at which the text fits).

I would be glad to at least determine the correct size for use in uppercase, without accents or special character fonts (this would be the beginning, although naturally I would need to use any characters). <b> But at least somehow determine the size and position (relative to the (0,0) element) of the text content even in the simplest case .....

Font taps tightBoundingRect() seem to be the most accurate in size, but it seems impossible to determine its position so that I can somehow properly create my objects and possibly resize / shift them correctly to fit on the canvas.


Here are some examples of my struggle to determine at least the exact size and position of the text relative to the (0,0) element (provided that as soon as I do this, I can expose this information on the street or enable a shift in the transformation of the object when resizing).
Please note that the size of text advertised with font labels does not always cover the text, and for different fonts I canโ€™t position a hard bounding box (magenta) around the text itself. (I made a few guesses, the code below is just one - the lines are trying to show different sizes of font metrics).

enter image description here

Above were experiments with the drawing function of a text element that inherits QGraphicsTextItem :

 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { // draw text QGraphicsTextItem::paint(painter, option, widget); QPen p; p.setWidthF(0); QFontMetricsF fm(this->font()); qreal ascent = fm.ascent(), descent = fm.descent(), hheight = fm.height(); QRectF r = QGraphicsTextItem::boundingRect(); QRectF rFont= fm.tightBoundingRect(toPlainText()); qreal xmax = r.right(); painter->save(); painter->setBrush(Qt::NoBrush); // where is "ascent + descent" p.setColor(Qt::green); painter->setPen(p); painter->drawLine(QPointF(2, ascent), QPointF(2, ascent + descent)); painter->drawLine(QPointF(2, ascent + descent), QPointF(xmax/2, ascent + descent)); // where is "height" p.setColor(Qt::red); painter->setPen(p); painter->drawLine(QPointF(xmax/2, 0), QPointF(xmax/2, hheight)); painter->drawLine(QPointF(xmax/2, ascent + descent), QPointF(xmax, ascent + descent)); // where is "ascent" p.setColor(Qt::yellow); painter->setPen(p); painter->drawLine(QPointF(6, 0), QPointF(6, ascent)); painter->drawLine(QPointF(6, ascent), QPointF(xmax, ascent)); // something that may look like top of the text p.setColor(Qt::blue); painter->setPen(p); qreal yyy = ascent + rFont.y() + 1; painter->drawLine(QPointF(5, yyy), QPointF(xmax, yyy)); // this should be useful... should be the natural offset qreal yoffset = (r.height() - rFont.height()) / 2; // qDebug() << yoffset << r << rFont; //qreal y0 = (r.height() - fm.height())/2; p.setColor(Qt::darkGreen); painter->drawEllipse(10, yoffset, 1, 1); // where is the font rect p.setColor(Qt::magenta); painter->setPen(p); yoffset = (r.height() + rFont.height()) / 2; painter->translate(0, yoffset); painter->drawRect(rFont); painter->restore(); } 

I also tried not to use QGraphicsTextItem , but I will draw the text inside the rectangle. The same thing is happening.

(Qt 4.7 - 5.x)

+5
source share
1 answer

This is a bad decision. . This is an attempt to solve my own problem - at the first iteration - to set text with a given width and height using font metrics.

Reasons not to be good -

  • Even after resizing the text is less than desired, I do not understand why

  • The position is incorrect, based on the font style, the text may be above or below the canvas, which means that it is cropped.


I resize it using a coefficient computed from an element that bounds the size of the rectangle and font metrics that bound the rectangle (I used the limited bounding box for a more accurate size).

 myText->setItemFontSize(12); // If I use font metrics I need to reset text size on every change, because resizing loses font info QFontMetricsF fm(myText->font()); QRectF fmRect = fm.tightBoundingRect(myText.toPlainText().toUpper()); // without toUpper() the size is too small - even so it is a bit small // I read tightBoundingRect is slow - but boundingRect and height and ascent all give values that result in even smaller size //qreal absH = fm.ascent(); qreal absH = fmRect.height(); qreal absW = fmRect.width(); qreal absHeightRatio = myText->getItemSize().height() / absH; qreal absWidthRatio = myText->getItemSize().width() / absW; 

Then set the size:

 myText->setItemSize(QSizeF(absWidthRatio * textLength, absHeightRatio * fontHeight)); // This function scales the `QTransform` on item // but since I request a final position dependent on item size // (including blank space around it) - it has no chance of being accurate..... // This is where my next effort will go, figuring out how to get rid of the fluff // around the item inside the scaling 

Position setting function: attempt to center the text:

 // leftShift = 10, rightShift = 10 in example myText->setPos(0,0); QRectF r = myText->mapToScene(myText->boundingRect()).boundingRect(); QSizeF sz = r.size(); qreal w = sz.width(); qreal h = sz.height(); qreal cx = (m_docLength - w + leftShift - rightShift)/2 - r.left(); qreal cy = (m_docHeight - h)/2 - r.top(); myText->setPos(cx, cy); 

Images below for fontHeight = m_docHeight -

It is desirable:
- either the entire text size (climb + descent) is equal to the height of the document, and the text is centered vertically based on the content
- or the font size in upper case is equal to the height of the document, and the descent is lower than the document (with the text in the center only in upper case) - this will seem simpler based on how QGraphicsTextItem seems to position it

Actual:
- the text is smaller no matter what options I use to scale and centered based on uppercase text

enter image description here

As shown above - I have no idea how I could center vertically based on the content (so that the descent will fit for the text from edge to edge) - and instead, all I really want is upper case capital letters, but I donโ€™t I can achieve this.

Oh, and they are for the Arial font - one of the best. Other fonts jump everywhere, above or below the canvas. And for some fonts, the resulting text is actually smaller - which is inexplicable to me, because how can the limited bounding box be smaller than the bounding box of an element ...

However, this is until I got the text as close to the โ€œtrueโ€ size and placed on the canvas that matches its size.

+2
source

All Articles