QPainter::drawText, получить ограничивающие рамки для каждого символа

Я использую QPainter для рисования многострочного текста в QImage. Однако мне также нужно отобразить цветной прямоугольник вокруг ограничивающей рамки каждого символа.

Поэтому мне нужно знать ограничивающую рамку, которая была у каждого символа при рисовании.

Например, для

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

Мне нужно было бы получить 10 прямоугольников с учетом новых строк, переноса слов, вкладок и т.д.

Например, прямоугольник второго 'l' будет ниже прямоугольника первого 'l', а не справа от 'e' из-за новой строки.

Что-то вроде координат красных прямоугольников на этой картинке (я поставил их вручную, поэтому они не совсем правильные):

введите здесь описание изображения


person sashoalm    schedule 13.11.2012    source источник


Ответы (2)


Это может быть не лучшее решение, но это лучшее, что я могу придумать.

Я считаю, что вам придется "сделать это самостоятельно". То есть вместо того, чтобы рисовать блок текста, рисуйте каждый символ по одному. Затем вы можете использовать QFontMetrics, чтобы получить ограничивающую рамку каждого символа.

Это немного работы, но не так уж плохо. Что-то вроде (псевдокод, а не код):

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

}

Проверьте QFontMetrics, у него есть несколько различных методов для получения ограничивающих рамок, минимальных ограничивающих рамок и т. д.

QFontMetrics 4.7

Аааа... Теперь я вижу, что используемая вами перегрузка возвращает фактический ограничивающий прямоугольник. Вы можете просто использовать это и пропустить QFontMetrics, если хотите - в остальном общий алгоритм тот же.

person Mark Stevens    schedule 13.11.2012
comment
Ок, спасибо, все попробую реализовать. Я думал найти фактический исходный код из QPainter::drawText, и используя это, но это оказалось слишком сложным, я даже не мог найти, где на самом деле происходит отрисовка персонажа. Рисование обрабатывается очень большой функцией под названием qt_format_text, я думаю, что они использовали QTextLine для рисования. - person sashoalm; 14.11.2012

Вы можете получить ограничивающие рамки отдельных символов с помощью QFontMetrics::boundingRect(QChar), но они должны отображаться со смещением (QFontMetrics::ascent сверху, а также как QFontMetrics::width предыдущих символов слева), потому что они относительные до базовой линии шрифта, а не до нижней части ограничивающей рамки всей строки.
Несколько строк также необходимо обрабатывать отдельно. QFontMetrics::lineSpacing даст вам их смещение.

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()});
    }
}

приводит к

что, к сожалению, не идеально.

person Darklighter    schedule 03.11.2017