File indexing completed on 2024-05-19 04:26:58

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 #include "VectorLayer.h"
0007 #include <kis_shape_layer.h>
0008 #include <kis_image.h>
0009 #include <SvgWriter.h>
0010 #include <SvgParser.h>
0011 #include <QBuffer>
0012 #include <commands/KoShapeCreateCommand.h>
0013 #include <commands/KoShapeGroupCommand.h>
0014 #include <KoShapeGroup.h>
0015 #include <KisDocument.h>
0016 #include <kis_processing_applicator.h>
0017 #include <kis_group_layer.h>
0018 
0019 #include "Krita.h"
0020 #include "GroupShape.h"
0021 #include "LibKisUtils.h"
0022 
0023 
0024 VectorLayer::VectorLayer(KoShapeControllerBase* shapeController, KisImageSP image, QString name, QObject *parent) :
0025     Node(image, new KisShapeLayer(shapeController, image, name, OPACITY_OPAQUE_U8), parent)
0026 {
0027 
0028 }
0029 
0030 VectorLayer::VectorLayer(KisShapeLayerSP layer, QObject *parent):
0031     Node(layer->image(), layer, parent)
0032 {
0033 
0034 }
0035 
0036 VectorLayer::~VectorLayer()
0037 {
0038 
0039 }
0040 
0041 QString VectorLayer::type() const
0042 {
0043     return "vectorlayer";
0044 }
0045 
0046 QList<Shape *> VectorLayer::shapes() const
0047 {
0048     QList<Shape*> shapes;
0049     KisShapeLayerSP vector = KisShapeLayerSP(dynamic_cast<KisShapeLayer*>(this->node().data()));
0050     if (vector) {
0051         QList<KoShape*> originalShapes = vector->shapes();
0052         std::sort(originalShapes.begin(), originalShapes.end(), KoShape::compareShapeZIndex);
0053         for (int i=0; i<vector->shapeCount(); i++) {
0054             if (dynamic_cast<KoShapeGroup*>(originalShapes.at(i))) {
0055                 shapes << new GroupShape(dynamic_cast<KoShapeGroup*>(originalShapes.at(i)));
0056             } else {
0057                 shapes << new Shape(originalShapes.at(i));
0058             }
0059         }
0060     }
0061     return shapes;
0062 }
0063 
0064 QString VectorLayer::toSvg()
0065 {
0066     QString svgData;
0067     KisShapeLayerSP vector = KisShapeLayerSP(dynamic_cast<KisShapeLayer*>(this->node().data()));
0068 
0069     if (vector) {
0070         QBuffer buffer;
0071         QList<KoShape*> originalShapes = vector->shapes();
0072 
0073         std::sort(originalShapes.begin(), originalShapes.end(), KoShape::compareShapeZIndex);
0074 
0075         const QSizeF sizeInPx = this->node()->image()->bounds().size();
0076         const QSizeF pageSize(sizeInPx.width() / this->node()->image()->xRes(),
0077                           sizeInPx.height() / this->node()->image()->yRes());
0078 
0079         buffer.open(QIODevice::WriteOnly);
0080 
0081         SvgWriter writer(originalShapes);
0082 
0083         writer.save(buffer, pageSize);
0084         buffer.close();
0085 
0086         svgData = QString::fromUtf8(buffer.data());
0087     }
0088 
0089     return svgData;
0090 
0091 }
0092 
0093 QList<Shape *> VectorLayer::addShapesFromSvg(const QString &svgData)
0094 {
0095     QList<Shape*> shapes;
0096     QList<KoShape*> originalShapes;
0097 
0098     if (svgData.isEmpty() || !svgData.contains("<svg") ) {
0099         return shapes;
0100     }
0101 
0102     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(this->node().data());
0103 
0104     if (container) {
0105         QSizeF fragmentSize;
0106         QString errorMsg;
0107         int errorLine = 0;
0108         int errorColumn = 0;
0109 
0110         QDomDocument dom = SvgParser::createDocumentFromSvg(svgData, &errorMsg, &errorLine, &errorColumn);
0111 
0112         if (dom.isNull()) {
0113             qWarning() << "Failed to process an SVG string at"
0114                        << errorLine << ":" << errorColumn << "->" << errorMsg;
0115             return shapes;
0116         }
0117 
0118         Document *document = Krita::instance()->activeDocument();
0119 
0120         if (!document) {
0121             document = LibKisUtils::findNodeInDocuments(this->node());
0122             if (!document) {
0123                 return shapes;
0124             }
0125         }
0126 
0127         SvgParser parser(document->document()->shapeController()->resourceManager());
0128 
0129         parser.setResolution(this->node()->image()->bounds(), this->node()->image()->xRes() * 72.0);
0130 
0131         originalShapes = parser.parseSvg(dom.documentElement(), &fragmentSize);
0132 
0133         KUndo2Command *cmd = new KoShapeCreateCommand(document->document()->shapeController(), originalShapes, container);
0134 
0135         KisProcessingApplicator::runSingleCommandStroke(this->node()->image(), cmd);
0136         this->node()->image()->waitForDone();
0137         delete document;
0138 
0139         std::sort(originalShapes.begin(), originalShapes.end(), KoShape::compareShapeZIndex);
0140         for (int i=0; i<originalShapes.size(); i++) {
0141             if (dynamic_cast<KoShapeGroup*>(originalShapes.at(i))) {
0142                 shapes << new GroupShape(dynamic_cast<KoShapeGroup*>(originalShapes.at(i)));
0143             } else {
0144                 shapes << new Shape(originalShapes.at(i));
0145             }
0146         }
0147 
0148     }
0149 
0150     return shapes;
0151 }
0152 
0153 Shape* VectorLayer::shapeAtPosition(const QPointF &position) const
0154 {
0155     KisShapeLayerSP vector = KisShapeLayerSP(dynamic_cast<KisShapeLayer*>(this->node().data()));
0156 
0157     if (!vector) return 0;
0158 
0159     KoShape* shape = vector->shapeManager()->shapeAt(position);
0160 
0161     if (!shape) return 0;
0162 
0163     if (dynamic_cast<KoShapeGroup*>(shape)) {
0164         return new GroupShape(dynamic_cast<KoShapeGroup*>(shape));
0165     } else {
0166         return new Shape(shape);
0167     }
0168 
0169 }
0170 
0171 QList<Shape *> VectorLayer::shapesInRect(const QRectF &rect, bool omitHiddenShapes, bool containedMode) const {
0172     QList<Shape *> shapes;
0173     KisShapeLayerSP vector = KisShapeLayerSP(dynamic_cast<KisShapeLayer*>(this->node().data()));
0174 
0175     if (vector) {
0176         QList<KoShape *> originalShapes = vector->shapeManager()->shapesAt(rect, omitHiddenShapes, containedMode);
0177 
0178         std::sort(originalShapes.begin(), originalShapes.end(), KoShape::compareShapeZIndex);
0179         for (int i=0; i<originalShapes.size(); i++) {
0180             if (dynamic_cast<KoShapeGroup*>(originalShapes.at(i))) {
0181                 shapes << new GroupShape(dynamic_cast<KoShapeGroup*>(originalShapes.at(i)));
0182             } else {
0183                 shapes << new Shape(originalShapes.at(i));
0184             }
0185         }
0186     }
0187     return shapes;
0188 }
0189 
0190 Shape* VectorLayer::createGroupShape(const QString &name, QList<Shape *> shapes) const
0191 {
0192     if (shapes.isEmpty()) return 0;
0193 
0194     QList<KoShape *> originalShapes;
0195     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(this->node().data());
0196 
0197     if (!container) return 0;
0198 
0199     for (Shape* shape : shapes) {
0200         KoShape *originalShape = shape->shape();
0201 
0202         if (originalShape && originalShape->parent() == container) {
0203             originalShapes << originalShape;
0204         } else {
0205             qWarning() << "Attempt to add an invalid shape.";
0206             return 0;
0207         }
0208     }
0209 
0210     if (originalShapes.isEmpty()) return 0;
0211 
0212     Document *document = Krita::instance()->activeDocument();
0213 
0214     if (!document) {
0215         document = LibKisUtils::findNodeInDocuments(this->node());
0216         if (!document) return 0;
0217     }
0218 
0219     KoShapeGroup *group = new KoShapeGroup();
0220     const int groupZIndex = originalShapes.last()->zIndex();
0221 
0222     group->setZIndex(groupZIndex);
0223     group->setName(name);
0224 
0225     KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
0226     new KoShapeCreateCommand(document->document()->shapeController(), group, container, cmd);
0227     new KoShapeGroupCommand(group, originalShapes, true, cmd);
0228 
0229     KisProcessingApplicator::runSingleCommandStroke(this->node()->image(), cmd);
0230     this->node()->image()->waitForDone();
0231     delete document;
0232 
0233     return new GroupShape(group);
0234 }