File indexing completed on 2024-11-17 04:17:27
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "mapcssdeclaration_p.h" 0008 #include "logging.h" 0009 #include "mapcssproperty.h" 0010 0011 #include <QDebug> 0012 #include <QIODevice> 0013 0014 #include <cstring> 0015 0016 using namespace KOSMIndoorMap; 0017 0018 // keep this sorted by property name! 0019 struct { 0020 const char* name; 0021 MapCSSProperty property; 0022 int flags; 0023 } static constexpr const property_types[] = { 0024 // only those properties have their corresonding flag set that actually trigger emission of a scene graph item 0025 // 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 0026 { "casing-color", MapCSSProperty::CasingColor, MapCSSDeclaration::NoFlag }, 0027 { "casing-dashes", MapCSSProperty::CasingDashes, MapCSSDeclaration::NoFlag }, 0028 { "casing-linecap", MapCSSProperty::CasingLineCap, MapCSSDeclaration::NoFlag }, 0029 { "casing-linejoin", MapCSSProperty::CasingLineJoin, MapCSSDeclaration::NoFlag }, 0030 { "casing-opacity", MapCSSProperty::CasingOpacity, MapCSSDeclaration::NoFlag }, 0031 { "casing-width", MapCSSProperty::CasingWidth, MapCSSDeclaration::NoFlag }, 0032 { "color", MapCSSProperty::Color, MapCSSDeclaration::LineProperty }, 0033 { "dashes", MapCSSProperty::Dashes, MapCSSDeclaration::NoFlag }, 0034 { "extrude", MapCSSProperty::Extrude, MapCSSDeclaration::ExtrudeProperty }, 0035 { "fill-color", MapCSSProperty::FillColor, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty }, // TODO this also applies to lines 0036 { "fill-image", MapCSSProperty::FillImage, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty }, 0037 { "fill-opacity", MapCSSProperty::FillOpacity, MapCSSDeclaration::AreaProperty }, 0038 { "font-family", MapCSSProperty::FontFamily, MapCSSDeclaration::NoFlag }, 0039 { "font-size", MapCSSProperty::FontSize, MapCSSDeclaration::NoFlag }, 0040 { "font-style", MapCSSProperty::FontStyle, MapCSSDeclaration::NoFlag }, 0041 { "font-variant", MapCSSProperty::FontVariant, MapCSSDeclaration::NoFlag }, 0042 { "font-weight", MapCSSProperty::FontWeight, MapCSSDeclaration::NoFlag }, 0043 { "icon-allow-icon-overlap", MapCSSProperty::IconAllowIconOverlap, MapCSSDeclaration::NoFlag }, 0044 { "icon-allow-text-overlap", MapCSSProperty::IconAllowTextOverlap, MapCSSDeclaration::NoFlag }, 0045 { "icon-color", MapCSSProperty::IconColor, MapCSSDeclaration::NoFlag }, 0046 { "icon-height", MapCSSProperty::IconHeight, MapCSSDeclaration::NoFlag }, 0047 { "icon-image", MapCSSProperty::IconImage, MapCSSDeclaration::LabelProperty }, 0048 { "icon-opacity", MapCSSProperty::IconOpacity, MapCSSDeclaration::NoFlag }, 0049 { "icon-width", MapCSSProperty::IconWidth, MapCSSDeclaration::NoFlag }, 0050 { "image", MapCSSProperty::Image, MapCSSDeclaration::LineProperty }, 0051 { "linecap", MapCSSProperty::LineCap, MapCSSDeclaration::NoFlag }, 0052 { "linejoin", MapCSSProperty::LineJoin, MapCSSDeclaration::NoFlag }, 0053 { "max-width", MapCSSProperty::MaxWidth, MapCSSDeclaration::NoFlag }, 0054 { "opacity", MapCSSProperty::Opacity, MapCSSDeclaration::NoFlag }, 0055 { "shield-casing-color", MapCSSProperty::ShieldCasingColor, MapCSSDeclaration::LabelProperty }, 0056 { "shield-casing-width", MapCSSProperty::ShieldCasingWidth, MapCSSDeclaration::NoFlag }, 0057 { "shield-color", MapCSSProperty::ShieldColor, MapCSSDeclaration::LabelProperty }, 0058 { "shield-frame-color", MapCSSProperty::ShieldFrameColor, MapCSSDeclaration::LabelProperty }, 0059 { "shield-frame-width", MapCSSProperty::ShieldFrameWidth, MapCSSDeclaration::NoFlag }, 0060 { "shield-image", MapCSSProperty::ShieldImage, MapCSSDeclaration::LabelProperty }, 0061 { "shield-opacity", MapCSSProperty::ShieldOpacity, MapCSSDeclaration::NoFlag }, 0062 { "shield-shape", MapCSSProperty::ShieldShape, MapCSSDeclaration::NoFlag }, 0063 { "shield-text", MapCSSProperty::ShieldText, MapCSSDeclaration::LabelProperty }, 0064 { "text", MapCSSProperty::Text, MapCSSDeclaration::LabelProperty }, 0065 { "text-color", MapCSSProperty::TextColor, MapCSSDeclaration::CanvasProperty }, 0066 { "text-decoration", MapCSSProperty::TextDecoration, MapCSSDeclaration::NoFlag }, 0067 { "text-halo-color", MapCSSProperty::TextHaloColor, MapCSSDeclaration::NoFlag }, 0068 { "text-halo-radius", MapCSSProperty::TextHaloRadius, MapCSSDeclaration::NoFlag }, 0069 { "text-offset", MapCSSProperty::TextOffset, MapCSSDeclaration::NoFlag }, 0070 { "text-opacity", MapCSSProperty::TextOpacity, MapCSSDeclaration::NoFlag }, 0071 { "text-position", MapCSSProperty::TextPosition, MapCSSDeclaration::NoFlag }, 0072 { "text-transform", MapCSSProperty::TextTransform, MapCSSDeclaration::NoFlag }, 0073 { "width", MapCSSProperty::Width, MapCSSDeclaration::LineProperty }, 0074 { "z-index", MapCSSProperty::ZIndex, MapCSSDeclaration::NoFlag }, 0075 }; 0076 0077 struct { 0078 const char *name; 0079 Qt::PenCapStyle capStyle; 0080 } static constexpr const capstyle_map[] = { 0081 { "none", Qt::FlatCap }, 0082 { "round", Qt::RoundCap }, 0083 { "square", Qt::SquareCap }, 0084 }; 0085 0086 struct { 0087 const char *name; 0088 Qt::PenJoinStyle joinStyle; 0089 } static constexpr const joinstyle_map[] = { 0090 { "bevel", Qt::BevelJoin }, 0091 { "miter", Qt::MiterJoin }, 0092 { "round", Qt::RoundJoin }, 0093 }; 0094 0095 struct { 0096 const char *name; 0097 QFont::Capitalization capitalizationStyle; 0098 } static constexpr const capitalizationstyle_map[] = { 0099 { "capitalize", QFont::Capitalize }, 0100 { "lowercase", QFont::AllLowercase }, 0101 { "none", QFont::MixedCase }, 0102 { "normal", QFont::MixedCase }, 0103 { "small-caps", QFont::SmallCaps }, 0104 { "uppercase", QFont::AllUppercase }, 0105 }; 0106 0107 struct { 0108 const char *name; 0109 MapCSSDeclaration::Unit unit; 0110 } static constexpr const unit_map[] = { 0111 { "m", MapCSSDeclaration::Meters }, 0112 { "pt", MapCSSDeclaration::Point }, 0113 { "px", MapCSSDeclaration::Pixels }, 0114 }; 0115 0116 struct { 0117 const char *name; 0118 MapCSSDeclaration::Position position; 0119 } static constexpr const position_map[] = { 0120 { "center", MapCSSDeclaration::Position::Center }, 0121 { "line", MapCSSDeclaration::Position::Line }, 0122 }; 0123 0124 MapCSSDeclaration::MapCSSDeclaration(Type type) 0125 : m_type(type) 0126 { 0127 } 0128 0129 MapCSSDeclaration::~MapCSSDeclaration() = default; 0130 0131 bool MapCSSDeclaration::isValid() const 0132 { 0133 switch (m_type) { 0134 case PropertyDeclaration: 0135 return property() != MapCSSProperty::Unknown; 0136 case TagDeclaration: 0137 return !m_identValue.isEmpty(); 0138 case ClassDeclaration: 0139 return !m_class.isNull(); 0140 } 0141 0142 Q_UNREACHABLE(); 0143 return false; 0144 } 0145 0146 MapCSSDeclaration::Type MapCSSDeclaration::type() const 0147 { 0148 return m_type; 0149 } 0150 0151 MapCSSProperty MapCSSDeclaration::property() const 0152 { 0153 return m_property; 0154 } 0155 0156 int MapCSSDeclaration::propertyFlags() const 0157 { 0158 return m_flags; 0159 } 0160 0161 int MapCSSDeclaration::intValue() const 0162 { 0163 return m_doubleValue; 0164 } 0165 0166 double MapCSSDeclaration::doubleValue() const 0167 { 0168 return m_doubleValue; 0169 } 0170 0171 bool MapCSSDeclaration::boolValue() const 0172 { 0173 return m_boolValue; 0174 } 0175 0176 QString MapCSSDeclaration::stringValue() const 0177 { 0178 return m_stringValue; 0179 } 0180 0181 QColor MapCSSDeclaration::colorValue() const 0182 { 0183 if (!m_colorValue.isValid() && !m_stringValue.isEmpty()) { 0184 return QColor(m_stringValue); 0185 } 0186 return m_colorValue; 0187 } 0188 0189 QByteArray MapCSSDeclaration::keyValue() const 0190 { 0191 return m_identValue; 0192 } 0193 0194 QVector<double> MapCSSDeclaration::dashesValue() const 0195 { 0196 return m_dashValue; 0197 } 0198 0199 OSM::TagKey MapCSSDeclaration::tagKey() const 0200 { 0201 return m_tagKey; 0202 } 0203 0204 void MapCSSDeclaration::setDoubleValue(double val) 0205 { 0206 m_doubleValue = val; 0207 } 0208 0209 void MapCSSDeclaration::setBoolValue(bool val) 0210 { 0211 m_boolValue = val; 0212 } 0213 0214 void MapCSSDeclaration::setPropertyName(const char *name, std::size_t len) 0215 { 0216 const auto it = std::lower_bound(std::begin(property_types), std::end(property_types), name, [len](const auto &lhs, const char *rhs) { 0217 const auto lhsLen = std::strlen(lhs.name); 0218 const auto cmp = std::strncmp(lhs.name, rhs, std::min(lhsLen, len)); 0219 return cmp < 0 || (cmp == 0 && lhsLen < len); 0220 }); 0221 if (it == std::end(property_types) || std::strncmp((*it).name, name, std::max(len, std::strlen((*it).name))) != 0) { 0222 qCWarning(Log) << "Unknown property declaration:" << QByteArray::fromRawData(name, len); 0223 m_property = MapCSSProperty::Unknown; 0224 return; 0225 } 0226 m_property = (*it).property; 0227 m_flags = (*it).flags; 0228 } 0229 0230 void MapCSSDeclaration::setIdentifierValue(const char *val, int len) 0231 { 0232 m_identValue = QByteArray(val, len); 0233 } 0234 0235 void MapCSSDeclaration::setStringValue(char *str) 0236 { 0237 m_stringValue = QString::fromUtf8(str); 0238 free(str); 0239 } 0240 0241 void MapCSSDeclaration::setColorRgba(uint32_t argb) 0242 { 0243 m_colorValue = QColor::fromRgba(argb); 0244 //qDebug() << m_colorValue << argb; 0245 } 0246 0247 void MapCSSDeclaration::setDashesValue(const QVector<double> &dashes) 0248 { 0249 m_dashValue = dashes; 0250 } 0251 0252 Qt::PenCapStyle MapCSSDeclaration::capStyle() const 0253 { 0254 for (const auto &c : capstyle_map) { 0255 if (std::strcmp(c.name, m_identValue.constData()) == 0) { 0256 return c.capStyle; 0257 } 0258 } 0259 qDebug() << "unknown line cap style:" << m_identValue; 0260 return Qt::FlatCap; 0261 } 0262 0263 Qt::PenJoinStyle MapCSSDeclaration::joinStyle() const 0264 { 0265 for (const auto &j : joinstyle_map) { 0266 if (std::strcmp(j.name, m_identValue.constData()) == 0) { 0267 return j.joinStyle; 0268 } 0269 } 0270 return Qt::RoundJoin; 0271 } 0272 0273 QFont::Capitalization MapCSSDeclaration::capitalizationStyle() const 0274 { 0275 for (const auto &c : capitalizationstyle_map) { 0276 if (std::strcmp(c.name, m_identValue.constData()) == 0) { 0277 return c.capitalizationStyle; 0278 } 0279 } 0280 return QFont::MixedCase; 0281 } 0282 0283 bool MapCSSDeclaration::isBoldStyle() const 0284 { 0285 return m_identValue == "bold"; 0286 } 0287 0288 bool MapCSSDeclaration::isItalicStyle() const 0289 { 0290 return m_identValue == "italic"; 0291 } 0292 0293 bool MapCSSDeclaration::isUnderlineStyle() const 0294 { 0295 return m_identValue == "underline"; 0296 } 0297 0298 MapCSSDeclaration::Position MapCSSDeclaration::textPosition() const 0299 { 0300 for (const auto &p : position_map) { 0301 if (std::strcmp(p.name, m_identValue.constData()) == 0) { 0302 return p.position; 0303 } 0304 } 0305 return Position::NoPostion; 0306 } 0307 0308 MapCSSDeclaration::Unit MapCSSDeclaration::unit() const 0309 { 0310 return m_unit; 0311 } 0312 0313 void MapCSSDeclaration::setUnit(const char *val, int len) 0314 { 0315 for (const auto &u : unit_map) { 0316 if (std::strncmp(u.name, val, std::max<std::size_t>(std::strlen(u.name), len)) == 0) { 0317 m_unit = u.unit; 0318 return; 0319 } 0320 } 0321 qCWarning(Log) << "unknown unit:" << QByteArray(val, len); 0322 m_unit = NoUnit; 0323 } 0324 0325 ClassSelectorKey MapCSSDeclaration::classSelectorKey() const 0326 { 0327 return m_class; 0328 } 0329 0330 void MapCSSDeclaration::setClassSelectorKey(ClassSelectorKey key) 0331 { 0332 m_class = key; 0333 } 0334 0335 void MapCSSDeclaration::compile(const OSM::DataSet &dataSet) 0336 { 0337 Q_UNUSED(dataSet); 0338 // TODO resolve tag key if m_identValue is one 0339 if (m_type == TagDeclaration) { 0340 // TODO handle the case that the tag isn't actually available in dataSet 0341 m_tagKey = dataSet.tagKey(m_identValue.constData()); 0342 } 0343 } 0344 0345 void MapCSSDeclaration::write(QIODevice *out) const 0346 { 0347 out->write(" "); 0348 0349 switch (m_type) { 0350 case PropertyDeclaration: 0351 for (const auto &p : property_types) { 0352 if (p.property == m_property) { 0353 out->write(p.name); 0354 break; 0355 } 0356 } 0357 0358 out->write(": "); 0359 if (!std::isnan(m_doubleValue)) { 0360 out->write(QByteArray::number(m_doubleValue)); 0361 } else if (m_colorValue.isValid()) { 0362 out->write(m_colorValue.name(QColor::HexArgb).toUtf8()); 0363 } else if (!m_dashValue.isEmpty()) { 0364 for (const auto &d : m_dashValue) { 0365 out->write(QByteArray::number(d)); 0366 out->write(", "); 0367 } 0368 } else if (!m_stringValue.isNull()) { 0369 out->write("\""); 0370 out->write(m_stringValue.toUtf8()); // this would need to be quoted... 0371 out->write("\""); 0372 } else if (!m_identValue.isEmpty()) { 0373 out->write(m_identValue); 0374 } else { 0375 out->write(m_boolValue ? "true" : "false"); 0376 } 0377 0378 for (const auto &u : unit_map) { 0379 if (u.unit == m_unit) { 0380 out->write(u.name); 0381 break; 0382 } 0383 } 0384 break; 0385 case TagDeclaration: 0386 out->write("set "); 0387 out->write(m_identValue); 0388 if (!std::isnan(m_doubleValue)) { 0389 out->write(" = "); 0390 out->write(QByteArray::number(m_doubleValue)); 0391 } else if (!m_stringValue.isEmpty()) { 0392 out->write(" = \""); 0393 out->write(m_stringValue.toUtf8()); // this would need to be quoted... 0394 out->write("\""); 0395 } 0396 break; 0397 case ClassDeclaration: 0398 out->write("set ."); 0399 out->write(m_class.name()); 0400 break; 0401 } 0402 0403 out->write(";\n"); 0404 }