Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

drawing text with monospace font not giving the same bounding rect (Qt 5.13.2)



  • It is getting late. I'm giving up for tonight. There is something that I don't understand. I was expecting exact same bounding rect for text strings having the same number of characters with a monospace font.

    I have tried 3 different fonts:

    • Source Code Pro
    • Courier New
    • DejaVu Sans Mono

    In LibreOffice, I get the expected result but somehow with Qt it doesn't. I have nailed down the problem to:

    QString("%1").arg(val, 5, 'f', 2);
    

    '10.99' and ' 8.88' will have different length. (what might be an important detail in the mystery is that this number is appended to a string ending with a space. Therefore, there can be 1 or 2 adjacent consecutive spaces. I suspect that some space used by the 2 spaces is 'optimized' out somewhere by something but this is all I can tell or guess...)

    My code looks like this (disregard the various font settings. I have been shooting in the dark but none of them is changing the result:

    LinkedChart::LinkedChart(const QString &name, QGraphicsItem *parent, Qt::WindowFlags wFlags)
    : QChart(parent, wFlags),
      m_name(name),
      m_textFont("Source Code Pro", 8),
      m_textItem(NULL)
    {
        m_textFont.setWeight(QFont::ExtraLight);
        m_textFont.setFixedPitch(true);
        m_textFont.setKerning(false);
        QFontInfo info(m_textFont);
        qDebug("family " + info.family().toLatin1() + " px: %d", info.pixelSize());
    }
    void StupidSimpleText::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(widget);
    
        painter->setFont(font());
        painter->setPen(pen());
        QFontMetrics metrics(font());
        QString txt = text();
        QSize textSize  = metrics.size(Qt::TextSingleLine, txt);
        QRectF boundingRect;
        painter->drawText(QRectF(QPointF(0,0), textSize), Qt::AlignLeft|Qt::AlignVCenter, txt, &boundingRect);
        if (txt.startsWith("Ask Cm Vol:")) {
            qDebug() << txt.size() << boundingRect;
        }
    }
    

    The output is:

    family Source Code Pro px: 11
    77 QRectF(0,0 526x17)
    77 QRectF(0,0 526x17)
    77 QRectF(0,0 530x17)
    77 QRectF(0,0 526x17)
    77 QRectF(0,0 526x17)
    77 QRectF(0,0 530x17)
    77 QRectF(0,0 526x17)
    

    Is there some setting or secret text drawing knowledge that I am not aware of that would ensure that the bounding rect is always the same dimension for any strings of equal size?

    thank you very much!


  • Lifetime Qt Champion

    I would check textSize instead boundingRect since the boudingRect is the real width needed afaiu.



  • Ok good point. I would expect those 2 values to be equivalent (especially for monospace fonts...).

    I did add textSize to my qDebug statement, in my case, they seem to be the same:

    qDebug() << txt.size() << boundingRect << textSize;
    
    family Source Code Pro px: 11
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 518x17) QSize(518, 17)
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 530x17) QSize(530, 17)
    77 QRectF(0,0 526x17) QSize(526, 17)
    
    

    Ok, I guess it is time to read some good QPainter::drawText() code... I tried to avoid this but it seems to be the only way to figure out....

    Update: There is no way that I'm going to hunt this bug down inside Qt guts. I did put my nose into QTextEngine code and this beast is monstrously complex...

    I did play with:

    QString("%1").arg(val, 5, 'f', 2);
    

    by changing the fill character.... I don't understand anymore what monospace mean. This is the root of my problem. If I pick 'a', 'A' or '.' (dot) as the fill character, the issue is still present... BUT if I choose '0' (zero). The boundingRect becomes rock solid and stable for all my strings.


  • Lifetime Qt Champion

    Monospace means that every character has the same width and height - for example 8x14 pixels. This means no other character will be drawn inside this rect. It does not mean that all character will fill the 8x14 pixel - some will only fill e.g. 2x13 (e.g. an 'i'), some maybe 8x12 (e.g. 'w') - and this is why you get back different widths for the bounding rect.



  • Christian, I would agree if the bouding rect width difference was due to only the trailing character and I wouldn't care.

    This is a string that is updated based on mouse cursor position and the width difference occurs almost at the beginning of the string. This makes monitoring it almost as sickening as being rocked while in a boat during a big storm...

    Beside, I don't get it. If I take any text editor, open any file from a terminal with vi or emacs. The 79th character will always be aligned with all the other lines's 79th character no matter what is the text content. I can even achieve this with LibreOffice Writer when I use a monospace font.

    Why can't I with Qt? Why would it be different?


  • Lifetime Qt Champion

    @lano1106 said in drawing text with monospace font not giving the same bounding rect (Qt 5.13.2):

    The 79th character will always be aligned with all the other lines's 79th character no matter what is the text content

    This is also true for Qt - otherwise you qtcreator would not work. I would guess QPainter::drawText() is given a wrong rectangle and therefore it's jumping. I would use the QPointF version. Also please provide a minimal compilable example so we can debug your problem.



  • Christian,

    thanks a lot for your support. I'm trying to nail down the issue. Right now, I have a small program and this one gives me the correct behavior. I will build up from there until I'm able to reproduce the issue.

    monospace.h:

    #include <QWidget>
    
    class MonospaceIssue : public QWidget
    {
        Q_OBJECT
    public:
        MonospaceIssue(QWidget *parent = NULL);
        virtual ~MonospaceIssue();
    protected:
        virtual void paintEvent(QPaintEvent *event);
    };
    

    main.cpp:

    #include <QApplication>
    #include <QPainter>
    #include "monospace.h"
    
    MonospaceIssue::MonospaceIssue(QWidget *parent)
    : QWidget(parent)
    {
    }
    
    MonospaceIssue::~MonospaceIssue()
    {
    }
    
    void MonospaceIssue::paintEvent(QPaintEvent *event)
    {
        QPainter painter(this);
        QFont monoFont("Source Code Pro", 8);
        painter.setFont(monoFont);
        QPen magentaPen = QPen(QColor(Qt::magenta), 1);
        painter.setPen(magentaPen);
        QString shouldHaveSameWidthTxt =
    "WWWWWWWWWWW 27.87aaaaa 10.88Ecmd\n"
    "iiiiiiiiiii  7,14MMMMM  7.66Ecmd";
        QFontMetrics metrics(monoFont);
        QSize textSize  = metrics.size(0, shouldHaveSameWidthTxt);
        QRectF boundingRect;
        painter.drawText(QRectF(QPointF(0,0), textSize),
                         Qt::AlignLeft|Qt::AlignVCenter, shouldHaveSameWidthTxt, &boundingRect);
    }
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        MonospaceIssue MyWindow;
        MyWindow.show();
        return app.exec();
    }
    

    stay tuned.


  • Lifetime Qt Champion

    Maybe add a variable string for your painting by e.g enable mouse tracking and print out the mouse positions.



  • Not necessary. I did just plug in the crafted string literal into my application. This is obviously not monospace that I am getting.

    I will simplify things until I isolate the issue.

    Simple test program output:
    Simple test program output

    Application output:
    Application output

    Almost there...



  • Ok. I am ashamed.... When I get stupid problems like this one... It means that I start to be tired and should look at it back the next morning... I have found my problem. The problem is all good. There was a crucial missing line in my code:

    void LinkedChart::setInfoText(const QString &text)
    {
        if (text == m_text)
            return;
        m_text = text;
        if (!m_textItem) {
            if (!text.size()) return;
    //        m_textItem = scene()->addSimpleText(text, m_textFont);
            m_textItem = new StupidSimpleText(text);
            scene()->addItem(m_textItem);
            QPen magentaPen = QPen(QColor(Qt::magenta), 1);
            m_textItem->setPen(magentaPen);
    
            m_textItem->setFont(m_textFont); // <<<----- I omitted to set my super duper monospace font in the Graphics Item
    
            QPointF plotTopLeft = plotArea().topLeft();
            plotTopLeft.rx() += 5.0;
            plotTopLeft.ry() += 5.0;
            m_textItem->setPos(plotTopLeft);
            
            /*
             * Connection to reposition the graphics item when/if the plotarea is changing.
             */
            connect(this, SIGNAL(plotAreaChanged(const QRectF &)),
                    this, SLOT(ourPlotAreaChanged(const QRectF &)));
        }
        m_textItem->setText(text);
    }
    

  • Lifetime Qt Champion

    Nice to see the problem solved :)


Log in to reply