PDA

View Full Version : For Qt 4.6.x, how to auto-size text to fit in a specified width?



dchow
4th February 2010, 20:03
Inside of my QGraphicsRectItem::paint(), I am trying to draw the name of the item within its rect(). However, for each of the different items, they can be of variable width and similarly names can be of variable length.

Currently I am starting with a maximum font size, checking if it fits and decrementing it until I find a font size that fits. So far, I haven't been able to find a quick and easy way to do this. Is there a better, or more efficient way to do this?

Thanks!


void checkFontSize(QPainter *painter, const QString& name) {
// check the font size - need a better algorithm... this could take awhile
while (painter->fontMetrics().width(name) > rect().width()) {
int newsize = painter->font().pointSize() - 1;
painter->setFont(QFont(painter->font().family(), newsize));
}
}

JohannesMunk
4th February 2010, 22:10
Hi there!

Two days ago I solved it with QFont::setPointSizeF like this:



float factor = rect().width() / painter->fontMetrics().width(name);
if ((factor < 1) || (factor > 1.25))
{
QFont f = painter->font();
f.setPointSizeF(f.pointSizeF()*factor);
painter->setFont(f);
}

This assumes, that your fonts width scales like its height. Works fine for me.

HIH

Johannes

ChrisW67
4th February 2010, 22:14
This, or something along the same lines seem like the only way to me. Be careful that you don't get a runaway loop with zero or negative font size (width == 0?); not sure what QFontMetrics will make of that.

An approximation could go like: Pre-calculating the width of an "M" at each point size (Substitute the character set's widest glyph if you are using non-Latin characters). When you get your string, take its length and select the font size that gets length() Ms into the available width. Might be a bit quicker if you call this a lot.

dchow
4th February 2010, 22:48
Johannes - I just tried out the code snippet you supplied and so far so good! I like how your solution comes up with a good approximation in one pass. Thanks!

remy_david
4th February 2011, 15:36
I have got the same problem, so I post here since this thread is in good place in Google :)
The solution provided by JohannesMunk works well for 1 line of text.
But what about auto-size text in a specified rectangle, possibly (but not necessary) wraping on multiple lines.
Do you have a solution for that ?

JohannesMunk
4th February 2011, 18:15
Hi!

How about using QFontMetrics::boundingRect with the Qt::TextWordWrap flag set?

The problem will be that word wrapping will usually only occur, after the scaling has been applied.
So the text will not be wrapped when called the first time.

Mmh..

Johannes

JohannesMunk
5th February 2011, 13:31
I needed this functionality myself - here is what I came up with:



qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
QRectF r = boundingRect();
QFont f = painter->font();
qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth();
int pixelsize = sqrt(r.width()*r.height()/aspectRatio/(stitle.length()*3))*aspectRatio;
f.setPixelSize(pixelsize);
int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap;
if ((pixelsize * lod) < 13)
flags |= Qt::TextWrapAnywhere;
QFontMetricsF fm(f);
QRectF tbr = fm.boundingRect(r,flags,stitle);
pixelsize = f.pixelSize()*qMin(r.width()*0.95/tbr.width(),r.height()*0.95/tbr.height());
f.setPixelSize(pixelsize);
painter->setFont(f);
painter->drawText(r,flags,stitle);

The idea of the first attempt to calculate a pixelsize is to calculate how big a letter can be if you want to distribute a certain amount of letters on the available space.

numberOfLetters = width / charWidth * height / (charWidth*aspectRatio)

=> pixelSize = charWidth * aspectRatio = sqrt ( width * height / numberOfLetters / aspectRatio) * aspectRatio;

For my scenario numberOfLetters is 3 times the amount of text I want to display, because I don't want to cover the whole item with text.

With this guess I call QFontMetricF::boundingRect and fine tune the result accordingly.

This works fine for my purpose.

HIH

Johannes

corrado1972
4th January 2012, 15:38
This works fine for my purpose.


I'm confirming you that this solution (i named it the 2nd) fit at the best my requirements too.
Here is my two solutions comparison:
7232

My scene doesn't has too much items, so the drawing performances are acceptable.
Thank you guys for shared your work.

victtim
22nd March 2016, 04:39
Hi Johannes / All

Could you please explain the math behind this?

"numberOfLetters = width / charWidth * height / (charWidth*aspectRatio)

=> pixelSize = charWidth * aspectRatio = sqrt ( width * height / numberOfLetters / aspectRatio) * aspectRatio;
"

I don't follow it.

Thank you

Tim