/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

#include <memory>

#include <QCursor>
#include <QDebug>
#include <QDrag>
#include <QFile>
#include <QList>
#include <QMessageBox>
#include <QMimeData>
#include <QPainterPath>
#include <QRegExp>
#include <QStack>
#include <QTemporaryFile>
#include <QUrl>

#include "svgplugin.h"

#include "commonstrings.h"
#include "fonts/scfontmetrics.h"
#include "fpointarray.h"
#include "loadsaveplugin.h"
#include "pageitem.h"
#include "prefsfile.h"
#include "prefsmanager.h"
#include "qtiocompressor.h"
#include "scclocale.h"
#include "sccolorengine.h"
#include "scconfig.h"
#include "sclimits.h"
#include "scmimedata.h"
#include "scpaths.h"
#include "scpattern.h"
#include "scraction.h"
#include "scribusXml.h"
#include "scribuscore.h"
#include "scribusdoc.h"
#include "scribusdoc.h"
#include "scribusview.h"
#include "selection.h"
#include "ui/customfdialog.h"
#include "ui/propertiespalette.h"
#include "ui/scmessagebox.h"
#include "ui/scmwmenumanager.h"
#include "undomanager.h"
#include "util.h"
#include "util_formats.h"
#include "util_math.h"


using namespace std;

int svgimplugin_getPluginAPIVersion()
{
	return PLUGIN_API_VERSION;
}

ScPlugin* svgimplugin_getPlugin()
{
	auto* plug = new SVGImportPlugin();
	Q_CHECK_PTR(plug);
	return plug;
}

void svgimplugin_freePlugin(ScPlugin* plugin)
{
	SVGImportPlugin* plug = qobject_cast<SVGImportPlugin*>(plugin);
	Q_ASSERT(plug);
	delete plug;
}

SVGImportPlugin::SVGImportPlugin() :
	importAction(new ScrAction(ScrAction::DLL, "", QKeySequence(), this))
{
	// Set action info in languageChange, so we only have to do
	// it in one place. This includes registering file format
	// support.
	registerFormats();
	languageChange();
}

SVGImportPlugin::~SVGImportPlugin()
{
	unregisterAll();
}

void SVGImportPlugin::languageChange()
{
	importAction->setText( tr("Import &SVG..."));
	FileFormat* fmt = getFormatByExt("svg");
	fmt->trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG);
	fmt->filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG);
}

QString SVGImportPlugin::fullTrName() const
{
	return QObject::tr("SVG Import");
}

const ScActionPlugin::AboutData* SVGImportPlugin::getAboutData() const
{
	auto* about = new AboutData;
	about->authors = "Franz Schmid <franz@scribus.info>";
	about->shortDescription = tr("Imports SVG Files");
	about->description = tr("Imports most SVG files into the current document, converting their vector data into Scribus objects.");
	about->license = "GPL";
	Q_CHECK_PTR(about);
	return about;
}

void SVGImportPlugin::deleteAboutData(const AboutData* about) const
{
	Q_ASSERT(about);
	delete about;
}

void SVGImportPlugin::registerFormats()
{
	FileFormat fmt(this);
	fmt.trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG);
	fmt.formatId = 0;
	fmt.filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG);
	fmt.fileExtensions = QStringList() << "svg" << "svgz";
	fmt.load = true;
	fmt.save = false;
	fmt.thumb = true;
	fmt.mimeTypes = FormatsManager::instance()->mimetypeOfFormat(FormatsManager::SVG);
	fmt.priority = 64;
	registerFormat(fmt);
}

bool SVGImportPlugin::fileSupported(QIODevice* /* file */, const QString & fileName) const
{
	// TODO: identify valid SVG
	return true;
}

bool SVGImportPlugin::loadFile(const QString & fileName, const FileFormat & /* fmt */, int flags, int /*index*/)
{
	// For now, "load file" and import are the same thing for this plugin
	return importFile(fileName, flags);
}

bool SVGImportPlugin::importFile(QString filename, int flags)
{
	if (!checkFlags(flags))
		return false;

	m_Doc = ScCore->primaryMainWindow()->doc;
	ScribusMainWindow* mw = (m_Doc == nullptr) ? ScCore->primaryMainWindow() : m_Doc->scMW();

	if (filename.isEmpty())
	{
		flags |= lfInteractive;
		PrefsContext* prefs = PrefsManager::instance().prefsFile->getPluginContext("SVGPlugin");
		QString wdir = prefs->get("wdir", ".");
		CustomFDialog diaf(mw, wdir, QObject::tr("Open"), FormatsManager::instance()->fileDialogFormatList(FormatsManager::SVG));
		if (diaf.exec() != QDialog::Accepted)
			return true;
		filename = diaf.selectedFile();
		prefs->set("wdir", filename.left(filename.lastIndexOf("/")));
	}
	
	UndoTransaction activeTransaction;
	bool emptyDoc = (m_Doc == nullptr);
	bool hasCurrentPage = (m_Doc && m_Doc->currentPage());

	TransactionSettings trSettings;
	trSettings.targetName   = hasCurrentPage ? m_Doc->currentPage()->getUName() : "";
	trSettings.targetPixmap = Um::IImageFrame;
	trSettings.actionName   = Um::ImportSVG;
	trSettings.description  = filename;
	trSettings.actionPixmap = Um::ISVG;
	if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted))
		UndoManager::instance()->setUndoEnabled(false);
	if (UndoManager::undoEnabled())
		activeTransaction = UndoManager::instance()->beginTransaction(trSettings);

	auto pPlug = std::make_shared<SVGPlug>(m_Doc, flags);
	Q_CHECK_PTR(pPlug);
	pPlug->importFile(filename, trSettings, flags);

	if (activeTransaction)
		activeTransaction.commit();
	if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted))
		UndoManager::instance()->setUndoEnabled(true);

	if (pPlug->importCanceled)
	{
		if (pPlug->importFailed)
			ScMessageBox::warning(mw, CommonStrings::trWarning, tr("The file could not be imported"));
	//	else if (pPlug->unsupported)
	//		ScMessageBox::warning(mw, CommonStrings::trWarning, tr("SVG file contains some unsupported features"));
	}

	return true;
}

QImage SVGImportPlugin::readThumbnail(const QString& fileName)
{
	if (fileName.isEmpty())
		return QImage();

	UndoManager::instance()->setUndoEnabled(false);
	m_Doc = nullptr;
	auto pPlug = std::make_shared<SVGPlug>(m_Doc, lfCreateThumbnail);
	Q_CHECK_PTR(pPlug);
	QImage ret = pPlug->readThumbnail(fileName);
	UndoManager::instance()->setUndoEnabled(true);

	return ret;
}

SVGPlug::SVGPlug(ScribusDoc* doc, int flags)
	   : m_Doc(doc),
	     importerFlags(flags)
{
	tmpSel = new Selection(this, false);

	importedColors.clear();
	importedGradients.clear();
	importedPatterns.clear();

	interactive = (flags & LoadSavePlugin::lfInteractive);
}

SVGPlug::~SVGPlug()
{
	delete tmpSel;
}

QImage SVGPlug::readThumbnail(const QString& fName)
{
	if (!loadData(fName))
		return QImage();
	QString CurDirP = QDir::currentPath();
	QFileInfo efp(fName);
	QDir::setCurrent(efp.path());
	auto *gc = new SvgStyle;
	QDomElement docElem = inpdoc.documentElement();
	QSizeF wh = parseWidthHeight(docElem);
	m_Doc = new ScribusDoc();
	m_Doc->setup(0, 1, 1, 1, 1, "Custom", "Custom");
	m_Doc->setPage(wh.width(), wh.height(), 0, 0, 0, 0, 0, 0, false, false);
	m_Doc->addPage(0);
	m_Doc->setGUI(false, ScCore->primaryMainWindow(), nullptr);
	m_Doc->setLoading(true);
	m_Doc->DoDrawing = false;
	m_Doc->scMW()->setScriptRunning(true);
	gc->FontFamily = m_Doc->itemToolPrefs().textFont;
	if (!m_Doc->PageColors.contains("Black"))
		m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255));
	m_gc.push(gc);
	viewTransformX = 0;
	viewTransformY = 0;
	viewScaleX = 1;
	viewScaleY = 1;
	if (!docElem.attribute("viewBox").isEmpty())
	{
		QString viewbox(docElem.attribute("viewBox"));
		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
		if (points.size() > 3)
		{
			QTransform matrix;
			QSizeF wh2 = parseWidthHeight(docElem);
			double w2 = wh2.width();
			double h2 = wh2.height();
			addGraphicContext();
			viewTransformX = ScCLocale::toDoubleC(points[0]);
			viewTransformY = ScCLocale::toDoubleC(points[1]);
			viewScaleX = w2 / ScCLocale::toDoubleC(points[2]);
			viewScaleY = h2 / ScCLocale::toDoubleC(points[3]);
			matrix.translate(-viewTransformX * viewScaleX, -viewTransformY * viewScaleY);
			matrix.scale(viewScaleX, viewScaleY);
			m_gc.top()->matrix = matrix;
		}
	}

	m_nodeMap = buildNodeMap(docElem);
	QList<PageItem*> Elements = parseGroup(docElem);

	tmpSel->clear();
	QImage tmpImage;
	if (!Elements.isEmpty())
	{
		m_Doc->groupObjectsList(Elements);
		m_Doc->DoDrawing = true;
		m_Doc->m_Selection->delaySignalsOn();
		tmpSel->addItems(Elements);
		tmpSel->setGroupRect();
		double xs = tmpSel->width();
		double ys = tmpSel->height();
		tmpImage = Elements.at(0)->DrawObj_toImage(500);
		tmpImage.setText("XSize", QString("%1").arg(xs));
		tmpImage.setText("YSize", QString("%1").arg(ys));
		m_Doc->m_Selection->delaySignalsOff();
	}
	m_Doc->scMW()->setScriptRunning(false);
	m_Doc->setLoading(false);
	delete m_Doc;
	m_Doc = nullptr;
	QDir::setCurrent(CurDirP);
	return tmpImage;
}

bool SVGPlug::importFile(const QString& fname, const TransactionSettings& trSettings, int flags)
{
	m_currentSvgFilePath.clear();
	m_currentSvgFileDir.clear();

	if (!loadData(fname))
	{
		importFailed = true;
		return false;
	}

	QFileInfo fInfo(fname);
	m_currentSvgFilePath = fInfo.absoluteFilePath();
	m_currentSvgFileDir = fInfo.absolutePath();

	QString CurDirP = QDir::currentPath();
	QDir::setCurrent(fInfo.path());
	convert(trSettings, flags);
	QDir::setCurrent(CurDirP);

	return true;
}

bool SVGPlug::loadData(const QString& fName)
{
	bool isCompressed = false;
	bool success = false;
	QFile fi(fName);
	if (fi.open(QIODevice::ReadOnly))
	{
		QByteArray bb(3, ' ');
		fi.read(bb.data(), 2);
		fi.close();
		// Qt4 bb[0]->QChar(bb[0])
		if ((QChar(bb[0]) == QChar(0x1F)) && (QChar(bb[1]) == QChar(0x8B)))
			isCompressed = true;
	}
	if ((fName.right(2) == "gz") || isCompressed)
	{
		QFile file(fName);
		QtIOCompressor compressor(&file);
		compressor.setStreamFormat(QtIOCompressor::GzipFormat);
		if (!compressor.open(QIODevice::ReadOnly))
			return false;
		success = inpdoc.setContent(&compressor);
		compressor.close();
	}
	else
	{
		QFile file(fName);
		if (!file.open(QIODevice::ReadOnly))
			return false;
		success = inpdoc.setContent(&file);
		file.close();
	}
	return success;
}

QMap<QString, QDomElement> SVGPlug::buildNodeMap(const QDomElement &e) const
{
	const QString idAttribute("id");
	QMap<QString, QDomElement> nodeMap;

	QStack<QDomElement> elementStack;
	elementStack.push(e);

	while (!elementStack.isEmpty())
	{
		QDomElement domElem = elementStack.pop();

		for (QDomNode n = domElem.firstChild(); !n.isNull(); n = n.nextSibling())
		{
			QDomElement e = n.toElement();
			if (e.isNull())
				continue;

			QString id = e.attribute(idAttribute);
			if (!id.isEmpty())
				nodeMap.insert(id, e);

			if (e.hasChildNodes())
				elementStack.push(e);
		}
	}

	return nodeMap;
}

void SVGPlug::convert(const TransactionSettings& trSettings, int flags)
{
	bool ret = false;
	QDomElement docElem = inpdoc.documentElement();
	QSizeF wh = parseWidthHeight(docElem);
	double width = wh.width();
	double height = wh.height();
	if (m_Doc && (!interactive || (flags & LoadSavePlugin::lfInsertPage)))
	{
		m_Doc->setPage(width, height, 0, 0, 0, 0, 0, 0, false, false);
		if (m_Doc->Pages->isEmpty())
		{
			m_Doc->addPage(0);
			m_Doc->view()->addPage(0);
		}
	}
	else if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
	{
		m_Doc = ScCore->primaryMainWindow()->doFileNew(width, height, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
		ScCore->primaryMainWindow()->HaveNewDoc();
		ret = true;
	}
	if (ret || !interactive)
	{
		if (width > height)
			m_Doc->setPageOrientation(1);
		else
			m_Doc->setPageOrientation(0);
		m_Doc->setPageSize("Custom");
	}
	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
		m_Doc->view()->deselectItems();
	m_Doc->setLoading(true);
	m_Doc->DoDrawing = false;
	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
		m_Doc->view()->updatesOn(false);
	m_Doc->scMW()->setScriptRunning(true);
	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	auto *gc = new SvgStyle;
	gc->FontFamily = m_Doc->itemToolPrefs().textFont;
	if (!m_Doc->PageColors.contains("Black"))
		m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255));
	m_gc.push(gc);

	viewTransformX = 0;
	viewTransformY = 0;
	viewScaleX = 1;
	viewScaleY = 1;
	if (!docElem.attribute("viewBox").isEmpty())
	{
		QString viewbox(docElem.attribute("viewBox"));
		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
		if (points.size() > 3)
		{
			QTransform matrix;
			QSizeF wh2 = parseWidthHeight(docElem);
			double w2 = wh2.width();
			double h2 = wh2.height();
			addGraphicContext();
			viewTransformX = ScCLocale::toDoubleC(points[0]);
			viewTransformY = ScCLocale::toDoubleC(points[1]);
			viewScaleX = w2 / ScCLocale::toDoubleC(points[2]);
			viewScaleY = h2 / ScCLocale::toDoubleC(points[3]);
			matrix.translate(-viewTransformX * viewScaleX, -viewTransformY * viewScaleY);
			matrix.scale(viewScaleX, viewScaleY);
			m_gc.top()->matrix = matrix;
		}
	}

	m_nodeMap = buildNodeMap(docElem);
	Elements += parseDoc(docElem);

	if (flags & LoadSavePlugin::lfCreateDoc)
	{
		m_Doc->documentInfo().setTitle(docTitle);
		m_Doc->documentInfo().setComments(docDesc);
	}
	tmpSel->clear();
	if (Elements.isEmpty())
	{
		importFailed = true;
		if (!importedColors.isEmpty() && ((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns)))
			importFailed = false;
		if (!importedGradients.isEmpty() && ((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepPatterns)))
			importFailed = false;
		if (!importedPatterns.isEmpty() && (flags & LoadSavePlugin::lfKeepPatterns))
			importFailed = false;
		if (!importedColors.isEmpty() && !((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns)))
		{
			for (const auto& importedColor : importedColors)
				m_Doc->PageColors.remove(importedColor);
		}
		if (!importedGradients.isEmpty() && !((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepPatterns)))
		{
			for (const auto& importedGradient : importedGradients)
				m_Doc->docGradients.remove(importedGradient);
		}
		if (!importedPatterns.isEmpty() && !(flags & LoadSavePlugin::lfKeepPatterns))
		{
			for (const auto& importedPattern : importedPatterns)
				m_Doc->docPatterns.remove(importedPattern);
		}
	}
	if ((Elements.count() > 1) && (!(flags & LoadSavePlugin::lfCreateDoc)))
	{
		m_Doc->groupObjectsList(Elements);
	}
	m_Doc->DoDrawing = true;
	m_Doc->scMW()->setScriptRunning(false);
	if (interactive)
		m_Doc->setLoading(false);
	QApplication::changeOverrideCursor(QCursor(Qt::ArrowCursor));
	if (!Elements.isEmpty() && !ret && interactive)
	{
		if (flags & LoadSavePlugin::lfScripted)
		{
			bool loadF = m_Doc->isLoading();
			m_Doc->setLoading(false);
			m_Doc->changed();
			m_Doc->setLoading(loadF);
			if (!(flags & LoadSavePlugin::lfLoadAsPattern))
			{
				m_Doc->m_Selection->delaySignalsOn();
				m_Doc->m_Selection->addItems(Elements);
				m_Doc->m_Selection->delaySignalsOff();
				m_Doc->m_Selection->setGroupRect();
				if (m_Doc->view() != nullptr)
					m_Doc->view()->updatesOn(true);
			}
			importCanceled = false;
		}
		else
		{
			m_Doc->DragP = true;
			m_Doc->DraggedElem = nullptr;
			m_Doc->DragElements.clear();
			m_Doc->m_Selection->delaySignalsOn();
			tmpSel->addItems(Elements);
			tmpSel->setGroupRect();
			ScElemMimeData* md = ScriXmlDoc::writeToMimeData(m_Doc, tmpSel);
			m_Doc->itemSelection_DeleteItem(tmpSel);
			m_Doc->view()->updatesOn(true);
			if (!importedColors.isEmpty() && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns))))
			{
				for (const auto& importedColor : importedColors)
					m_Doc->PageColors.remove(importedColor);
			}
			if (!importedGradients.isEmpty() && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepPatterns))))
			{
				for (const auto& importedGradient : importedGradients)
					m_Doc->docGradients.remove(importedGradient);
			}
			if (!importedPatterns.isEmpty() && (!(flags & LoadSavePlugin::lfKeepPatterns)))
			{
				for (const auto& importedPattern : importedPatterns)
					m_Doc->docPatterns.remove(importedPattern);
			}
			m_Doc->m_Selection->delaySignalsOff();
			// We must copy the TransationSettings object as it is owned
			// by handleObjectImport method afterwards
			auto* transacSettings = new TransactionSettings(trSettings);
			m_Doc->view()->handleObjectImport(md, transacSettings);
			m_Doc->DragP = false;
			m_Doc->DraggedElem = nullptr;
			m_Doc->DragElements.clear();
		}
	}
	else
	{
		bool loadF = m_Doc->isLoading();
		m_Doc->setLoading(false);
		m_Doc->changed();
		m_Doc->reformPages();
		if (!(flags & LoadSavePlugin::lfLoadAsPattern))
			m_Doc->view()->updatesOn(true);
		m_Doc->setLoading(loadF);
	}
	if (!(flags & LoadSavePlugin::lfLoadAsPattern))
	{
		if (interactive)
			m_Doc->view()->DrawNew();
	}
	QApplication::restoreOverrideCursor();
}

void SVGPlug::addGraphicContext()
{
	auto *gc = new SvgStyle;
	if (m_gc.top())
	{
		*gc = *(m_gc.top());
		gc->Opacity = 1.0; // opacity is not inheritable contrary to fill-opacity or stroke-opacity
		gc->filter.clear(); // filter is not inheritable
	}
	m_gc.push(gc);
}

void SVGPlug::setupNode(const QDomElement &e)
{
	addGraphicContext();
	setupTransform(e);
	parseStyle(m_gc.top(), e);
}

void SVGPlug::setupTransform(const QDomElement &e)
{
	SvgStyle *gc = m_gc.top();
	QTransform mat = parseTransform(e.attribute("transform"));
	if (!e.attribute("transform").isEmpty())
		gc->matrix = mat * gc->matrix;
}

PageItem *SVGPlug::finishNode(const QDomNode &e, PageItem* item)
{
	PageItem* startArrow = nullptr;
	PageItem* endArrow = nullptr;
	const SvgStyle *gc = m_gc.top();
	QTransform gcm = gc->matrix;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double coeff1 = sqrt(gcm.m11() * gcm.m11() + gcm.m12() * gcm.m12());
	double coeff2 = sqrt(gcm.m21() * gcm.m21() + gcm.m22() * gcm.m22());
	switch (item->itemType())
	{
	case PageItem::ImageFrame:
		{
			item->ClipEdited = true;
			item->FrameType = 3;
			QTransform mm = gc->matrix;
			item->PoLine.map(mm);
			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
			FPoint wh = getMaxClipF(&item->PoLine);
			item->setWidthHeight(wh.x(), wh.y());
			m_Doc->adjustItemSize(item);
			item->ContourLine = item->PoLine.copy();
//			item->moveBy(mm.dx(), mm.dy());
//			item->setWidthHeight(item->width() * mm.m11(), item->height() * mm.m22());
//			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
			if (item->imageIsAvailable)
			{
				item->setImageXYOffset(0.0, 0.0);
				item->setImageXYScale(item->width() / (item->pixm.width() * (item->pixm.imgInfo.xres / 72.0)),
									  item->height() / (item->pixm.height() * (item->pixm.imgInfo.yres / 72.0)));
				item->setImageScalingMode(false, false); // fit to frame
			}
			break;
		}
	case PageItem::TextFrame:
		{
			//QTransform mm = gc->matrix;
			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
			item->itemText.trim();
		}
		break;
	default:
		{
			item->ClipEdited = true;
			item->FrameType = 3;
			QTransform mm = gc->matrix;
			item->PoLine.map(mm);
			/*if (haveViewBox)
			{
				QTransform mv;
				mv.translate(viewTransformX, viewTransformY);
				mv.scale(viewScaleX, viewScaleY);
				ite->PoLine.map(mv);
			}*/
			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
			FPoint wh = getMaxClipF(&item->PoLine);
			item->setWidthHeight(wh.x(), wh.y());
//			if (item->asPolyLine())
//				item->setPolyClip(qRound(qMax(item->lineWidth() / 2.0, 1)));
//			else
//				item->Clip = flattenPath(item->PoLine, item->Segments);
			FPoint wx = getMinClipF(&item->PoLine);
			inGroupXOrigin = qMin(inGroupXOrigin, wx.x());
			inGroupYOrigin = qMin(inGroupYOrigin, wx.y());
			m_Doc->adjustItemSize(item);
			break;
		}
	}
	item->setRedrawBounding();
	item->OwnPage = m_Doc->OnPage(item);
	if (e.isElement())
	{
		if (!(importerFlags & LoadSavePlugin::lfCreateDoc))
		{
			QString nodeId = e.toElement().attribute("id");
			if (!nodeId.isEmpty())
				item->setItemName(" "+nodeId);
		}
	}
	item->setFillTransparency(1 - gc->FillOpacity * gc->Opacity);
	item->setLineTransparency(1 - gc->StrokeOpacity * gc->Opacity);
	item->PLineEnd = gc->PLineEnd;
	item->PLineJoin = gc->PLineJoin;
	if (item->fillColor() == CommonStrings::None)
		item->setTextFlowMode(PageItem::TextFlowDisabled);
	else
		item->setTextFlowMode(PageItem::TextFlowUsesFrameShape);
	item->DashOffset = gc->dashOffset;
	item->DashValues = gc->dashArray;
	if (gc->FillGradientType != 0)
	{
		if (gc->FillGradientType == 8)
		{
			item->GrType = gc->FillGradientType;
			item->setPattern(importedPattTrans[gc->GFillCol1]);
			QTransform mm = gc->matrixgf;
			double rot = getRotationFromMatrix(mm, 0.0) * 180 / M_PI;
			mm.rotate(rot);
			double patDx = (item->xPos() - baseX) - mm.dx();
			double patDy = (item->yPos() - baseY) - mm.dy();
			item->setPatternTransform(mm.m11(), mm.m22(), patDx, patDy, -rot, 0, 0);
		}
		else
		{
			if (gc->FillGradient.stops() > 1)
			{
				item->fill_gradient = gc->FillGradient;
				item->setGradient(importedGradTrans[gc->GFillCol1]);
				item->setGradientExtend(VGradient::pad);
				if (!gc->FillCSpace)
				{
					item->GrStartX = gc->GradFillX1 * item->width();
					item->GrStartY = gc->GradFillY1 * item->height();
					item->GrEndX   = gc->GradFillX2 * item->width();
					item->GrEndY   = gc->GradFillY2 * item->height();
					item->GrFocalX = gc->GradFillFX * item->width();
					item->GrFocalY = gc->GradFillFY * item->height();
					double angle1 = atan2(gc->GradFillY2-gc->GradFillY1,gc->GradFillX2-gc->GradFillX1)*(180.0/M_PI);
					double angle2 = atan2(item->GrEndY - item->GrStartX, item->GrEndX - item->GrStartX)*(180.0/M_PI);
					double dx = item->GrStartX + (item->GrEndX - item->GrStartX) / 2.0;
					double dy = item->GrStartY + (item->GrEndY - item->GrStartY) / 2.0;
					QTransform mm, mm2;
					if ((gc->GradFillY1 < gc->GradFillY2) && (gc->GradFillX1 < gc->GradFillX2))
					{
						mm.rotate(-angle2);
						mm2.rotate(angle1);
					}
					FPointArray gra;
					gra.setPoints(3, item->GrStartX-dx, item->GrStartY-dy, item->GrEndX-dx, item->GrEndY-dy, item->GrFocalX-dx, item->GrFocalY-dy);
					gra.map(mm*mm2);
					gra.translate(dx, dy);
					item->GrStartX = gra.point(0).x();
					item->GrStartY = gra.point(0).y();
					item->GrEndX   = gra.point(1).x();
					item->GrEndY   = gra.point(1).y();
					item->GrFocalX = gra.point(2).x();
					item->GrFocalY = gra.point(2).y();
				}
				else
				{
					QTransform mm = gc->matrix;
					mm = gc->matrixgf * mm;
					FPointArray gra;
					gra.setPoints(3, gc->GradFillX1, gc->GradFillY1, gc->GradFillX2, gc->GradFillY2, gc->GradFillFX, gc->GradFillFY);
					gra.map(mm);
					item->GrStartX = gra.point(0).x() - item->xPos() + baseX;
					item->GrStartY = gra.point(0).y() - item->yPos() + baseY;
					item->GrEndX   = gra.point(1).x() - item->xPos() + baseX;
					item->GrEndY   = gra.point(1).y() - item->yPos() + baseY;
					item->GrFocalX = gra.point(2).x() - item->xPos() + baseX;
					item->GrFocalY = gra.point(2).y() - item->yPos() + baseY;
					double ScaleX = 1.0;
					double ScaleY = 1.0;
					getScaleFromMatrix(mm, ScaleX, ScaleY);
					if (ScaleX != ScaleY)
						item->GrScale = ScaleY / ScaleX;
				}
				item->GrType = gc->FillGradientType;
			}
			else
			{
				item->GrType = 0;
				QList<VColorStop*> cstops = gc->FillGradient.colorStops();
				item->setFillColor(cstops.at(0)->name);
				item->setFillShade(cstops.at(0)->shade);
			}
		}
	}
	if (gc->StrokeGradientType != 0)
	{
		if (gc->StrokeGradientType == 8)
		{
			item->GrTypeStroke = gc->StrokeGradientType;
			item->setPattern(importedPattTrans[gc->GStrokeCol1]);
			QTransform mm = gc->matrixgs;
			double rot = getRotationFromMatrix(mm, 0.0) * 180 / M_PI;
			mm.rotate(rot);
			double patDx = (item->xPos() - baseX) - mm.dx();
			double patDy = (item->yPos() - baseY) - mm.dy();
			item->setStrokePatternTransform(mm.m11(), mm.m22(), patDx, patDy, -rot, 0, 0, 1);
		}
		else
		{
			if (gc->StrokeGradient.stops() > 1)
			{
				item->stroke_gradient = gc->StrokeGradient;
				item->setStrokeGradient(importedGradTrans[gc->GStrokeCol1]);
				item->setStrokeGradientExtend(VGradient::pad);
				if (!gc->StrokeCSpace)
				{
					item->GrStrokeStartX = gc->GradStrokeX1 * item->width();
					item->GrStrokeStartY = gc->GradStrokeY1 * item->height();
					item->GrStrokeEndX   = gc->GradStrokeX2 * item->width();
					item->GrStrokeEndY   = gc->GradStrokeY2 * item->height();
					item->GrStrokeFocalX = gc->GradStrokeFX * item->width();
					item->GrStrokeFocalY = gc->GradStrokeFY * item->height();
					double angle1 = atan2(gc->GradStrokeY2 - gc->GradStrokeY1, gc->GradStrokeX2 - gc->GradStrokeX1)*(180.0/M_PI);
					double angle2 = atan2(item->GrStrokeEndY - item->GrStrokeStartX, item->GrStrokeEndX - item->GrStrokeStartX)*(180.0/M_PI);
					double dx = item->GrStrokeStartX + (item->GrStrokeEndX - item->GrStrokeStartX) / 2.0;
					double dy = item->GrStrokeStartY + (item->GrStrokeEndY - item->GrStrokeStartY) / 2.0;
					QTransform mm, mm2;
					if ((gc->GradStrokeY1 < gc->GradStrokeY2) && (gc->GradStrokeX1 < gc->GradStrokeX2))
					{
						mm.rotate(-angle2);
						mm2.rotate(angle1);
					}
					FPointArray gra;
					gra.setPoints(3, item->GrStrokeStartX - dx, item->GrStrokeStartY - dy, item->GrStrokeEndX-dx, item->GrStrokeEndY-dy, item->GrStrokeFocalX-dx, item->GrStrokeFocalY-dy);
					gra.map(mm*mm2);
					gra.translate(dx, dy);
					item->GrStrokeStartX = gra.point(0).x();
					item->GrStrokeStartY = gra.point(0).y();
					item->GrStrokeEndX   = gra.point(1).x();
					item->GrStrokeEndY   = gra.point(1).y();
					item->GrStrokeFocalX = gra.point(2).x();
					item->GrStrokeFocalY = gra.point(2).y();
				}
				else
				{
					QTransform mm = gc->matrix;
					mm = gc->matrixgs * mm;
					FPointArray gra;
					gra.setPoints(3, gc->GradStrokeX1, gc->GradStrokeY1, gc->GradStrokeX2, gc->GradStrokeY2, gc->GradStrokeFX, gc->GradStrokeFY);
					gra.map(mm);
					item->GrStrokeStartX = gra.point(0).x() - item->xPos() + baseX;
					item->GrStrokeStartY = gra.point(0).y() - item->yPos() + baseY;
					item->GrStrokeEndX   = gra.point(1).x() - item->xPos() + baseX;
					item->GrStrokeEndY   = gra.point(1).y() - item->yPos() + baseY;
					item->GrStrokeFocalX = gra.point(2).x() - item->xPos() + baseX;
					item->GrStrokeFocalY = gra.point(2).y() - item->yPos() + baseY;
				}
				item->GrTypeStroke = gc->StrokeGradientType;
			}
			else
			{
				item->GrTypeStroke = 0;
				QList<VColorStop*> cstops = gc->StrokeGradient.colorStops();
				item->setLineColor(cstops.at(0)->name);
				item->setLineShade(cstops.at(0)->shade);
			}
		}
	}
	if (!gc->filter.isEmpty())
	{
		if (filters.contains(gc->filter))
		{
			filterSpec filter = filters[gc->filter];
			item->setFillBlendmode(filter.blendMode);
		}
	}
	if (!gc->endMarker.isEmpty())
	{
		QString marker = gc->endMarker;
		if (importedPattTrans.contains(marker))
			marker = importedPattTrans[marker];
		if (markers.contains(marker))
		{
			FPoint End = item->PoLine.point(item->PoLine.size()-2);
			for (uint xx = item->PoLine.size()-1; xx > 0; xx -= 2)
			{
				FPoint Vector = item->PoLine.point(xx);
				if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
				{
					double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI);
					QTransform arrowTrans;
					double bX = item->xPos() + End.x();
					double bY = item->yPos() + End.y();
					ScPattern pat = m_Doc->docPatterns[marker];
					double dX = (pat.width * item->lineWidth()) / 2.0;
					double dY = (pat.height * item->lineWidth()) / 2.0;
					arrowTrans.translate(bX, bY);
					arrowTrans.rotate(r);
					arrowTrans.translate(-dX, -dY);
					FPoint ba = FPoint(0.0, 0.0).transformPoint(arrowTrans, false);
					int z = m_Doc->itemAdd(PageItem::Symbol, PageItem::Unspecified, ba.x(), ba.y(), dX * 2.0, dY * 2.0, 0, CommonStrings::None, CommonStrings::None);
					endArrow = m_Doc->Items->at(z);
					endArrow->setPattern(marker);
					endArrow->setRotation(r, true);
					endArrow->setRedrawBounding();
					endArrow->OwnPage = m_Doc->OnPage(endArrow);
				//	Elements.append(ite);
					break;
				}
			}
		}
	}
	if (!gc->startMarker.isEmpty())
	{
		QString marker = gc->startMarker;
		if (importedPattTrans.contains(marker))
			marker = importedPattTrans[marker];
		if (markers.contains(marker))
		{
			FPoint End = item->PoLine.point(0);
			for (int xx = 1; xx < item->PoLine.size(); xx += 2)
			{
				FPoint Vector = item->PoLine.point(xx);
				if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
				{
					double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI) - 180.0;
					QTransform arrowTrans;
					double bX = item->xPos() + End.x();
					double bY = item->yPos() + End.y();
					ScPattern pat = m_Doc->docPatterns[marker];
					double dX = (pat.width * item->lineWidth()) / 2.0;
					double dY = (pat.height * item->lineWidth()) / 2.0;
					arrowTrans.translate(bX, bY);
					arrowTrans.rotate(r);
					arrowTrans.translate(-dX, -dY);
					FPoint ba = FPoint(0.0, 0.0).transformPoint(arrowTrans, false);
					int z = m_Doc->itemAdd(PageItem::Symbol, PageItem::Unspecified, ba.x(), ba.y(), dX * 2.0, dY * 2.0, 0, CommonStrings::None, CommonStrings::None);
					startArrow = m_Doc->Items->at(z);
					startArrow->setPattern(marker);
					startArrow->setRotation(r, true);
					startArrow->setRedrawBounding();
					startArrow->OwnPage = m_Doc->OnPage(startArrow);
				//	Elements.append(ite);
					break;
				}
			}
		}
	}
	if ((endArrow != nullptr) || (startArrow != nullptr))
	{
		QList<PageItem*> aElements;
		aElements.append(item);
		if (startArrow != nullptr)
			aElements.append(startArrow);
		if (endArrow != nullptr)
			aElements.append(endArrow);
		return m_Doc->groupObjectsList(aElements);
	}
	return item;
}

bool SVGPlug::isIgnorableNode(const QDomElement &e) const
{
	QString nodeName(e.tagName());
	return isIgnorableNodeName(nodeName);
}

bool SVGPlug::isIgnorableNodeName(const QString &n) const
{
	return n.startsWith("sodipodi") || n.startsWith("inkscape") || n == "metadata";
}

FPoint SVGPlug::parseTextPosition(const QDomElement &e, const FPoint* pos) const
{
	// FIXME According to spec, we should in fact return a point list
	double  x = pos ? pos->x() : 0.0;
	double  y = pos ? pos->y() : 0.0;

	if (e.hasAttribute("x"))
	{
		QString xatt =  e.attribute("x" , "0");
		if (xatt.contains(',') || xatt.contains(' '))
		{
			xatt.replace(QChar(','), QChar(' '));
			QStringList xl(xatt.split(QChar(' '), Qt::SkipEmptyParts));
			xatt = xl.first();
		}
		x = parseUnit(xatt);
	}

	if (e.hasAttribute("y"))
	{
		QString yatt =  e.attribute("y" , "0");
		if (yatt.contains(',') || yatt.contains(' '))
		{
			yatt.replace(QChar(','), QChar(' '));
			QStringList yl(yatt.split(QChar(' '), Qt::SkipEmptyParts));
			yatt = yl.first();
		}
		y = parseUnit(yatt);
	}

	if (e.hasAttribute("dx"))
	{
		QString dxatt =  e.attribute("dx" , "0");
		if (dxatt.contains(',') || dxatt.contains(' '))
		{
			dxatt.replace(QChar(','), QChar(' '));
			QStringList xl(dxatt.split(QChar(' '), Qt::SkipEmptyParts));
			dxatt = xl.first();
		}
		x += parseUnit(dxatt);
	}

	if (e.hasAttribute("dy"))
	{
		QString dyatt =  e.attribute("dy" , "0");
		if (dyatt.contains(',') || dyatt.contains(' '))
		{
			dyatt.replace(QChar(','), QChar(' '));
			QStringList xl(dyatt.split(QChar(' '), Qt::SkipEmptyParts));
			dyatt = xl.first();
		}
		y += parseUnit(dyatt);
	}

	return FPoint(x, y);
}

QSizeF SVGPlug::parseWidthHeight(const QDomElement &e)
{
	QSizeF size(550, 841);
	QString sw = e.attribute("width", "100%");
	QString sh = e.attribute("height", "100%");
	double w = 550;
	double h = 841;
	if (!sw.isEmpty())
		w = sw.endsWith("%") ? fromPercentage(sw) : parseUnit(sw);
	if (!sh.isEmpty())
		h = sh.endsWith("%") ? fromPercentage(sh) : parseUnit(sh);
	if (!e.attribute("viewBox").isEmpty())
	{
		QRectF viewBox = parseViewBox(e);
		double scw = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.width()  : size.width();
		double sch = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.height() : size.height();
		w *= (sw.endsWith("%") ? scw : 1.0);
		h *= (sh.endsWith("%") ? sch : 1.0);
	}
	else
	{
		w *= (sw.endsWith("%") ? size.width() : 1.0);
		h *= (sh.endsWith("%") ? size.height() : 1.0);
	}
	// OpenOffice files may not have width and height attributes, so avoid unnecessary large dimensions
	if (w > 10000 || h > 10000)
	{
		double m = max(w, h);
		w = w / m * 842;
		h = h / m * 842;
	}
	size.setWidth(w);
	size.setHeight(h);
	return size;
}

QRectF SVGPlug::parseViewBox(const QDomElement &e)
{
	QRectF box(0, 0, 0, 0);
	if (!e.attribute("viewBox").isEmpty())
	{
		QString viewbox(e.attribute("viewBox"));
		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
		if (points.size() > 3)
		{
			double left   = ScCLocale::toDoubleC(points[0]);
			double bottom = ScCLocale::toDoubleC(points[1]);
			double width  = ScCLocale::toDoubleC(points[2]);
			double height = ScCLocale::toDoubleC(points[3]);
			box.setCoords((int) left, (int) bottom, (int) (left + width), (int) (bottom + height));
		}
	}
	return box;
}

void SVGPlug::parseDefs(const QDomElement &e)
{
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement b = n.toElement();
		if (b.isNull())
			continue;
		SvgStyle svgStyle;
		parseStyle(&svgStyle, b);
		if (!svgStyle.Display)
			continue;
		QString STag2 = parseTagName(b);
		if (STag2 == "g")
			parseDefs(b);
		else if (STag2 == "clipPath")
			parseClipPath(b);
		else if (STag2 == "filter")
			parseFilter(b);
		else if (STag2 == "linearGradient" || STag2 == "radialGradient")
			parseGradient(b);
		else if (STag2 == "marker")
			parseMarker(b);
		else if (STag2 == "pattern")
			parsePattern(b);
	}
}

void SVGPlug::parseClipPath(const QDomElement &e)
{
	QString id(e.attribute("id"));
	if (!id.isEmpty())
	{
		FPointArray clip;
		QDomNode n2 = e.firstChild();
		QDomElement b2 = n2.toElement();
		while (b2.nodeName() == "use")
			b2 = getReferencedNode(b2);
		if (b2.nodeName() == "path")
			clip.parseSVG(b2.attribute("d"));
		else if (b2.nodeName() == "rect")
		{
			double x = parseUnit(b2.attribute("x", "0.0"));
			double y = parseUnit(b2.attribute("y", "0.0"));
			double width  = parseUnit(b2.attribute("width"));
			double height = parseUnit(b2.attribute("height"));
			clip.addQuadPoint(x, y, x, y, width+x, y, width+x, y);
			clip.addQuadPoint(width+x, y, width+x, y, width+x, height+y, width+x, height+y);
			clip.addQuadPoint(width+x, height+y, width+x, height+y, x, height+y, x, height+y);
			clip.addQuadPoint(x, height+y, x, height+y, x, y, x, y);
		}
		if (b2.hasAttribute("transform"))
		{
			QTransform transform = parseTransform(b2.attribute("transform"));
			clip.map(transform);
		}
		if (clip.size() >= 2)
			m_clipPaths.insert(id, clip);
	}
}

void SVGPlug::parseClipPathAttr(const QDomElement &e, FPointArray& clipPath) const
{
	clipPath.resize(0);
/*	if (e.hasAttribute("style"))
	{
		QString style = e.attribute("style").simplified();
		QStringList substyles = style.split(';', Qt::SkipEmptyParts);
		for (QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
		{
			QStringList substyle = (*it).split(':', Qt::SkipEmptyParts);
			QString command(substyle[0].trimmed());
			QString params(substyle[1].trimmed());
			if (command == "clip-path")
			{
				if (params.startsWith("url("))
				{
					unsigned int start = params.indexOf("#") + 1;
					unsigned int end = params.lastIndexOf(")");
					QString key = params.mid(start, end - start);
					QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
					if (it != m_clipPaths.end())
						clipPath = it.value().copy();
				}
			}
		}
	} */
	if (e.hasAttribute("clip-path"))
	{
		QString attr = e.attribute("clip-path");
		if (attr.startsWith("url("))
		{
			unsigned int start = attr.indexOf("#") + 1;
			unsigned int end = attr.lastIndexOf(")");
			QString key = attr.mid(start, end - start);
			auto it = m_clipPaths.find(key);
			if (it != m_clipPaths.end())
				clipPath = it.value().copy();
		}
	}
}

void SVGPlug::parseFilterAttr(const QDomElement &e, PageItem* item) const
{
	if (!e.hasAttribute("filter"))
		return;
	QString filterName;
	QString attr = e.attribute("filter");
	if (attr.startsWith("url("))
	{
		unsigned int start = attr.indexOf("#") + 1;
		unsigned int end = attr.lastIndexOf(")");
		filterName = attr.mid(start, end - start);
		if (filterName.isEmpty())
			return;
	}
	if (filters.contains(filterName))
	{
		filterSpec spec = filters[filterName];
		item->setFillBlendmode(spec.blendMode);
	}
}

QList<PageItem*> SVGPlug::parseA(const QDomElement &e)
{
	QList<PageItem*> aElements;
	setupNode(e);
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement b = n.toElement();
		if (b.isNull() || isIgnorableNode(b))
			continue;
		SvgStyle svgStyle;
		parseStyle(&svgStyle, b);
		if (!svgStyle.Display)
			continue;
		QList<PageItem*> el = parseElement(b);
		for (int ec = 0; ec < el.count(); ++ec)
			aElements.append(el.at(ec));
	}
	delete (m_gc.pop());
	return aElements;
}

QList<PageItem*> SVGPlug::parseGroup(const QDomElement &e)
{
	FPointArray clipPath;
	QList<PageItem*> GElements;
	QList<PageItem*> gElements;

	if ((importerFlags & LoadSavePlugin::lfCreateDoc) && (e.hasAttribute("inkscape:groupmode")) && (e.attribute("inkscape:groupmode") == "layer"))
	{
		setupNode(e);
		QString layerName = e.attribute("inkscape:label", "Layer");
		double trans = m_gc.top()->Opacity;
		int currentLayer = 0;
		if (!firstLayer)
			currentLayer = m_Doc->addLayer(layerName, true);
		else
			m_Doc->changeLayerName(currentLayer, layerName);
		m_Doc->setLayerVisible(currentLayer, true);
		m_Doc->setLayerLocked(currentLayer, false);
		m_Doc->setLayerPrintable(currentLayer, true);
		m_Doc->setLayerTransparency(currentLayer, trans);
		firstLayer = false;
		for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
		{
			QDomElement b = n.toElement();
			if (b.isNull() || isIgnorableNode(b))
				continue;
			SvgStyle svgStyle;
			parseStyle(&svgStyle, b);
			if (!svgStyle.Display)
				continue;
			QList<PageItem*> el = parseElement(b);
			for (int ec = 0; ec < el.count(); ++ec)
				GElements.append(el.at(ec));
		}
		delete (m_gc.pop());
		return GElements;
	}

	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	groupLevel++;
	setupNode(e);
	parseClipPathAttr(e, clipPath);
	int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, baseX, baseY, 1, 1, 0, CommonStrings::None, CommonStrings::None);
	PageItem *neu = m_Doc->Items->at(z);
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement b = n.toElement();
		if (b.isNull() || isIgnorableNode(b))
			continue;
		SvgStyle svgStyle;
		parseStyle(&svgStyle, b);
		if (!svgStyle.Display)
			continue;
		QList<PageItem*> el = parseElement(b);
		for (int ec = 0; ec < el.count(); ++ec)
			gElements.append(el.at(ec));
	}
	groupLevel--;
	const SvgStyle *gc = m_gc.top();
	if (clipPath.empty())
	{
		if (!gc->clipPath.empty())
			clipPath = gc->clipPath.copy();
	}
	parseFilterAttr(e, neu);

	if (gElements.isEmpty()|| (gElements.count() < 2 && (clipPath.empty()) && (gc->Opacity == 1.0)))
	{
		// Unfortunately we have to take the long route here, or we risk crash on undo/redo
		// FIXME : create group object after parsing grouped objects
		/*m_Doc->Items->takeAt(z);
		delete neu;*/
		Selection tmpSelection(m_Doc, false);
		tmpSelection.addItem(neu);
		m_Doc->itemSelection_DeleteItem(&tmpSelection);
		for (int gr = 0; gr < gElements.count(); ++gr)
		{
			GElements.append(gElements.at(gr));
		}
		delete (m_gc.pop());
		return GElements;
	}

	double minx =  std::numeric_limits<double>::max();
	double miny =  std::numeric_limits<double>::max();
	double maxx = -std::numeric_limits<double>::max();
	double maxy = -std::numeric_limits<double>::max();
	GElements.append(neu);
	for (int gr = 0; gr < gElements.count(); ++gr)
	{
		const PageItem* currItem = gElements.at(gr);
		double x1, x2, y1, y2;
		currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
		minx = qMin(minx, x1);
		miny = qMin(miny, y1);
		maxx = qMax(maxx, x2);
		maxy = qMax(maxy, y2);
	}
	double gx = minx;
	double gy = miny;
	double gw = maxx - minx;
	double gh = maxy - miny;
	if (((gx > -9999999) && (gx < 9999999)) && ((gy > -9999999) && (gy < 9999999)) && ((gw > 0) && (gw < 9999999)) && ((gh > 0) && (gh < 9999999)))
	{
		neu->setXYPos(gx, gy);
		neu->setWidthHeight(gw, gh);
		if (!clipPath.empty())
		{
			QTransform mm = gc->matrix;
			neu->PoLine = clipPath.copy();
			neu->PoLine.map(mm);
			neu->PoLine.translate(-gx + baseX, -gy + baseY);
			clipPath.resize(0);
			neu->Clip = flattenPath(neu->PoLine, neu->Segments);
			neu->ClipEdited = true;
			neu->FrameType = 3;
		}
		else
			neu->SetRectFrame();
		if (!e.attribute("id").isEmpty())
			neu->setItemName(e.attribute("id"));
		else
			neu->setItemName( tr("Group%1").arg(m_Doc->GroupCounter));
		neu->setFillTransparency(1 - gc->Opacity);
		neu->gXpos = neu->xPos() - gx;
		neu->gYpos = neu->yPos() - gy;
		neu->groupWidth = gw;
		neu->groupHeight = gh;
		for (int gr = 0; gr < gElements.count(); ++gr)
		{
			PageItem* currItem = gElements.at(gr);
			currItem->gXpos = currItem->xPos() - gx;
			currItem->gYpos = currItem->yPos() - gy;
			currItem->gWidth = gw;
			currItem->gHeight = gh;
			currItem->Parent = neu;
			neu->groupItemList.append(currItem);
			m_Doc->Items->removeAll(currItem);
		}
		neu->setRedrawBounding();
		neu->setTextFlowMode(PageItem::TextFlowDisabled);
		m_Doc->GroupCounter++;
	}
	else
	{
		// Group is out of valid coordinates, remove it
		GElements.removeAll(neu);
		Selection tmpSelection(m_Doc, false);
		tmpSelection.addItem(neu);
		tmpSelection.addItems(gElements);
		m_Doc->itemSelection_DeleteItem(&tmpSelection);
	}
	delete (m_gc.pop());

	return GElements;
}

QList<PageItem*> SVGPlug::parseDoc(const QDomElement &e)
{
	QList<PageItem*> GElements;
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement b = n.toElement();
		if (b.isNull() || isIgnorableNode(b))
			continue;
		SvgStyle svgStyle;
		parseStyle(&svgStyle, b);
		if (!svgStyle.Display)
			continue;
		QList<PageItem*> el = parseElement(b);
		for (int ec = 0; ec < el.count(); ++ec)
			GElements.append(el.at(ec));
	}
	return GElements;
}

QList<PageItem*> SVGPlug::parseElement(const QDomElement &e)
{
	QList<PageItem*> GElements;

	QString STag = parseTagName(e);
	if (STag.startsWith("svg:"))
		STag = STag.mid(4, - 1);
	if (STag == "g")
	{
		GElements = parseGroup(e);
		return GElements;
	}
	if (STag == "defs")
		parseDefs(e);
	else if (STag == "a")
		GElements = parseA(e);
	else if (STag == "circle")
		GElements = parseCircle(e);
	else if (STag == "clipPath")
		parseClipPath(e);
	else if (STag == "desc")
	{
		if (groupLevel == 1)
			docDesc = e.text();
	}
	else if (STag == "ellipse")
		GElements = parseEllipse(e);
	else if (STag == "image")
		GElements = parseImage(e);
	else if (STag == "line")
		GElements = parseLine(e);
	else if (STag == "linearGradient" || STag == "radialGradient")
		parseGradient(e);
	else if (STag == "path")
		GElements = parsePath(e);
	else if (STag == "polyline" || STag == "polygon")
		GElements = parsePolyline(e);
	else if (STag == "rect")
		GElements = parseRect(e);
	else if (STag == "switch")
		GElements = parseSwitch(e);
	else if (STag == "symbol")
		GElements = parseSymbol(e);
	else if (STag == "text")
		GElements = parseText(e);
	else if (STag == "title")
	{
		if (groupLevel == 1)
			docTitle = e.text();
	}
	else if (STag == "use")
		GElements = parseUse(e);
/*	else if (STag == "i:pgf")
	{
		QByteArray cdat;
		QByteArray ddat;
		cdat = e.text().simplified();
		QList<QByteArray> cdlist = cdat.split(' ');
		for (int cd = 0; cd < cdlist.count(); cd++)
		{
			ddat += QByteArray::fromBase64(cdlist[cd]);
		}
		QFile outf("/home/franz/testdata.txt");
		outf.open(QIODevice::WriteOnly);
		outf.write(ddat);
		outf.close();
		QString f2 = "/home/franz/testdata_decom.ai";
		FILE *source = fopen("/home/franz/testdata.txt", "rb");
		FILE *dest = fopen(f2, "wb");
		int ret;
		unsigned have;
		z_stream strm;
		char in[4096];
		char out[4096];
		strm.zalloc = Z_NULL;
		strm.zfree = Z_NULL;
		strm.opaque = Z_NULL;
		strm.avail_in = 0;
		strm.next_in = Z_NULL;
		ret = inflateInit(&strm);
		if (ret != Z_OK)
			return GElements;
		do
		{
			strm.avail_in = fread(in, 1, 4096, source);
			if (ferror(source))
			{
				(void)inflateEnd(&strm);
				return GElements;
			}
			if (strm.avail_in == 0)
				break;
			strm.next_in = (Bytef*)in;
			do
			{
				strm.avail_out = 4096;
				strm.next_out = (Bytef*)out;
				ret = inflate(&strm, Z_NO_FLUSH);
				assert(ret != Z_STREAM_ERROR);
				switch (ret)
				{
					case Z_NEED_DICT:
						ret = Z_DATA_ERROR;
					case Z_DATA_ERROR:
					case Z_MEM_ERROR:
						(void)inflateEnd(&strm);
						return GElements;
				}
				have = 4096 - strm.avail_out;
				if (fwrite(out, 1, have, dest) != have || ferror(dest))
				{
					(void)inflateEnd(&strm);
					return GElements;
				}
			}
			while (strm.avail_out == 0);
		}
		while (ret != Z_STREAM_END);
		(void)inflateEnd(&strm);
		fclose(source);
		fclose(dest);
	} */
	else if (!isIgnorableNodeName(STag))
	{
		// warn if unsupported SVG feature are encountered
		if (!m_unsupportedFeatures.contains(STag))
		{
			m_unsupportedFeatures.insert(STag, STag);
	//		qDebug() << QString("unsupported SVG feature: %1").arg(STag);
			unsupported = true;
		}
	}
	return GElements;
}

QList<PageItem*> SVGPlug::parseCircle(const QDomElement &e)
{
	QList<PageItem*> CElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double r = parseUnit(e.attribute("r"));
	double x = parseUnit(e.attribute("cx")) - r;
	double y = parseUnit(e.attribute("cy")) - r;
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, baseX, baseY, r * 2.0, r * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol);
	PageItem* ite = m_Doc->Items->at(z);
	QTransform mm;
	mm.translate(x, y);
	ite->PoLine.map(mm);
	FPoint wh = getMaxClipF(&ite->PoLine);
	ite->setWidthHeight(wh.x(), wh.y());
	finishNode(e, ite);
	CElements.append(ite);
	delete (m_gc.pop());
	return CElements;
}

QList<PageItem*> SVGPlug::parseEllipse(const QDomElement &e)
{
	QList<PageItem*> EElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double rx = parseUnit(e.attribute("rx"));
	double ry = parseUnit(e.attribute("ry"));
	double x = parseUnit(e.attribute("cx")) - rx;
	double y = parseUnit(e.attribute("cy")) - ry;
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, baseX, baseY, rx * 2.0, ry * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol);
	PageItem* ite = m_Doc->Items->at(z);
	QTransform mm;
	mm.translate(x, y);
	ite->PoLine.map(mm);
	FPoint wh = getMaxClipF(&ite->PoLine);
	ite->setWidthHeight(wh.x(), wh.y());
	finishNode(e, ite);
	EElements.append(ite);
	delete (m_gc.pop());
	return EElements;
}

QList<PageItem*> SVGPlug::parseImage(const QDomElement &e)
{
	FPointArray clipPath;
	QList<PageItem*> IElements;
	QString hrefData = e.attribute("xlink:href");
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double x = e.attribute("x").isEmpty() ? 0.0 : parseUnit(e.attribute("x"));
	double y = e.attribute("y").isEmpty() ? 0.0 : parseUnit(e.attribute("y"));
	double w = e.attribute("width").isEmpty() ? 1.0 : parseUnit(e.attribute("width"));
	double h = e.attribute("height").isEmpty() ? 1.0 : parseUnit(e.attribute("height"));
	setupNode(e);
	parseClipPathAttr(e, clipPath);
	int z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, baseX, baseY, w, h, 1, m_Doc->itemToolPrefs().imageFillColor, m_Doc->itemToolPrefs().imageStrokeColor);
	PageItem* ite = m_Doc->Items->at(z);
	if (!hrefData.isEmpty())
	{
		// hrefData contains a filename
		if (!hrefData.startsWith("data:"))
		{
			// Be a bit careful about where we load from
			QUrl fileUrl(hrefData);
			if (fileUrl.isValid())
			{
				bool isLocalFile = fileUrl.isLocalFile();
				QString host = fileUrl.host();
				if (isLocalFile && host.isEmpty())
				{
					QString imageFilePath = fileUrl.toLocalFile();
					m_Doc->loadPict(imageFilePath, ite);
				}
				else if (host.isEmpty())
				{
					QString imageFilePath(hrefData);
					if (fileUrl.isRelative())
						imageFilePath = m_currentSvgFileDir + "/" + fileUrl.path();
					m_Doc->loadPict(imageFilePath, ite);
				}
			}
		}
		else
		{
			int startData = hrefData.indexOf(",");
			QString dataType = hrefData.left(startData);
			hrefData.remove(0, startData + 1);
			QByteArray ba;
			ba.append(hrefData.toLatin1());
			if (dataType.contains("base64"))
				ba = QByteArray::fromBase64(ba);
			QTemporaryFile tempFile(QDir::tempPath() + "/scribus_temp_svg_XXXXXX.png");
			if (tempFile.open())
			{
				QString fileName = getLongPathName(tempFile.fileName());
				tempFile.setAutoRemove(false);
				tempFile.close();
				ite->isTempFile = true;
				ite->isInlineImage = true;
				QImage img;
				img.loadFromData(ba);
				img.save(fileName, "PNG");
				m_Doc->loadPict(fileName, ite);
			}
		}
	}
	if (!clipPath.empty())
		ite->PoLine = clipPath.copy();
	clipPath.resize(0);
	ite->PoLine.map(QTransform(1.0, 0.0, 0.0, 1.0, x, y));
	ite->Clip = flattenPath(ite->PoLine, ite->Segments);
	finishNode(e, ite);
	IElements.append(ite);
	delete (m_gc.pop());
	return IElements;
}

QList<PageItem*> SVGPlug::parseLine(const QDomElement &e)
{
	QList<PageItem*> LElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double x1 = e.attribute("x1").isEmpty() ? 0.0 : parseUnit(e.attribute("x1"));
	double y1 = e.attribute("y1").isEmpty() ? 0.0 : parseUnit(e.attribute("y1"));
	double x2 = e.attribute("x2").isEmpty() ? 0.0 : parseUnit(e.attribute("x2"));
	double y2 = e.attribute("y2").isEmpty() ? 0.0 : parseUnit(e.attribute("y2"));
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	int z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
	PageItem* ite = m_Doc->Items->at(z);
	ite->PoLine.resize(4);
	ite->PoLine.setPoint(0, FPoint(x1, y1));
	ite->PoLine.setPoint(1, FPoint(x1, y1));
	ite->PoLine.setPoint(2, FPoint(x2, y2));
	ite->PoLine.setPoint(3, FPoint(x2, y2));
	ite = finishNode(e, ite);
	LElements.append(ite);
	delete (m_gc.pop());
	return LElements;
}

QVector<double> SVGPlug::parseNumbersList(const QString& numbersStr) const
{
	QVector<double> numbers;
	if (numbersStr.isEmpty())
		return numbers;
	numbers.reserve(8);

	const QChar* str = numbersStr.data();

	while (str->isSpace())
		++str;
	while (ScCLocale::isDigit(str->unicode()) ||
	       *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
	       *str == QLatin1Char('.'))
	{
		numbers.append(ScCLocale::toDoubleC(str));

		while (str->isSpace())
			++str;
		if (*str == QLatin1Char(','))
			++str;

		//eat the rest of space
		while (str->isSpace())
			++str;
	}

	return numbers;
}

QList<PageItem*> SVGPlug::parsePath(const QDomElement &e)
{
	FPointArray pArray;
	QList<PageItem*> PElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	PageItem::ItemType itype = pArray.parseSVG(e.attribute("d")) ? PageItem::PolyLine : PageItem::Polygon; 
	int z = m_Doc->itemAdd(itype, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
	PageItem* ite = m_Doc->Items->at(z);
	ite->fillRule = (gc->fillRule != "nonzero");
	ite->PoLine = pArray;
	if (ite->PoLine.size() < 4)
	{
		tmpSel->addItem(ite);
		m_Doc->itemSelection_DeleteItem(tmpSel);
	}
	else
	{
		ite = finishNode(e, ite);
		PElements.append(ite);
	}
	delete (m_gc.pop());
	return PElements;
}

QList<PageItem*> SVGPlug::parsePolyline(const QDomElement &e)
{
	int z;
	QList<PageItem*> PElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	QString points = e.attribute("points");
	if (!points.isEmpty())
	{
		QString STag = parseTagName(e);
		points = points.simplified().replace(',', " ");
		QStringList pointList = points.split(' ', Qt::SkipEmptyParts);
		if ((STag == "polygon") && (pointList.count() > 4))
			z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
		else
			z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
		PageItem* ite = m_Doc->Items->at(z);
		ite->fillRule = (gc->fillRule != "nonzero");
		ite->PoLine.resize(0);
		ite->PoLine.svgInit();
		bool bFirst = true;
		double x = 0.0;
		double y = 0.0;
		for (auto it = pointList.begin(); it != pointList.end(); it++)
		{
			x = ScCLocale::toDoubleC(*(it++));
			y = ScCLocale::toDoubleC(*it);
			if (bFirst)
			{
				ite->PoLine.svgMoveTo(x, y);
				bFirst = false;
			}
			else
			{
				ite->PoLine.svgLineTo(x, y);
			}
		}
		if ((STag == "polygon") && (pointList.count() > 4))
			ite->PoLine.svgClosePath();
		else
			ite->convertTo(PageItem::PolyLine);
		if (ite->PoLine.size() < 4)
		{
			tmpSel->addItem(ite);
			m_Doc->itemSelection_DeleteItem(tmpSel);
		}
		else
		{
			ite = finishNode(e, ite);
			PElements.append(ite);
		}
	}
	delete (m_gc.pop());
	return PElements;
}

QList<PageItem*> SVGPlug::parseRect(const QDomElement &e)
{
	QList<PageItem*> RElements;
	double baseX = m_Doc->currentPage()->xOffset();
	double baseY = m_Doc->currentPage()->yOffset();
	double x = parseUnit(e.attribute("x"));
	double y = parseUnit(e.attribute("y"));
	double width = parseUnit(e.attribute("width"));
	double height = parseUnit(e.attribute("height"));
	double rx = e.attribute("rx").isEmpty() ? 0.0 : parseUnit(e.attribute("rx"));
	double ry = e.attribute("ry").isEmpty() ? 0.0 : parseUnit(e.attribute("ry"));
	setupNode(e);
	const SvgStyle *gc = m_gc.top();
	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, baseX, baseY, width, height, gc->LWidth, gc->FillCol, gc->StrokeCol);
	PageItem* ite = m_Doc->Items->at(z);
	if ((rx != 0) || (ry != 0))
	{
		ite->setCornerRadius(qMax(rx, ry));
		ite->SetFrameRound();
		m_Doc->setRedrawBounding(ite);
	}
	QTransform mm;
	mm.translate(x, y);
	ite->PoLine.map(mm);
	FPoint wh = getMaxClipF(&ite->PoLine);
	ite->setWidthHeight(wh.x(), wh.y());
	finishNode(e, ite);
	RElements.append(ite);
	delete (m_gc.pop());
	return RElements;
}

QList<PageItem*> SVGPlug::parseText(const QDomElement &e)
{
	QList<PageItem*> GElements;
	setupNode(e);
	double chunkWidth = 0;
	FPoint currentPos = parseTextPosition(e);
	const SvgStyle *gc = m_gc.top();
	if (gc->textAnchor != "start")
		getTextChunkWidth(e, chunkWidth);
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		if (n.isElement())
		{
			if (parseTagName(n.toElement()) == "tspan")
			{
				QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkWidth);
				for (int ec = 0; ec < el.count(); ++ec)
					GElements.append(el.at(ec));
			}
			else if (parseTagName(n.toElement()) == "textPath")
			{
				GElements = parseText(n.toElement());
				break;
			}
		}
		if (n.isText())
		{
			QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkWidth);
			for (int ec = 0; ec < el.count(); ++ec)
				GElements.append(el.at(ec));
		}
	}
	delete (m_gc.pop());
	return GElements;
}

QList<PageItem*> SVGPlug::parseTextSpan(const QDomElement& e, FPoint& currentPos, double chunkW)
{
	QList<PageItem*> GElements;
	setupNode(e);
	currentPos   = parseTextPosition(e, &currentPos);
	const SvgStyle *gc = m_gc.top();
	if ((e.hasAttribute("x") || e.hasAttribute("y")) && (gc->textAnchor != "start"))
	{
		chunkW = 0;
		getTextChunkWidth(e, chunkW);
	}
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		if (n.isElement() && (parseTagName(n.toElement()) == "tspan"))
		{
			QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkW);
			for (int ec = 0; ec < el.count(); ++ec)
				GElements.append(el.at(ec));
		}
		if (n.isText())
		{
			QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkW);
			for (int ec = 0; ec < el.count(); ++ec)
				GElements.append(el.at(ec));
		}
	}
	delete (m_gc.pop());
	return GElements;
}

QList<PageItem*> SVGPlug::parseTextNode(const QDomText& e, FPoint& currentPos, double chunkW)
{
	QList<PageItem*> GElements;
	double baseX  = m_Doc->currentPage()->xOffset();
	double baseY  = m_Doc->currentPage()->yOffset();
	double startX = currentPos.x();
	double startY = currentPos.y();
	
	QString textString = e.data().simplified();
	if (textString.isEmpty())
		return GElements;
	
	const SvgStyle *gc   = m_gc.top();
	QFont textFont = getFontFromStyle(*gc);
	QFontMetrics fm(textFont);
	double width   = fm.horizontalAdvance(textString);

	if (gc->textAnchor == "middle")
		startX -= chunkW / 2.0;
	else if (gc->textAnchor == "end")
		startX -= chunkW;

	FPointArray textPath;
	QString textFillColor   = gc->FillCol;
	QString textStrokeColor = gc->StrokeCol;

	// Text outline is generated using a big font size
	// and scaled afterwards. This is done in order to
	// overcome some QPainterPath issues when using
	// small font sizes, especially bad glyph advances
	QFont painterFont = textFont;
	painterFont.setPointSizeF(100.0);
	double fontScale = textFont.pointSizeF() / 100.0;

	QPainterPath painterPath;
	painterPath.addText(0.0, 0.0, painterFont, textString);

	QTransform textTrans;
	textTrans.translate(startX, startY);
	textTrans.scale(fontScale, fontScale);
	painterPath = textTrans.map(painterPath);

	textPath.fromQPainterPath(painterPath);
	if (!textPath.empty())
	{
//		double  lineWidth = 0.0;
		int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, textFillColor, textStrokeColor);
		PageItem* ite = m_Doc->Items->at(z);
		ite->PoLine = textPath;
		finishNode(e, ite);
		GElements.append(ite);
	}
	currentPos.setX(currentPos.x() + width);
	return GElements;
}

QList<PageItem*> SVGPlug::parseSwitch(const QDomElement &e)
{
	QString href;
	QStringList hrefs;
	QList<PageItem*> SElements;
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement de = n.toElement();
		QString STag = parseTagName(de);
		if (STag == "foreignObject")
		{
			if (de.hasAttribute("xlink:href"))
			{
				href = de.attribute("xlink:href").mid(1);
				if (!href.isEmpty())
					hrefs.append(href);
			}
			for (QDomNode n1 = de.firstChild(); !n1.isNull(); n1 = n1.nextSibling())
			{
				QDomElement de1 = n1.toElement();
				if (de1.hasAttribute("xlink:href"))
				{
					href = de1.attribute("xlink:href").mid(1);
					if (!href.isEmpty())
						hrefs.append(href);
				}
			}
		}
		else
		{
			if (de.hasAttribute("requiredExtensions") || de.hasAttribute("requiredFeatures"))
				continue;
			if (de.hasAttribute("id") && hrefs.contains(de.attribute("id")))
				continue;
			SElements = parseElement(de);
			if (!SElements.isEmpty())
				break;
		}
	}
	return SElements;
}

QList<PageItem*> SVGPlug::parseSymbol(const QDomElement &e)
{
	QList<PageItem*> SElements;
	QString id = e.attribute("id");
	if (!id.isEmpty())
		m_nodeMap.insert(id, e);
	return SElements;
}

QList<PageItem*> SVGPlug::parseUse(const QDomElement &e)
{
	QList<PageItem*> UElements;
	setupNode(e);
	if (e.hasAttribute("x") || e.hasAttribute("y"))
	{
		QTransform matrix;
		double  xAtt = ScCLocale::toDoubleC(e.attribute("x", "0.0"));
		double  yAtt = ScCLocale::toDoubleC(e.attribute("y", "0.0"));
		SvgStyle *gc = m_gc.top();
		gc->matrix   = QTransform(1.0, 0.0, 0.0, 1.0, xAtt, yAtt) * gc->matrix;
	}
	QString href = e.attribute("xlink:href").mid(1);
	QMap<QString, QDomElement>::Iterator it = m_nodeMap.find(href);
	if (it != m_nodeMap.end())
	{
		QDomElement elem = it.value().toElement();
		if (parseTagName(elem) == "symbol")
			UElements = parseGroup(elem);
		else
			UElements = parseElement(elem);
	}
	delete (m_gc.pop());
	return UElements;
}

QFont SVGPlug::getFontFromStyle(const SvgStyle& style) const
{
	QFont font(QApplication::font());
	font.setStyleStrategy(QFont::PreferOutline);

	if (!style.FontFamily.isEmpty())
		font.setFamily(style.FontFamily);

	if (!style.FontStyle.isEmpty())
	{
		if (style.FontStyle == "normal")
			font.setStyle(QFont::StyleNormal);
		else if (style.FontStyle == "italic")
			font.setStyle(QFont::StyleItalic);
		else if (style.FontStyle == "oblique")
			font.setStyle(QFont::StyleOblique);
	}

	if (!style.FontWeight.isEmpty())
	{
		if (style.FontWeight == "normal")
			font.setWeight(QFont::Normal);
		else if (style.FontWeight == "bold")
			font.setWeight(QFont::Bold);
		else if (style.FontWeight == "bolder")
			font.setWeight(QFont::DemiBold);
		else if (style.FontWeight == "lighter")
			font.setWeight(QFont::Light);
		else
		{
			bool weightIsNum = false;
			int  fontWeight  = style.FontWeight.toInt(&weightIsNum);
			if (weightIsNum)
			{
				if (fontWeight == 100 || fontWeight == 200)
					font.setWeight(QFont::Light);
				else if (fontWeight == 300 || fontWeight == 400)
					font.setWeight(QFont::Normal);
				else if (fontWeight == 500 || fontWeight == 600)
					font.setWeight(QFont::DemiBold);
				else if (fontWeight == 700 || fontWeight == 800)
					font.setWeight(QFont::Bold);
				else if (fontWeight == 900)
					font.setWeight(QFont::Black);
			}
		}
	}

	if (!style.FontStretch.isEmpty())
	{
		if (style.FontStretch == "normal")
			font.setStretch(QFont::Unstretched);
		else if (style.FontStretch == "ultra-condensed")
			font.setStretch(QFont::UltraCondensed);
		else if (style.FontStretch == "extra-condensed")
			font.setStretch(QFont::ExtraCondensed);
		else if (style.FontStretch == "condensed")
			font.setStretch(QFont::Condensed);
		else if (style.FontStretch == "semi-condensed")
			font.setStretch(QFont::SemiCondensed);
		else if (style.FontStretch == "semi-expanded")
			font.setStretch(QFont::SemiExpanded);
		else if (style.FontStretch == "expanded")
			font.setStretch(QFont::Expanded);
		else if (style.FontStretch == "extra-expanded")
			font.setStretch(QFont::ExtraExpanded);
		else if (style.FontStretch == "ultra-expanded")
			font.setStretch(QFont::UltraExpanded);
		else if (style.FontStretch == "narrower")
			font.setStretch(QFont::SemiCondensed);
		else if (style.FontStretch == "wider")
			font.setStretch(QFont::SemiExpanded);
	}
	if (!style.textDecoration.isEmpty())
	{
		bool underline = false;
		bool overline = false;
		bool strikeOut = false;
		if (style.textDecoration == "underline")
			underline = true;
		else if (style.textDecoration == "overline")
			overline = true;
		else if (style.textDecoration == "line-through")
			strikeOut = true;
		font.setUnderline(underline);
		font.setOverline(overline);
		font.setStrikeOut(strikeOut);
	}
	font.setPointSizeF(style.FontSize);
	return font;
}

QDomElement SVGPlug::getReferencedNode(const QDomElement &e) const
{
	QDomElement ret;
	QString href = e.attribute("xlink:href").mid(1);
	auto it = m_nodeMap.find(href);
	if (it != m_nodeMap.end())
		ret = it.value().toElement();
	return ret;
}

bool SVGPlug::getTextChunkWidth(const QDomElement &e, double& width)
{
	bool doBreak = false;
	setupNode(e);

	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		if (n.isElement() && (parseTagName(n.toElement()) == "tspan"))
		{
			QDomElement elem = n.toElement();
			if (elem.hasAttribute("x") || elem.hasAttribute("y"))
			{
				doBreak = true;
				break;
			}
			doBreak = getTextChunkWidth(n.toElement(), width);
			if (doBreak) break;
		}
		if (n.isText())
		{
			QDomText text = n.toText();
			QString  textString = text.data().simplified();
			if (textString.length() > 0)
			{
				const SvgStyle *gc   = m_gc.top();
				QFont textFont = getFontFromStyle(*gc);

				// This is to match the scaling done in
				// parseTextNode()
				double fontSize = textFont.pointSizeF();
				textFont.setPointSizeF(100.0);

				QFontMetrics fm(textFont);
				width += fm.horizontalAdvance(textString) * (fontSize / 100.0);
			}
		}
	}
	delete (m_gc.pop());
	return doBreak;
}

double SVGPlug::fromPercentage(const QString &s) const
{
	QString s1 = s;
	if (s1.endsWith(";"))
		s1.chop(1);
	if (s1.endsWith("%"))
	{
		s1.chop(1);
		return ScCLocale::toDoubleC(s1) / 100.0;
	}
	return ScCLocale::toDoubleC(s1);
}

double SVGPlug::parseFontSize(const QString& fsize) const
{
	bool noUnit  = true;
	QString unit = fsize.right(2);
	if (unit == "pt" || unit == "cm" || unit == "mm" || 
		unit == "in" || unit == "px")
	{
		noUnit = false;
	}
	double value = parseUnit(fsize);
	if (noUnit)
		value *= 0.8;
	return value;
}

double SVGPlug::parseUnit(const QString &unit) const
{
	bool noUnit = false;
	QString unitval = unit;
	if (unit.right(2) == "pt")
		unitval.replace("pt", "");
	else if (unit.right(2) == "cm")
		unitval.replace("cm", "");
	else if (unit.right(2) == "mm")
		unitval.replace("mm" , "");
	else if (unit.right(2) == "in")
		unitval.replace("in", "");
	else if (unit.right(2) == "px")
		unitval.replace("px", "");
	if (unitval == unit)
		noUnit = true;
	double value = ScCLocale::toDoubleC(unitval);
	if (unit.right(2) == "pt")
		{}/* value = value; */ //no change
	else if (unit.right(2) == "cm")
		value = (value / 2.54) * 72;
	else if (unit.right(2) == "mm")
		value = (value / 25.4) * 72;
	else if (unit.right(2) == "in")
		value = value * 72;
	else if (unit.right(2) == "px")
		value = value * 0.8;
	else if (noUnit)
		{}/* value = value; */ //no change
	return value;
}

QTransform SVGPlug::parseTransform(const QString &transform) const
{
	QTransform ret;
	// Workaround for QString::split() bug when string ends with space
	QString trans = transform.simplified();
	// Split string for handling 1 transform statement at a time
	QStringList subtransforms = trans.split(')', Qt::SkipEmptyParts);
	QStringList::ConstIterator it = subtransforms.begin();
	QStringList::ConstIterator end = subtransforms.end();
	for (; it != end; ++it)
	{
		QTransform result;
		QStringList subtransform = it->split('(', Qt::SkipEmptyParts);
		subtransform[0] = subtransform[0].trimmed().toLower();
		subtransform[1] = subtransform[1].simplified();
		QVector<double> params = parseNumbersList(subtransform[1]);
		if (subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
			subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
		if (subtransform[0] == "rotate")
		{
			if (params.count() == 3)
			{
				double x = params[1];
				double y = params[2];
				result.translate(x, y);
				result.rotate(params[0]);
				result.translate(-x, -y);
			}
			else
				result.rotate(params[0]);
		}
		else if (subtransform[0] == "translate")
		{
			if (params.count() == 2)
				result.translate(params[0], params[1]);
			else    // Spec : if only one param given, assume 2nd param to be 0
				result.translate(params[0], 0);
		}
		else if (subtransform[0] == "scale")
		{
			if (params.count() == 2)
				result.scale(params[0], params[1]);
			else    // Spec : if only one param given, assume uniform scaling
				result.scale(params[0], params[0]);
		}
		else if (subtransform[0] == "skewx")
			result.shear(tan(params[0] * 0.01745329251994329576), 0.0F);
		else if (subtransform[0] == "skewy")
			result.shear(0.0F, tan(params[0] * 0.01745329251994329576));
		else if (subtransform[0] == "matrix")
		{
			if (params.count() >= 6)
			{
				double sx = params[0];
				double sy = params[3];
				double p1 = params[1];
				double p2 = params[2];
				double p4 = params[4];
				double p5 = params[5];
				result = QTransform(sx, p1, p2, sy, p4, p5);
			}
		}
		ret = result * ret;
	}
	return ret;
}

const char * SVGPlug::getCoord(const char *ptr, double &number) const
{
	int integer(0);
	int exponent(0);
	double decimal(0.0);
	double frac(1.0);
	int sign(1);
	int expsign(1);

	// read the sign
	if (*ptr == '+')
		ptr++;
	else if (*ptr == '-')
	{
		ptr++;
		sign = -1;
	}

	// read the integer part
	while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
		integer = (integer * 10) + *(ptr++) - '0';
	if (*ptr == '.') // read the decimals
	{
		ptr++;
		while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
			decimal += (*(ptr++) - '0') * (frac *= 0.1);
	}

	if (*ptr == 'e' || *ptr == 'E') // read the exponent part
	{
		ptr++;

		// read the sign of the exponent
		if (*ptr == '+')
			ptr++;
		else if (*ptr == '-')
		{
			ptr++;
			expsign = -1;
		}

		exponent = 0;
		while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
		{
			exponent *= 10;
			exponent += *ptr - '0';
			ptr++;
		}
	}
	number = integer + decimal;
	number *= sign * pow(static_cast<double>(10), static_cast<double>(expsign * exponent));
	// skip the following space
	if (*ptr == ' ')
		ptr++;

	return ptr;
}

QString SVGPlug::parseColor(const QString &s)
{
	QString str = s.trimmed();
	if (str == "none")
		return CommonStrings::None;

	QColor c;
	if (str.length() > 11) // icc-color()
	{
		int iccColorIndex  = str.indexOf("icc-color");
		if (iccColorIndex >= 0)
		{
			QString iccColorName = parseIccColor(str);
			if (iccColorName.length() > 0)
				return iccColorName;
		}
	}

	if (str.startsWith("rgb("))
	{
		QStringList colors = str.split(',', Qt::SkipEmptyParts);
		QString r = colors[0].right(colors[0].length() - 4);
		QString g = colors[1];
		QString b = colors[2].left(colors[2].length() - 1);
		if (r.contains("%"))
		{
			r.chop(1);
			r = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(r)) / 100.0) );
		}
		if (g.contains("%"))
		{
			g.chop(1);
			g = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(g)) / 100.0) );
		}
		if (b.contains("%"))
		{
			b.chop(1);
			b = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(b)) / 100.0) );
		}
		c = QColor(r.toInt(), g.toInt(), b.toInt());
	}
	else
		c.setNamedColor(str);

	ScColor tmp;
	tmp.fromQColor(c);
	tmp.setSpotColor(false);
	tmp.setRegistrationColor(false);
	QString newColorName = "FromSVG" + c.name();
	QString colorName = m_Doc->PageColors.tryAddColor(newColorName, tmp);
	if (colorName == newColorName)
		importedColors.append(newColorName);
	return colorName;
}

QString SVGPlug::parseIccColor(const QString &s)
{
	QColor color;
	QString ret;
	bool iccColorFound = false;
	int iccColorIndex  = s.indexOf("icc-color");
	if (iccColorIndex < 0)
		return ret;
	int iccFirst = s.indexOf("(", iccColorIndex);
	int iccLast  = s.indexOf(")", iccColorIndex);
	if (iccFirst >= 0 && iccLast >= 0)
	{
		QString iccColor = s.mid(iccFirst + 1, iccLast - iccFirst - 1);
		iccColor = iccColor.trimmed();
		QStringList colors = iccColor.split(',', Qt::SkipEmptyParts);
		if (colors.count() == 5) // then we assume this is a cmyk color
		{
			QString cs = colors[1];
			QString ms = colors[2];
			QString ys = colors[3];
			QString ks = colors[4];
			if (cs.contains("%"))
			{
				cs.chop(1);
				cs = QString::number(ScCLocale::toDoubleC(cs) / 100);
			}
			if (ms.contains("%"))
			{
				ms.chop(1);
				ms = QString::number(ScCLocale::toDoubleC(ms) / 100);
			}
			if (ys.contains("%"))
			{
				ys.chop(1);
				ys = QString::number(ScCLocale::toDoubleC(ys) / 100);
			}
			if (ks.contains("%"))
			{
				ks.chop(1);
				ks = QString::number(ScCLocale::toDoubleC(ks) / 100);
			}
			double cv = ScCLocale::toDoubleC(cs);
			double mv = ScCLocale::toDoubleC(ms);
			double yv = ScCLocale::toDoubleC(ys);
			double kv = ScCLocale::toDoubleC(ks);
			color.setCmykF(cv, mv, yv, kv);
			iccColorFound = true;
		}
	}
	if (!iccColorFound)
		return ret;
	ScColor tmp;
	tmp.fromQColor(color);
	tmp.setSpotColor(false);
	tmp.setRegistrationColor(false);
	QString newColorName = "FromSVG"+tmp.name();
	QString fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
	if (fNam == newColorName)
		importedColors.append(newColorName);
	ret = fNam;
	return ret;
}

void SVGPlug::parsePA(SvgStyle *obj, const QString &command, const QString &params)
{
	if (command == "display")
		obj->Display = params != "none";
	else if (command == "stroke-opacity")
		obj->StrokeOpacity  = fromPercentage(params);
	else if (command == "fill-opacity")
		obj->FillOpacity = fromPercentage(params);
	else if (command == "opacity")
		obj->Opacity = fromPercentage(params);
	else if (command == "fill")
	{
//		if ((obj->InherCol) && (params == "currentColor"))
		if (params == "currentColor")
			obj->FillCol = obj->CurCol;
		else if (params == "none")
		{
			obj->FillCol = CommonStrings::None;
			obj->FillGradientType = 0;
		}
		else if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			QString key = params.mid(start, end - start);
			obj->FillGradientType = 0;
			obj->matrixgf = QTransform();
			bool firstMatrixValid = false;
			if (m_gradients[key].matrixValid)
			{
				firstMatrixValid = true;
				obj->matrixgf = m_gradients[key].matrix;
			}
			while (!m_gradients[key].reference.isEmpty())
			{
				QString key2 = m_gradients[key].reference;
				const GradientHelper& gradientHelper(m_gradients[key2]);
				if (gradientHelper.typeValid)
					obj->FillGradientType = gradientHelper.type;
				if (obj->FillGradientType != 8)
				{
					obj->GFillCol1 = key2;
					if (gradientHelper.gradientValid)
						obj->FillGradient = gradientHelper.gradient;
					if (gradientHelper.cspaceValid)
						obj->FillCSpace = gradientHelper.cspace;
					if (gradientHelper.x1Valid)
						obj->GradFillX1 = gradientHelper.x1;
					if (gradientHelper.y1Valid)
						obj->GradFillY1 = gradientHelper.y1;
					if (gradientHelper.x2Valid)
						obj->GradFillX2 = gradientHelper.x2;
					if (gradientHelper.y2Valid)
						obj->GradFillY2 = gradientHelper.y2;
					if (gradientHelper.fxValid)
						obj->GradFillFX = gradientHelper.fx;
					if (gradientHelper.fyValid)
						obj->GradFillFY = gradientHelper.fy;
					if (gradientHelper.matrixValid)
						obj->matrixgf = gradientHelper.matrix;
				}
				else
				{
					obj->GFillCol1 = key2;
					if ((m_gradients[key2].matrixValid) && (!firstMatrixValid))
						obj->matrixgf *= m_gradients[key2].matrix;
				}
				key = m_gradients[key].reference;
			}
			if (obj->FillGradientType != 8)
			{
				key = params.mid(start, end - start);
				const GradientHelper& gradientHelper(m_gradients[key]);
				if (gradientHelper.typeValid)
					obj->FillGradientType = m_gradients[key].type;
				key = m_gradients[key].reference;
				if (obj->FillGradientType != 8)
				{
					if (gradientHelper.gradientValid)
						obj->FillGradient = gradientHelper.gradient;
					if (gradientHelper.cspaceValid)
						obj->FillCSpace = gradientHelper.cspace;
					if (gradientHelper.x1Valid)
						obj->GradFillX1 = gradientHelper.x1;
					if (gradientHelper.y1Valid)
						obj->GradFillY1 = gradientHelper.y1;
					if (gradientHelper.x2Valid)
						obj->GradFillX2 = gradientHelper.x2;
					if (gradientHelper.y2Valid)
						obj->GradFillY2 = gradientHelper.y2;
					if (gradientHelper.fxValid)
						obj->GradFillFX = gradientHelper.fx;
					if (gradientHelper.fyValid)
						obj->GradFillFY = gradientHelper.fy;
					if (gradientHelper.matrixValid)
						obj->matrixgf = gradientHelper.matrix;
					obj->GFillCol1 = key;
				}
				else
				{
					obj->GFillCol1 = key;
					if (m_gradients[key].matrixValid)
						obj->matrixgf = m_gradients[key].matrix;
				}
			}
			obj->FillCol = CommonStrings::None;
		}
		else
		{
			obj->FillCol = parseColor(params);
			obj->FillGradientType = 0;
		}
	}
	else if (command == "fill-rule")
	{
		obj->fillRule = params;
	}
	else if (command == "color")
	{
		if (params == "none")
			obj->CurCol = CommonStrings::None;
		else if (params.startsWith("url("))
			obj->CurCol = CommonStrings::None;
		else if (params == "currentColor")
			obj->CurCol = obj->CurCol;
		else
			obj->CurCol = parseColor(params);
	}
	else if (command == "stroke")
	{
//		if ((obj->InherCol) && (params == "currentColor"))
		if (params == "currentColor")
			obj->StrokeCol = obj->CurCol;
		else if (params == "none")
		{
			obj->StrokeCol = CommonStrings::None;
			obj->StrokeGradientType = 0;
		}
		else if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			QString key = params.mid(start, end - start);
			obj->StrokeGradientType = 0;
			obj->matrixgs = QTransform();
			bool firstMatrixValid = false;
			if (m_gradients[key].matrixValid)
			{
				firstMatrixValid = true;
				obj->matrixgs = m_gradients[key].matrix;
			}
			while (!m_gradients[key].reference.isEmpty())
			{
				QString key2 = m_gradients[key].reference;
				const GradientHelper& gradientHelper(m_gradients[key2]);
				if (gradientHelper.typeValid)
					obj->StrokeGradientType = gradientHelper.type;
				if (obj->StrokeGradientType != 8)
				{
					obj->GStrokeCol1 = key2;
					if (gradientHelper.gradientValid)
						obj->StrokeGradient = gradientHelper.gradient;
					if (gradientHelper.cspaceValid)
						obj->StrokeCSpace = gradientHelper.cspace;
					if (gradientHelper.x1Valid)
						obj->GradStrokeX1 = gradientHelper.x1;
					if (gradientHelper.y1Valid)
						obj->GradStrokeY1 = gradientHelper.y1;
					if (gradientHelper.x2Valid)
						obj->GradStrokeX2 = gradientHelper.x2;
					if (gradientHelper.y2Valid)
						obj->GradStrokeY2 = gradientHelper.y2;
					if (gradientHelper.fxValid)
						obj->GradStrokeFX = gradientHelper.fx;
					if (gradientHelper.fyValid)
						obj->GradStrokeFY = gradientHelper.fy;
					if (gradientHelper.matrixValid)
						obj->matrixgs = gradientHelper.matrix;
				}
				else
				{
					obj->GStrokeCol1 = key2;
					if ((m_gradients[key2].matrixValid) && (!firstMatrixValid))
						obj->matrixgs *= m_gradients[key2].matrix;
				}
				key = m_gradients[key].reference;
			}
			if (obj->StrokeGradientType != 8)
			{
				key = params.mid(start, end - start);
				const GradientHelper& gradientHelper(m_gradients[key]);
				if (gradientHelper.typeValid)
					obj->StrokeGradientType = gradientHelper.type;
				key = m_gradients[key].reference;
				if (obj->StrokeGradientType != 8)
				{
					if (gradientHelper.gradientValid)
						obj->StrokeGradient = gradientHelper.gradient;
					if (gradientHelper.cspaceValid)
						obj->StrokeCSpace = gradientHelper.cspace;
					if (gradientHelper.x1Valid)
						obj->GradStrokeX1 = gradientHelper.x1;
					if (gradientHelper.y1Valid)
						obj->GradStrokeY1 = gradientHelper.y1;
					if (gradientHelper.x2Valid)
						obj->GradStrokeX2 = gradientHelper.x2;
					if (gradientHelper.y2Valid)
						obj->GradStrokeY2 = gradientHelper.y2;
					if (gradientHelper.fxValid)
						obj->GradStrokeFX = gradientHelper.fx;
					if (gradientHelper.fyValid)
						obj->GradStrokeFY = gradientHelper.fy;
					if (gradientHelper.matrixValid)
						obj->matrixgs = gradientHelper.matrix;
					obj->GStrokeCol1 = key;
				}
				else
				{
					obj->GStrokeCol1 = key;
					if (m_gradients[key].matrixValid)
						obj->matrixgs = m_gradients[key].matrix;
				}
			}
			obj->StrokeCol = CommonStrings::None;
		}
		else
		{
			obj->StrokeCol = parseColor(params);
			obj->StrokeGradientType = 0;
		}
	}
	else if (command == "stroke-width")
		obj->LWidth = parseUnit(params);
	else if (command == "stroke-linejoin")
	{
		if (params == "miter")
			obj->PLineJoin = Qt::MiterJoin;
		else if (params == "round")
			obj->PLineJoin = Qt::RoundJoin;
		else if (params == "bevel")
			obj->PLineJoin = Qt::BevelJoin;
	}
	else if (command == "stroke-linecap")
	{
		if (params == "butt")
			obj->PLineEnd = Qt::FlatCap;
		else if (params == "round")
			obj->PLineEnd = Qt::RoundCap;
		else if (params == "square")
			obj->PLineEnd = Qt::SquareCap;
	}
	//	else if (command == "stroke-miterlimit")
	//		gc->stroke.setMiterLimit(params.todouble());
	else if (command == "stroke-dasharray")
	{
		QVector<double> array;
		if (params != "none")
		{
			QString params2 = params.simplified().replace(',', " ");
			QStringList dashes = params2.split(' ', Qt::SkipEmptyParts);
			if (!dashes.isEmpty() && (parseUnit(dashes[0]) != 0.0))
			{
				for (const auto& dashStr : dashes)
					array.append(parseUnit(dashStr));
			}
		}
		obj->dashArray = array;
	}
	else if (command == "stroke-dashoffset")
		obj->dashOffset = ScCLocale::toDoubleC(params);
	else if (command == "font-family")
		obj->FontFamily = params;
	else if (command == "font-style")
		obj->FontStyle = params;
	else if (command == "font-weight")
		obj->FontWeight = params;
	else if (command == "font-stretch")
		obj->FontStretch = params;
	else if (command == "font-size")
		obj->FontSize = parseFontSize(params);
	else if (command == "text-anchor")
		obj->textAnchor = params;
	else if (command == "text-decoration")
		obj->textDecoration = params;
	else if (command == "clip-path")
	{
		if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			QString key = params.mid(start, end - start);
			QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
			if (it != m_clipPaths.end())
				obj->clipPath = it.value().copy();
		}
	}
	else if (command == "filter")
	{
		if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			obj->filter = params.mid(start, end - start);
		}
	}
	else if (command == "marker-end")
	{
		if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			obj->endMarker = params.mid(start, end - start);
		}
	}
	else if (command == "marker-start")
	{
		if (params.startsWith("url("))
		{
			unsigned int start = params.indexOf("#") + 1;
			unsigned int end = params.lastIndexOf(")");
			obj->startMarker = params.mid(start, end - start);
		}
	}
	else if (!isIgnorableNodeName(command))
	{
		if (!m_unsupportedFeatures.contains(command))
		{
			m_unsupportedFeatures.insert(command, command);
			qDebug() << QString("unsupported SVG feature: %1").arg(command);
			unsupported = true;
		}
	}
}

void SVGPlug::parseStyle(SvgStyle *obj, const QDomElement &e)
{
	SvgStyle *gc = m_gc.top();
	if (!gc)
		return;
	if (!e.attribute("display").isEmpty())
		parsePA(obj, "display", e.attribute("display"));
	if (!e.attribute("color").isEmpty())
	{
		if (e.attribute("color") == "inherit")
			gc->InherCol = true;
		else
			parsePA(obj, "color", e.attribute("color"));
	}
	if (!e.attribute("fill").isEmpty())
		parsePA(obj, "fill", e.attribute("fill"));
	if (!e.attribute("stroke").isEmpty())
		parsePA(obj, "stroke", e.attribute("stroke"));
	if (!e.attribute("stroke-width").isEmpty())
		parsePA(obj, "stroke-width", e.attribute("stroke-width"));
	if (!e.attribute("stroke-linejoin").isEmpty())
		parsePA(obj, "stroke-linejoin", e.attribute("stroke-linejoin"));
	if (!e.attribute("stroke-linecap").isEmpty())
		parsePA(obj, "stroke-linecap", e.attribute("stroke-linecap"));
	if (!e.attribute("stroke-dasharray").isEmpty())
		parsePA(obj, "stroke-dasharray", e.attribute("stroke-dasharray"));
	if (!e.attribute("stroke-dashoffset").isEmpty())
		parsePA(obj, "stroke-dashoffset", e.attribute("stroke-dashoffset"));
	if (!e.attribute("stroke-opacity").isEmpty())
		parsePA(obj, "stroke-opacity", e.attribute("stroke-opacity"));
	/*	if (!e.attribute("stroke-miterlimit").isEmpty())
			parsePA(obj, "stroke-miterlimit", e.attribute("stroke-miterlimit"));   */
	if (!e.attribute("fill-rule").isEmpty())
		parsePA(obj, "fill-rule", e.attribute("fill-rule"));
	if (!e.attribute("fill-opacity").isEmpty())
		parsePA(obj, "fill-opacity", e.attribute("fill-opacity"));
	if (!e.attribute("opacity").isEmpty())
		parsePA(obj, "opacity", e.attribute("opacity"));
	if (!e.attribute("font-family").isEmpty())
		parsePA(obj, "font-family", e.attribute("font-family"));
	if (!e.attribute("font-style").isEmpty())
		parsePA(obj, "font-style", e.attribute("font-style"));
	if (!e.attribute("font-weight").isEmpty())
		parsePA(obj, "font-weight", e.attribute("font-weight"));
	if (!e.attribute("font-stretch").isEmpty())
		parsePA(obj, "font-stretch", e.attribute("font-stretch"));
	if (!e.attribute("font-size").isEmpty())
		parsePA(obj, "font-size", e.attribute("font-size"));
	if (!e.attribute("text-anchor").isEmpty())
		parsePA(obj, "text-anchor", e.attribute("text-anchor"));
	if (!e.attribute("text-decoration").isEmpty())
		parsePA(obj, "text-decoration", e.attribute("text-decoration"));
	if (!e.attribute("filter").isEmpty())
		parsePA(obj, "filter", e.attribute("filter"));
	if (!e.attribute("marker-end").isEmpty())
		parsePA(obj, "marker-end", e.attribute("marker-end"));
	if (!e.attribute("marker-start").isEmpty())
		parsePA(obj, "marker-start", e.attribute("marker-start"));
	QString style = e.attribute("style").simplified();
	QStringList substyles = style.split(';', Qt::SkipEmptyParts);
	for (auto it = substyles.begin(); it != substyles.end(); ++it)
	{
		QStringList substyle = it->split(':', Qt::SkipEmptyParts);
		if (substyle.count() >= 2)
		{
			QString command(substyle.at(0).trimmed());
			QString params(substyle.at(1).trimmed());
			parsePA(obj, command, params);
		}
	}
}

void SVGPlug::parseColorStops(GradientHelper *gradient, const QDomElement &e)
{
	QString Col = "Black";
	double offset = 0;
	double opa;
	SvgStyle svgStyle;
	parseStyle(&svgStyle, e);
	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		opa = 1.0;
		QDomElement stop = n.toElement();
		if (parseTagName(stop) == "stop")
		{
			QString temp = stop.attribute("offset");
			if (temp.contains('%'))
			{
				temp.chop(1);
				offset = ScCLocale::toDoubleC(temp) / 100.0;
			}
			else
				offset = ScCLocale::toDoubleC(temp);
			if (stop.hasAttribute("stop-opacity"))
				opa = fromPercentage(stop.attribute("stop-opacity"));
			if (stop.hasAttribute("stop-color"))
			{
				if (stop.attribute("stop-color") == "currentColor")
					Col = svgStyle.CurCol;
				else
					Col = parseColor(stop.attribute("stop-color"));
			}
			else if (stop.hasAttribute("style"))
			{
				QString style = stop.attribute("style").simplified();
				QStringList substyles = style.split(';', Qt::SkipEmptyParts);
				for (auto it = substyles.begin(); it != substyles.end(); ++it)
				{
					QStringList substyle = it->split(':', Qt::SkipEmptyParts);
					if (substyle.count() >= 2)
					{
						QString command(substyle.at(0).trimmed());
						QString params(substyle.at(1).trimmed());
						if (command == "stop-color")
							Col = parseColor(params);
						if (command == "stop-opacity")
							opa = fromPercentage(params);
					}
				}
			}
			else
				Col = "Black";
		}
		const ScColor& gradC = m_Doc->PageColors[Col];
		gradient->gradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), offset, 0.5, opa, Col, 100);
		gradient->gradientValid = true;
	}
	if (gradient->gradientValid)
		gradient->gradient.filterStops();
}

void SVGPlug::parseFilter(const QDomElement &b)
{
	QString id = b.attribute("id", QString());
	if (id.isEmpty())
		return;

	filterSpec fspec;
	fspec.blendMode = 0;

	QDomElement child = b.firstChildElement();
	if (child.isNull() || (child.tagName() != "feBlend"))
	{
		filters.insert(id, fspec);
		return;
	}

	QString blendModeStr(child.attribute("mode"));
	if (blendModeStr == "normal")
		fspec.blendMode = 0;
	if (blendModeStr == "darken")
		fspec.blendMode = 1;
	if (blendModeStr == "lighten")
		fspec.blendMode = 2;
	if (blendModeStr == "multiply")
		fspec.blendMode = 3;
	if (blendModeStr == "screen")
		fspec.blendMode = 4;

	filters.insert(id, fspec);
}

void SVGPlug::parseMarker(const QDomElement &b)
{
	QString id = b.attribute("id", "");
	if (id.isEmpty())
		return;
	QString origName = id;

	inGroupXOrigin = 999999;
	inGroupYOrigin = 999999;
	markerDesc mark;
	mark.xref = parseUnit(b.attribute("refX", "0"));
	mark.yref = parseUnit(b.attribute("refY", "0"));
	mark.wpat = parseUnit(b.attribute("markerWidth", "3"));
	mark.hpat = parseUnit(b.attribute("markerHeight", "3"));

	setupNode(b);
	SvgStyle *gc = m_gc.top();
	gc->matrix   = QTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);

	QList<PageItem*> GElements;
	GElements = parseGroup(b);
	if (!GElements.isEmpty())
	{
		ScPattern pat(m_Doc);
		PageItem* currItem = GElements.at(0);
		m_Doc->DoDrawing = true;
		double minx =  std::numeric_limits<double>::max();
		double miny =  std::numeric_limits<double>::max();
		double maxx = -std::numeric_limits<double>::max();
		double maxy = -std::numeric_limits<double>::max();
		double x1, x2, y1, y2;
		currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
		minx = qMin(minx, x1);
		miny = qMin(miny, y1);
		maxx = qMax(maxx, x2);
		maxy = qMax(maxy, y2);
		currItem->gXpos = currItem->xPos() - minx;
		currItem->gYpos = currItem->yPos() - miny;
		currItem->setXYPos(currItem->gXpos, currItem->gYpos, true);
		pat.pattern = currItem->DrawObj_toImage(qMin(qMax(maxx - minx, maxy - miny), 500.0));
		pat.width = maxx - minx;
		pat.height = maxy - miny;
		m_Doc->DoDrawing = false;
		pat.items.append(currItem);
		m_Doc->Items->removeAll(currItem);
		m_Doc->addPattern(id, pat);
		importedPatterns.append(id);
		importedPattTrans.insert(origName, id);
		markers.insert(id, mark);
	}
	delete (m_gc.pop());
}

void SVGPlug::parsePattern(const QDomElement &b)
{
	GradientHelper gradhelper;
	QString href = b.attribute("xlink:href").mid(1);
	if (!href.isEmpty())
	{
		if (m_gradients.contains(href))
		{
			gradhelper.type = m_gradients[href].type;
			gradhelper.gradientValid = m_gradients[href].gradientValid;
			gradhelper.typeValid = m_gradients[href].typeValid;
			gradhelper.matrix = m_gradients[href].matrix;
			gradhelper.matrixValid = m_gradients[href].matrixValid;
		}
		gradhelper.reference = href;
	}
	QString id = b.attribute("id", QString());
	QString origName = id;
	if (!id.isEmpty())
	{
		inGroupXOrigin = 999999;
		inGroupYOrigin = 999999;
		double wpat = parseUnit(b.attribute("width", "0"));
		double hpat = parseUnit(b.attribute("height", "0"));
		QList<PageItem*> GElements;
		GElements = parseGroup(b);
		if (!GElements.isEmpty())
		{
			ScPattern pat(m_Doc);
			PageItem* currItem = GElements.at(0);
			m_Doc->DoDrawing = true;
			pat.pattern = currItem->DrawObj_toImage(qMin(qMax(wpat, hpat), 500.0));
			double xOrg = 0.0;
			double yOrg = 0.0;
			if (inGroupXOrigin < 0.0)
				xOrg = inGroupXOrigin;
			if (inGroupYOrigin < 0.0)
				yOrg = inGroupYOrigin;
			if ((xOrg != 0.0) || (yOrg != 0.0))
				pat.pattern = pat.pattern.copy(-xOrg, -yOrg, wpat, hpat);
			pat.xoffset = xOrg;
			pat.yoffset = yOrg;
			pat.width = wpat;
			pat.height = hpat;
			m_Doc->DoDrawing = false;
			pat.items.append(currItem);
			m_Doc->Items->removeAll(currItem);
			m_Doc->addPattern(id, pat);
			importedPatterns.append(id);
			importedPattTrans.insert(origName, id);
		}
		QString transf = b.attribute("patternTransform");
		if (!transf.isEmpty())
		{
			gradhelper.matrix = parseTransform(b.attribute("patternTransform"));
			gradhelper.matrixValid = true;
		}
		else
			gradhelper.matrixValid = false;
		gradhelper.gradientValid = true;
		gradhelper.gradient.clearStops();
		gradhelper.gradient.setRepeatMethod(VGradient::none);
		gradhelper.type = 8;
		gradhelper.typeValid = true;
		m_gradients.insert(origName, gradhelper);
	}
}

void SVGPlug::parseGradient(const QDomElement &e)
{
	GradientHelper gradhelper;
	gradhelper.gradientValid = false;
	gradhelper.gradient.clearStops();
	gradhelper.gradient.setRepeatMethod(VGradient::none);

	QString href = e.attribute("xlink:href").mid(1);
	double x1 = 0, y1 = 0, x2 = 0, fx = 0, fy = 0;
	if (!href.isEmpty())
	{
		if (m_gradients.contains(href))
		{
			gradhelper.type = m_gradients[href].type;
			gradhelper.gradient = m_gradients[href].gradient;
			gradhelper.x1 = m_gradients[href].x1;
			gradhelper.y1 = m_gradients[href].y1;
			gradhelper.x2 = m_gradients[href].x2;
			gradhelper.y2 = m_gradients[href].y2;
			gradhelper.fx = m_gradients[href].fx;
			gradhelper.fy = m_gradients[href].fy;
			gradhelper.cspace = m_gradients[href].cspace;
			gradhelper.matrix = m_gradients[href].matrix;
			gradhelper.x1Valid = m_gradients[href].x1Valid;
			gradhelper.x2Valid = m_gradients[href].x2Valid;
			gradhelper.y1Valid = m_gradients[href].y1Valid;
			gradhelper.y2Valid = m_gradients[href].y2Valid;
			gradhelper.fxValid = m_gradients[href].fxValid;
			gradhelper.fyValid = m_gradients[href].fyValid;
			gradhelper.cspaceValid = m_gradients[href].cspaceValid;
			gradhelper.matrixValid = m_gradients[href].matrixValid;
			gradhelper.gradientValid = m_gradients[href].gradientValid;
			gradhelper.typeValid = m_gradients[href].typeValid;
		}
		gradhelper.reference = href;
	}
	QString id = e.attribute("id", "");
	QString origName = id;
	if (parseTagName(e) == "linearGradient")
	{
		if (e.hasAttribute("x1"))
		{
			gradhelper.x1 = parseUnit(e.attribute("x1", "0"));
			gradhelper.x1Valid = true;
		}
		if (e.hasAttribute("y1"))
		{
			gradhelper.y1 = parseUnit(e.attribute("y1", "0"));
			gradhelper.y1Valid = true;
		}
		if (e.hasAttribute("x2"))
		{
			gradhelper.x2 = parseUnit(e.attribute("x2", "1"));
			gradhelper.x2Valid = true;
		}
		if (e.hasAttribute("y2"))
		{
			gradhelper.y2 = parseUnit(e.attribute("y2", "0"));
			gradhelper.y2Valid = true;
		}
		gradhelper.fx = x1;
		gradhelper.fy = y1;
		gradhelper.type = 6;
		gradhelper.typeValid = true;
	}
	else
	{
		if (e.hasAttribute("cx"))
		{
			x1 = parseUnit(e.attribute("cx", "0.5"));
			gradhelper.x1Valid = true;
		}
		if (e.hasAttribute("cy"))
		{
			y1 = parseUnit(e.attribute("cy", "0.5"));
			gradhelper.y1Valid = true;
		}
		if (e.hasAttribute("r"))
		{
			x2 = parseUnit(e.attribute("r", "0.5"));
			gradhelper.x2Valid = true;
		}
	//	y2 = y1;
		fx = e.hasAttribute("fx") ? parseUnit(e.attribute("fx", ScCLocale::toQStringC(x1))) : x1;
		gradhelper.fxValid = true;
		fy = e.hasAttribute("fy") ? parseUnit(e.attribute("fy", ScCLocale::toQStringC(y1))) : y1;
		gradhelper.fyValid = true;
		double squareRadius = (fx - x1) * (fx - x1) + (fy - y1) * (fy - y1);
		if (squareRadius > (x2 * x2))
		{
			fx = x1 + x2 * (fx - x1) / sqrt(squareRadius);
			fy = y1 + x2 * (fy - y1) / sqrt(squareRadius);
		}
		gradhelper.y2Valid = true;
		gradhelper.x1 = x1;
		gradhelper.y1 = y1;
		gradhelper.x2 = x1 + x2;
		gradhelper.y2 = y1;
		gradhelper.fx = fx;
		gradhelper.fy = fy;
		gradhelper.type = 7;
		gradhelper.typeValid = true;
	}
	if (!e.attribute("gradientUnits").isEmpty())
	{
		QString uni = e.attribute("gradientUnits");
		gradhelper.cspace = (uni == "userSpaceOnUse");
		gradhelper.cspaceValid = true;
	}
	else
	{
		gradhelper.cspace = false;
		gradhelper.cspaceValid = false;
	}
	QString transf = e.attribute("gradientTransform");
	if (!transf.isEmpty())
	{
		gradhelper.matrix = parseTransform(e.attribute("gradientTransform"));
		gradhelper.matrixValid = true;
	}
	else
		gradhelper.matrixValid = false;
	QString spreadMethod = e.attribute("spreadMethod");
	if (!spreadMethod.isEmpty())
	{
		if (spreadMethod == "reflect")
			gradhelper.gradient.setRepeatMethod(VGradient::reflect);
		else if (spreadMethod == "repeat")
			gradhelper.gradient.setRepeatMethod(VGradient::repeat);
	}
	parseColorStops(&gradhelper, e);
	m_gradients.insert(id, gradhelper);
	if (gradhelper.gradientValid)
	{
		if (m_Doc->addGradient(id, gradhelper.gradient))
			importedGradients.append(id);
	}
	importedGradTrans.insert(origName, id);
}

QString SVGPlug::parseTagName(const QDomElement& element) const
{
	QString tagName(element.tagName());
	if (tagName.startsWith("svg:"))
		return tagName.mid(4, -1);
	return tagName;
}
