Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Impossible to get true text hight with QFontMetrics/QFontMetricsF

Impossible to get true text hight with QFontMetrics/QFontMetricsF

Scheduled Pinned Locked Moved Unsolved General and Desktop
17 Posts 4 Posters 2.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Chris Kawa
    11 Aug 2020, 13:01

    Works for me. Which Qt version and OS are you using? Do you have resolution scaling enabled in the OS?

    Btw. if all you want to do is center text you don't need to calculate precise rectangle for that manually. Just pass rect() and appropriate alignment flags to drawText, e.g.

    p.drawText(rect(), Qt::AlignCenter, myText);
    
    V Offline
    V Offline
    Violet Giraffe
    wrote on 11 Aug 2020, 13:16 last edited by Violet Giraffe 8 Nov 2020, 13:17
    #5

    @Chris-Kawa said in Impossible to get true text hight with QFontMetrics/QFontMetricsF:

    Just pass rect() and appropriate alignment flags to drawText

    That doesn't work either. As I said, there is an offset to the right and to the bottom. Compensating for the offset seems to solve the problem. So the problem is not with the height of the font, although I have doubts in that as well (after measuring the pixel sizes on the screen), but with the unexpected offset at which the text is drawn.

    Tested only on Windows so far, but on different PCs. Qt 5.14.1 and 5.15, no change between them. I do have a 175% and a 100% monitor in this system, AA_EnableHighDpiScaling enabled, but I have also tried on a pure 100% PC (single monitor, no scaling). I have a repro and I think I should report a bug.
    Here's the repro:

    #include <QApplication>
    #include <QFontMetricsF>
    #include <QMainWindow>
    #include <QPainter>
    
    struct TestWidget : QWidget
    {
    protected:
    	void paintEvent(QPaintEvent*) override
    	{
    		const QString text = "Hello!";
    		QPainter p(this);
    		p.fillRect(rect(), Qt::darkBlue);
    
    		auto textRect = QFontMetricsF(font()).tightBoundingRect(text);
    		textRect.moveTopLeft(QPointF(0.0, 0.0));
    		//textRect.setTopLeft(QPointF(textRect.left() - 2.0, textRect.top() - 6.0));
    
    		p.fillRect(textRect, Qt::green);
    		p.drawText(textRect, 0, text);
    	}
    };
    
    int main(int argc, char *argv[])
    {
    	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    	QApplication a(argc, argv);
    
    	QMainWindow mw;
    	auto* widget = new TestWidget;
    
    	auto f = widget->font();
    	f.setPixelSize(30);
    	widget->setFont(f);
    
    	mw.setCentralWidget(widget);
    	mw.show();
    
    	return a.exec();
    }
    

    Here's its output:

    alt text

    1 Reply Last reply
    0
    • V Offline
      V Offline
      Violet Giraffe
      wrote on 11 Aug 2020, 13:25 last edited by Violet Giraffe 8 Nov 2020, 13:29
      #6

      Same problem on macOS 10.15, even the amount of the offset is about the same.

      To clarify: p.drawText(rect(), Qt::AlignCenter, myText); also exhibits the same offset, so when total height of the widget is only a few pixels larger than the text height it becomes obvious that the text is not centered.

      A 1 Reply Last reply 11 Aug 2020, 14:21
      0
      • C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 11 Aug 2020, 14:18 last edited by
        #7

        Well you still need to center text in that calculated rectangle:

        p.drawText(textRect, Qt::AlignCenter, text);
        

        With that change I get
        text alignment 1

        And here's a slightly modified version:

        void paintEvent(QPaintEvent*) override
        {
            const QString text = "Hello!";
            QPainter p(this);
            
            const QRectF textRect = QFontMetricsF(font()).boundingRect(rect(), Qt::AlignCenter, text);
            
            p.fillRect(rect(), Qt::darkBlue);
            p.fillRect(textRect, Qt::green);
            p.drawText(rect(), Qt::AlignCenter, text);
        }
        

        This gives me

        text alignment 2

        And if I change textRect to

        QRectF textRect = QFontMetricsF(font()).tightBoundingRect(text);
        textRect.moveCenter(QRectF(rect()).center());
        

        I get this

        text alignment 3

        So everything seems as it should.
        I don't know. Seems to be something specific to your setup.

        V 1 Reply Last reply 11 Aug 2020, 14:40
        1
        • V Violet Giraffe
          11 Aug 2020, 13:25

          Same problem on macOS 10.15, even the amount of the offset is about the same.

          To clarify: p.drawText(rect(), Qt::AlignCenter, myText); also exhibits the same offset, so when total height of the widget is only a few pixels larger than the text height it becomes obvious that the text is not centered.

          A Offline
          A Offline
          Asperamanca
          wrote on 11 Aug 2020, 14:21 last edited by
          #8

          @Violet-Giraffe
          Normally, when you call drawText, it will render the text including any spacing that should go with it. The tightBoundingRect gives you the minimum space necessary to render the characters itself, but the drawText will still try to put the default spacing on top. You have to calculate the necessary offsets, and you get this information from the QFontMetricsF class: ascent and descent.

          Take a look at this stackoverflow question and the highest rated answer. It should help.

          V 1 Reply Last reply 11 Aug 2020, 14:42
          3
          • C Chris Kawa
            11 Aug 2020, 14:18

            Well you still need to center text in that calculated rectangle:

            p.drawText(textRect, Qt::AlignCenter, text);
            

            With that change I get
            text alignment 1

            And here's a slightly modified version:

            void paintEvent(QPaintEvent*) override
            {
                const QString text = "Hello!";
                QPainter p(this);
                
                const QRectF textRect = QFontMetricsF(font()).boundingRect(rect(), Qt::AlignCenter, text);
                
                p.fillRect(rect(), Qt::darkBlue);
                p.fillRect(textRect, Qt::green);
                p.drawText(rect(), Qt::AlignCenter, text);
            }
            

            This gives me

            text alignment 2

            And if I change textRect to

            QRectF textRect = QFontMetricsF(font()).tightBoundingRect(text);
            textRect.moveCenter(QRectF(rect()).center());
            

            I get this

            text alignment 3

            So everything seems as it should.
            I don't know. Seems to be something specific to your setup.

            V Offline
            V Offline
            Violet Giraffe
            wrote on 11 Aug 2020, 14:40 last edited by Violet Giraffe 8 Nov 2020, 14:43
            #9

            @Chris-Kawa said in Impossible to get true text hight with QFontMetrics/QFontMetricsF:

            Well you still need to center text in that calculated rectangle

            Why? Since when is it a requirement such that if you don't center the text it's rendered at a wrong position?
            In other words, in which reality does the output of my original example make any sense?

            I agree that the second two of your results are interesting, but they require AlignCenter for the same reason. Thanks for the tip, though, I didn't realize such combination of manual alignment (by specifying the pre-aligned rect) + automatic alignment (AlignCenter) improves the situation.

            Note that I say "improves", not "fixes". Even on your last screenshot the offset from the top is 11px and from the bottom it's 10px! But I can live with that. Probably due to integer rounding and high DPI re-calculations.

            C 1 Reply Last reply 11 Aug 2020, 14:50
            0
            • A Asperamanca
              11 Aug 2020, 14:21

              @Violet-Giraffe
              Normally, when you call drawText, it will render the text including any spacing that should go with it. The tightBoundingRect gives you the minimum space necessary to render the characters itself, but the drawText will still try to put the default spacing on top. You have to calculate the necessary offsets, and you get this information from the QFontMetricsF class: ascent and descent.

              Take a look at this stackoverflow question and the highest rated answer. It should help.

              V Offline
              V Offline
              Violet Giraffe
              wrote on 11 Aug 2020, 14:42 last edited by
              #10

              @Asperamanca said in Impossible to get true text hight with QFontMetrics/QFontMetricsF:

              Take a look at this stackoverflow question and the highest rated answer. It should help.

              Thanks! This is a great description, but it matches what I already understood intuitively thus far.

              1 Reply Last reply
              0
              • V Violet Giraffe
                11 Aug 2020, 14:40

                @Chris-Kawa said in Impossible to get true text hight with QFontMetrics/QFontMetricsF:

                Well you still need to center text in that calculated rectangle

                Why? Since when is it a requirement such that if you don't center the text it's rendered at a wrong position?
                In other words, in which reality does the output of my original example make any sense?

                I agree that the second two of your results are interesting, but they require AlignCenter for the same reason. Thanks for the tip, though, I didn't realize such combination of manual alignment (by specifying the pre-aligned rect) + automatic alignment (AlignCenter) improves the situation.

                Note that I say "improves", not "fixes". Even on your last screenshot the offset from the top is 11px and from the bottom it's 10px! But I can live with that. Probably due to integer rounding and high DPI re-calculations.

                C Offline
                C Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on 11 Aug 2020, 14:50 last edited by
                #11

                @Violet-Giraffe said:

                In other words, in which reality does the output of my original example make any sense?

                In this one :) As @Asperamanca said there's more to text layout than a simple rectangle. I'm guessing offset is the ascent you also see in my second picture. Query it from the font metrics to see if that matches.

                Even on your last screenshot the offset from the top is 11px and from the bottom it's 10px!

                How would you draw sharp half a pixel?
                If you really want to you can enable antialiasing on the painter with

                p.setRenderHint(QPainter::RenderHint::Antialiasing);
                

                and this will give you blurry 10.5 pixel i.e. sharp 10 and alphablended 0.5.

                V 1 Reply Last reply 11 Aug 2020, 15:14
                2
                • C Chris Kawa
                  11 Aug 2020, 14:50

                  @Violet-Giraffe said:

                  In other words, in which reality does the output of my original example make any sense?

                  In this one :) As @Asperamanca said there's more to text layout than a simple rectangle. I'm guessing offset is the ascent you also see in my second picture. Query it from the font metrics to see if that matches.

                  Even on your last screenshot the offset from the top is 11px and from the bottom it's 10px!

                  How would you draw sharp half a pixel?
                  If you really want to you can enable antialiasing on the painter with

                  p.setRenderHint(QPainter::RenderHint::Antialiasing);
                  

                  and this will give you blurry 10.5 pixel i.e. sharp 10 and alphablended 0.5.

                  V Offline
                  V Offline
                  Violet Giraffe
                  wrote on 11 Aug 2020, 15:14 last edited by Violet Giraffe 8 Nov 2020, 15:25
                  #12

                  @Chris-Kawa, you're of course right about half a pixel, I realized 1px difference is just rounding just as I submitted the comment, and I should have deleted that part.

                  But how about this code? As you can see, there's still a bug, and I don't mean the string :)

                  struct TestWidget : QWidget
                  {
                  protected:
                  	void paintEvent(QPaintEvent*) override
                  	{
                  		const QString text = "bug()";
                  		QPainter p(this);
                  		p.fillRect(rect(), Qt::darkBlue);
                  
                  		auto textRect = QFontMetricsF(font()).tightBoundingRect(text);
                  		textRect.moveTo(0.0, 0.0);
                  		p.fillRect(textRect, Qt::green);
                  
                  		p.drawText(textRect, Qt::AlignCenter, text);
                  	}
                  };
                  
                  int main(int argc, char *argv[])
                  {
                  	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
                  	QApplication a(argc, argv);
                  
                  	QMainWindow mw;
                  	auto* widget = new TestWidget;
                  
                  	auto f = widget->font();
                  	f.setPixelSize(30);
                  	widget->setFont(f);
                  
                  	mw.setCentralWidget(widget);
                  	mw.show();
                  
                  	return a.exec();
                  }
                  

                  e1f0daae-7ff9-4b10-9f58-826a77ecdfa4-image.png

                  Also, could you please explain the meaning of negative top left coordinates that boundingRect() / tightRect() often have? That's why moveTo(0.0, 0.0) is needed.

                  P. S. I have established that the following magic manipulation solves the problem, but these numbers depend on the font and its size. Any idea how to calculate them, what font properties should be factored in to get this 7?

                  textRect.setTopLeft(QPointF(textRect.left(), textRect.top() - 7.0));

                  This is what it looks like:

                  569f3b56-38d8-4089-9206-06ff0b7eae4b-image.png

                  1 Reply Last reply
                  0
                  • V Offline
                    V Offline
                    Violet Giraffe
                    wrote on 11 Aug 2020, 16:00 last edited by
                    #13

                    I think the magic offset is -fm.descent() - fm.underlinePos(). Note that it's NOT translation, it is expansion of the original rect towards the top.

                    J 1 Reply Last reply 11 Aug 2020, 16:34
                    0
                    • V Violet Giraffe
                      11 Aug 2020, 16:00

                      I think the magic offset is -fm.descent() - fm.underlinePos(). Note that it's NOT translation, it is expansion of the original rect towards the top.

                      J Offline
                      J Offline
                      JonB
                      wrote on 11 Aug 2020, 16:34 last edited by JonB 8 Nov 2020, 16:41
                      #14

                      @Violet-Giraffe
                      Try putting your font into italic and see whether it does not fit, horizontally?
                      I'm thinking there might need to be a modification to textRect.left() too? leftBearing()? rightBearing()?
                      tightBoundingRect:

                      Note that the bounding rectangle may extend to the left of (0, 0), e.g. for italicized fonts

                      V 1 Reply Last reply 11 Aug 2020, 17:24
                      2
                      • J JonB
                        11 Aug 2020, 16:34

                        @Violet-Giraffe
                        Try putting your font into italic and see whether it does not fit, horizontally?
                        I'm thinking there might need to be a modification to textRect.left() too? leftBearing()? rightBearing()?
                        tightBoundingRect:

                        Note that the bounding rectangle may extend to the left of (0, 0), e.g. for italicized fonts

                        V Offline
                        V Offline
                        Violet Giraffe
                        wrote on 11 Aug 2020, 17:24 last edited by
                        #15

                        @JonB, you are correct, thanks for this tip!

                        J 1 Reply Last reply 11 Aug 2020, 18:00
                        0
                        • V Violet Giraffe
                          11 Aug 2020, 17:24

                          @JonB, you are correct, thanks for this tip!

                          J Offline
                          J Offline
                          JonB
                          wrote on 11 Aug 2020, 18:00 last edited by JonB 8 Nov 2020, 18:01
                          #16

                          @Violet-Giraffe
                          And that's where your "magic" - fm.underlinePos() comes from: you have removed the underline position from your area, for right or for wrong. If the text were to be underlined, you would currently exclude it, I'm thinking. :)

                          V 1 Reply Last reply 11 Aug 2020, 18:45
                          0
                          • J JonB
                            11 Aug 2020, 18:00

                            @Violet-Giraffe
                            And that's where your "magic" - fm.underlinePos() comes from: you have removed the underline position from your area, for right or for wrong. If the text were to be underlined, you would currently exclude it, I'm thinking. :)

                            V Offline
                            V Offline
                            Violet Giraffe
                            wrote on 11 Aug 2020, 18:45 last edited by
                            #17

                            @JonB, I don't think so, because as I said, I did not just translate the rect, and certainly didn't shrink it. I expanded it towards the top (possibly into negative coordinates).

                            1 Reply Last reply
                            0

                            14/17

                            11 Aug 2020, 16:34

                            • Login

                            • Login or register to search.
                            14 out of 17
                            • First post
                              14/17
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved