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. Correct use of QFontMetrics.leftBearing for drawing Hindi characters using QRawFont
QtWS25 Last Chance

Correct use of QFontMetrics.leftBearing for drawing Hindi characters using QRawFont

Scheduled Pinned Locked Moved Solved General and Desktop
fontskerningfontmetricsdevnagarihindi
14 Posts 3 Posters 985 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.
  • T Offline
    T Offline
    TheCrowKaka
    wrote on last edited by
    #1

    Hello

    I am using the Qt15.5.2 version on MacOS for programming. This problem does not change even if i program on Windows.
    I am unable to understand how to understand how to use the QFontMetrics to draw the Hindi glyphs correctly.

    My code is as follows:

                        QChar mytempchar = tempmarkstring.at(i);
                        bool myres=false;
                        while(!myres)
                            myres = myFont->glyphIndexesForChars(&mytempchar,1,mychar,&numgly); // get the glyph index
                        QPainterPath charpath = myFont->pathForGlyph(mychar[0]);    // get the glyph path for character.
                        qDebug()<<"Metrics "<<myfontmetrics.horizontalAdvance(mytempchar)<<myfontmetrics.leftBearing(mytempchar)<<myfontmetrics.rightBearing(mytempchar);
                        widt = (myfontmetrics.horizontalAdvance(mytempchar)+(myfontmetrics.leftBearing(mytempchar)>0?0:myfontmetrics.leftBearing(mytempchar))
                                +(myfontmetrics.rightBearing(mytempchar)>0?0:myfontmetrics.rightBearing(mytempchar)))* textsize;
    
    

    where

            myFont = new QRawFont;
            *myFont = QRawFont::fromFont(QFont(targetitem->FontFileName(),textsize));
            QFontMetricsF myfontmetrics(QFont(targetitem->FontFileName(),textsize));
    
    

    This is my English output using this same code..
    ![English characters screenshot](Screen Shot 2022-06-30 at 6.19.29 PM.png image url)

    However on typing this..
    Screen Shot 2022-06-30 at 6.19.45 PM.png
    I see something like this.
    Screen Shot 2022-06-30 at 6.18.30 PM.png
    I have being trying to get this right but somehow i fail to understand where i am going wrong.
    Hope this code and illustration is good.
    Please help.

    A Qt Enthusiastic...

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Can you provide a minimal compilable example that reproduces that ?

      Can you check whether it's still the case with Qt 6 ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      T 3 Replies Last reply
      0
      • SGaistS SGaist

        Hi,

        Can you provide a minimal compilable example that reproduces that ?

        Can you check whether it's still the case with Qt 6 ?

        T Offline
        T Offline
        TheCrowKaka
        wrote on last edited by
        #3

        @SGaist I have modified the wigglywidget example of Qt. So i am giving here the sources of the two files which i have modified.. not giving the other file sources.
        WigglyWidget.h file

        #ifndef WIGGLYWIDGET_H
        #define WIGGLYWIDGET_H
        
        #include <QBasicTimer>
        #include <QRawFont>
        #include <QPainterPath>
        #include <QWidget>
        
        //! [0]
        class WigglyWidget : public QWidget
        {
            Q_OBJECT
        
        public:
            WigglyWidget(QWidget *parent = nullptr);
        
        public slots:
            void setText(const QString &newText) { text = newText; update();}
        
        protected:
            void paintEvent(QPaintEvent *event) override;
            void timerEvent(QTimerEvent *event) override;
        
        private:
            QBasicTimer timer;
            QString text;
            int step;
        };
        //! [0]
        
        #endif
        
        

        WigglyWidget.cpp file

        
        #include "wigglywidget.h"
        
        #include <QFontMetrics>
        #include <QPainter>
        #include <QTimerEvent>
        
        //! [0]
        WigglyWidget::WigglyWidget(QWidget *parent)
            : QWidget(parent), step(0)
        {
            setBackgroundRole(QPalette::Midlight);
            setAutoFillBackground(true);
        //    setFont(QFont("Devanagari Sangam MN");
            QFont newFont("Devanagari Sangam MN");// = font();
            newFont.setPointSize(newFont.pointSize() + 20);
            setFont(newFont);
        
        //    timer.start(60, this);
        }
        //! [0]
        
        //! [1]
        void WigglyWidget::paintEvent(QPaintEvent * /* event */)
        //! [1] //! [2]
        {
            quint32 mychar[3];
            int numgly;
            QRawFont *myFont = new QRawFont;
            *myFont = QRawFont::fromFont(font());
            QFontMetricsF myfontmetrics(font());
        
            for(int i=0;i<text.length();i++)
            {
                QChar mytempchar = text.at(i);
                bool myres=false;
                while(!myres)
                    myres = myFont->glyphIndexesForChars(&mytempchar,1,mychar,&numgly); // get the glyph index
                QPainterPath charpath = myFont->pathForGlyph(mychar[0]);    // get the glyph path for character.
                qreal widt = (myfontmetrics.horizontalAdvance(mytempchar)+(myfontmetrics.leftBearing(mytempchar)>0?0:myfontmetrics.leftBearing(mytempchar))
                        +(myfontmetrics.rightBearing(mytempchar)>0?0:myfontmetrics.rightBearing(mytempchar)));
        
            }
        
            static constexpr int sineTable[16] = {
                0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38
            };
        
            QFontMetrics metrics(font());
            int x = (width() - metrics.horizontalAdvance(text)) / 2;
            int y = (height() + metrics.ascent() - metrics.descent()) / 2;
            QColor color;
        //! [2]
        
        //! [3]
            QPainter painter(this);
            QPainterPath basepath;
        //! [3] //! [4]
            for (int i = 0; i < text.size(); ++i) {
                int index = (step + i) % 16;
                color.setHsv((15 - index) * 16, 255, 191);
                painter.setPen(color);
                QChar mytempchar = text.at(i);
                bool myres=false;
                while(!myres)
                    myres = myFont->glyphIndexesForChars(&mytempchar,1,mychar,&numgly); // get the glyph index
                QPainterPath charpath = myFont->pathForGlyph(mychar[0]);    // get the glyph path for character.
                qreal widt = (myfontmetrics.horizontalAdvance(mytempchar)+(myfontmetrics.leftBearing(mytempchar)>0?0:myfontmetrics.leftBearing(mytempchar))
                        +(myfontmetrics.rightBearing(mytempchar)>0?0:myfontmetrics.rightBearing(mytempchar)));
                QPointF charpoint(x,y - (sineTable[index] * metrics.height()) / 400);
                charpath = charpath.translated(charpoint);
                basepath.addPath(charpath);
        //        painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400),
        //                         QString(text[i]));
                x += widt;
            }
            painter.drawPath(basepath);
        }
        //! [4]
        
        //! [5]
        void WigglyWidget::timerEvent(QTimerEvent *event)
        //! [5] //! [6]
        {
            if (event->timerId() == timer.timerId()) {
                ++step;
                update();
            } else {
                QWidget::timerEvent(event);
            }
        //! [6]
        }
        

        this is the image of the application with the standard english characters.

        Screen Shot 2022-06-30 at 10.22.54 PM.png

        And the image with Devnagari characters,
        Screen Shot 2022-06-30 at 10.25.16 PM.png

        I have not yet tried this on Qt6. I am downloading that and hope to try tomorrow and will post the result.

        A Qt Enthusiastic...

        1 Reply Last reply
        0
        • SGaistS SGaist

          Hi,

          Can you provide a minimal compilable example that reproduces that ?

          Can you check whether it's still the case with Qt 6 ?

          T Offline
          T Offline
          TheCrowKaka
          wrote on last edited by
          #4

          @SGaist Hello
          I have now tested the same application with Qt6.2.1 and there is no difference in the results.

          It is very confusing for me to understand the right solution to this issue.

          A Qt Enthusiastic...

          1 Reply Last reply
          0
          • SGaistS SGaist

            Hi,

            Can you provide a minimal compilable example that reproduces that ?

            Can you check whether it's still the case with Qt 6 ?

            T Offline
            T Offline
            TheCrowKaka
            wrote on last edited by
            #5

            @SGaist Hello

            I tried something else.

            In the same example, instead of trying to paint individual characters, i tried painting the complete text and lo.., i got perfect result.
            Which means the Qt font engine has all the text shaper rules to draw the text properly.

                basepath.addText(x,y,font(),text);
                painter.drawPath(basepath);
            
            

            OR

            painter.drawText(x,y,text);
            

            Both give perfect result as seen here.
            Screen Shot 2022-07-07 at 12.01.57 AM.png

            This will work for all straight line text applications.. However, my application is simillar to the wiggly example where i need align the text along a curve. In that case, i am unable to understand how to get the glyphs of individual characters so that i can align them along a path.

            A Qt Enthusiastic...

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              Then that might be a font specific issue.
              Can you try with a different one that provides the char set you need ?

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              T 1 Reply Last reply
              0
              • SGaistS SGaist

                Then that might be a font specific issue.
                Can you try with a different one that provides the char set you need ?

                T Offline
                T Offline
                TheCrowKaka
                wrote on last edited by
                #7

                @SGaist
                No. It does not seem to be a font specific issue. I have tried this will all the indic fonts i have and still have same results.

                I feel, this is a lot to do with the kerning of fonts. Since the results are proper with Path.addText and painter.drawText, i think Qt has the necessary tools to make this happen.

                Now, because I am interested in aligning text on a path just the way it is seen in Inkscape.. as seen here,
                Screenshot_8.png

                In order to do this, I have 2 options.
                Option 1: use the QFontMetrics and other classes available in Qt that give the right distances to combine 2 characters. Along with that get the combined character glyph paths which can be worked upon to align characters on the path.

                Option 2: I can get the combined paths using painterpath.addtext. I need to find out the right way to get the individual glyphs to align them on the path.

                Option 3: any other method that i dont know....

                Is this possible in Qt?

                A Qt Enthusiastic...

                1 Reply Last reply
                0
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Good question. I recommend that you bring it to the interest mailing where you will find Qt's developers/maintainers.

                  You might also want to check the bug report system. There are likely related tickets.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  T 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    Good question. I recommend that you bring it to the interest mailing where you will find Qt's developers/maintainers.

                    You might also want to check the bug report system. There are likely related tickets.

                    T Offline
                    T Offline
                    TheCrowKaka
                    wrote on last edited by
                    #9

                    @SGaist OK. I will do both these things. Thanks for all the help.

                    A Qt Enthusiastic...

                    T 1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      Allan Jensen
                      wrote on last edited by
                      #10

                      QRawFont only does basic kerning, it doesn't do HarfBuzz shaping, so you can't really use QRawFont for any complex scripts (It assumes a 1 letter to 1 glyph mapping).

                      T 2 Replies Last reply
                      0
                      • A Allan Jensen

                        QRawFont only does basic kerning, it doesn't do HarfBuzz shaping, so you can't really use QRawFont for any complex scripts (It assumes a 1 letter to 1 glyph mapping).

                        T Offline
                        T Offline
                        TheCrowKaka
                        wrote on last edited by
                        #11

                        @Allan-Jensen Thanks for this response.

                        Yes, I am getting that. However, unable to understand what function will give the resultant metrics and even more important the combined glyph indexes after the shaping result.

                        A Qt Enthusiastic...

                        1 Reply Last reply
                        0
                        • A Allan Jensen

                          QRawFont only does basic kerning, it doesn't do HarfBuzz shaping, so you can't really use QRawFont for any complex scripts (It assumes a 1 letter to 1 glyph mapping).

                          T Offline
                          T Offline
                          TheCrowKaka
                          wrote on last edited by
                          #12

                          @Allan-Jensen @SGaist I have managed to get a few things done... now just a small thing remains.

                          As mentioned in my earlier post, I got a solution to the Option2: Get individual glyph paths for the combined paths in the Hindi fonts. SO now that i have the individual glyph paths, i can align them as I want.
                          This is how I could achieve this result. Here is the changed paintEvent.

                          void WigglyWidget::paintEvent(QPaintEvent * /* event */)
                          //! [1] //! [2]
                          {
                              QRawFont *myFont = new QRawFont;
                              *myFont = QRawFont::fromFont(font());
                          
                              QFontMetrics metrics(font());
                              int x = (width() - metrics.horizontalAdvance(text)) / 2;
                              int y = (height() + metrics.ascent() - metrics.descent()) / 2;
                              QColor color;
                          //! [2]
                          
                          //! [3]
                              QPainter painter(this);
                              QPainterPath basepath;
                              QTextLayout curlay(text,font());
                              curlay.beginLayout();
                              curlay.createLine();
                              curlay.endLayout();
                              QList<QGlyphRun> curglyrunlist = curlay.glyphRuns();
                              QGlyphRun curglyrun = curglyrunlist[0];
                              QList<quint32> chindices = curglyrun.glyphIndexes();
                              *myFont =curglyrun.rawFont();
                              QList<QPointF> curglyadvlist = myFont->advancesForGlyphIndexes(chindices,QRawFont::KernedAdvances);
                          
                          //    qreal prevx;
                              for (int i = 0; i < chindices.count(); ++i) {
                                  color.setRgb(255,0,0);
                                  painter.setPen(color);
                                  QPainterPath charpath = myFont->pathForGlyph(chindices[i]);    // get the glyph path for character.
                                  qreal widt = curglyadvlist[i].x();
                          //        if(widt==0)
                          //            charpath = charpath.translated(prevx,y);
                          //        else
                                      charpath = charpath.translated(x,y);
                                  basepath.addPath(charpath);
                          //        basepath.addEllipse(x,y,5,5);
                          //        qDebug()<<widt<<x<<y;
                          //        prevx = x;
                                  x += (widt);
                              }
                              painter.drawPath(basepath);
                              painter.drawText((width() - metrics.horizontalAdvance(text)) / 2,y-50,text);
                          }
                          

                          And this is the result.Screenshot_1.png

                          Here you can see that the one drawn below is as per the painter.drawPath which is the result of individual glyphs drawn as painterpaths.
                          While the top one is drawn with painter.drawText.
                          Now the problem remains that the bottom addition to the main character is not drawn at the right location. The advancesForGlyphIndexes function is not returning the right advance for this character. Whereas when the painter.drawText is drawing it perfectly well.

                          Now, am I missing something... some leftBearing or RightBearing that needs to be added or subtracted from the characters... ?

                          A Qt Enthusiastic...

                          1 Reply Last reply
                          1
                          • T TheCrowKaka

                            @SGaist OK. I will do both these things. Thanks for all the help.

                            T Offline
                            T Offline
                            TheCrowKaka
                            wrote on last edited by
                            #13

                            @Allan-Jensen @SGaist I have finally solved it.

                            I substituted myFont->advancesForGlyphIndexes(chindices,QRawFont::KernedAdvances); with curglyrun.positions(); and that gave me the right positions for all glyphs.

                            A Qt Enthusiastic...

                            1 Reply Last reply
                            1
                            • SGaistS Offline
                              SGaistS Offline
                              SGaist
                              Lifetime Qt Champion
                              wrote on last edited by
                              #14

                              Great !

                              Thanks for sharing your solution :-)

                              Interested in AI ? www.idiap.ch
                              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                              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