|
| 1 | +/****************************************************************************** |
| 2 | + ** @file svg_generator.cpp |
| 3 | + ** @author Evans PERRET |
| 4 | + ** @date September 21, 2024 |
| 5 | + ** |
| 6 | + ** @brief |
| 7 | + ** @copyright |
| 8 | + ** This source code is part of the Seamly2D project, a pattern making |
| 9 | + ** program, whose allow create and modeling patterns of clothing. |
| 10 | + ** Copyright (C) 2013-2022 Seamly2D project |
| 11 | + ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved. |
| 12 | + ** |
| 13 | + ** Seamly2D is free software: you can redistribute it and/or modify |
| 14 | + ** it under the terms of the GNU General Public License as published by |
| 15 | + ** the Free Software Foundation, either version 3 of the License, or |
| 16 | + ** (at your option) any later version. |
| 17 | + ** |
| 18 | + ** Seamly2D is distributed in the hope that it will be useful, |
| 19 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | + ** GNU General Public License for more details. |
| 22 | + ** |
| 23 | + ** You should have received a copy of the GNU General Public License |
| 24 | + ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>. |
| 25 | + ** |
| 26 | + *****************************************************************************/ |
| 27 | + |
| 28 | +#include "svg_generator.h" |
| 29 | +#include <QFile> |
| 30 | +#include <QDir> |
| 31 | +#include <QDebug> |
| 32 | +#include <QSvgGenerator> |
| 33 | +#include <QGraphicsItem> |
| 34 | +#include <QPainter> |
| 35 | +#include <QFileInfo> |
| 36 | + |
| 37 | +SvgGenerator::SvgGenerator(QGraphicsRectItem *paper, QString name, QString description, int resolution): |
| 38 | + m_svgCounter(0), |
| 39 | + m_paper(paper), |
| 40 | + m_filepath(name), |
| 41 | + m_description(description), |
| 42 | + m_resolution(resolution), |
| 43 | + m_tempSvgPrefix("tempSvg") |
| 44 | +{ |
| 45 | + QFileInfo fileInfo(m_filepath); |
| 46 | + m_folderPath = fileInfo.absolutePath(); |
| 47 | + m_name = fileInfo.baseName(); |
| 48 | + m_tempSvgFolderName = m_tempSvgPrefix + "_" + m_name; |
| 49 | +} |
| 50 | + |
| 51 | + |
| 52 | +bool SvgGenerator::loadSvgIntoDom(QDomDocument &domDocument, const QString &filePath) |
| 53 | +{ |
| 54 | + QFile file(filePath); |
| 55 | + if (!file.open(QIODevice::ReadOnly)) { |
| 56 | + qDebug() << "Error : Impossible to open the SVG file :" << filePath; |
| 57 | + return false; |
| 58 | + } |
| 59 | + if (!domDocument.setContent(&file)) { |
| 60 | + qDebug() << "Error : Impossible to load the SVG content in the QDomDocument."; |
| 61 | + file.close(); |
| 62 | + return false; |
| 63 | + } |
| 64 | + file.close(); |
| 65 | + return true; |
| 66 | +} |
| 67 | + |
| 68 | +QDomDocument SvgGenerator::mergeSvgDoms(const QList<QDomDocument> &domList) |
| 69 | +{ |
| 70 | + /*domList contains DOM representations of multiple SVG |
| 71 | + Assuming each svg contains a main group containing every graphical item of the svg, |
| 72 | + this function adds to the first svg of the list all the main groups of the other svgs, |
| 73 | + thus creating a single svg with each svg of the list in it, every svg being in its own group. |
| 74 | + This function is used in order to create svgs containing groups*/ |
| 75 | + |
| 76 | + if (domList.isEmpty()) { |
| 77 | + qDebug() << "Error : the SVG list is empty"; |
| 78 | + return QDomDocument(); |
| 79 | + } |
| 80 | + |
| 81 | + QDomDocument mergedSvg = domList.at(0); |
| 82 | + |
| 83 | + QDomElement mergedSvgRoot = mergedSvg.documentElement(); |
| 84 | + if (mergedSvgRoot.tagName() != "svg") { |
| 85 | + qDebug() << "Error : the first SVG does not contain a <svg> tag."; |
| 86 | + return QDomDocument(); |
| 87 | + } |
| 88 | + |
| 89 | + for (int i = 1; i < domList.size(); ++i) { |
| 90 | + QDomDocument domSvg = domList.at(i); |
| 91 | + QDomElement svgRoot = domSvg.documentElement(); |
| 92 | + if (svgRoot.tagName() != "svg") { |
| 93 | + qDebug() << "Error : the SVG does not contain a <svg> tag."; |
| 94 | + return QDomDocument(); |
| 95 | + } |
| 96 | + QDomNodeList svgGroups = svgRoot.elementsByTagName("g"); |
| 97 | + if (svgGroups.isEmpty()) { |
| 98 | + qDebug() << "Error : the SVG does not contain a <g> tag."; |
| 99 | + return QDomDocument(); |
| 100 | + } |
| 101 | + QDomElement mainGroup = svgGroups.at(0).toElement(); |
| 102 | + mergedSvgRoot.appendChild(mainGroup); |
| 103 | + } |
| 104 | + |
| 105 | + return mergedSvg; |
| 106 | +} |
| 107 | + |
| 108 | +void SvgGenerator::addSvgFromScene(QGraphicsScene *scene) |
| 109 | +{ |
| 110 | + m_svgCounter++; |
| 111 | + |
| 112 | + QSvgGenerator svgGenerator; |
| 113 | + svgGenerator.setFileName(getTempFilePath(m_svgCounter)); |
| 114 | + svgGenerator.setSize(m_paper->rect().size().toSize()); |
| 115 | + svgGenerator.setViewBox(m_paper->rect()); |
| 116 | + svgGenerator.setTitle(QObject::tr("Pattern")); |
| 117 | + svgGenerator.setDescription(m_description); |
| 118 | + svgGenerator.setResolution(m_resolution); |
| 119 | + |
| 120 | + QPainter painter; |
| 121 | + painter.begin(&svgGenerator); |
| 122 | + painter.setFont( QFont( "Arial", 8, QFont::Normal ) ); |
| 123 | + painter.setRenderHint(QPainter::Antialiasing, true); |
| 124 | + painter.setBrush ( QBrush ( Qt::NoBrush ) ); |
| 125 | + scene->render(&painter, m_paper->rect(), m_paper->rect(), Qt::IgnoreAspectRatio); |
| 126 | + painter.end(); |
| 127 | +} |
| 128 | + |
| 129 | + |
| 130 | +void SvgGenerator::generate() |
| 131 | +{ |
| 132 | + QList<QDomDocument> domList; |
| 133 | + |
| 134 | + for(int i = 1; i <= m_svgCounter; i++) |
| 135 | + { |
| 136 | + QDomDocument domDoc; |
| 137 | + QString path = getTempFilePath(i); |
| 138 | + QFile file(path); |
| 139 | + if (!file.exists()) { |
| 140 | + qDebug() << "Error : the SVG file does not exist."; |
| 141 | + continue; |
| 142 | + } |
| 143 | + loadSvgIntoDom(domDoc, path); |
| 144 | + domList.append(domDoc); |
| 145 | + |
| 146 | + if (!file.remove()) { |
| 147 | + qDebug() << "Error : unable to remove " << path; |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + QDomDocument mergedSvg = mergeSvgDoms(domList); |
| 152 | + |
| 153 | + QFile outputFile(m_filepath); |
| 154 | + if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Text)) { |
| 155 | + qDebug() << "Error : Couldn't write the output file."; |
| 156 | + } |
| 157 | + |
| 158 | + QTextStream stream(&outputFile); |
| 159 | + stream << mergedSvg.toString(); |
| 160 | + outputFile.close(); |
| 161 | + |
| 162 | + QDir dir(m_folderPath); |
| 163 | + if(!dir.rmdir(m_tempSvgFolderName)) |
| 164 | + { |
| 165 | + qDebug() << "Error : Couldn't remove the temp SVG folder."; |
| 166 | + } |
| 167 | + |
| 168 | + qDebug() << "Merged SVG Generated!"; |
| 169 | +} |
| 170 | + |
| 171 | + |
| 172 | +QString SvgGenerator::getTempFilePath(int id) |
| 173 | +{ |
| 174 | + QDir dir(m_folderPath); |
| 175 | + if (!dir.cd(m_tempSvgFolderName)) |
| 176 | + { |
| 177 | + dir.mkdir(m_tempSvgFolderName); |
| 178 | + dir.cd(m_tempSvgFolderName); |
| 179 | + } |
| 180 | + |
| 181 | + return dir.path() + "/" + m_tempSvgPrefix + "_" + m_name + "_" + QString::number(id) + ".svg"; |
| 182 | +} |
0 commit comments