Question

I'm using QPainter to draw multiline text on QImage. However, I also need to display a colored rectangle around each character's bounding box.

So I need to know the bounding box that each character had when being drawn.

For example, for

painter.drawText(QRect(100, 100, 200, 200), Qt::TextWordWrap, "line\nline2", &r);

I would need to get 10 rectangles, taking into account newlines, word-wrap, tabs, etc.

For example, the rectangle of the second 'l' would be below the rectangle of the first 'l', instead of being to the right of 'e', because of the newline.

Something like the coordinates of the red rectangles in this picture (I've put them by hand so they're not really the correct positions):

enter image description here

Was it helpful?

Solution

This may not be the best solution, but it's the best one I can think of.

I believe you will have to "do it yourself". That is, instead of drawing a block of text, draw each character one at a time. Then you can use QFontMetrics to get the bounding box of each character.

It's a little work, but not too bad. Something like (pseudo code, not code):

QFontMetrics fm(myFont, paintDevice);
int x = startX;
int y = startY;
for (unsigned int i = 0; i < numChars; i++)
{
    char myChar = mystr[i];  // get character to print/bound
    QRect rect = fm.boundingRect( myChar );   // get that char's bounding box
    painter.drawText(x, y, Qt::TextWordWrap, mystr[i], &r);  // output char
    painter.drawRect(...); // draw char's bounding box using 'rect'
    x += rect.width();     // advance current position horizontally

    // TODO:
    // if y > lineLen      // handle cr
    //     x = startX;
    //     y += line height

}

Check out QFontMetrics, it has a number of different methods for getting bounding boxes, minimum bounding boxes, etc.

QFontMetrics 4.7

Ahhh... I see now that the overload you're using returns the actual bounding rect. You can just use that and skip the QFontMetrics if you like - otherwise the overall algorithm is the same.

OTHER TIPS

You can retrieve the bounding boxes of individual characters with QFontMetrics::boundingRect(QChar), but they have to be rendered at an offset (QFontMetrics::ascent from the top as well as QFontMetrics::width of the preceding characters from the left) because they are relative to the font’s base line and not to the bottom of the bounding box of the complete string.
Several lines also have to be handled separately. QFontMetrics::lineSpacing give you their offset.

QPainter painter(this);
painter.setFont(QFont("Arial", 72));

auto pen = painter.pen();

QString text{"line\nline2\ngg\n`"};
QRect boundingRect;
painter.drawText(rect(), Qt::AlignLeft | Qt::AlignTop, text, &boundingRect);
painter.drawRect(boundingRect.adjusted(0, 0, -pen.width(), -pen.width()));

pen.setColor(Qt::red);
painter.setPen(pen);
const auto lines = text.split('\n');
const auto fm = painter.fontMetrics();
for (int linei = 0; linei < lines.size(); ++linei) {
    const auto & line = lines[linei];
    for (int chi = 0; chi < line.size(); ++chi) {
        const auto bounds = fm.boundingRect(line[chi]);
        const auto xoffset = bounds.x() + fm.width(line, chi);
        const auto lineOffset = linei * fm.lineSpacing() + fm.ascent();
        const auto yoffset = lineOffset + bounds.y();
        painter.drawRect(QRect{xoffset, yoffset, bounds.width(), bounds.height()});
    }
}

results in

which, sadly – isn’t perfect though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top