File indexing completed on 2025-07-06 10:21:33

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2002-2005, 2007 Rob Buis <buis@kde.org>
0003  * SPDX-FileCopyrightText: 2002-2004 Nicolas Goutte <nicolasg@snafu.de>
0004  * SPDX-FileCopyrightText: 2005-2006 Tim Beaulen <tbscope@gmail.com>
0005  * SPDX-FileCopyrightText: 2005-2009 Jan Hambrecht <jaham@gmx.net>
0006  * SPDX-FileCopyrightText: 2005, 2007 Thomas Zander <zander@kde.org>
0007  * SPDX-FileCopyrightText: 2006-2007 Inge Wallin <inge@lysator.liu.se>
0008  * SPDX-FileCopyrightText: 2007-2008 Thorsten Zachmann <t.zachmann@zagge.de>
0009 
0010  * SPDX-License-Identifier: LGPL-2.0-or-later
0011  */
0012 
0013 #include "SvgImport.h"
0014 #include "SvgParser.h"
0015 
0016 #include <KarbonPart.h>
0017 #include <KarbonDocument.h>
0018 
0019 #include <KoShape.h>
0020 #include <KoShapeLayer.h>
0021 #include <KoShapeGroup.h>
0022 #include <KoFilterChain.h>
0023 #include <commands/KoShapeUngroupCommand.h>
0024 #include <KoXmlReader.h>
0025 #include <KoPAPage.h>
0026 #include <KoPAMasterPage.h>
0027 #include <KoPageLayout.h>
0028 
0029 #include <kpluginfactory.h>
0030 #include <KCompressionDevice>
0031 
0032 #include <QFileInfo>
0033 #include <QDebug>
0034 #include <QLoggingCategory>
0035 
0036 K_PLUGIN_FACTORY_WITH_JSON(SvgImportFactory, "calligra_filter_svg2karbon.json",
0037                            registerPlugin<SvgImport>();)
0038 
0039 const QLoggingCategory &SVG_LOG()
0040 {
0041     static const QLoggingCategory category("calligra.filter.svg2karbon");
0042     return category;
0043 }
0044 
0045 #define debugSvg qCDebug(SVG_LOG)
0046 #define warnSvg qCWarning(SVG_LOG)
0047 #define errorSvg qCCritical(SVG_LOG)
0048 
0049 
0050 SvgImport::SvgImport(QObject*parent, const QVariantList&)
0051         : KoFilter(parent), m_document(0)
0052 {
0053 }
0054 
0055 SvgImport::~SvgImport()
0056 {
0057 }
0058 
0059 KoFilter::ConversionStatus SvgImport::convert(const QByteArray& from, const QByteArray& to)
0060 {
0061     // check for proper conversion
0062     if (to != "application/vnd.oasis.opendocument.graphics")
0063         return KoFilter::NotImplemented;
0064     if (from != "image/svg+xml" && from != "image/svg+xml-compressed")
0065         return KoFilter::NotImplemented;
0066 
0067     //Find the last extension
0068     QString strExt;
0069     QString fileIn(m_chain->inputFile());
0070     const int result = fileIn.lastIndexOf('.');
0071     if (result >= 0)
0072         strExt = fileIn.mid(result).toLower();
0073 
0074     const KCompressionDevice::CompressionType compressionType =
0075         (strExt == QLatin1String(".gz"))         //in case of .svg.gz (logical extension)
0076          || (strExt == QLatin1String(".svgz")) ? //in case of .svgz (extension used prioritary)
0077             KCompressionDevice::GZip :
0078         (strExt == QLatin1String(".bz2")) ?      //in case of .svg.bz2 (logical extension)
0079             KCompressionDevice::BZip2 :
0080             KCompressionDevice::None;
0081 
0082     /*debugSvg <<"File extension: -" << strExt <<"- Compression:" << strMime;*/
0083 
0084     QIODevice* in = new KCompressionDevice(fileIn, compressionType);
0085     if (!in->open(QIODevice::ReadOnly)) {
0086         errorSvg << "Cannot open file! Aborting!" << endl;
0087         delete in;
0088         return KoFilter::FileNotFound;
0089     }
0090 
0091     int line, col;
0092     QString errormessage;
0093 
0094     KoXmlDocument inputDoc;
0095 
0096     const bool parsed = inputDoc.setContent(in, &errormessage, &line, &col);
0097 
0098     in->close();
0099     delete in;
0100 
0101     if (! parsed) {
0102         errorSvg << "Error while parsing file: "
0103         << "at line " << line << " column: " << col
0104         << " message: " << errormessage << endl;
0105         // ### TODO: feedback to the user
0106         return KoFilter::ParsingError;
0107     }
0108 
0109     m_document = dynamic_cast<KarbonDocument*>(m_chain->outputDocument());
0110     if (!m_document) {
0111         return KoFilter::CreationError;
0112     }
0113     if (m_document->pages().isEmpty()) {
0114         KoPAMasterPage *mp = dynamic_cast<KoPAMasterPage*>(m_document->pages(true).value(0));
0115         if (!mp) {
0116             mp = new KoPAMasterPage();
0117             m_document->insertPage(mp, 0);
0118         }
0119         m_document->insertPage(new KoPAPage(mp), 0);
0120     }
0121     // Do the conversion!
0122     convert(inputDoc.documentElement());
0123 
0124     return KoFilter::OK;
0125 }
0126 
0127 void SvgImport::convert(const KoXmlElement &rootElement)
0128 {
0129     if (! m_document)
0130         return;
0131 
0132     // set default page size to A4
0133     QSizeF pageSize(550.0, 841.0);
0134 
0135     SvgParser parser(m_document->resourceManager());
0136 
0137     parser.setXmlBaseDir(QFileInfo(m_chain->inputFile()).filePath());
0138 
0139     QList<KoShape*> toplevelShapes = parser.parseSvg(rootElement, &pageSize);
0140     // parse the root svg element
0141     buildDocument(toplevelShapes, parser.shapes());
0142 
0143     // set the page size
0144     KoPageLayout & layout = m_document->pages().at(0)->pageLayout();
0145     layout.width = pageSize.width();
0146     layout.height = pageSize.height();
0147 }
0148 
0149 void SvgImport::buildDocument(const QList<KoShape*> &toplevelShapes, const QList<KoShape*> &shapes)
0150 {
0151     Q_UNUSED(shapes);
0152     KoPAPageBase *page = m_document->pages().first();
0153     // if we have only top level groups, make them layers
0154     bool onlyTopLevelGroups = true;
0155     foreach(KoShape * shape, toplevelShapes) {
0156         if (! dynamic_cast<KoShapeGroup*>(shape) || shape->filterEffectStack()) {
0157             onlyTopLevelGroups = false;
0158             break;
0159         }
0160     }
0161     KoShapeLayer *oldLayer = 0;
0162     if (page->shapeCount()) {
0163         oldLayer = dynamic_cast<KoShapeLayer*>(page->shapes().first());
0164     }
0165     if (onlyTopLevelGroups) {
0166         foreach(KoShape * shape, toplevelShapes) {
0167             // ungroup toplevel groups
0168             KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
0169             QList<KoShape*> children = group->shapes();
0170             KoShapeUngroupCommand cmd(group, children, QList<KoShape*>() << group);
0171             cmd.redo();
0172 
0173             KoShapeLayer *layer = new KoShapeLayer();
0174             foreach(KoShape * child, children) {
0175                 layer->addShape(child);
0176             }
0177             if (!group->name().isEmpty()) {
0178                 layer->setName(group->name());
0179             }
0180             layer->setVisible(group->isVisible());
0181             layer->setZIndex(group->zIndex());
0182             page->addShape(layer);
0183             delete group;
0184         }
0185     } else {
0186         KoShapeLayer *layer = new KoShapeLayer();
0187         foreach(KoShape * shape, toplevelShapes) {
0188             layer->addShape(shape);
0189         }
0190         page->addShape(layer);
0191     }
0192     if (oldLayer) {
0193         page->removeShape(oldLayer);
0194         delete oldLayer;
0195     }
0196 }
0197 
0198 #include "SvgImport.moc"