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 }