KOSMIndoorMap

mapcssdeclaration.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "mapcssdeclaration_p.h"
8
9#include "logging.h"
10#include "mapcssproperty.h"
11#include "mapcssvalue_p.h"
12
13#include <QDebug>
14#include <QIODevice>
15
16#include <cstring>
17
18using namespace KOSMIndoorMap;
19
20// keep this sorted by property name!
21struct {
22 const char* name;
23 MapCSSProperty property;
24 int flags;
25} static constexpr const property_types[] = {
26 // only those properties have their corresonding flag set that actually trigger emission of a scene graph item
27 // e.g. for a label we either need a text or an icon, the visual properties for those on their own would be a no-op
28 { "casing-color", MapCSSProperty::CasingColor, MapCSSDeclaration::NoFlag },
29 { "casing-dashes", MapCSSProperty::CasingDashes, MapCSSDeclaration::NoFlag },
30 { "casing-linecap", MapCSSProperty::CasingLineCap, MapCSSDeclaration::NoFlag },
31 { "casing-linejoin", MapCSSProperty::CasingLineJoin, MapCSSDeclaration::NoFlag },
32 { "casing-opacity", MapCSSProperty::CasingOpacity, MapCSSDeclaration::NoFlag },
33 { "casing-width", MapCSSProperty::CasingWidth, MapCSSDeclaration::NoFlag },
34 { "color", MapCSSProperty::Color, MapCSSDeclaration::LineProperty },
35 { "dashes", MapCSSProperty::Dashes, MapCSSDeclaration::NoFlag },
36 { "extrude", MapCSSProperty::Extrude, MapCSSDeclaration::ExtrudeProperty },
37 { "fill-color", MapCSSProperty::FillColor, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty }, // TODO this also applies to lines
38 { "fill-image", MapCSSProperty::FillImage, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty },
39 { "fill-opacity", MapCSSProperty::FillOpacity, MapCSSDeclaration::AreaProperty },
40 { "font-family", MapCSSProperty::FontFamily, MapCSSDeclaration::NoFlag },
41 { "font-size", MapCSSProperty::FontSize, MapCSSDeclaration::NoFlag },
42 { "font-style", MapCSSProperty::FontStyle, MapCSSDeclaration::NoFlag },
43 { "font-variant", MapCSSProperty::FontVariant, MapCSSDeclaration::NoFlag },
44 { "font-weight", MapCSSProperty::FontWeight, MapCSSDeclaration::NoFlag },
45 { "icon-allow-icon-overlap", MapCSSProperty::IconAllowIconOverlap, MapCSSDeclaration::NoFlag },
46 { "icon-allow-text-overlap", MapCSSProperty::IconAllowTextOverlap, MapCSSDeclaration::NoFlag },
47 { "icon-color", MapCSSProperty::IconColor, MapCSSDeclaration::NoFlag },
48 { "icon-height", MapCSSProperty::IconHeight, MapCSSDeclaration::NoFlag },
49 { "icon-image", MapCSSProperty::IconImage, MapCSSDeclaration::LabelProperty },
50 { "icon-opacity", MapCSSProperty::IconOpacity, MapCSSDeclaration::NoFlag },
51 { "icon-width", MapCSSProperty::IconWidth, MapCSSDeclaration::NoFlag },
52 { "image", MapCSSProperty::Image, MapCSSDeclaration::LineProperty },
53 { "linecap", MapCSSProperty::LineCap, MapCSSDeclaration::NoFlag },
54 { "linejoin", MapCSSProperty::LineJoin, MapCSSDeclaration::NoFlag },
55 { "max-width", MapCSSProperty::MaxWidth, MapCSSDeclaration::NoFlag },
56 { "opacity", MapCSSProperty::Opacity, MapCSSDeclaration::NoFlag },
57 { "shield-casing-color", MapCSSProperty::ShieldCasingColor, MapCSSDeclaration::LabelProperty },
58 { "shield-casing-width", MapCSSProperty::ShieldCasingWidth, MapCSSDeclaration::NoFlag },
59 { "shield-color", MapCSSProperty::ShieldColor, MapCSSDeclaration::LabelProperty },
60 { "shield-frame-color", MapCSSProperty::ShieldFrameColor, MapCSSDeclaration::LabelProperty },
61 { "shield-frame-width", MapCSSProperty::ShieldFrameWidth, MapCSSDeclaration::NoFlag },
62 { "shield-image", MapCSSProperty::ShieldImage, MapCSSDeclaration::LabelProperty },
63 { "shield-opacity", MapCSSProperty::ShieldOpacity, MapCSSDeclaration::NoFlag },
64 { "shield-shape", MapCSSProperty::ShieldShape, MapCSSDeclaration::NoFlag },
65 { "shield-text", MapCSSProperty::ShieldText, MapCSSDeclaration::LabelProperty },
66 { "text", MapCSSProperty::Text, MapCSSDeclaration::LabelProperty },
67 { "text-color", MapCSSProperty::TextColor, MapCSSDeclaration::CanvasProperty },
68 { "text-decoration", MapCSSProperty::TextDecoration, MapCSSDeclaration::NoFlag },
69 { "text-halo-color", MapCSSProperty::TextHaloColor, MapCSSDeclaration::NoFlag },
70 { "text-halo-radius", MapCSSProperty::TextHaloRadius, MapCSSDeclaration::NoFlag },
71 { "text-offset", MapCSSProperty::TextOffset, MapCSSDeclaration::NoFlag },
72 { "text-opacity", MapCSSProperty::TextOpacity, MapCSSDeclaration::NoFlag },
73 { "text-position", MapCSSProperty::TextPosition, MapCSSDeclaration::NoFlag },
74 { "text-transform", MapCSSProperty::TextTransform, MapCSSDeclaration::NoFlag },
75 { "width", MapCSSProperty::Width, MapCSSDeclaration::LineProperty },
76 { "z-index", MapCSSProperty::ZIndex, MapCSSDeclaration::NoFlag },
77};
78
79struct {
80 const char *name;
81 Qt::PenCapStyle capStyle;
82} static constexpr const capstyle_map[] = {
83 { "none", Qt::FlatCap },
84 { "round", Qt::RoundCap },
85 { "square", Qt::SquareCap },
86};
87
88struct {
89 const char *name;
90 Qt::PenJoinStyle joinStyle;
91} static constexpr const joinstyle_map[] = {
92 { "bevel", Qt::BevelJoin },
93 { "miter", Qt::MiterJoin },
94 { "round", Qt::RoundJoin },
95};
96
97struct {
98 const char *name;
99 QFont::Capitalization capitalizationStyle;
100} static constexpr const capitalizationstyle_map[] = {
101 { "capitalize", QFont::Capitalize },
102 { "lowercase", QFont::AllLowercase },
103 { "none", QFont::MixedCase },
104 { "normal", QFont::MixedCase },
105 { "small-caps", QFont::SmallCaps },
106 { "uppercase", QFont::AllUppercase },
107};
108
109struct {
110 const char *name;
111 MapCSSDeclaration::Unit unit;
112} static constexpr const unit_map[] = {
113 { "m", MapCSSDeclaration::Meters },
114 { "pt", MapCSSDeclaration::Point },
115 { "px", MapCSSDeclaration::Pixels },
116};
117
118struct {
119 const char *name;
120 MapCSSDeclaration::Position position;
121} static constexpr const position_map[] = {
122 { "center", MapCSSDeclaration::Position::Center },
123 { "line", MapCSSDeclaration::Position::Line },
124};
125
126MapCSSDeclaration::MapCSSDeclaration(Type type)
127 : m_type(type)
128{
129}
130
131MapCSSDeclaration::~MapCSSDeclaration() = default;
132
133bool MapCSSDeclaration::isValid() const
134{
135 switch (m_type) {
136 case PropertyDeclaration:
137 return property() != MapCSSProperty::Unknown;
138 case TagDeclaration:
139 return !m_identValue.isEmpty();
140 case ClassDeclaration:
141 return !m_class.isNull();
142 }
143
144 Q_UNREACHABLE();
145 return false;
146}
147
148MapCSSDeclaration::Type MapCSSDeclaration::type() const
149{
150 return m_type;
151}
152
153MapCSSProperty MapCSSDeclaration::property() const
154{
155 return m_property;
156}
157
158int MapCSSDeclaration::propertyFlags() const
159{
160 return m_flags;
161}
162
163int MapCSSDeclaration::intValue() const
164{
165 return m_doubleValue;
166}
167
168double MapCSSDeclaration::doubleValue() const
169{
170 return m_doubleValue;
171}
172
173bool MapCSSDeclaration::boolValue() const
174{
175 return m_boolValue;
176}
177
178QString MapCSSDeclaration::stringValue() const
179{
180 return m_stringValue;
181}
182
183QColor MapCSSDeclaration::colorValue() const
184{
185 if (!m_colorValue.isValid() && !m_stringValue.isEmpty()) {
186 return QColor(m_stringValue);
187 }
188 return m_colorValue;
189}
190
191QByteArray MapCSSDeclaration::keyValue() const
192{
193 return m_identValue;
194}
195
196QVector<double> MapCSSDeclaration::dashesValue() const
197{
198 return m_dashValue;
199}
200
201OSM::TagKey MapCSSDeclaration::tagKey() const
202{
203 return m_tagKey;
204}
205
206void MapCSSDeclaration::setDoubleValue(double val)
207{
208 m_doubleValue = val;
209}
210
211void MapCSSDeclaration::setBoolValue(bool val)
212{
213 m_boolValue = val;
214}
215
216bool MapCSSDeclaration::hasExpression() const
217{
218 return m_evalExpression.isValid();
219}
220
221MapCSSValue MapCSSDeclaration::evaluateExpression(const MapCSSExpressionContext &context) const
222{
223 return m_evalExpression.evaluate(context);
224}
225
226MapCSSProperty MapCSSDeclaration::propertyFromName(const char *name, std::size_t len)
227{
228 const auto it = std::lower_bound(std::begin(property_types), std::end(property_types), name, [len](const auto &lhs, const char *rhs) {
229 const auto lhsLen = std::strlen(lhs.name);
230 const auto cmp = std::strncmp(lhs.name, rhs, std::min(lhsLen, len));
231 return cmp < 0 || (cmp == 0 && lhsLen < len);
232 });
233 if (it == std::end(property_types) || std::strncmp((*it).name, name, std::max(len, std::strlen((*it).name))) != 0) {
234 return MapCSSProperty::Unknown;
235 }
236 return (*it).property;
237}
238
239void MapCSSDeclaration::setPropertyName(const char *name, std::size_t len)
240{
241 const auto it = std::lower_bound(std::begin(property_types), std::end(property_types), name, [len](const auto &lhs, const char *rhs) {
242 const auto lhsLen = std::strlen(lhs.name);
243 const auto cmp = std::strncmp(lhs.name, rhs, std::min(lhsLen, len));
244 return cmp < 0 || (cmp == 0 && lhsLen < len);
245 });
246 if (it == std::end(property_types) || std::strncmp((*it).name, name, std::max(len, std::strlen((*it).name))) != 0) {
247 qCWarning(Log) << "Unknown property declaration:" << QByteArray::fromRawData(name, len);
248 m_property = MapCSSProperty::Unknown;
249 return;
250 }
251 m_property = (*it).property;
252 m_flags = (*it).flags;
253}
254
255void MapCSSDeclaration::setIdentifierValue(const char *val, int len)
256{
257 m_identValue = QByteArray(val, len);
258}
259
260void MapCSSDeclaration::setStringValue(char *str)
261{
262 m_stringValue = QString::fromUtf8(str);
263 free(str);
264}
265
266void MapCSSDeclaration::setColorRgba(uint32_t argb)
267{
268 m_colorValue = QColor::fromRgba(argb);
269 //qDebug() << m_colorValue << argb;
270}
271
272void MapCSSDeclaration::setDashesValue(const QVector<double> &dashes)
273{
274 m_dashValue = dashes;
275}
276
277Qt::PenCapStyle MapCSSDeclaration::capStyle() const
278{
279 for (const auto &c : capstyle_map) {
280 if (std::strcmp(c.name, m_identValue.constData()) == 0) {
281 return c.capStyle;
282 }
283 }
284 qDebug() << "unknown line cap style:" << m_identValue;
285 return Qt::FlatCap;
286}
287
288Qt::PenJoinStyle MapCSSDeclaration::joinStyle() const
289{
290 for (const auto &j : joinstyle_map) {
291 if (std::strcmp(j.name, m_identValue.constData()) == 0) {
292 return j.joinStyle;
293 }
294 }
295 return Qt::RoundJoin;
296}
297
298QFont::Capitalization MapCSSDeclaration::capitalizationStyle() const
299{
300 for (const auto &c : capitalizationstyle_map) {
301 if (std::strcmp(c.name, m_identValue.constData()) == 0) {
302 return c.capitalizationStyle;
303 }
304 }
305 return QFont::MixedCase;
306}
307
308bool MapCSSDeclaration::isBoldStyle() const
309{
310 return m_identValue == "bold";
311}
312
313bool MapCSSDeclaration::isItalicStyle() const
314{
315 return m_identValue == "italic";
316}
317
318bool MapCSSDeclaration::isUnderlineStyle() const
319{
320 return m_identValue == "underline";
321}
322
323MapCSSDeclaration::Position MapCSSDeclaration::textPosition() const
324{
325 for (const auto &p : position_map) {
326 if (std::strcmp(p.name, m_identValue.constData()) == 0) {
327 return p.position;
328 }
329 }
330 return Position::NoPostion;
331}
332
333MapCSSDeclaration::Unit MapCSSDeclaration::unit() const
334{
335 return m_unit;
336}
337
338void MapCSSDeclaration::setUnit(const char *val, int len)
339{
340 for (const auto &u : unit_map) {
341 if (std::strncmp(u.name, val, std::max<std::size_t>(std::strlen(u.name), len)) == 0) {
342 m_unit = u.unit;
343 return;
344 }
345 }
346 qCWarning(Log) << "unknown unit:" << QByteArray(val, len);
347 m_unit = NoUnit;
348}
349
350ClassSelectorKey MapCSSDeclaration::classSelectorKey() const
351{
352 return m_class;
353}
354
355void MapCSSDeclaration::setClassSelectorKey(ClassSelectorKey key)
356{
357 m_class = key;
358}
359
360void MapCSSDeclaration::compile(OSM::DataSet &dataSet)
361{
362 // TODO resolve tag key if m_identValue is one
363 if (m_type == TagDeclaration) {
364 m_tagKey = dataSet.makeTagKey(m_identValue.constData());
365 }
366
367 if (m_evalExpression.isValid()) {
368 m_evalExpression.compile(dataSet);
369 }
370}
371
372static void writeQuotedString(QIODevice *out, QByteArrayView str)
373{
374 out->write("\"");
375 for (const auto c : str) {
376 switch (c) {
377 case '"':
378 out->write("\\\"");
379 break;
380 case '\n':
381 out->write("\\n");
382 break;
383 case '\t':
384 out->write("\\t");
385 break;
386 default:
387 out->write(&c, 1);
388 }
389 }
390 out->write("\"");
391}
392
393void MapCSSDeclaration::write(QIODevice *out) const
394{
395 out->write(" ");
396
397 switch (m_type) {
398 case PropertyDeclaration:
399 for (const auto &p : property_types) {
400 if (p.property == m_property) {
401 out->write(p.name);
402 break;
403 }
404 }
405
406 out->write(": ");
407 if (!std::isnan(m_doubleValue)) {
408 out->write(QByteArray::number(m_doubleValue));
409 } else if (m_colorValue.isValid()) {
410 out->write(m_colorValue.name(QColor::HexArgb).toUtf8());
411 } else if (!m_dashValue.isEmpty()) {
412 for (const auto &d : m_dashValue) {
413 out->write(QByteArray::number(d));
414 out->write(", ");
415 }
416 } else if (!m_stringValue.isNull()) {
417 writeQuotedString(out, m_stringValue.toUtf8());
418 } else if (!m_identValue.isEmpty()) {
419 out->write(m_identValue);
420 } else if (m_evalExpression.isValid()) {
421 out->write("eval(");
422 m_evalExpression.write(out);
423 out->write(")");
424 } else {
425 out->write(m_boolValue ? "true" : "false");
426 }
427
428 for (const auto &u : unit_map) {
429 if (u.unit == m_unit) {
430 out->write(u.name);
431 break;
432 }
433 }
434 break;
435 case TagDeclaration:
436 out->write("set ");
437 out->write(m_identValue);
438 if (!std::isnan(m_doubleValue)) {
439 out->write(" = ");
440 out->write(QByteArray::number(m_doubleValue));
441 } else if (!m_stringValue.isEmpty()) {
442 out->write(" = ");
443 writeQuotedString(out, m_stringValue.toUtf8());
444 } else if (m_evalExpression.isValid()) {
445 out->write(" = eval(");
446 m_evalExpression.write(out);
447 out->write(")");
448 }
449 break;
450 case ClassDeclaration:
451 out->write("set .");
452 out->write(m_class.name());
453 break;
454 }
455
456 out->write(";\n");
457}
A set of nodes, ways and relations.
Definition datatypes.h:343
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:28
A key of an OSM tag.
Definition datatypes.h:179
OSM-based multi-floor indoor maps for buildings.
MapCSSProperty
Known properties in MapCSS declarations.
QByteArray fromRawData(const char *data, qsizetype size)
QByteArray number(double n, char format, int precision)
QColor fromRgba(QRgb rgba)
Capitalization
qint64 write(const QByteArray &data)
QString fromUtf8(QByteArrayView str)
PenCapStyle
PenJoinStyle
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:46 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.