Is there a way to draw text with fractional point sizes in Qt 5.
I'm trying to use QFont::setPointSizeF()
but it doesn't seem to work on any platform I tried on (mac/linux/windows) and point size is always rounded.
QFontDatabase::isScalable
and QFontDatabase::isSmoothlyScalable
returns true
for the font in all cases.
I tried setting various QFont::fontHintingPreference
and QPainter::RenderHint
.
I might be able to work around this using QFont::setPixelSize
and QPainter::scale
, but seem odd that QFont::setPointSizeF
is broken?!
Am I missing something or doing something wrong?
Simple program that shows the problem:
#include <QtWidgets>
class MyWidget : public QWidget
{
public:
MyWidget() : QWidget(0)
{
}
protected:
void paintEvent(QPaintEvent */*e*/)
{
QPainter p(this);
int y=10;
for (qreal i = 10; i < 20; i += 0.2) {
QFont font("Times"); // or any font font in the system
font.setPointSizeF(i);
p.setFont(font);
p.drawText(1, y, QString("This should be point size %1 but is %2!").arg(font.pointSizeF()).arg(QFontInfo(font).pointSizeF()));
y += i;
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyWidget widget;
widget.resize(400, 740);
widget.show();
return app.exec();
}
It seems that Qt calculates pixel size based on point size and that ends up at QFont::setPixelSize which takes int as parameter so it gets rounded (or something like that).
So to get better precision I can do something like:
void paintEvent(QPaintEvent * /*e*/)
{
QPainter p(this);
int y=10;
for (qreal i = 10; i < 20; i += 0.2) {
QFont font("Times"); // or any font
font.setPointSizeF(i); // this has round to int error (on 72 dpi screen 10.2 would be rounded to 10 and 10.6 to 11.0 etc)
p.setFont(font);
qreal piX = i * p.device()->logicalDpiX() / 72.0;
qreal piY = i * p.device()->logicalDpiY() / 72.0;
qreal xscale = piX / qRound(piX);
qreal yscale = piY / qRound(piY);
//p.save();
p.translate(1, y);
p.scale(xscale, yscale);
p.drawText(0, 0, QString("This should be point size %1 but is %2!").arg(font.pointSizeF()).arg(QFontInfo(font).pointSizeF() * xscale));
p.resetTransform();
//p.restore();
y += i;
}
}
I can get desired result this way.