Marble

OsmcSymbol.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2017 Sergey Popov <[email protected]>
4 //
5 
6 #include "OsmcSymbol.h"
7 
8 #include <QDebug>
9 #include <QDomDocument>
10 #include <QFile>
11 #include <QPainter>
12 
13 OsmcSymbol::OsmcSymbol(const QString &tag, int size)
14  : m_wayColor(Qt::white)
15  , m_backgroundColor(Qt::black)
16  , m_foreground(nullptr)
17  , m_foreground2(nullptr)
18  , m_textColor(Qt::black)
19  , m_side(size)
20 {
21  m_backgroundTypes
22  << "round" << "circle" << "frame";
23 
24  m_foregroundTypes
25  << "dot" << "bowl" << "circle" << "bar"
26  << "stripe" << "cross" << "x" << "slash"
27  << "backslash" << "rectangle" << "rectangle_line"
28  << "triangle" << "triangle_turned" << "triangle_line"
29  << "diamond" << "pointer" << "fork" << "arch"
30  << "turned_T" << "L" << "lower" << "corner"
31  << "drop_line" << "horse" << "hiker";
32 
33  m_precoloredForegroundTypes
34  << "wolfshook" << "shell" << "shell_modern" << "ammonit"
35  << "mine" << "hiker" << "heart" << "tower" << "bridleway";
36 
37  if (parseTag(tag)) {
38  render();
39  }
40 }
41 
42 OsmcSymbol::~OsmcSymbol()
43 {
44  delete m_foreground;
45  delete m_foreground2;
46 }
47 
48 bool OsmcSymbol::parseTag(const QString &tag)
49 {
50  QStringList parts = tag.split(':');
51 
52  if (parts.size() < 2) {
53  return false;
54  }
55 
56  if (m_foreground) {
57  delete m_foreground;
58  m_foreground = nullptr;
59  }
60  if (m_foreground2) {
61  delete m_foreground2;
62  m_foreground2 = nullptr;
63  }
64 
65  // Determine way color
66  if (QColor::isValidColor(parts.at(0))) {
67  m_wayColor.setNamedColor(parts.at(0));
68  } else {
69  return false;
70  }
71 
72  if (!parseBackground(parts.at(1))) {
73  return false;
74  }
75 
76  if (parts.size() == 3) {
77  m_foreground = parseForeground(parts.at(2));
78  } else if (parts.size() == 4) {
79  if (QColor::isValidColor(parts.at(3))) {
80  m_text = parts.at(2);
81  m_textColor = parts.at(3);
82  } else {
83  m_foreground = parseForeground(parts.at(2));
84  m_foreground2 = parseForeground(parts.at(3));
85  }
86  } else if (parts.size() == 5) {
87  m_foreground = parseForeground(parts.at(2));
88  if (QColor::isValidColor(parts.at(4))) {
89  m_text = parts.at(3);
90  m_textColor = parts.at(4);
91  } else {
92  return false;
93  }
94  } else if (parts.size() == 6) {
95  m_foreground = parseForeground(parts.at(2));
96  m_foreground2 = parseForeground(parts.at(3));
97  if (QColor::isValidColor(parts.at(5))) {
98  m_text = parts.at(4);
99  m_textColor.setNamedColor(parts.at(5));
100  } else {
101  return false;
102  }
103  } else {
104  return false;
105  }
106 
107  return true;
108 }
109 
110 bool OsmcSymbol::parseBackground(const QString &bg)
111 {
112  QString color = bg.section("_", 0, 0);
113  QString type = bg.section("_", 1, -1);
114 
115  if (!QColor::isValidColor(color)) {
116  return false;
117  }
118 
119  // Plain color was provided
120  if (type.isEmpty()) {
121  m_backgroundColor.setNamedColor(color);
122  m_backgroundType = type;
123  } else if (m_backgroundTypes.contains(type)) {
124  m_backgroundColor.setNamedColor(color);
125  m_backgroundType = type;
126  } else {
127  return false;
128  }
129 
130  return true;
131 }
132 
133 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue);
134 
135 QSvgRenderer* OsmcSymbol::parseForeground(const QString &fg)
136 {
137  if (m_precoloredForegroundTypes.contains(fg)) {
138  return new QSvgRenderer(QString(":/osmc-symbols/%1.svg").arg(fg));
139  }
140 
141  QString color = fg.section('_', 0, 0);
142  QString type = fg.section('_', 1, -1);
143  if (QColor::isValidColor(color) && m_foregroundTypes.contains(type)) {
144  // Open svg resource and load contents to QByteArray
145  QFile file(QString(":/osmc-symbols/%1.svg").arg(type));
146  file.open(QIODevice::ReadOnly);
147  QByteArray baData = file.readAll();
148 
149  // Load svg contents to xml document
150  QDomDocument doc;
151  doc.setContent(baData);
152 
153  // Recursively change color
154  QDomElement rootElement = doc.documentElement();
155  setXMLAttribute(rootElement, "path", "fill", color);
156 
157  // Create and return svg renderer with edited contents
158  return new QSvgRenderer(doc.toByteArray());
159  }
160 
161  return nullptr;
162 }
163 
164 void OsmcSymbol::render()
165 {
166  m_image = QImage(m_side, m_side, QImage::Format_ARGB32);
167  m_image.fill(Qt::transparent);
168 
169  QPainter painter(&m_image);
170  painter.setRenderHint(QPainter::Antialiasing);
171 
172  // Default size of background
173  int w = m_side, h = m_side;
174 
175  // If there is some text, our background size must be recalculated
176  if (!m_text.isEmpty()) {
177  QFont font = painter.font();
178  font.setPixelSize(int(m_side * 0.8));
179  font.setBold(true);
180  painter.setFont(font);
181  QFontMetrics fm = QFontMetrics(font);
182 
183  h = fm.height();
184  w = qMax(h, fm.horizontalAdvance(m_text));
185  }
186 
187  const QRect bgRect = QRect((m_side - w) / 2, (m_side - h) / 2, w, h);
188 
189  // Draw symbol's background
190  if (m_backgroundType.isEmpty()) {
191  painter.fillRect(bgRect, m_backgroundColor);
192  } else if (m_backgroundType == "round") {
193  painter.setBrush(m_backgroundColor);
194  painter.setPen(m_backgroundColor);
195  painter.drawEllipse(bgRect);
196  } else if (m_backgroundType == "circle") {
197  painter.setBrush(Qt::white);
198  painter.setPen(QPen(m_backgroundColor, m_side / 10));
199  painter.drawEllipse(bgRect);
200  } else if (m_backgroundType == "frame") {
201  painter.setPen(QPen(m_backgroundColor, m_side / 10));
202  painter.fillRect(bgRect, Qt::white);
203  painter.drawRect(bgRect);
204  }
205 
206  QPixmap foregrounds(bgRect.size());
207  foregrounds.fill(Qt::transparent);
208  QPainter fgPainter(&foregrounds);
209  m_foreground ? m_foreground->render(&fgPainter) : void();
210  m_foreground2 ? m_foreground2->render(&fgPainter) : void();
211  painter.drawPixmap(bgRect, foregrounds);
212 
213  if (!m_text.isEmpty()) {
214  // Draw text with provided color
215  painter.setPen(m_textColor);
216  painter.drawText(bgRect, Qt::AlignCenter, m_text);
217  }
218 
219  painter.end();
220 }
221 
222 QImage OsmcSymbol::icon() const
223 {
224  return m_image;
225 }
226 
227 QColor OsmcSymbol::wayColor() const
228 {
229  return m_wayColor;
230 }
231 
232 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue)
233 {
234  // If elem's tag is equal to the provided one then overwrite desired attribute
235  if (elem.tagName() == tag) {
236  elem.setAttribute(attr, attrValue);
237  }
238 
239  // Do the same for all the child nodes
240  for (int i = 0; i < elem.childNodes().count(); ++i) {
241  if (!elem.childNodes().at(i).isElement()) {
242  continue;
243  }
244 
245  QDomElement child = elem.childNodes().at(i).toElement();
246  setXMLAttribute(child, tag, attr, attrValue);
247  }
248 }
AlignCenter
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const
QDomElement toElement() const const
QSize size() const const
QString tagName() const const
Type type(const QSqlDatabase &db)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int horizontalAdvance(const QString &text, int len) const const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const const
void setAttribute(const QString &name, const QString &value)
int size() const const
void setPixelSize(int pixelSize)
const T & at(int i) const const
QDomNodeList childNodes() const const
QDomElement documentElement() const const
bool isValidColor(const QString &name)
void setBold(bool enable)
bool isElement() const const
void render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, QWidget::RenderFlags renderFlags)
int height() const const
QByteArray toByteArray(int indent) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:27 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.