File indexing completed on 2024-06-09 04:23:51

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_asl_xml_parser.h"
0008 
0009 #include <stdexcept>
0010 #include <string>
0011 
0012 #include <QBuffer>
0013 #include <QDomDocument>
0014 #include <QIODevice>
0015 #include <QFileInfo>
0016 
0017 #include <QColor>
0018 #include <QHash>
0019 
0020 #include <KoColorSpaceRegistry.h>
0021 #include <KoColorConversions.h>
0022 #include <resources/KoSegmentGradient.h>
0023 
0024 #include "kis_dom_utils.h"
0025 
0026 #include "compression.h"
0027 #include "kis_debug.h"
0028 #include "psd.h"
0029 #include "psd_utils.h"
0030 
0031 #include "kis_asl_object_catcher.h"
0032 
0033 namespace Private
0034 {
0035 void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher);
0036 
0037 class CurveObjectCatcher : public KisAslObjectCatcher
0038 {
0039 public:
0040     void addText(const QString &path, const QString &value) override
0041     {
0042         if (path == "/Nm  ") {
0043             m_name = value;
0044         } else {
0045             warnKrita << "XML (ASL): failed to parse curve object" << path << value;
0046         }
0047     }
0048 
0049     void addPoint(const QString &path, const QPointF &value) override
0050     {
0051         if (!m_arrayMode) {
0052             warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode);
0053         }
0054 
0055         m_points.append(value);
0056     }
0057 
0058 public:
0059     QVector<QPointF> m_points;
0060     QString m_name;
0061 };
0062 
0063 KoColor parseColorObject(QDomElement parent, QString classID)
0064 {
0065     KoColor color;
0066     KoColor error = KoColor::fromXML("<color channeldepth='U8'><sRGB r='1.0' g='0.0' b='0.0'/></color>");
0067     QDomDocument doc;
0068     QDomElement root;
0069     QString spotBook;
0070     QString spotName;
0071     int spotValue = 0;
0072     double h = 0;
0073     double s = 0;
0074     double v= 0;
0075 
0076     if (classID == "RGBC" || classID == "HSBC") {
0077         color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
0078         root = doc.createElement("sRGB");
0079     } else if (classID == "CMYC") {
0080         root = doc.createElement("CMYK");
0081     } else if (classID == "LbCl") {
0082         root = doc.createElement("Lab");
0083     } else if (classID == "Grsc") {
0084         root = doc.createElement("Gray");
0085     } else {
0086         // Can be 'UnsC', or something else.
0087         warnKrita << "Unknown color type:" << ppVar(classID);
0088         return error;
0089     }
0090 
0091     QDomNode child = parent.firstChild();
0092     while (!child.isNull()) {
0093         QDomElement childEl = child.toElement();
0094 
0095         QString type = childEl.attribute("type", "<unknown>");
0096         QString key = childEl.attribute("key", "");
0097 
0098         if (type == "Double" || type == "UnitFloat") {
0099             if (classID == "RGBC") {
0100                 // For RGBC we'll just directly write to the KoColor data, to have as
0101                 // few rounding errors possible.
0102                 double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
0103 
0104                 if (key == "Rd  ") {
0105                     color.data()[2] = value;
0106                 } else if (key == "Grn ") {
0107                     color.data()[1] = value;
0108                 } else if (key == "Bl  ") {
0109                     color.data()[0] = value;
0110                 } else {
0111                     warnKrita << "Unknown color key value double:" << ppVar(key);
0112                     return error;
0113                 }
0114             } else if (classID == "CMYC") {
0115                 double value = KisDomUtils::toDouble(childEl.attribute("value", "0")) * 0.01;
0116                 // CMYK is stored in percentages...
0117                 if (key == "Cyn ") {
0118                     root.setAttribute("c", value);
0119                 } else if (key == "Mgnt") {
0120                     root.setAttribute("m", value);
0121                 } else if (key == "Ylw ") {
0122                     root.setAttribute("y", value);
0123                 } else if (key == "Blck") {
0124                     root.setAttribute("k", value);
0125                 } else {
0126                     warnKrita << "Unknown color key value double:" << ppVar(key);
0127                     return error;
0128                 }
0129             } else if (classID == "LbCl") {
0130                 if (key == "Lmnc") {
0131                     root.setAttribute("L", childEl.attribute("value", "0"));
0132                 } else if (key == "A   ") {
0133                     root.setAttribute("a", childEl.attribute("value", "0"));
0134                 } else if (key == "B   ") {
0135                     root.setAttribute("b", childEl.attribute("value", "0"));
0136                 } else {
0137                     warnKrita << "Unknown color key value:" << ppVar(key);
0138                     return error;
0139                 }
0140             } else if (classID == "Grsc") {
0141                 // Unsure that grey is stored as a percentage, might also be 255.
0142                 double value = KisDomUtils::toDouble(childEl.attribute("value", "0")) * 0.01;
0143                 if (key == "Gry ") {
0144                     root.setAttribute("g", value);
0145                 } else {
0146                     warnKrita << "Unknown color key value:" << ppVar(key);
0147                     return error;
0148                 }
0149             } else if (classID == "HSBC") {
0150                 double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
0151                 if (key == "H   ") {
0152                     h = value;
0153                 } else if (key == "Strt") {
0154                     s = value * 0.01;
0155                 } else if (key == "Brgh") {
0156                     v = value * 0.01;
0157                 } else {
0158                     warnKrita << "Unknown color key value:" << ppVar(key);
0159                     return error;
0160                 }
0161             }
0162         } else if (type == "Text") {
0163             if (key== "Bk  ") {
0164                 spotBook = childEl.attribute("value", "");
0165             } else if (key== "Nm  ") {
0166                 spotName = childEl.attribute("value", "");
0167             } else {
0168                 warnKrita << "Unknown color key value string:" << ppVar(key);
0169             }
0170         } else if (type == "Integer") {
0171             if (key== "bookID") {
0172                 spotValue = KisDomUtils::toInt(childEl.attribute("value", "0"));
0173             } else {
0174                 warnKrita << "Unknown color key value integer:" << ppVar(key);
0175             }
0176         } else {
0177             qDebug() << "Unknown color component type:" << ppVar(type) << ppVar(key);
0178             return error;
0179         }
0180 
0181         child = child.nextSibling();
0182     }
0183     if (classID == "HSBC") {
0184         float r = 0.0;
0185         float b = 0.0;
0186         float g = 0.0;
0187         HSVToRGB(h, s, v, &r, &g, &b);
0188         root.setAttribute("r", r);
0189         root.setAttribute("g", g);
0190         root.setAttribute("b", b);
0191     }
0192     if (classID != "RGBC") {
0193         color = KoColor::fromXML(root, "U8");
0194     }
0195     color.setOpacity(OPACITY_OPAQUE_U8);
0196     if (!spotName.isEmpty()) {
0197         color.addMetadata("spotName", spotName);
0198         color.addMetadata("psdSpotBook", spotBook);
0199         color.addMetadata("psdSpotBookId", spotValue);
0200     }
0201 
0202     return color;
0203 }
0204 
0205 void parseColorStopsList(QDomElement parent,
0206                          QVector<qreal> &startLocations,
0207                          QVector<qreal> &middleOffsets,
0208                          QVector<KoColor> &colors,
0209                          QVector<KoGradientSegmentEndpointType> &types)
0210 {
0211     QDomNode child = parent.firstChild();
0212     while (!child.isNull()) {
0213         QDomElement childEl = child.toElement();
0214 
0215         QString type = childEl.attribute("type", "<unknown>");
0216         QString key = childEl.attribute("key", "");
0217         QString classId = childEl.attribute("classId", "");
0218 
0219         if (type == "Descriptor" && classId == "Clrt") {
0220             // sorry for naming...
0221             QDomNode child = childEl.firstChild();
0222             while (!child.isNull()) {
0223                 QDomElement childEl = child.toElement();
0224 
0225                 QString type = childEl.attribute("type", "<unknown>");
0226                 QString key = childEl.attribute("key", "");
0227                 QString classId = childEl.attribute("classId", "");
0228 
0229                 if (type == "Integer" && key == "Lctn") {
0230                     int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
0231                     startLocations.append(qreal(value) / 4096.0);
0232 
0233                 } else if (type == "Integer" && key == "Mdpn") {
0234                     int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
0235                     middleOffsets.append(qreal(value) / 100.0);
0236 
0237                 } else if (type == "Descriptor" && key == "Clr ") {
0238                     colors.append(parseColorObject(childEl, classId));
0239 
0240                 } else if (type == "Enum" && key == "Type") {
0241                     QString typeId = childEl.attribute("typeId", "");
0242 
0243                     if (typeId != "Clry") {
0244                         warnKrita << "WARNING: Invalid typeId of a gradient stop type" << typeId;
0245                     }
0246 
0247                     QString value = childEl.attribute("value", "");
0248                     if (value == "BckC") {
0249                         types.append(BACKGROUND_ENDPOINT);
0250                     } else if (value == "FrgC") {
0251                         types.append(FOREGROUND_ENDPOINT);
0252                     } else {
0253                         types.append(COLOR_ENDPOINT);
0254                     }
0255                 }
0256 
0257                 child = child.nextSibling();
0258             }
0259         } else {
0260             warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
0261         }
0262 
0263         child = child.nextSibling();
0264     }
0265 }
0266 
0267 void parseTransparencyStopsList(QDomElement parent, QVector<qreal> &startLocations, QVector<qreal> &middleOffsets, QVector<qreal> &transparencies)
0268 {
0269     QDomNode child = parent.firstChild();
0270     while (!child.isNull()) {
0271         QDomElement childEl = child.toElement();
0272 
0273         QString type = childEl.attribute("type", "<unknown>");
0274         QString key = childEl.attribute("key", "");
0275         QString classId = childEl.attribute("classId", "");
0276 
0277         if (type == "Descriptor" && classId == "TrnS") {
0278             // sorry for naming again...
0279             QDomNode child = childEl.firstChild();
0280             while (!child.isNull()) {
0281                 QDomElement childEl = child.toElement();
0282 
0283                 QString type = childEl.attribute("type", "<unknown>");
0284                 QString key = childEl.attribute("key", "");
0285 
0286                 if (type == "Integer" && key == "Lctn") {
0287                     int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
0288                     startLocations.append(qreal(value) / 4096.0);
0289                 } else if (type == "Integer" && key == "Mdpn") {
0290                     int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
0291                     middleOffsets.append(qreal(value) / 100.0);
0292                 } else if (type == "UnitFloat" && key == "Opct") {
0293                     QString unit = childEl.attribute("unit", "");
0294                     if (unit != "#Prc") {
0295                         warnKrita << "WARNING: Invalid unit of a gradient stop transparency" << unit;
0296                     }
0297 
0298                     qreal value = KisDomUtils::toDouble(childEl.attribute("value", "100"));
0299                     transparencies.append(value / 100.0);
0300                 }
0301 
0302                 child = child.nextSibling();
0303             }
0304 
0305         } else {
0306             warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
0307         }
0308 
0309         child = child.nextSibling();
0310     }
0311 }
0312 
0313 inline QString buildPath(const QString &parent, const QString &key)
0314 {
0315     return parent + "/" + key;
0316 }
0317 
0318 bool tryParseDescriptor(const QDomElement &el, const QString &path, const QString &classId, KisAslObjectCatcher &catcher)
0319 {
0320     bool retval = true;
0321 
0322     if (classId == "null") {
0323         catcher.newStyleStarted();
0324         // here we just notify that a new style is started, we haven't
0325         // processed the whole block yet, so return false.
0326         retval = false;
0327     } else if (el.attribute("key", " ") == "Clr ") {
0328         catcher.addColor(path, parseColorObject(el, classId));
0329     } else if (el.attribute("key", " ") == "hglC" || el.attribute("key", " ") == "sdwC") {
0330         // like Clr, but /ebbl/ likes to do everything differently - see bug 464218
0331         catcher.addColor(path, parseColorObject(el, classId));
0332     } else if (classId == "ShpC") {
0333         CurveObjectCatcher curveCatcher;
0334 
0335         QDomNode child = el.firstChild();
0336         while (!child.isNull()) {
0337             parseElement(child.toElement(), "", curveCatcher);
0338             child = child.nextSibling();
0339         }
0340 
0341         catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points);
0342 
0343     } else if (classId == "CrPt") {
0344         QPointF point;
0345 
0346         QDomNode child = el.firstChild();
0347         while (!child.isNull()) {
0348             QDomElement childEl = child.toElement();
0349 
0350             QString type = childEl.attribute("type", "<unknown>");
0351             QString key = childEl.attribute("key", "");
0352 
0353             if (type == "Boolean" && key == "Cnty") {
0354                 warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita";
0355                 warnKrita << "        " << ppVar(type) << ppVar(key) << ppVar(path);
0356 
0357                 child = child.nextSibling();
0358                 continue;
0359             }
0360 
0361             if (type != "Double") {
0362                 warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path);
0363                 return false;
0364             }
0365 
0366             double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
0367 
0368             if (key == "Hrzn") {
0369                 point.setX(value);
0370             } else if (key == "Vrtc") {
0371                 point.setY(value);
0372             } else {
0373                 warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
0374                 return false;
0375             }
0376 
0377             child = child.nextSibling();
0378         }
0379 
0380         catcher.addPoint(path, point);
0381 
0382     } else if (classId == "Pnt ") {
0383         QPointF point;
0384 
0385         QDomNode child = el.firstChild();
0386         while (!child.isNull()) {
0387             QDomElement childEl = child.toElement();
0388 
0389             QString type = childEl.attribute("type", "<unknown>");
0390             QString key = childEl.attribute("key", "");
0391             QString unit = childEl.attribute("unit", "");
0392 
0393             if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) {
0394                 warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path);
0395                 return false;
0396             }
0397 
0398             double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
0399 
0400             if (key == "Hrzn") {
0401                 point.setX(value);
0402             } else if (key == "Vrtc") {
0403                 point.setY(value);
0404             } else {
0405                 warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
0406                 return false;
0407             }
0408 
0409             child = child.nextSibling();
0410         }
0411 
0412         catcher.addPoint(path, point);
0413 
0414     } else if (classId == "KisPattern") {
0415         QByteArray patternData;
0416         QString patternUuid;
0417 
0418         QDomNode child = el.firstChild();
0419         while (!child.isNull()) {
0420             QDomElement childEl = child.toElement();
0421 
0422             QString type = childEl.attribute("type", "<unknown>");
0423             QString key = childEl.attribute("key", "");
0424 
0425             if (type == "Text" && key == "Idnt") {
0426                 patternUuid = childEl.attribute("value", "").trimmed();
0427             }
0428 
0429             if (type == "KisPatternData" && key == "Data") {
0430                 QDomNode dataNode = child.firstChild();
0431 
0432                 if (!dataNode.isCDATASection()) {
0433                     warnKrita << "WARNING: failed to parse KisPatternData XML section!";
0434                     continue;
0435                 }
0436 
0437                 QDomCDATASection dataSection = dataNode.toCDATASection();
0438                 QByteArray data = dataSection.data().toLatin1();
0439                 data = QByteArray::fromBase64(data);
0440                 data = qUncompress(data);
0441 
0442                 if (data.isEmpty()) {
0443                     warnKrita << "WARNING: failed to parse KisPatternData XML section!";
0444                     continue;
0445                 }
0446 
0447                 patternData = data;
0448             }
0449 
0450             child = child.nextSibling();
0451         }
0452 
0453         if (!patternUuid.isEmpty() && !patternData.isEmpty()) {
0454             QString fileName = QString("%1.pat").arg(patternUuid);
0455 
0456             QSharedPointer<KoPattern> pattern(new KoPattern(fileName));
0457 
0458             QBuffer buffer(&patternData);
0459             buffer.open(QIODevice::ReadOnly);
0460 
0461             if (pattern->loadPatFromDevice(&buffer) && pattern->valid()) {
0462                 catcher.addPattern(path, pattern, patternUuid);
0463             }
0464             else {
0465                 warnKrita << "WARNING: failed to create pattern:" << ppVar(patternUuid) << ppVar(pattern);
0466             }
0467         } else {
0468             warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid);
0469         }
0470 
0471     } else if (classId == "Ptrn") { // reference to an existing pattern
0472         QString patternUuid;
0473         QString patternName;
0474 
0475         QDomNode child = el.firstChild();
0476         while (!child.isNull()) {
0477             QDomElement childEl = child.toElement();
0478 
0479             QString type = childEl.attribute("type", "<unknown>");
0480             QString key = childEl.attribute("key", "");
0481 
0482             if (type == "Text" && key == "Idnt") {
0483                 patternUuid = childEl.attribute("value", "");
0484             } else if (type == "Text" && key == "Nm  ") {
0485                 patternName = childEl.attribute("value", "");
0486             } else {
0487                 warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key);
0488             }
0489 
0490             child = child.nextSibling();
0491         }
0492 
0493         catcher.addPatternRef(path, patternUuid, patternName);
0494 
0495     } else if (classId == "Grdn") {
0496         QString gradientName;
0497         qreal gradientSmoothness = 100.0;
0498 
0499         QVector<qreal> startLocations;
0500         QVector<qreal> middleOffsets;
0501         QVector<KoColor> colors;
0502         QVector<KoGradientSegmentEndpointType> types;
0503 
0504         QVector<qreal> transpStartLocations;
0505         QVector<qreal> transpMiddleOffsets;
0506         QVector<qreal> transparencies;
0507 
0508         QDomNode child = el.firstChild();
0509         while (!child.isNull()) {
0510             QDomElement childEl = child.toElement();
0511 
0512             QString type = childEl.attribute("type", "<unknown>");
0513             QString key = childEl.attribute("key", "");
0514 
0515             if (type == "Text" && key == "Nm  ") {
0516                 gradientName = childEl.attribute("value", "");
0517             } else if (type == "Enum" && key == "GrdF") {
0518                 QString typeId = childEl.attribute("typeId", "");
0519                 QString value = childEl.attribute("value", "");
0520 
0521                 if (typeId != "GrdF" || value != "CstS") {
0522                     warnKrita << "WARNING: Unsupported gradient type (probably, noise-based):" << value;
0523                     return true;
0524                 }
0525             } else if (type == "Double" && key == "Intr") {
0526                 double value = KisDomUtils::toDouble(childEl.attribute("value", "4096"));
0527                 gradientSmoothness = 100.0 * value / 4096.0;
0528             } else if (type == "List" && key == "Clrs") {
0529                 parseColorStopsList(childEl, startLocations, middleOffsets, colors, types);
0530             } else if (type == "List" && key == "Trns") {
0531                 parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies);
0532             }
0533 
0534             child = child.nextSibling();
0535         }
0536 
0537         if (colors.size() < transparencies.size()) {
0538             const KoColor lastColor = !colors.isEmpty() ? colors.last() : KoColor();
0539             const KoGradientSegmentEndpointType lastType = !types.isEmpty() ? types.last() : COLOR_ENDPOINT;
0540             while (colors.size() != transparencies.size()) {
0541                 const int index = colors.size();
0542                 colors.append(lastColor);
0543                 startLocations.append(transpStartLocations[index]);
0544                 middleOffsets.append(transpMiddleOffsets[index]);
0545                 types.append(lastType);
0546             }
0547         }
0548 
0549         if (colors.size() > transparencies.size()) {
0550             const qreal lastTransparency = !transparencies.isEmpty() ? transparencies.last() : 1.0;
0551             while (colors.size() != transparencies.size()) {
0552                 const int index = transparencies.size();
0553                 transparencies.append(lastTransparency);
0554                 transpStartLocations.append(startLocations[index]);
0555                 transpMiddleOffsets.append(middleOffsets[index]);
0556             }
0557         }
0558 
0559         if (colors.size() == 1) {
0560             colors.append(colors.last());
0561             startLocations.append(1.0);
0562             middleOffsets.append(0.5);
0563             types.append(COLOR_ENDPOINT);
0564 
0565             transparencies.append(transparencies.last());
0566             transpStartLocations.append(1.0);
0567             transpMiddleOffsets.append(0.5);
0568         }
0569 
0570         /**
0571          * Filenames in Krita cannot have slashes inside, but some of the
0572          * styles saved in 4.x days could have that. Here we just forcefully
0573          * crop the directory part of the gradient to make sure that it fits
0574          * the new policy.
0575          *
0576          * Since ASL doesn't use this name as any linkage (actually, gradients
0577          * are always embedded into the style) so we don't really care about
0578          * the contents of the filename field. It should just be somewhat unique.
0579          */
0580         const QString fileName = QFileInfo(gradientName).fileName() + ".ggr";
0581         QSharedPointer<KoSegmentGradient> gradient(new KoSegmentGradient(fileName));
0582         Q_UNUSED(gradientSmoothness);
0583         gradient->setName(gradientName);
0584 
0585         if (colors.size() >= 2) {
0586             for (int i = 1; i < colors.size(); i++) {
0587                 KoColor startColor = colors[i - 1];
0588                 KoColor endColor = colors[i];
0589                 startColor.setOpacity(transparencies[i - 1]);
0590                 endColor.setOpacity(transparencies[i]);
0591 
0592                 qreal start = startLocations[i - 1];
0593                 qreal end = startLocations[i];
0594                 qreal middle = start + middleOffsets[i - 1] * (end - start);
0595 
0596                 KoGradientSegmentEndpointType startType = types[i - 1];
0597                 KoGradientSegmentEndpointType endType = types[i];
0598 
0599                 gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, start, end, middle, startColor, endColor, startType, endType);
0600             }
0601             gradient->setValid(true);
0602             gradient->updatePreview();
0603         } else {
0604             gradient->setValid(false);
0605         }
0606 
0607         catcher.addGradient(path, gradient);
0608     } else {
0609         retval = false;
0610     }
0611 
0612     return retval;
0613 }
0614 
0615 void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher)
0616 {
0617     KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
0618 
0619     QString type = el.attribute("type", "<unknown>");
0620     QString key = el.attribute("key", "");
0621 
0622     if (type == "Descriptor") {
0623         QString classId = el.attribute("classId", "<noClassId>");
0624         QString containerName = key.isEmpty() ? classId : key;
0625         QString containerPath = buildPath(parentPath, containerName);
0626 
0627         if (!tryParseDescriptor(el, containerPath, classId, catcher)) {
0628             QDomNode child = el.firstChild();
0629             while (!child.isNull()) {
0630                 parseElement(child.toElement(), containerPath, catcher);
0631                 child = child.nextSibling();
0632             }
0633         }
0634     } else if (type == "List") {
0635         catcher.setArrayMode(true);
0636 
0637         QString containerName = key;
0638         QString containerPath = buildPath(parentPath, containerName);
0639 
0640         QDomNode child = el.firstChild();
0641         while (!child.isNull()) {
0642             parseElement(child.toElement(), containerPath, catcher);
0643             child = child.nextSibling();
0644         }
0645 
0646         catcher.setArrayMode(false);
0647     } else if (type == "Double") {
0648         double v = KisDomUtils::toDouble(el.attribute("value", "0"));
0649         catcher.addDouble(buildPath(parentPath, key), v);
0650     } else if (type == "UnitFloat") {
0651         QString unit = el.attribute("unit", "<unknown>");
0652         double v = KisDomUtils::toDouble(el.attribute("value", "0"));
0653         catcher.addUnitFloat(buildPath(parentPath, key), unit, v);
0654     } else if (type == "Text") {
0655         QString v = el.attribute("value", "");
0656         catcher.addText(buildPath(parentPath, key), v);
0657     } else if (type == "Enum") {
0658         QString v = el.attribute("value", "");
0659         QString typeId = el.attribute("typeId", "<unknown>");
0660         catcher.addEnum(buildPath(parentPath, key), typeId, v);
0661     } else if (type == "Integer") {
0662         int v = KisDomUtils::toInt(el.attribute("value", "0"));
0663         catcher.addInteger(buildPath(parentPath, key), v);
0664     } else if (type == "Boolean") {
0665         int v = KisDomUtils::toInt(el.attribute("value", "0"));
0666         catcher.addBoolean(buildPath(parentPath, key), v);
0667     } else {
0668         warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key);
0669     }
0670 }
0671 
0672 } // namespace
0673 
0674 void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher)
0675 {
0676     QDomElement root = doc.documentElement();
0677     if (root.tagName() != "asl") {
0678         return;
0679     }
0680 
0681     QDomNode child = root.firstChild();
0682     while (!child.isNull()) {
0683         Private::parseElement(child.toElement(), "", catcher);
0684         child = child.nextSibling();
0685     }
0686 }