Improved text layout handling

This commit is contained in:
Martin Tůma 2018-11-28 23:39:33 +01:00
parent 085a90e0e5
commit 8567ef44d1
9 changed files with 117 additions and 63 deletions

View File

@ -278,8 +278,7 @@ bool Style::Layer::Paint::antialias(Layer::Type type, int zoom) const
}
Style::Layer::Layout::Layout(const QJsonObject &json)
: _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), _font("Open Sans"),
_viewportAlignment(false)
: _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), _font("Open Sans")
{
// line
_lineCap = FunctionS(json["line-cap"], "butt");
@ -292,18 +291,17 @@ Style::Layer::Layout::Layout(const QJsonObject &json)
_textMaxWidth = FunctionF(json["text-max-width"], 10);
_textMaxAngle = FunctionF(json["text-max-angle"], 45);
_textTransform = FunctionS(json["text-transform"], "none");
_textRotationAlignment = FunctionS(json["text-rotation-alignment"]);
_textAnchor = FunctionS(json["text-anchor"]);
if (json.contains("text-font") && json["text-font"].isArray())
_font = Font::fromJsonArray(json["text-font"].toArray());
if (json.contains("text-rotation-alignment")
&& json["text-rotation-alignment"].isString())
if (json["text-rotation-alignment"].toString() == "viewport")
_viewportAlignment = true;
_textAnchor = FunctionS(json["text-anchor"]);
// icon
_icon = Template(FunctionS(json["icon-image"]));
// symbol
_symbolPlacement = FunctionS(json["symbol-placement"]);
}
QFont Style::Layer::Layout::font(int zoom) const
@ -367,6 +365,31 @@ Qt::PenJoinStyle Style::Layer::Layout::lineJoin(int zoom) const
return Qt::MiterJoin;
}
Text::SymbolPlacement Style::Layer::Layout::symbolPlacement(int zoom) const
{
QString placement(_symbolPlacement.value(zoom));
if (placement == "line")
return Text::Line;
else if (placement == "line-center")
return Text::LineCenter;
else
return Text::Point;
}
Text::RotationAlignment Style::Layer::Layout::textRotationAlignment(int zoom)
const
{
QString alignment(_textRotationAlignment.value(zoom));
if (alignment == "map")
return Text::Map;
else if (alignment == "viewport")
return Text::Viewport;
else
return Text::Auto;
}
Style::Layer::Layer(const QJsonObject &json)
: _type(Unknown), _minZoom(-1), _maxZoom(-1)
{
@ -437,6 +460,8 @@ void Style::Layer::setTextProperties(Tile &tile) const
tile.text().setAnchor(_layout.textAnchor(tile.zoom()));
tile.text().setPen(_paint.pen(_type, tile.zoom()));
tile.text().setFont(_layout.font(tile.zoom()));
tile.text().setSymbolPlacement(_layout.symbolPlacement(tile.zoom()));
tile.text().setRotationAlignment(_layout.textRotationAlignment(tile.zoom()));
}
void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
@ -447,13 +472,7 @@ void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
return;
QString icon = _layout.icon(tile.zoom(), tags);
if (_layout.viewportAlignment())
tile.text().addLabel(text, path.elementAt(0), false, sprites.icon(icon));
else if (path.elementCount() == 1 && path.elementAt(0).isMoveTo())
tile.text().addLabel(text, path.elementAt(0), true, sprites.icon(icon));
else
tile.text().addLabel(text, path);
tile.text().addLabel(text, sprites.icon(icon), path);
}
bool Style::load(const QString &fileName)

View File

@ -94,7 +94,7 @@ private:
class Layout {
public:
Layout() : _textSize(16), _textMaxWidth(10), _textMaxAngle(45),
_font("Open Sans"), _viewportAlignment(false) {}
_font("Open Sans") {}
Layout(const QJsonObject &json);
qreal maxTextWidth(int zoom) const
@ -109,7 +109,8 @@ private:
Qt::PenCapStyle lineCap(int zoom) const;
Qt::PenJoinStyle lineJoin(int zoom) const;
Text::Anchor textAnchor(int zoom) const;
bool viewportAlignment() const {return _viewportAlignment;}
Text::SymbolPlacement symbolPlacement(int zoom) const;
Text::RotationAlignment textRotationAlignment(int zoom) const;
private:
QFont::Capitalization textTransform(int zoom) const;
@ -123,8 +124,9 @@ private:
FunctionS _lineJoin;
FunctionS _textAnchor;
FunctionS _textTransform;
FunctionS _symbolPlacement;
FunctionS _textRotationAlignment;
QFont _font;
bool _viewportAlignment;
};
class Paint {

View File

@ -1,4 +1,4 @@
#include <QFontMetrics>
#include <QFontMetrics>
#include <QPainter>
#include "text.h"
#include "textpointitem.h"
@ -20,15 +20,36 @@ void Text::render(QPainter *painter) const
}
}
void Text::addLabel(const QString &text, const QPointF &pos, bool overlap,
const QImage &icon)
void Text::addLabel(const QString &text, const QImage &icon,
const QPainterPath &path)
{
TextPointItem *ti = new TextPointItem(text, pos, _font, _maxWidth, _anchor,
icon);
if (!overlap && !_sceneRect.contains(ti->boundingRect())) {
delete ti;
return;
TextItem *ti;
switch (_placement) {
case Line:
if (_alignment == Viewport)
ti = new TextPointItem(text, path.elementAt(0), _font,
_maxWidth, _anchor, icon);
else
ti = new TextPathItem(text, path, _font, _maxAngle, _sceneRect);
if (!_sceneRect.contains(ti->boundingRect()))
ti->setVisible(false);
break;
case LineCenter:
ti = new TextPointItem(text, path.pointAtPercent(0.5), _font,
_maxWidth, _anchor, icon);
if (!_sceneRect.contains(ti->boundingRect()))
ti->setVisible(false);
break;
default:
ti = new TextPointItem(text, path.elementAt(0), _font, _maxWidth,
_anchor, icon);
if (_alignment == Viewport
&& !_sceneRect.contains(ti->boundingRect()))
ti->setVisible(false);
break;
}
ti->setPen(_pen);
addItem(ti);
@ -37,23 +58,6 @@ void Text::addLabel(const QString &text, const QPointF &pos, bool overlap,
ci[i]->setVisible(false);
}
void Text::addLabel(const QString &text, const QPainterPath &path)
{
TextPathItem *ti = new TextPathItem(text, path, _font, _maxAngle,
_sceneRect);
if (!_sceneRect.contains(ti->boundingRect())) {
delete ti;
return;
}
ti->setPen(_pen);
addItem(ti);
QList<TextItem*> ci = collidingItems(ti);
for (int i = 0; i < ci.size(); i++)
ci[i]->setVisible(false);
}
QList<TextItem*> Text::collidingItems(const TextItem *item) const
{
QList<TextItem*> list;
@ -61,7 +65,7 @@ QList<TextItem*> Text::collidingItems(const TextItem *item) const
if (!item->isVisible())
return list;
for (int i = 0; i < _items.size();i ++) {
for (int i = 0; i < _items.size(); i++) {
const TextItem *ti = _items.at(i);
if (ti != item && ti->isVisible() && ti->collidesWithItem(item))
list.append(const_cast<TextItem*>(ti));
@ -69,3 +73,16 @@ QList<TextItem*> Text::collidingItems(const TextItem *item) const
return list;
}
void Text::setSymbolPlacement(SymbolPlacement placement)
{
_placement = placement;
if (_placement != Text::Point) {
for (int i = 0; i < _items.size(); i++) {
TextItem *ti = _items[i];
if (!_sceneRect.contains(ti->boundingRect()))
ti->setVisible(false);
}
}
}

View File

@ -22,6 +22,18 @@ public:
Bottom
};
enum SymbolPlacement {
Point,
Line,
LineCenter
};
enum RotationAlignment {
Map,
Viewport,
Auto
};
Text(const QSize &size)
: _sceneRect(QRectF(QPointF(0, 0), size)) {}
~Text();
@ -31,10 +43,12 @@ public:
void setAnchor(Anchor anchor) {_anchor = anchor;}
void setMaxWidth(int width) {_maxWidth = width;}
void setMaxAngle(int angle) {_maxAngle = angle;}
void setSymbolPlacement(SymbolPlacement placement);
void setRotationAlignment(RotationAlignment alignment)
{_alignment = alignment;}
void addLabel(const QString &text, const QPointF &pos, bool overlap,
const QImage &icon);
void addLabel(const QString &text, const QPainterPath &path);
void addLabel(const QString &text, const QImage &icon,
const QPainterPath &path);
void render(QPainter *painter) const;
@ -48,6 +62,8 @@ private:
int _maxWidth;
int _maxAngle;
Anchor _anchor;
SymbolPlacement _placement;
RotationAlignment _alignment;
QFont _font;
QPen _pen;
};

View File

@ -11,6 +11,10 @@ public:
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;}
virtual QPainterPath shape() const = 0;
virtual QRectF boundingRect() const = 0;
@ -49,6 +53,8 @@ protected:
private:
QString _text;
QFont _font;
QPen _pen;
bool _visible;
};

View File

@ -134,7 +134,7 @@ static bool reverse(const QPainterPath &path)
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
const QFont &font, int maxAngle, const QRectF &tileRect)
: TextItem(text), _font(font)
: TextItem(text)
{
int cw = avgCharWidth(text, font);
int textWidth = text.size() * cw;
@ -146,6 +146,7 @@ TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
return;
_path = reverse(tp) ? tp.toReversed() : tp;
setFont(font);
QPainterPathStroker s;
s.setWidth(font.pixelSize());
@ -159,14 +160,14 @@ void TextPathItem::paint(QPainter *painter) const
//painter->setPen(Qt::red);
//painter->drawPath(_shape);
QFontMetrics fm(_font);
QFontMetrics fm(font());
int textWidth = fm.width(text());
qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth);
qreal percent = (1.0 - factor) / 2.0;
painter->setFont(_font);
painter->setPen(_pen);
painter->setFont(font());
painter->setPen(pen());
QTransform t = painter->transform();

View File

@ -16,14 +16,10 @@ public:
QRectF boundingRect() const {return _boundingRect;}
void paint(QPainter *painter) const;
void setPen(const QPen &pen) {_pen = pen;}
private:
QPainterPath _path;
QPainterPath _shape;
QRectF _boundingRect;
QFont _font;
QPen _pen;
};
#endif // TEXTPATHITEM_H

View File

@ -72,7 +72,7 @@ QRectF TextPointItem::fuzzyBoundingRect(const QString &str,
QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
{
QRectF iconRect = _icon.isNull() ? QRectF() : _icon.rect();
QRectF textRect = brf(text(), _font, _maxWidth);
QRectF textRect = brf(text(), font(), _maxWidth);
switch (_anchor) {
case Text::Center:
@ -101,9 +101,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), _font(font), _icon(icon), _maxWidth(maxWidth),
: TextItem(text), _pos(pos), _icon(icon), _maxWidth(maxWidth),
_anchor(anchor)
{
setFont(font);
_boundingRect = computeTextRect(fuzzyBoundingRect);
if (!_icon.isNull()) {
@ -129,7 +130,7 @@ void TextPointItem::paint(QPainter *painter) const
} else
textRect = computeTextRect(fuzzyBoundingRect);
painter->setFont(_font);
painter->setPen(_pen);
painter->setFont(font());
painter->setPen(pen());
painter->drawText(textRect, FLAGS, text());
}

View File

@ -17,8 +17,6 @@ public:
QPainterPath shape() const {return _shape;}
void paint(QPainter *painter) const;
void setPen(const QPen &pen) {_pen = pen;}
private:
typedef QRectF (*BoundingRectFunction)(const QString &, const QFont &, int);
@ -32,8 +30,6 @@ private:
QPointF _pos;
QPainterPath _shape;
QRectF _boundingRect;
QFont _font;
QPen _pen;
QImage _icon;
int _maxWidth;
Text::Anchor _anchor;