PDA

View Full Version : How to get the line height of a QTextBlock?



therefore
6th February 2013, 18:49
Windows 7 SP1
MSVS 2010
Qt 4.8.4

Given this code:



#include <QTGui>

int main(int argc, char *argv[])
{
QTextDocument* text_document = new QTextDocument("testing");
QTextBlock text_block = text_document->begin();
qDebug() << text_block.text() << text_block.blockFormat().lineHeight()
<< text_block.blockFormat().lineHeightType();
}

The console displays:

"testing" 0 0

Question: Why doesn't lineHeight return "the LineHeight property for the paragraph"? The lineHeightType is set for single spacing.

Even trying:


qDebug() << text_block.text() << text_block.layout()->boundingRect().height();

gives me zero.

ChrisW67
7th February 2013, 06:07
Was there something wrong with the answer you got the last time you asked exactly this question?

therefore
7th February 2013, 06:42
Yes, it did not answer the question (Why doesn't lineHeight return "the LineHeight property for the paragraph"?). Nor did the suggested technique for solving the problem help with a QTextBlock with multiple fonts, i.e., multiple fragments nor did it help with the general one of handling Unicode. I was hoping that this forum may have been more appropriate.

I believe the general solution is to iterate the fragments and search for the maximum ascent & maximum descent (one font may have the max descent, another the max ascent) and then add 1 for the baseline. Then add in the maximum leading for the fonts(s) with the maximum height. This is theoretical but it appears to work with the small set of experiments I have tried. I would prefer to have lineHeight method return the property to me so that I do not have to guess. I suspect I am misunderstanding how to get at this property -- I've tried the above, cursors on the block and the document, and several other methods to no avail.

For example, Consalas 14 has height 22, ascent 17, descent 4 and leading 0. If that is the only font in the text block, then the line height is 22.

Arial 14 has height 22, ascent 18, descent 3 and leading 1. If that is the only font, then the line height is 18 + 3 + 1 + 1 = 23.

If you have Consolas 14 and Arial 14 fragments in a text block, then the line height = max ascent (18) + max descent (4) + 1 + max leading(1) = 24.

Consolas 24 has height 37, ascent 29, descent 7, leading 0. The line height is 37 whether it is combined with Aria 14 or not because it has all the maximum values and its leading 0 is used because it is the larger font. That would be reversed if Arial was the larger font (i.e., leading of 1 would be used).

Anyway, again, all theoretical. I would prefer to get Qt's answer, the property LineHeight, gotten from lineHeight.

ChrisW67
7th February 2013, 08:37
This property is an input to the formatter for you to dictate what the line height policy is to be, not to report what the formatter has ultimately allocated for a given text. QTextBlockFormat::lineHeight() does return the line height property of the block format. Unless you have done something to change this with setLineHeight() it will continue to return 0 and a type of SingleHeight, and the formatter lays out the paragraph at its natural height during rendering. If you set the type to MinimumHeight or FixedHeight and set a line height of 32 pixels that's what will be returned and the formatter will act accordingly.

If you want to know the ultimate height of the entire text block (which may span several lines?) then you may be able to get it from the QAbstractTextDocumentLayout::blockBoundingRect() method of the QTextDocument's documentLayout.


QAbstractTextDocumentLayout *layout = text_document->documentLayout();
qDebug() << layout->blockBoundingRect(text_block);
// QRectF(4,4 44x14) for your example on my machine


If you want to know what the actual height of a particular rendered line is... The layout engine does this stuff with QTextLayout and QTextLine (see Rich Text Layouts). I cannot really help with custom layout. Perhaps now we have some more information one of the others may know.

therefore
7th February 2013, 20:02
Thank you. That answers the first question why lineHeight does not return the rendered height of the line -- because that is not what it is. Instead, it is the programmer dictated line height. So, I should stop at attempt #10 to get a non-zero number. ;)

Yes, I am interested in the rendered line height (not the paragraph's). I will go drill into Qt source to see if I can see how it calculates it vs. trying to reverse engineer. Unless someone can point me in the right direction to use the API instead.

Thanks again!

ChrisW67
7th February 2013, 22:47
You can use QTextLayout yourself (per the docs I linked) but then you are calculating your own line contents and heights anyway, which is basically what you'd like to avoid. However, your own layout engine may be the best way to align the text with other things on the page; to ensure the text lines up with a pre-printed form for example.

See getLineHeightParams() and QTextDocumentLayoutPrivate::layoutBlock() in src/gui/text/qtextdocumentlayout.cpp for a start.

therefore
9th February 2013, 21:39
Thanks for the guidance! I am not trying to calculate my own line heights -- I am trying to determine what the rendered heights are. To simplify things, here is what I want to do:


#include <QTGui>

int CalculateLineHeight(QTextBlock text_block);

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow* window = new QMainWindow;
QTextEdit* editor = new QTextEdit(window);

QTextDocument* text_document = new QTextDocument(window);
editor->setDocument(text_document);

QFile file("test.html");
if (file.open(QFile::ReadOnly | QFile::Text))
editor->setHtml(file.readAll());

QTextBlock text_block = text_document->begin();
while (text_block.isValid() )
{
qDebug() << text_block.text() << CalculateLineHeight(text_block);
text_block = text_block.next();
}
window->setCentralWidget(editor);
window->show();
return app.exec();
}

int CalculateLineHeight(QTextBlock text_block)
{
QList<QTextLayout::FormatRange> text_block_format_ranges;

// Gather the format ranges for each fragment of the text block.
for (QTextBlock::Iterator fragment_it = text_block.begin(); !(fragment_it.atEnd()); ++fragment_it)
{
QTextFragment fragment = fragment_it.fragment();

QTextCharFormat fragment_format = fragment.charFormat();
QTextLayout::FormatRange text_block_format_range;
text_block_format_range.format = fragment_format;
text_block_format_range.start = fragment.position();
text_block_format_range.length = fragment.length();

text_block_format_ranges << text_block_format_range;
}
// Create text layout
QTextLayout text_layout(text_block.text());
text_layout.setAdditionalFormats( text_block_format_ranges );
text_layout.beginLayout();
QTextLine line = text_layout.createLine();
text_layout.endLayout();

return text_layout.boundingRect().height();
}

This is test.html:


<!DOCTYPE html>
<html>
<head>
<style>
.consolas{
font-family: "Consolas";
font-size:14pt;
background-color: cyan;
}
.arial{
font-family: "Arial";
font-size:14pt;
background-color: cyan;
}
</style>
</head>

<body>
<p><span class="consolas">E</span></p>
<p><span class="arial">E</span></p>
<p><span class="consolas">E</span><span class="arial">E</span></p>
</body>
</html>
The window displays (with my annotations indicating the LineHeights):

8696

But I get the following console output:

"E" 22
"E" 13
"EE" 13

So, it apparently calculates it properly on the first line, but subsequent lines it does not calculate it properly (2nd line s/b 23, 3rd s/b 24). So, I believe I am closer but I suspect my problem lies in how I am handling the text layouts.

Any thoughts?

therefore
10th February 2013, 04:40
I finally got it -- couldn't have done it without your guidance, so THANKS! :)

I did not realize that fragment.position is the position within the document and not the text block. So had to subtract out the document position of the text block start. And I needed to make sure the calculation on the QTextLine included the leading.


int CalculateLineHeight(QTextBlock text_block)
{
QList<QTextLayout::FormatRange> text_block_format_ranges;

// Gather the format ranges for each fragment of the text block.
for (QTextBlock::Iterator fragment_it = text_block.begin(); !(fragment_it.atEnd()); ++fragment_it)
{
QTextFragment fragment = fragment_it.fragment();

QTextCharFormat fragment_format = fragment.charFormat();
QTextLayout::FormatRange text_block_format_range;
text_block_format_range.format = fragment_format;
// fragment.position = position within the document whereas
// .start = position within the text block. Therefore, need
// to subtract out the text block's starting position within the document.
text_block_format_range.start = fragment.position()
- text_block.position();
text_block_format_range.length = fragment.length();

text_block_format_ranges << text_block_format_range;
}

// Create text layout
QTextLayout text_layout(text_block.text());
text_layout.setAdditionalFormats( text_block_format_ranges );
text_layout.beginLayout();
QTextLine line = text_layout.createLine();
text_layout.endLayout();
line.setLeadingIncluded(true); // Need to include the leading
return line.height();
}

YAPPO
4th April 2013, 15:56
Did I understand this right that QTextBlock contains text from '\n' up to the next '\n' unless the text between this two new_line symbols contains less or equal chars than QTextBlockFormat::FixedHeight? And if the number of symbols is greater then this piece of the text is separated on two QTextBlocks. Can I forbid to add new chars in the line if this line already contains max number of chars that QTextBlockFormat allows to have so that one line won't be separated in two pieces?