File indexing completed on 2025-01-05 03:59:23

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2017 Sergey Popov <sergobot@protonmail.com>
0004 //
0005 
0006 #include "OsmcSymbol.h"
0007 
0008 #include <QDomDocument>
0009 #include <QFile>
0010 #include <QPainter>
0011 
0012 #include "digikam_debug.h"
0013 
0014 OsmcSymbol::OsmcSymbol(const QString &tag, int size)
0015     : m_wayColor(Qt::white)
0016     , m_backgroundColor(Qt::black)
0017     , m_foreground(nullptr)
0018     , m_foreground2(nullptr)
0019     , m_textColor(Qt::black)
0020     , m_side(size)
0021 {
0022     m_backgroundTypes
0023             << QString::fromUtf8("round") << QString::fromUtf8("circle") << QString::fromUtf8("frame");
0024 
0025     m_foregroundTypes
0026             << QString::fromUtf8("dot") << QString::fromUtf8("bowl") << QString::fromUtf8("circle") << QString::fromUtf8("bar")
0027             << QString::fromUtf8("stripe") << QString::fromUtf8("cross") << QString::fromUtf8("x") << QString::fromUtf8("slash")
0028             << QString::fromUtf8("backslash") << QString::fromUtf8("rectangle") << QString::fromUtf8("rectangle_line")
0029             << QString::fromUtf8("triangle") << QString::fromUtf8("triangle_turned") << QString::fromUtf8("triangle_line")
0030             << QString::fromUtf8("diamond") << QString::fromUtf8("pointer") << QString::fromUtf8("fork") << QString::fromUtf8("arch")
0031             << QString::fromUtf8("turned_T") << QString::fromUtf8("L") << QString::fromUtf8("lower") << QString::fromUtf8("corner")
0032             << QString::fromUtf8("drop_line") << QString::fromUtf8("horse") << QString::fromUtf8("hiker");
0033 
0034     m_precoloredForegroundTypes
0035             << QString::fromUtf8("wolfshook") << QString::fromUtf8("shell") << QString::fromUtf8("shell_modern") << QString::fromUtf8("ammonit")
0036             << QString::fromUtf8("mine") << QString::fromUtf8("hiker") << QString::fromUtf8("heart") << QString::fromUtf8("tower") << QString::fromUtf8("bridleway");
0037 
0038     if (parseTag(tag)) {
0039         render();
0040     }
0041 }
0042 
0043 OsmcSymbol::~OsmcSymbol()
0044 {
0045     delete m_foreground;
0046     delete m_foreground2;
0047 }
0048 
0049 bool OsmcSymbol::parseTag(const QString &tag)
0050 {
0051     QStringList parts = tag.split(QLatin1Char(':'));
0052 
0053     if (parts.size() < 2) {
0054         return false;
0055     }
0056 
0057     if (m_foreground) {
0058         delete m_foreground;
0059         m_foreground = nullptr;
0060     }
0061     if (m_foreground2) {
0062         delete m_foreground2;
0063         m_foreground2 = nullptr;
0064     }
0065 
0066     // Determine way color
0067     if (QColor::isValidColor(parts.at(0))) {
0068         m_wayColor.setNamedColor(parts.at(0));
0069     } else {
0070         return false;
0071     }
0072 
0073     if (!parseBackground(parts.at(1))) {
0074         return false;
0075     }
0076 
0077     if (parts.size() == 3) {
0078         m_foreground = parseForeground(parts.at(2));
0079     } else if (parts.size() == 4) {
0080         if (QColor::isValidColor(parts.at(3))) {
0081             m_text = parts.at(2);
0082             m_textColor = parts.at(3);
0083         } else {
0084             m_foreground = parseForeground(parts.at(2));
0085             m_foreground2 = parseForeground(parts.at(3));
0086         }
0087     } else if (parts.size() == 5) {
0088         m_foreground = parseForeground(parts.at(2));
0089         if (QColor::isValidColor(parts.at(4))) {
0090             m_text = parts.at(3);
0091             m_textColor = parts.at(4);
0092         } else {
0093             return false;
0094         }
0095     } else if (parts.size() == 6) {
0096         m_foreground = parseForeground(parts.at(2));
0097         m_foreground2 = parseForeground(parts.at(3));
0098         if (QColor::isValidColor(parts.at(5))) {
0099             m_text = parts.at(4);
0100             m_textColor.setNamedColor(parts.at(5));
0101         } else {
0102             return false;
0103         }
0104     } else {
0105         return false;
0106     }
0107 
0108     return true;
0109 }
0110 
0111 bool OsmcSymbol::parseBackground(const QString &bg)
0112 {
0113     QString color = bg.section(QString::fromUtf8("_"), 0, 0);
0114     QString type = bg.section(QString::fromUtf8("_"), 1, -1);
0115 
0116     if (!QColor::isValidColor(color)) {
0117         return false;
0118     }
0119 
0120     // Plain color was provided
0121     if (type.isEmpty()) {
0122         m_backgroundColor.setNamedColor(color);
0123         m_backgroundType = type;
0124     } else if (m_backgroundTypes.contains(type)) {
0125         m_backgroundColor.setNamedColor(color);
0126         m_backgroundType = type;
0127     } else {
0128         return false;
0129     }
0130 
0131     return true;
0132 }
0133 
0134 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue);
0135 
0136 QSvgRenderer* OsmcSymbol::parseForeground(const QString &fg)
0137 {
0138     if (m_precoloredForegroundTypes.contains(fg)) {
0139         return new QSvgRenderer(QString::fromUtf8(":/osmc-symbols/%1.svg").arg(fg));
0140     }
0141 
0142     QString color = fg.section(QLatin1Char('_'), 0, 0);
0143     QString type = fg.section(QLatin1Char('_'), 1, -1);
0144     if (QColor::isValidColor(color) && m_foregroundTypes.contains(type)) {
0145         // Open svg resource and load contents to QByteArray
0146         QFile file(QString::fromUtf8(":/osmc-symbols/%1.svg").arg(type));
0147         file.open(QIODevice::ReadOnly);
0148         QByteArray baData = file.readAll();
0149 
0150         // Load svg contents to xml document
0151         QDomDocument doc;
0152         doc.setContent(baData);
0153 
0154         // Recursively change color
0155         QDomElement rootElement = doc.documentElement();
0156         setXMLAttribute(rootElement, QString::fromUtf8("path"), QString::fromUtf8("fill"), color);
0157 
0158         // Create and return svg renderer with edited contents
0159         return new QSvgRenderer(doc.toByteArray());
0160     }
0161 
0162     return nullptr;
0163 }
0164 
0165 void OsmcSymbol::render()
0166 {
0167     m_image = QImage(m_side, m_side, QImage::Format_ARGB32);
0168     m_image.fill(Qt::transparent);
0169 
0170     QPainter painter(&m_image);
0171     painter.setRenderHint(QPainter::Antialiasing);
0172 
0173     // Default size of background
0174     int w = m_side, h = m_side;
0175 
0176     // If there is some text, our background size must be recalculated
0177     if (!m_text.isEmpty()) {
0178         QFont font = painter.font();
0179         font.setPixelSize(int(m_side * 0.8));
0180         font.setBold(true);
0181         painter.setFont(font);
0182         QFontMetrics fm = QFontMetrics(font);
0183 
0184         h = fm.height();
0185         w = qMax(h, fm.horizontalAdvance(m_text));
0186     }
0187 
0188     const QRect bgRect = QRect((m_side - w) / 2, (m_side - h) / 2, w, h);
0189 
0190     // Draw symbol's background
0191     if (m_backgroundType.isEmpty()) {
0192         painter.fillRect(bgRect, m_backgroundColor);
0193     } else if (m_backgroundType == QString::fromUtf8("round")) {
0194         painter.setBrush(m_backgroundColor);
0195         painter.setPen(m_backgroundColor);
0196         painter.drawEllipse(bgRect);
0197     } else if (m_backgroundType == QString::fromUtf8("circle")) {
0198         painter.setBrush(Qt::white);
0199         painter.setPen(QPen(m_backgroundColor, m_side / 10));
0200         painter.drawEllipse(bgRect);
0201     } else if (m_backgroundType == QString::fromUtf8("frame")) {
0202         painter.setPen(QPen(m_backgroundColor, m_side / 10));
0203         painter.fillRect(bgRect, Qt::white);
0204         painter.drawRect(bgRect);
0205     }
0206 
0207     QPixmap foregrounds(bgRect.size());
0208     foregrounds.fill(Qt::transparent);
0209     QPainter fgPainter(&foregrounds);
0210     m_foreground ? m_foreground->render(&fgPainter) : void();
0211     m_foreground2 ? m_foreground2->render(&fgPainter) : void();
0212     painter.drawPixmap(bgRect, foregrounds);
0213 
0214     if (!m_text.isEmpty()) {
0215         // Draw text with provided color
0216         painter.setPen(m_textColor);
0217         painter.drawText(bgRect, Qt::AlignCenter, m_text);
0218     }
0219 
0220     painter.end();
0221 }
0222 
0223 QImage OsmcSymbol::icon() const
0224 {
0225     return m_image;
0226 }
0227 
0228 QColor OsmcSymbol::wayColor() const
0229 {
0230     return m_wayColor;
0231 }
0232 
0233 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue)
0234 {
0235     // If elem's tag is equal to the provided one then overwrite desired attribute
0236     if (elem.tagName() == tag) {
0237         elem.setAttribute(attr, attrValue);
0238     }
0239 
0240     // Do the same for all the child nodes
0241     for (int i = 0; i < elem.childNodes().count(); ++i) {
0242         if (!elem.childNodes().at(i).isElement()) {
0243             continue;
0244         }
0245 
0246         QDomElement child = elem.childNodes().at(i).toElement();
0247         setXMLAttribute(child, tag, attr, attrValue);
0248     }
0249 }