Added font properties handling

Added support for boolean functions
This commit is contained in:
Martin Tůma 2018-11-06 22:35:30 +01:00
parent f376a9235a
commit c97f2235a9
8 changed files with 147 additions and 14 deletions

View File

@ -17,7 +17,8 @@ HEADERS += src/pbfhandler.h \
src/tile.h \ src/tile.h \
src/function.h \ src/function.h \
src/textpathitem.h \ src/textpathitem.h \
src/textitem.h src/textitem.h \
src/font.h
SOURCES += src/pbfplugin.cpp \ SOURCES += src/pbfplugin.cpp \
src/pbfhandler.cpp \ src/pbfhandler.cpp \
src/gzip.cpp \ src/gzip.cpp \
@ -27,7 +28,8 @@ SOURCES += src/pbfplugin.cpp \
src/text.cpp \ src/text.cpp \
src/function.cpp \ src/function.cpp \
src/textpathitem.cpp \ src/textpathitem.cpp \
src/textitem.cpp src/textitem.cpp \
src/font.cpp
RESOURCES += pbfplugin.qrc RESOURCES += pbfplugin.qrc
unix { unix {

61
src/font.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <QString>
#include <QFontDatabase>
#include <QJsonArray>
#include "font.h"
static const QStringList &fonts()
{
static QStringList l(QFontDatabase().families());
return l;
}
const QString fontFamily(const QString &str)
{
const QStringList &fl = fonts();
QString family(str.left(str.lastIndexOf(' ')));
for (int j = 0; j < fl.size(); j++)
if (fl.at(j).startsWith(family))
return family;
return QString();
}
static void setType(QFont &font, const QString &str)
{
QString type(str.right(str.length() - str.lastIndexOf(' ') - 1));
if (type == "Italic")
font.setItalic(true);
else if (type == "Bold")
font.setWeight(QFont::Bold);
else if (type == "Medium")
font.setWeight(QFont::Medium);
}
QFont Font::fromJsonArray(const QJsonArray &json)
{
if (json.isEmpty())
return QFont();
// Try exact match from the layout font list
for (int i = 0; i < json.size(); i++) {
if (!json.at(i).isString())
return QFont();
QString str(json.at(i).toString());
QString family(fontFamily(str));
if (!family.isNull()) {
QFont font(family);
setType(font, str);
return font;
}
}
// Use Qt's font matching logic on the first font in the list
QString str(json.first().toString());
QFont font(str.left(str.lastIndexOf(' ')));
setType(font, str);
return font;
}

13
src/font.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef FONT_H
#define FONT_H
#include <QFont>
class QJsonArray;
namespace Font
{
QFont fromJsonArray(const QJsonArray &json);
}
#endif // FONT_H

View File

@ -117,3 +117,36 @@ QColor FunctionC::value(qreal x) const
return _stops.last().second; return _stops.last().second;
} }
FunctionB::FunctionB(const QJsonObject &json, bool dflt) : _default(dflt)
{
if (!(json.contains("stops") && json["stops"].isArray()))
return;
QJsonArray stops = json["stops"].toArray();
for (int i = 0; i < stops.size(); i++) {
if (!stops.at(i).isArray())
return;
QJsonArray stop = stops.at(i).toArray();
if (stop.size() != 2)
return;
_stops.append(QPair<qreal, bool>(stop.at(0).toDouble(),
stop.at(1).toBool()));
}
}
bool FunctionB::value(qreal x) const
{
if (_stops.isEmpty())
return _default;
QPair<qreal, bool> v0(_stops.first());
for (int i = 0; i < _stops.size(); i++) {
if (x < _stops.at(i).first)
return v0.second;
else
v0 = _stops.at(i);
}
return _stops.last().second;
}

View File

@ -7,6 +7,18 @@
class QJsonObject; class QJsonObject;
class FunctionB {
public:
FunctionB(bool deflt = true) : _default(deflt) {}
FunctionB(const QJsonObject &json, bool dflt = true);
bool value(qreal x) const;
private:
QList<QPair<qreal, bool> > _stops;
bool _default;
};
class FunctionF { class FunctionF {
public: public:
FunctionF(qreal deflt = 0) : _default(deflt), _base(1.0) {} FunctionF(qreal deflt = 0) : _default(deflt), _base(1.0) {}

View File

@ -6,6 +6,7 @@
#include <QDebug> #include <QDebug>
#include "text.h" #include "text.h"
#include "color.h" #include "color.h"
#include "font.h"
#include "tile.h" #include "tile.h"
#include "style.h" #include "style.h"
@ -134,7 +135,7 @@ bool Style::Layer::Filter::match(const QVariantMap &tags) const
} }
Style::Layer::Paint::Paint(const QJsonObject &json) Style::Layer::Paint::Paint(const QJsonObject &json)
: _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0), _fillAntialias(true) : _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0)
{ {
if (json.contains("fill-opacity") && json["fill-opacity"].isDouble()) if (json.contains("fill-opacity") && json["fill-opacity"].isDouble())
_fillOpacity = FunctionF(json["fill-opacity"].toDouble()); _fillOpacity = FunctionF(json["fill-opacity"].toDouble());
@ -156,7 +157,9 @@ Style::Layer::Paint::Paint(const QJsonObject &json)
&& json["fill-outline-color"].isObject()) && json["fill-outline-color"].isObject())
_fillOutlineColor = FunctionC(json["fill-outline-color"].toObject()); _fillOutlineColor = FunctionC(json["fill-outline-color"].toObject());
if (json.contains("fill-antialias") && json["fill-antialias"].isBool()) if (json.contains("fill-antialias") && json["fill-antialias"].isBool())
_fillAntialias = json["fill-antialias"].toBool(); _fillAntialias = FunctionB(json["fill-antialias"].toBool());
else if (json.contains("fill-antialias") && json["fill-antialias"].isObject())
_fillAntialias = FunctionB(json["fill-antialias"].toObject());
if (json.contains("fill-pattern")) { if (json.contains("fill-pattern")) {
_fillColor = FunctionC(QColor()); _fillColor = FunctionC(QColor());
_fillOutlineColor = FunctionC(QColor()); _fillOutlineColor = FunctionC(QColor());
@ -256,11 +259,11 @@ qreal Style::Layer::Paint::opacity(Type type, int zoom) const
} }
} }
bool Style::Layer::Paint::antialias(Layer::Type type) const bool Style::Layer::Paint::antialias(Layer::Type type, int zoom) const
{ {
switch (type) { switch (type) {
case Fill: case Fill:
return _fillAntialias; return _fillAntialias.value(zoom);
case Line: case Line:
return true; return true;
default: default:
@ -297,7 +300,8 @@ Style::Layer::Layout::Layout(const QJsonObject &json)
_textMaxWidth = FunctionF(json["text-max-angle"].toObject()); _textMaxWidth = FunctionF(json["text-max-angle"].toObject());
else if (json.contains("text-max-angle") && json["text-max-angle"].isDouble()) else if (json.contains("text-max-angle") && json["text-max-angle"].isDouble())
_textMaxWidth = json["text-max-angle"].toDouble(); _textMaxWidth = json["text-max-angle"].toDouble();
if (json.contains("text-font") && json["text-font"].isArray())
_font = Font::fromJsonArray(json["text-font"].toArray());
if (json.contains("text-transform") && json["text-transform"].isString()) if (json.contains("text-transform") && json["text-transform"].isString())
_capitalize = json["text-transform"].toString() == "uppercase"; _capitalize = json["text-transform"].toString() == "uppercase";
@ -317,8 +321,8 @@ Style::Layer::Layout::Layout(const QJsonObject &json)
QFont Style::Layer::Layout::font(int zoom) const QFont Style::Layer::Layout::font(int zoom) const
{ {
QFont font; QFont font(_font);
font.setPixelSize(_textSize.value(zoom)); font.setPixelSize(qRound(_textSize.value(zoom)));
return font; return font;
} }
@ -385,7 +389,7 @@ void Style::Layer::drawPath(int zoom, const QPainterPath &path,
pen.setCapStyle(_layout.lineCap()); pen.setCapStyle(_layout.lineCap());
QPainter &p = tile.painter(); QPainter &p = tile.painter();
p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type)); p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, zoom));
p.setPen(pen); p.setPen(pen);
p.setBrush(brush); p.setBrush(brush);
p.setOpacity(_paint.opacity(_type, zoom)); p.setOpacity(_paint.opacity(_type, zoom));

View File

@ -8,6 +8,7 @@
#include <QSet> #include <QSet>
#include <QPen> #include <QPen>
#include <QBrush> #include <QBrush>
#include <QFont>
#include "function.h" #include "function.h"
@ -102,19 +103,19 @@ private:
FunctionF _textMaxAngle; FunctionF _textMaxAngle;
Qt::PenCapStyle _lineCap; Qt::PenCapStyle _lineCap;
Qt::PenJoinStyle _lineJoin; Qt::PenJoinStyle _lineJoin;
QFont _font;
bool _capitalize; bool _capitalize;
}; };
class Paint { class Paint {
public: public:
Paint() : _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0), Paint() : _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0) {}
_fillAntialias(true) {}
Paint(const QJsonObject &json); Paint(const QJsonObject &json);
QPen pen(Layer::Type type, int zoom) const; QPen pen(Layer::Type type, int zoom) const;
QBrush brush(Layer::Type type, int zoom) const; QBrush brush(Layer::Type type, int zoom) const;
qreal opacity(Layer::Type type, int zoom) const; qreal opacity(Layer::Type type, int zoom) const;
bool antialias(Layer::Type type) const; bool antialias(Layer::Type type, int zoom) const;
private: private:
FunctionC _textColor; FunctionC _textColor;
@ -125,7 +126,7 @@ private:
FunctionF _fillOpacity; FunctionF _fillOpacity;
FunctionF _lineOpacity; FunctionF _lineOpacity;
FunctionF _lineWidth; FunctionF _lineWidth;
bool _fillAntialias; FunctionB _fillAntialias;
QVector<qreal> _lineDasharray; QVector<qreal> _lineDasharray;
}; };

View File

@ -10,9 +10,16 @@ TextItem::TextItem(const QString &text, const QPointF &pos, const QFont &font,
{ {
QFontMetrics fm(font); QFontMetrics fm(font);
int limit = font.pixelSize() * maxTextWidth; int limit = font.pixelSize() * maxTextWidth;
// Italic fonts overflow the computed bounding rect, so reduce it
// a little bit.
if (font.italic())
limit -= fm.averageCharWidth();
QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, text); QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, text);
Q_ASSERT(br.isValid()); Q_ASSERT(br.isValid());
// Expand the bounding rect back to the real content size
if (font.italic())
br.adjust(-fm.averageCharWidth() / 2, 0, fm.averageCharWidth() / 2, 0);
setPos((pos - QPointF(br.width() / 2.0, br.height() / 2.0)).toPoint()); setPos((pos - QPointF(br.width() / 2.0, br.height() / 2.0)).toPoint());
_boundingRect = QRectF(0, 0, br.width(), br.height()); _boundingRect = QRectF(0, 0, br.width(), br.height());
} }