Search code examples
c++qtqt5qpainter

QPainter drawText with fractional pointsizes


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();
}

Solution

  • 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.