[SOLVED] Programatically determine the actual boundaries of rendered glyphs.

  • I'm trying to write a simple word processor for Tibetan Unicode that also has some simple layout functionality:

    !http://folk.uio.no/robinds/tib/qtextlayout_test1.png(Here's my experiment.)!

    In the example image you can see how two Tibetan glyph stacks at the top left actually extend past the font descent boundary. Both QFontMetrics.descent() and QTextLayout.boundingRect() does not give values that encompass the lowest point of these glyph stacks.

    Does anybody know of any good ways to solve this problem?

    "Here's the code used for the example.":https://gist.github.com/ironhouzi/10564915

    I've found something on "Stack Overflow":http://stackoverflow.com/questions/14792074/how-to-determine-the-rendered-height-of-the-lines-of-a-qt-document, but in order to follow it, I need to use an instance of QTextDocument and I'm not sure how I can use two QTextLayout instances in one QTextDocument. Is this possible to do and if so how? To my limited knowledge, I really think it's nice to use two instances of QTextLayout. One for each language, which makes a lot of sense from the users perspective.

    I'm completely new to Qt and I really want to learn the lower level design principles and implementation of text rendering in Qt.
    Are there any good sources on this subject? All I've found so far is basic stuff on how to use the premade widgets. Not only for solving this problem, but also to go deeper into the bowels of how Harfbuzz is implemented, so I can fix some low level implementation of highlighting ligatures of stacked glyphs. As of now, you can only select the entire glyph stack ligature, which again from the perspective of the user doesn't make much sense. Imagine if you could only select entire words and not individual characters in that word ... But that's a whole other discussion for the future.

    I really appreciate any feedback I can get. I have a lot to learn and a long path ahead of me.

  • I have been happy with creating my own QTextLayout, and using it's boundingRect(). But I'm exclusively using latin glyphs right now.

    I do something like this (incomplete code snip):

    @ const qreal leading = metrics.leading();
    qreal top = 0.0;

    QTextLayout textLayout;
        QTextLine line = textLayout.createLine();
        if ( ! line.isValid())
        top += leading;
        line.setPosition(QPointF(0, top));
        top += line.height();
        top += m_LineSpacingOffsetPx;

    ...and now I can use the boundingRect()

  • Thank you so much for your reply. Your snippet provides some valuable information to me.

    Are you using your custom QTextLayout with a QTextDocument? Do you know if and if so, how you can simultaneously use two instances of QTextLayout in the same QTextDocument?

    As I've already mentioned, using boundingRect() does not provide all the information I need to solve my problem. Therefore I need to investigate other possibilities.

  • For my use case, QTextDocument was simply too slow. Therefore I implemented a simple text viewer. To get the layout right, I looked a bit at how QTextDocument works internally, and realized I can implement a simplified layouting (suited to by needs) using QTextLayout.

    So, to answer your question: I do not work with QTextDocument and QTextLayout together.

    I must say, if you create the layout correctly, the boundingRect should be correct. If not, this sounds like a bug.
    However, if you need more specific information, maybe QTextLayout::glyphRuns can provide it. I haven't work with it myself, but of all methods, it sounds the most likely candidate.

    Edit: One more idea: When you create the layout, you might be able to sum up the line heights to get the correct total height.

  • Thank you for your continued input.

    I have successfully managed to create a bilingual page layout where I can resize, split and move lines as I wish. I just need to get the glyph rendering information, which I have to try and learn C++ for, since PySide doesn't have it yet.

    Asperama - Did you also implement text editing when you created your app using QTextLayout? Do you have any tips on how to handle the text editing? Should I create it from scratch, or use the QTextEdit as your base?

  • I have found something interesting. When I call QFontMetricsF.boundingRect(QFontMetricsF(QFont('himalaya', 30)), "ཧྐྵྨླྺྼྻྃ་ཊྛྜྜྷྞཱ་") I get QRectF(0, -22, 44, 66,656). If I draw a rectangle with the coordinates: (0, 11, 44, 67), it covers the rendered glyph stack perfectly.

    Does anybody know why the y coordinate is off?

    Now my task is to find a way to get my program to detect where along the x axis of the QTextLine the stacks occur. Does anybody know how I can get the screen position of the string "bar" in the QTextLine that holds the string "foo bar baz" ?

    1. No, I have not implemented text editing. For me, it was good enough to layout the font read-only, and use a ready-made text editor (in my case QGraphicsTextItem) for editing, accepting that the layout would be less than perfect during edit.

    2. Don't know about the negative y in your boundingRect. I have one suspicion, though. It might be that '0' denotes the overline of the font, and the -22 are extra space needed above that - a combination of ascender and spacing towards the next line. Take a look at "this link":http://brightlemon.com/blog/typography-01-font-basics-what-are-x-height-leading-kerning-tracking-ascender-and-descender

    I had the problem that I needed to align two fonts of different sizes nicely at their baseline. You can't do that just using the boundingRect, because fonts of different sizes will also have different descenders and spacings. So I subtracted the descender for both fonts, and got the common baseline position from that. Maybe you need to do something similar.

  • Thank you so much for your great feedback.

  • Thanks for all the excellent feedback. I have now managed to use QGlyphRun.positions() to get the QPointF objects for all rendered glyphs and can use QTextLayout.rect().contains() on the points to detect collisions.

    I had to change from Pyside to PyQt4 in order to get to use QGlyphRun objects, since Pyside does not have this as of 1.2.2, but this had no complications whatsoever. I just had to change the PySide imports to PyQt4.

Log in to reply

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.