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
Forum Updated to NodeBB v4.3 + New Features

Impossible to get true text hight with QFontMetrics/QFontMetricsF

Scheduled Pinned Locked Moved Unsolved General and Desktop
17 Posts 4 Posters 3.0k Views 3 Watching
  • 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.
  • V Violet Giraffe

    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 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
    3
    • Chris KawaC Chris Kawa

      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 last edited by Violet Giraffe
      #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.

      Chris KawaC 1 Reply Last reply
      0
      • A Asperamanca

        @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 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

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

          Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on 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
          2
          • Chris KawaC Chris Kawa

            @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 last edited by Violet Giraffe
            #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 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.

              JonBJ 1 Reply Last reply
              0
              • V Violet Giraffe

                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.

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #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
                2
                • JonBJ JonB

                  @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 last edited by
                  #15

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

                  JonBJ 1 Reply Last reply
                  0
                  • V Violet Giraffe

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

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #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
                    0
                    • JonBJ JonB

                      @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 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

                      • Login

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