Improved fuzzy text bounding rect computation

This commit is contained in:
Martin Tůma 2018-12-11 18:48:12 +01:00
parent a559b5237b
commit 6d02dafeb6
5 changed files with 30 additions and 64 deletions

View File

@ -32,7 +32,8 @@ SOURCES += src/pbfplugin.cpp \
src/textpathitem.cpp \
src/textpointitem.cpp \
src/font.cpp \
src/sprites.cpp
src/sprites.cpp \
src/textitem.cpp
RESOURCES += pbfplugin.qrc
unix:!macx{

View File

@ -12,12 +12,12 @@ class QPainter;
class TextItem
{
public:
TextItem(const QString &text) : _text(text), _visible(true) {}
TextItem(const QString &text, const QFont &font)
: _text(text), _font(font), _visible(true) {}
virtual ~TextItem() {}
const QString &text() const {return _text;}
const QFont &font() const {return _font;}
void setFont(const QFont &font) {_font = font;}
const QPen &pen() const {return _pen;}
void setPen(const QPen &pen) {_pen = pen;}
@ -28,33 +28,10 @@ public:
bool isVisible() const {return _visible;}
void setVisible(bool visible) {_visible = visible;}
bool collidesWithItem(const TextItem *other) const
{
QRectF r1(boundingRect());
QRectF r2(other->boundingRect());
if (r1.isEmpty() || r2.isEmpty() || !r1.intersects(r2))
return false;
return other->shape().intersects(shape());
}
bool collidesWithItem(const TextItem *other) const;
protected:
static int avgCharWidth(const QString &str, const QFont &font)
{
qreal ratio;
if (str.at(0).unicode() >= 0x2E80)
ratio = 1.0;
else {
ratio = (font.capitalization() == QFont::AllUppercase)
? 0.66 : 0.55;
if (font.bold())
ratio *= 1.1;
}
return ratio * font.pixelSize();
}
int avgCharWidth() const;
private:
QString _text;

View File

@ -134,9 +134,9 @@ static bool reverse(const QPainterPath &path)
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
const QFont &font, int maxAngle, const QRectF &tileRect)
: TextItem(text)
: TextItem(text, font)
{
int cw = avgCharWidth(text, font);
int cw = avgCharWidth();
int textWidth = text.size() * cw;
if (textWidth > path.length())
return;
@ -146,7 +146,6 @@ TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
return;
_path = reverse(tp) ? tp.toReversed() : tp;
setFont(font);
QPainterPathStroker s;
s.setWidth(font.pixelSize());

View File

@ -5,37 +5,32 @@
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
QRectF TextPointItem::exactBoundingRect(const QString &str,
const QFont &font, int maxWidth)
QRectF TextPointItem::exactBoundingRect() const
{
QFontMetrics fm(font);
int limit = font.pixelSize() * maxWidth;
QFontMetrics fm(font());
int limit = font().pixelSize() * _maxWidth;
// Italic fonts overflow the computed bounding rect, so reduce it
// a little bit.
if (font.italic())
limit -= font.pixelSize() / 2.0;
if (font().italic())
limit -= font().pixelSize() / 2.0;
QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, str);
QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, text());
Q_ASSERT(br.isValid());
// Expand the bounding rect back to the real content size
if (font.italic())
br.adjust(-font.pixelSize() / 4.0, 0, font.pixelSize() / 4.0, 0);
if (font().italic())
br.adjust(-font().pixelSize() / 4.0, 0, font().pixelSize() / 4.0, 0);
return br;
}
QRectF TextPointItem::fuzzyBoundingRect(const QString &str,
const QFont &font, int maxWidth)
QRectF TextPointItem::fuzzyBoundingRect() const
{
int limit = font.pixelSize() * maxWidth;
qreal cw = avgCharWidth(str, font);
qreal lh = font.pixelSize() * 1.25;
int limit = font().pixelSize() * _maxWidth;
qreal cw = avgCharWidth();
qreal lh = font().pixelSize() * 1.25;
int width = 0, lines = 0;
if (font.italic())
limit -= font.pixelSize() / 2.0;
QStringList l(str.split('\n'));
QStringList l(text().split('\n'));
for (int i = 0; i < l.size(); i++) {
int lw = (int)(l.at(i).length() * cw);
if (lw > limit) {
@ -69,11 +64,11 @@ QRectF TextPointItem::fuzzyBoundingRect(const QString &str,
}
QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
QRectF TextPointItem::computeTextRect(bool exact) const
{
QRectF iconRect = _icon.isNull() ? QRectF()
: QRectF(QPointF(0, 0), QSizeF(_icon.size()) / _icon.devicePixelRatioF());
QRectF textRect = brf(text(), font(), _maxWidth);
QRectF textRect = exact ? exactBoundingRect() : fuzzyBoundingRect();
switch (_anchor) {
case Text::Center:
@ -102,11 +97,10 @@ QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
const QFont &font, int maxWidth, Text::Anchor anchor, const QImage &icon)
: TextItem(text), _pos(pos), _icon(icon), _maxWidth(maxWidth),
: TextItem(text, font), _pos(pos), _icon(icon), _maxWidth(maxWidth),
_anchor(anchor)
{
setFont(font);
_boundingRect = computeTextRect(fuzzyBoundingRect);
_boundingRect = computeTextRect(false);
if (!_icon.isNull()) {
QRectF iconRect(QPointF(0, 0), QSizeF(_icon.size())
@ -126,12 +120,12 @@ void TextPointItem::paint(QPainter *painter) const
QRectF textRect;
if (!_icon.isNull()) {
textRect = computeTextRect(exactBoundingRect);
textRect = computeTextRect(true);
painter->drawImage(_pos - QPointF(_icon.width()
/ _icon.devicePixelRatioF() / 2, _icon.height()
/ _icon.devicePixelRatioF() / 2), _icon);
} else
textRect = computeTextRect(fuzzyBoundingRect);
textRect = _boundingRect;
painter->setFont(font());
painter->setPen(pen());

View File

@ -16,14 +16,9 @@ public:
void paint(QPainter *painter) const;
private:
typedef QRectF (*BoundingRectFunction)(const QString &, const QFont &, int);
static QRectF exactBoundingRect(const QString &str, const QFont &font,
int maxWidth);
static QRectF fuzzyBoundingRect(const QString &str, const QFont &font,
int maxWidth);
QRectF computeTextRect(BoundingRectFunction brf) const;
QRectF exactBoundingRect() const;
QRectF fuzzyBoundingRect() const;
QRectF computeTextRect(bool exact) const;
QPointF _pos;
QPainterPath _shape;