1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include <QCursor>
9 #include <QDebug>
10 #include <QDrag>
11 #include <QFile>
12 #include <QList>
13 #include <QMessageBox>
14 #include <QMimeData>
15 #include <QPainterPath>
16 #include <QRegExp>
17 #include <QTemporaryFile>
18 
19 #include "svgplugin.h"
20 
21 #include "commonstrings.h"
22 #include "fonts/scfontmetrics.h"
23 #include "fpointarray.h"
24 #include "loadsaveplugin.h"
25 #include "pageitem.h"
26 #include "prefsfile.h"
27 #include "prefsmanager.h"
28 #include "qtiocompressor.h"
29 #include "scclocale.h"
30 #include "sccolorengine.h"
31 #include "scconfig.h"
32 #include "sclimits.h"
33 #include "scmimedata.h"
34 #include "scpaths.h"
35 #include "scpattern.h"
36 #include "scraction.h"
37 #include "scribusXml.h"
38 #include "scribuscore.h"
39 #include "scribusdoc.h"
40 #include "scribusdoc.h"
41 #include "scribusview.h"
42 #include "selection.h"
43 #include "ui/customfdialog.h"
44 #include "ui/propertiespalette.h"
45 #include "ui/scmessagebox.h"
46 #include "ui/scmwmenumanager.h"
47 #include "undomanager.h"
48 #include "util.h"
49 #include "util_formats.h"
50 #include "util_math.h"
51 
52 
53 using namespace std;
54 
svgimplugin_getPluginAPIVersion()55 int svgimplugin_getPluginAPIVersion()
56 {
57 	return PLUGIN_API_VERSION;
58 }
59 
svgimplugin_getPlugin()60 ScPlugin* svgimplugin_getPlugin()
61 {
62 	SVGImportPlugin* plug = new SVGImportPlugin();
63 	Q_CHECK_PTR(plug);
64 	return plug;
65 }
66 
svgimplugin_freePlugin(ScPlugin * plugin)67 void svgimplugin_freePlugin(ScPlugin* plugin)
68 {
69 	SVGImportPlugin* plug = qobject_cast<SVGImportPlugin*>(plugin);
70 	Q_ASSERT(plug);
71 	delete plug;
72 }
73 
SVGImportPlugin()74 SVGImportPlugin::SVGImportPlugin() :
75 	importAction(new ScrAction(ScrAction::DLL, "", QKeySequence(), this))
76 {
77 	// Set action info in languageChange, so we only have to do
78 	// it in one place. This includes registering file format
79 	// support.
80 	registerFormats();
81 	languageChange();
82 }
83 /*
84 void SVGImportPlugin::addToMainWindowMenu(ScribusMainWindow *mw)
85 {
86 	importAction->setEnabled(true);
87 	connect(importAction, SIGNAL(triggered()), SLOT(import()));
88 	mw->scrMenuMgr->addMenuItem(importAction, "FileImport");
89 }
90 */
~SVGImportPlugin()91 SVGImportPlugin::~SVGImportPlugin()
92 {
93 	unregisterAll();
94 }
95 
languageChange()96 void SVGImportPlugin::languageChange()
97 {
98 	importAction->setText( tr("Import &SVG..."));
99 	FileFormat* fmt = getFormatByExt("svg");
100 	fmt->trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG);
101 	fmt->filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG);
102 }
103 
fullTrName() const104 QString SVGImportPlugin::fullTrName() const
105 {
106 	return QObject::tr("SVG Import");
107 }
108 
getAboutData() const109 const ScActionPlugin::AboutData* SVGImportPlugin::getAboutData() const
110 {
111 	AboutData* about = new AboutData;
112 	about->authors = "Franz Schmid <franz@scribus.info>";
113 	about->shortDescription = tr("Imports SVG Files");
114 	about->description = tr("Imports most SVG files into the current document, converting their vector data into Scribus objects.");
115 	about->license = "GPL";
116 	Q_CHECK_PTR(about);
117 	return about;
118 }
119 
deleteAboutData(const AboutData * about) const120 void SVGImportPlugin::deleteAboutData(const AboutData* about) const
121 {
122 	Q_ASSERT(about);
123 	delete about;
124 }
125 
registerFormats()126 void SVGImportPlugin::registerFormats()
127 {
128 	FileFormat fmt(this);
129 	fmt.trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG);
130 	fmt.formatId = 0;
131 	fmt.filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG);
132 	fmt.fileExtensions = QStringList() << "svg" << "svgz";
133 	fmt.load = true;
134 	fmt.save = false;
135 	fmt.thumb = true;
136 	fmt.mimeTypes = FormatsManager::instance()->mimetypeOfFormat(FormatsManager::SVG);
137 	fmt.priority = 64;
138 	registerFormat(fmt);
139 }
140 
fileSupported(QIODevice *,const QString & fileName) const141 bool SVGImportPlugin::fileSupported(QIODevice* /* file */, const QString & fileName) const
142 {
143 	// TODO: identify valid SVG
144 	return true;
145 }
146 
loadFile(const QString & fileName,const FileFormat &,int flags,int)147 bool SVGImportPlugin::loadFile(const QString & fileName, const FileFormat & /* fmt */, int flags, int /*index*/)
148 {
149 	// For now, "load file" and import are the same thing for this plugin
150 	return import(fileName, flags);
151 }
152 
import(QString filename,int flags)153 bool SVGImportPlugin::import(QString filename, int flags)
154 {
155 	if (!checkFlags(flags))
156 		return false;
157 	m_Doc = ScCore->primaryMainWindow()->doc;
158 	ScribusMainWindow* mw=(m_Doc==nullptr) ? ScCore->primaryMainWindow() : m_Doc->scMW();
159 	if (filename.isEmpty())
160 	{
161 		flags |= lfInteractive;
162 		PrefsContext* prefs = PrefsManager::instance().prefsFile->getPluginContext("SVGPlugin");
163 		QString wdir = prefs->get("wdir", ".");
164 		CustomFDialog diaf(mw, wdir, QObject::tr("Open"), FormatsManager::instance()->fileDialogFormatList(FormatsManager::SVG));
165 		if (diaf.exec())
166 		{
167 			filename = diaf.selectedFile();
168 			prefs->set("wdir", filename.left(filename.lastIndexOf("/")));
169 		}
170 		else
171 			return true;
172 	}
173 
174 	UndoTransaction activeTransaction;
175 	bool emptyDoc = (m_Doc == nullptr);
176 	bool hasCurrentPage = (m_Doc && m_Doc->currentPage());
177 	TransactionSettings trSettings;
178 	trSettings.targetName   = hasCurrentPage ? m_Doc->currentPage()->getUName() : "";
179 	trSettings.targetPixmap = Um::IImageFrame;
180 	trSettings.actionName   = Um::ImportSVG;
181 	trSettings.description  = filename;
182 	trSettings.actionPixmap = Um::ISVG;
183 	if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted))
184 		UndoManager::instance()->setUndoEnabled(false);
185 	if (UndoManager::undoEnabled())
186 		activeTransaction = UndoManager::instance()->beginTransaction(trSettings);
187 	SVGPlug *dia = new SVGPlug(m_Doc, flags);
188 	Q_CHECK_PTR(dia);
189 	dia->import(filename, trSettings, flags);
190 	if (activeTransaction)
191 		activeTransaction.commit();
192 	if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted))
193 		UndoManager::instance()->setUndoEnabled(true);
194 	if (dia->importCanceled)
195 	{
196 		if (dia->importFailed)
197 			ScMessageBox::warning(mw, CommonStrings::trWarning, tr("The file could not be imported"));
198 	//	else if (dia->unsupported)
199 	//		ScMessageBox::warning(mw, CommonStrings::trWarning, tr("SVG file contains some unsupported features"));
200 	}
201 
202 	delete dia;
203 	return true;
204 }
205 
readThumbnail(const QString & fileName)206 QImage SVGImportPlugin::readThumbnail(const QString& fileName)
207 {
208 	if (fileName.isEmpty())
209 		return QImage();
210 	UndoManager::instance()->setUndoEnabled(false);
211 	m_Doc = nullptr;
212 	SVGPlug *dia = new SVGPlug(m_Doc, lfCreateThumbnail);
213 	Q_CHECK_PTR(dia);
214 	QImage ret = dia->readThumbnail(fileName);
215 	UndoManager::instance()->setUndoEnabled(true);
216 	delete dia;
217 	return ret;
218 }
219 
SVGPlug(ScribusDoc * doc,int flags)220 SVGPlug::SVGPlug(ScribusDoc* doc, int flags)
221 {
222 	tmpSel = new Selection(this, false);
223 	m_Doc = doc;
224 	unsupported = false;
225 	importFailed = false;
226 	importCanceled = true;
227 	firstLayer = true;
228 	importedColors.clear();
229 	importedGradients.clear();
230 	importedPatterns.clear();
231 	docDesc = "";
232 	docTitle = "";
233 	groupLevel = 0;
234 	importerFlags = flags;
235 	interactive = (flags & LoadSavePlugin::lfInteractive);
236 //	m_gc.setAutoDelete(true);
237 }
238 
readThumbnail(const QString & fName)239 QImage SVGPlug::readThumbnail(const QString& fName)
240 {
241 	if (!loadData(fName))
242 		return QImage();
243 	QString CurDirP = QDir::currentPath();
244 	QFileInfo efp(fName);
245 	QDir::setCurrent(efp.path());
246 	SvgStyle *gc = new SvgStyle;
247 	QDomElement docElem = inpdoc.documentElement();
248 	QSizeF wh = parseWidthHeight(docElem);
249 	m_Doc = new ScribusDoc();
250 	m_Doc->setup(0, 1, 1, 1, 1, "Custom", "Custom");
251 	m_Doc->setPage(wh.width(), wh.height(), 0, 0, 0, 0, 0, 0, false, false);
252 	m_Doc->addPage(0);
253 	m_Doc->setGUI(false, ScCore->primaryMainWindow(), nullptr);
254 	m_Doc->setLoading(true);
255 	m_Doc->DoDrawing = false;
256 	m_Doc->scMW()->setScriptRunning(true);
257 	gc->FontFamily = m_Doc->itemToolPrefs().textFont;
258 	if (!m_Doc->PageColors.contains("Black"))
259 		m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255));
260 	m_gc.push(gc);
261 	viewTransformX = 0;
262 	viewTransformY = 0;
263 	viewScaleX = 1;
264 	viewScaleY = 1;
265 	if (!docElem.attribute("viewBox").isEmpty())
266 	{
267 		QString viewbox(docElem.attribute("viewBox"));
268 		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
269 		if (points.size() > 3)
270 		{
271 			QTransform matrix;
272 			QSizeF wh2 = parseWidthHeight(docElem);
273 			double w2 = wh2.width();
274 			double h2 = wh2.height();
275 			addGraphicContext();
276 			viewTransformX = ScCLocale::toDoubleC(points[0]);
277 			viewTransformY = ScCLocale::toDoubleC(points[1]);
278 			viewScaleX = w2 / ScCLocale::toDoubleC(points[2]);
279 			viewScaleY = h2 / ScCLocale::toDoubleC(points[3]);
280 			matrix.translate(-viewTransformX * viewScaleX, -viewTransformY * viewScaleY);
281 			matrix.scale(viewScaleX, viewScaleY);
282 			m_gc.top()->matrix = matrix;
283 		}
284 	}
285 	QList<PageItem*> Elements = parseGroup(docElem);
286 	tmpSel->clear();
287 	QImage tmpImage = QImage();
288 	if (Elements.count() > 0)
289 	{
290 		m_Doc->groupObjectsList(Elements);
291 		m_Doc->DoDrawing = true;
292 		m_Doc->m_Selection->delaySignalsOn();
293 		for (int dre=0; dre<Elements.count(); ++dre)
294 		{
295 			tmpSel->addItem(Elements.at(dre), true);
296 		}
297 		tmpSel->setGroupRect();
298 		double xs = tmpSel->width();
299 		double ys = tmpSel->height();
300 		tmpImage = Elements.at(0)->DrawObj_toImage(500);
301 		tmpImage.setText("XSize", QString("%1").arg(xs));
302 		tmpImage.setText("YSize", QString("%1").arg(ys));
303 		m_Doc->m_Selection->delaySignalsOff();
304 	}
305 	m_Doc->scMW()->setScriptRunning(false);
306 	m_Doc->setLoading(false);
307 	delete m_Doc;
308 	QDir::setCurrent(CurDirP);
309 	return tmpImage;
310 }
311 
import(const QString & fname,const TransactionSettings & trSettings,int flags)312 bool SVGPlug::import(const QString& fname, const TransactionSettings& trSettings, int flags)
313 {
314 	if (!loadData(fname))
315 	{
316 		importFailed = true;
317 		return false;
318 	}
319 	QString CurDirP = QDir::currentPath();
320 	QFileInfo efp(fname);
321 	QDir::setCurrent(efp.path());
322 	convert(trSettings, flags);
323 	QDir::setCurrent(CurDirP);
324 	return true;
325 }
326 
loadData(const QString & fName)327 bool SVGPlug::loadData(const QString& fName)
328 {
329 	bool isCompressed = false, success = false;
330 	QByteArray bb(3, ' ');
331 	QFile fi(fName);
332 	if (fi.open(QIODevice::ReadOnly))
333 	{
334 		fi.read(bb.data(), 2);
335 		fi.close();
336 		// Qt4 bb[0]->QChar(bb[0])
337 		if ((QChar(bb[0]) == QChar(0x1F)) && (QChar(bb[1]) == QChar(0x8B)))
338 			isCompressed = true;
339 	}
340 	if ((fName.right(2) == "gz") || (isCompressed))
341 	{
342 		QFile file(fName);
343 		QtIOCompressor compressor(&file);
344 		compressor.setStreamFormat(QtIOCompressor::GzipFormat);
345 		if (!compressor.open(QIODevice::ReadOnly))
346 			return false;
347 		success = inpdoc.setContent(&compressor);
348 		compressor.close();
349 	}
350 	else
351 	{
352 		QFile file(fName);
353 		if (!file.open(QIODevice::ReadOnly))
354 			return false;
355 		success = inpdoc.setContent(&file);
356 		file.close();
357 	}
358 	return success;
359 }
360 
convert(const TransactionSettings & trSettings,int flags)361 void SVGPlug::convert(const TransactionSettings& trSettings, int flags)
362 {
363 	bool ret = false;
364 	SvgStyle *gc = new SvgStyle;
365 	QDomElement docElem = inpdoc.documentElement();
366 	QSizeF wh = parseWidthHeight(docElem);
367 	double width = wh.width();
368 	double height = wh.height();
369 	if (!interactive || (flags & LoadSavePlugin::lfInsertPage))
370 	{
371 		m_Doc->setPage(width, height, 0, 0, 0, 0, 0, 0, false, false);
372 		if (m_Doc->Pages->count() == 0)
373 		{
374 			m_Doc->addPage(0);
375 			m_Doc->view()->addPage(0);
376 		}
377 	}
378 	else
379 	{
380 		if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
381 		{
382 			m_Doc=ScCore->primaryMainWindow()->doFileNew(width, height, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
383 			ScCore->primaryMainWindow()->HaveNewDoc();
384 			ret = true;
385 		}
386 	}
387 	if ((ret) || (!interactive))
388 	{
389 		if (width > height)
390 			m_Doc->setPageOrientation(1);
391 		else
392 			m_Doc->setPageOrientation(0);
393 		m_Doc->setPageSize("Custom");
394 	}
395 	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
396 		m_Doc->view()->deselectItems();
397 	m_Doc->setLoading(true);
398 	m_Doc->DoDrawing = false;
399 	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
400 		m_Doc->view()->updatesOn(false);
401 	m_Doc->scMW()->setScriptRunning(true);
402 	qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
403 	gc->FontFamily = m_Doc->itemToolPrefs().textFont;
404 	if (!m_Doc->PageColors.contains("Black"))
405 		m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255));
406 	m_gc.push(gc);
407 	viewTransformX = 0;
408 	viewTransformY = 0;
409 	viewScaleX = 1;
410 	viewScaleY = 1;
411 	if (!docElem.attribute("viewBox").isEmpty())
412 	{
413 		QString viewbox(docElem.attribute("viewBox"));
414 		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
415 		if (points.size() > 3)
416 		{
417 			QTransform matrix;
418 			QSizeF wh2 = parseWidthHeight(docElem);
419 			double w2 = wh2.width();
420 			double h2 = wh2.height();
421 			addGraphicContext();
422 			viewTransformX = ScCLocale::toDoubleC(points[0]);
423 			viewTransformY = ScCLocale::toDoubleC(points[1]);
424 			viewScaleX = w2 / ScCLocale::toDoubleC(points[2]);
425 			viewScaleY = h2 / ScCLocale::toDoubleC(points[3]);
426 			matrix.translate(-viewTransformX * viewScaleX, -viewTransformY * viewScaleY);
427 			matrix.scale(viewScaleX, viewScaleY);
428 			m_gc.top()->matrix = matrix;
429 		}
430 	}
431 	Elements += parseDoc(docElem);
432 	if (flags & LoadSavePlugin::lfCreateDoc)
433 	{
434 		m_Doc->documentInfo().setTitle(docTitle);
435 		m_Doc->documentInfo().setComments(docDesc);
436 	}
437 	tmpSel->clear();
438 	if (Elements.count() == 0)
439 	{
440 		importFailed = true;
441 		if ((importedColors.count() != 0) && ((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns)))
442 			importFailed = false;
443 		if ((importedGradients.count() != 0) && ((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepPatterns)))
444 			importFailed = false;
445 		if ((importedPatterns.count() != 0) && (flags & LoadSavePlugin::lfKeepPatterns))
446 			importFailed = false;
447 		if ((importedColors.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns))))
448 		{
449 			for (int cd = 0; cd < importedColors.count(); cd++)
450 			{
451 				m_Doc->PageColors.remove(importedColors[cd]);
452 			}
453 		}
454 		if ((importedGradients.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients || (flags & LoadSavePlugin::lfKeepPatterns)))))
455 		{
456 			for (int cd = 0; cd < importedGradients.count(); cd++)
457 			{
458 				m_Doc->docGradients.remove(importedGradients[cd]);
459 			}
460 		}
461 		if ((importedPatterns.count() != 0) && (!(flags & LoadSavePlugin::lfKeepPatterns)))
462 		{
463 			for (int cd = 0; cd < importedPatterns.count(); cd++)
464 			{
465 				m_Doc->docPatterns.remove(importedPatterns[cd]);
466 			}
467 		}
468 	}
469 	if ((Elements.count() > 1) && (!(flags & LoadSavePlugin::lfCreateDoc)))
470 	{
471 		m_Doc->groupObjectsList(Elements);
472 	}
473 	m_Doc->DoDrawing = true;
474 	m_Doc->scMW()->setScriptRunning(false);
475 	if (interactive)
476 		m_Doc->setLoading(false);
477 	qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
478 	if ((Elements.count() > 0) && (!ret) && (interactive))
479 	{
480 		if (flags & LoadSavePlugin::lfScripted)
481 		{
482 			bool loadF = m_Doc->isLoading();
483 			m_Doc->setLoading(false);
484 			m_Doc->changed();
485 			m_Doc->setLoading(loadF);
486 			if (!(flags & LoadSavePlugin::lfLoadAsPattern))
487 			{
488 				m_Doc->m_Selection->delaySignalsOn();
489 				for (int dre=0; dre<Elements.count(); ++dre)
490 				{
491 					m_Doc->m_Selection->addItem(Elements.at(dre), true);
492 				}
493 				m_Doc->m_Selection->delaySignalsOff();
494 				m_Doc->m_Selection->setGroupRect();
495 				if (m_Doc->view() != nullptr)
496 					m_Doc->view()->updatesOn(true);
497 			}
498 			importCanceled = false;
499 		}
500 		else
501 		{
502 			m_Doc->DragP = true;
503 			m_Doc->DraggedElem = nullptr;
504 			m_Doc->DragElements.clear();
505 			m_Doc->m_Selection->delaySignalsOn();
506 			for (int dre=0; dre<Elements.count(); ++dre)
507 			{
508 				tmpSel->addItem(Elements.at(dre), true);
509 			}
510 			tmpSel->setGroupRect();
511 			ScElemMimeData* md = ScriXmlDoc::writeToMimeData(m_Doc, tmpSel);
512 			m_Doc->itemSelection_DeleteItem(tmpSel);
513 			m_Doc->view()->updatesOn(true);
514 			if ((importedColors.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns))))
515 			{
516 				for (int cd = 0; cd < importedColors.count(); cd++)
517 				{
518 					m_Doc->PageColors.remove(importedColors[cd]);
519 				}
520 			}
521 			if ((importedGradients.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepPatterns))))
522 			{
523 				for (int cd = 0; cd < importedGradients.count(); cd++)
524 				{
525 					m_Doc->docGradients.remove(importedGradients[cd]);
526 				}
527 			}
528 			if ((importedPatterns.count() != 0) && (!(flags & LoadSavePlugin::lfKeepPatterns)))
529 			{
530 				for (int cd = 0; cd < importedPatterns.count(); cd++)
531 				{
532 					m_Doc->docPatterns.remove(importedPatterns[cd]);
533 				}
534 			}
535 			m_Doc->m_Selection->delaySignalsOff();
536 			// We must copy the TransationSettings object as it is owned
537 			// by handleObjectImport method afterwards
538 			TransactionSettings* transacSettings = new TransactionSettings(trSettings);
539 			m_Doc->view()->handleObjectImport(md, transacSettings);
540 			m_Doc->DragP = false;
541 			m_Doc->DraggedElem = nullptr;
542 			m_Doc->DragElements.clear();
543 		}
544 	}
545 	else
546 	{
547 		bool loadF = m_Doc->isLoading();
548 		m_Doc->setLoading(false);
549 		m_Doc->changed();
550 		m_Doc->reformPages();
551 		if (!(flags & LoadSavePlugin::lfLoadAsPattern))
552 			m_Doc->view()->updatesOn(true);
553 		m_Doc->setLoading(loadF);
554 	}
555 	if (!(flags & LoadSavePlugin::lfLoadAsPattern))
556 	{
557 		if (interactive)
558 			m_Doc->view()->DrawNew();
559 	}
560 	qApp->restoreOverrideCursor();
561 }
562 
addGraphicContext()563 void SVGPlug::addGraphicContext()
564 {
565 	SvgStyle *gc = new SvgStyle;
566 	if (m_gc.top())
567 	{
568 		*gc = *(m_gc.top());
569 		gc->Opacity = 1.0; // opacity is not inheritable contrary to fill-opacity or stroke-opacity
570 		gc->filter.clear(); // filter is not inheritable
571 	}
572 	m_gc.push(gc);
573 }
574 
setupNode(const QDomElement & e)575 void SVGPlug::setupNode(const QDomElement &e)
576 {
577 	addGraphicContext();
578 	setupTransform(e);
579 	parseStyle(m_gc.top(), e);
580 }
581 
setupTransform(const QDomElement & e)582 void SVGPlug::setupTransform(const QDomElement &e)
583 {
584 	SvgStyle *gc = m_gc.top();
585 	QTransform mat = parseTransform(e.attribute("transform"));
586 	if (!e.attribute("transform").isEmpty())
587 		gc->matrix = mat * gc->matrix;
588 }
589 
finishNode(const QDomNode & e,PageItem * item)590 PageItem *SVGPlug::finishNode(const QDomNode &e, PageItem* item)
591 {
592 	PageItem* startArrow = nullptr;
593 	PageItem* endArrow = nullptr;
594 	SvgStyle *gc = m_gc.top();
595 	QTransform gcm = gc->matrix;
596 	double baseX = m_Doc->currentPage()->xOffset();
597 	double baseY = m_Doc->currentPage()->yOffset();
598 	double coeff1 = sqrt(gcm.m11() * gcm.m11() + gcm.m12() * gcm.m12());
599 	double coeff2 = sqrt(gcm.m21() * gcm.m21() + gcm.m22() * gcm.m22());
600 	switch (item->itemType())
601 	{
602 	case PageItem::ImageFrame:
603 		{
604 			item->ClipEdited = true;
605 			item->FrameType = 3;
606 			QTransform mm = gc->matrix;
607 			item->PoLine.map(mm);
608 			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
609 			FPoint wh = getMaxClipF(&item->PoLine);
610 			item->setWidthHeight(wh.x(), wh.y());
611 			m_Doc->adjustItemSize(item);
612 			item->ContourLine = item->PoLine.copy();
613 //			item->moveBy(mm.dx(), mm.dy());
614 //			item->setWidthHeight(item->width() * mm.m11(), item->height() * mm.m22());
615 //			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
616 			if (item->imageIsAvailable)
617 			{
618 				item->setImageXYOffset(0.0, 0.0);
619 				item->setImageXYScale(item->width() / (item->pixm.width() * (item->pixm.imgInfo.xres / 72.0)),
620 									  item->height() / (item->pixm.height() * (item->pixm.imgInfo.yres / 72.0)));
621 				item->setImageScalingMode(false, false); // fit to frame
622 			}
623 			break;
624 		}
625 	case PageItem::TextFrame:
626 		{
627 			//QTransform mm = gc->matrix;
628 			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
629 			item->itemText.trim();
630 		}
631 		break;
632 	default:
633 		{
634 			item->ClipEdited = true;
635 			item->FrameType = 3;
636 			QTransform mm = gc->matrix;
637 			item->PoLine.map(mm);
638 			/*if (haveViewBox)
639 			{
640 				QTransform mv;
641 				mv.translate(viewTransformX, viewTransformY);
642 				mv.scale(viewScaleX, viewScaleY);
643 				ite->PoLine.map(mv);
644 			}*/
645 			item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
646 			FPoint wh = getMaxClipF(&item->PoLine);
647 			item->setWidthHeight(wh.x(), wh.y());
648 //			if (item->asPolyLine())
649 //				item->setPolyClip(qRound(qMax(item->lineWidth() / 2.0, 1)));
650 //			else
651 //				item->Clip = flattenPath(item->PoLine, item->Segments);
652 			FPoint wx = getMinClipF(&item->PoLine);
653 			inGroupXOrigin = qMin(inGroupXOrigin, wx.x());
654 			inGroupYOrigin = qMin(inGroupYOrigin, wx.y());
655 			m_Doc->adjustItemSize(item);
656 			break;
657 		}
658 	}
659 	item->setRedrawBounding();
660 	item->OwnPage = m_Doc->OnPage(item);
661 	if (e.isElement())
662 	{
663 		if (!(importerFlags & LoadSavePlugin::lfCreateDoc))
664 		{
665 			QString nodeId = e.toElement().attribute("id");
666 			if (!nodeId.isEmpty())
667 				item->setItemName(" "+nodeId);
668 		}
669 	}
670 	item->setFillTransparency(1 - gc->FillOpacity * gc->Opacity);
671 	item->setLineTransparency(1 - gc->StrokeOpacity * gc->Opacity);
672 	item->PLineEnd = gc->PLineEnd;
673 	item->PLineJoin = gc->PLineJoin;
674 	if (item->fillColor() == CommonStrings::None)
675 		item->setTextFlowMode(PageItem::TextFlowDisabled);
676 	else
677 		item->setTextFlowMode(PageItem::TextFlowUsesFrameShape);
678 	item->DashOffset = gc->dashOffset;
679 	item->DashValues = gc->dashArray;
680 	if (gc->FillGradientType != 0)
681 	{
682 		if (gc->FillGradientType == 8)
683 		{
684 			item->GrType = gc->FillGradientType;
685 			item->setPattern(importedPattTrans[gc->GFillCol1]);
686 			QTransform mm = gc->matrixgf;
687 			double rot = getRotationFromMatrix(mm, 0.0) * 180 / M_PI;
688 			mm.rotate(rot);
689 			double patDx = (item->xPos() - baseX) - mm.dx();
690 			double patDy = (item->yPos() - baseY) - mm.dy();
691 			item->setPatternTransform(mm.m11() * 100.0, mm.m22() * 100.0, patDx, patDy, -rot, 0, 0);
692 		}
693 		else
694 		{
695 			if (gc->FillGradient.stops() > 1)
696 			{
697 				item->fill_gradient = gc->FillGradient;
698 				item->setGradient(importedGradTrans[gc->GFillCol1]);
699 				item->setGradientExtend(VGradient::pad);
700 				if (!gc->FillCSpace)
701 				{
702 					item->GrStartX = gc->GradFillX1 * item->width();
703 					item->GrStartY = gc->GradFillY1 * item->height();
704 					item->GrEndX   = gc->GradFillX2 * item->width();
705 					item->GrEndY   = gc->GradFillY2 * item->height();
706 					item->GrFocalX = gc->GradFillFX * item->width();
707 					item->GrFocalY = gc->GradFillFY * item->height();
708 					double angle1 = atan2(gc->GradFillY2-gc->GradFillY1,gc->GradFillX2-gc->GradFillX1)*(180.0/M_PI);
709 					double angle2 = atan2(item->GrEndY - item->GrStartX, item->GrEndX - item->GrStartX)*(180.0/M_PI);
710 					double dx = item->GrStartX + (item->GrEndX - item->GrStartX) / 2.0;
711 					double dy = item->GrStartY + (item->GrEndY - item->GrStartY) / 2.0;
712 					QTransform mm, mm2;
713 					if ((gc->GradFillY1 < gc->GradFillY2) && (gc->GradFillX1 < gc->GradFillX2))
714 					{
715 						mm.rotate(-angle2);
716 						mm2.rotate(angle1);
717 					}
718 					FPointArray gra;
719 					gra.setPoints(3, item->GrStartX-dx, item->GrStartY-dy, item->GrEndX-dx, item->GrEndY-dy, item->GrFocalX-dx, item->GrFocalY-dy);
720 					gra.map(mm*mm2);
721 					gra.translate(dx, dy);
722 					item->GrStartX = gra.point(0).x();
723 					item->GrStartY = gra.point(0).y();
724 					item->GrEndX   = gra.point(1).x();
725 					item->GrEndY   = gra.point(1).y();
726 					item->GrFocalX = gra.point(2).x();
727 					item->GrFocalY = gra.point(2).y();
728 				}
729 				else
730 				{
731 					QTransform mm = gc->matrix;
732 					mm = gc->matrixgf * mm;
733 					FPointArray gra;
734 					gra.setPoints(3, gc->GradFillX1, gc->GradFillY1, gc->GradFillX2, gc->GradFillY2, gc->GradFillFX, gc->GradFillFY);
735 					gra.map(mm);
736 					item->GrStartX = gra.point(0).x() - item->xPos() + baseX;
737 					item->GrStartY = gra.point(0).y() - item->yPos() + baseY;
738 					item->GrEndX   = gra.point(1).x() - item->xPos() + baseX;
739 					item->GrEndY   = gra.point(1).y() - item->yPos() + baseY;
740 					item->GrFocalX = gra.point(2).x() - item->xPos() + baseX;
741 					item->GrFocalY = gra.point(2).y() - item->yPos() + baseY;
742 					double ScaleX = 1.0;
743 					double ScaleY = 1.0;
744 					getScaleFromMatrix(mm, ScaleX, ScaleY);
745 					if (ScaleX != ScaleY)
746 						item->GrScale = ScaleY / ScaleX;
747 				}
748 				item->GrType = gc->FillGradientType;
749 			}
750 			else
751 			{
752 				item->GrType = 0;
753 				QList<VColorStop*> cstops = gc->FillGradient.colorStops();
754 				item->setFillColor(cstops.at(0)->name);
755 				item->setFillShade(cstops.at(0)->shade);
756 			}
757 		}
758 	}
759 	if (gc->StrokeGradientType != 0)
760 	{
761 		if (gc->StrokeGradientType == 8)
762 		{
763 			item->GrTypeStroke = gc->StrokeGradientType;
764 			item->setPattern(importedPattTrans[gc->GStrokeCol1]);
765 			QTransform mm = gc->matrixgs;
766 			double rot = getRotationFromMatrix(mm, 0.0) * 180 / M_PI;
767 			mm.rotate(rot);
768 			double patDx = (item->xPos() - baseX) - mm.dx();
769 			double patDy = (item->yPos() - baseY) - mm.dy();
770 			item->setStrokePatternTransform(mm.m11() * 100.0, mm.m22() * 100.0, patDx, patDy, -rot, 0, 0, 1);
771 		}
772 		else
773 		{
774 			if (gc->StrokeGradient.stops() > 1)
775 			{
776 				item->stroke_gradient = gc->StrokeGradient;
777 				item->setStrokeGradient(importedGradTrans[gc->GStrokeCol1]);
778 				item->setStrokeGradientExtend(VGradient::pad);
779 				if (!gc->StrokeCSpace)
780 				{
781 					item->GrStrokeStartX = gc->GradStrokeX1 * item->width();
782 					item->GrStrokeStartY = gc->GradStrokeY1 * item->height();
783 					item->GrStrokeEndX   = gc->GradStrokeX2 * item->width();
784 					item->GrStrokeEndY   = gc->GradStrokeY2 * item->height();
785 					item->GrStrokeFocalX = gc->GradStrokeFX * item->width();
786 					item->GrStrokeFocalY = gc->GradStrokeFY * item->height();
787 					double angle1 = atan2(gc->GradStrokeY2 - gc->GradStrokeY1, gc->GradStrokeX2 - gc->GradStrokeX1)*(180.0/M_PI);
788 					double angle2 = atan2(item->GrStrokeEndY - item->GrStrokeStartX, item->GrStrokeEndX - item->GrStrokeStartX)*(180.0/M_PI);
789 					double dx = item->GrStrokeStartX + (item->GrStrokeEndX - item->GrStrokeStartX) / 2.0;
790 					double dy = item->GrStrokeStartY + (item->GrStrokeEndY - item->GrStrokeStartY) / 2.0;
791 					QTransform mm, mm2;
792 					if ((gc->GradStrokeY1 < gc->GradStrokeY2) && (gc->GradStrokeX1 < gc->GradStrokeX2))
793 					{
794 						mm.rotate(-angle2);
795 						mm2.rotate(angle1);
796 					}
797 					FPointArray gra;
798 					gra.setPoints(3, item->GrStrokeStartX - dx, item->GrStrokeStartY - dy, item->GrStrokeEndX-dx, item->GrStrokeEndY-dy, item->GrStrokeFocalX-dx, item->GrStrokeFocalY-dy);
799 					gra.map(mm*mm2);
800 					gra.translate(dx, dy);
801 					item->GrStrokeStartX = gra.point(0).x();
802 					item->GrStrokeStartY = gra.point(0).y();
803 					item->GrStrokeEndX   = gra.point(1).x();
804 					item->GrStrokeEndY   = gra.point(1).y();
805 					item->GrStrokeFocalX = gra.point(2).x();
806 					item->GrStrokeFocalY = gra.point(2).y();
807 				}
808 				else
809 				{
810 					QTransform mm = gc->matrix;
811 					mm = gc->matrixgs * mm;
812 					FPointArray gra;
813 					gra.setPoints(3, gc->GradStrokeX1, gc->GradStrokeY1, gc->GradStrokeX2, gc->GradStrokeY2, gc->GradStrokeFX, gc->GradStrokeFY);
814 					gra.map(mm);
815 					item->GrStrokeStartX = gra.point(0).x() - item->xPos() + baseX;
816 					item->GrStrokeStartY = gra.point(0).y() - item->yPos() + baseY;
817 					item->GrStrokeEndX   = gra.point(1).x() - item->xPos() + baseX;
818 					item->GrStrokeEndY   = gra.point(1).y() - item->yPos() + baseY;
819 					item->GrStrokeFocalX = gra.point(2).x() - item->xPos() + baseX;
820 					item->GrStrokeFocalY = gra.point(2).y() - item->yPos() + baseY;
821 				}
822 				item->GrTypeStroke = gc->StrokeGradientType;
823 			}
824 			else
825 			{
826 				item->GrTypeStroke = 0;
827 				QList<VColorStop*> cstops = gc->StrokeGradient.colorStops();
828 				item->setLineColor(cstops.at(0)->name);
829 				item->setLineShade(cstops.at(0)->shade);
830 			}
831 		}
832 	}
833 	if (!gc->filter.isEmpty())
834 	{
835 		if (filters.contains(gc->filter))
836 		{
837 			filterSpec filter = filters[gc->filter];
838 			item->setFillBlendmode(filter.blendMode);
839 		}
840 	}
841 	if (!gc->endMarker.isEmpty())
842 	{
843 		QString marker = gc->endMarker;
844 		if (importedPattTrans.contains(marker))
845 			marker = importedPattTrans[marker];
846 		if (markers.contains(marker))
847 		{
848 			FPoint End = item->PoLine.point(item->PoLine.size()-2);
849 			for (uint xx = item->PoLine.size()-1; xx > 0; xx -= 2)
850 			{
851 				FPoint Vector = item->PoLine.point(xx);
852 				if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
853 				{
854 					double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI);
855 					QTransform arrowTrans;
856 					double bX = item->xPos() + End.x();
857 					double bY = item->yPos() + End.y();
858 					ScPattern pat = m_Doc->docPatterns[marker];
859 					double dX = (pat.width * item->lineWidth()) / 2.0;
860 					double dY = (pat.height * item->lineWidth()) / 2.0;
861 					arrowTrans.translate(bX, bY);
862 					arrowTrans.rotate(r);
863 					arrowTrans.translate(-dX, -dY);
864 					FPoint ba = FPoint(0.0, 0.0).transformPoint(arrowTrans, false);
865 					int z = m_Doc->itemAdd(PageItem::Symbol, PageItem::Unspecified, ba.x(), ba.y(), dX * 2.0, dY * 2.0, 0, CommonStrings::None, CommonStrings::None);
866 					endArrow = m_Doc->Items->at(z);
867 					endArrow->setPattern(marker);
868 					endArrow->setRotation(r, true);
869 					endArrow->setRedrawBounding();
870 					endArrow->OwnPage = m_Doc->OnPage(endArrow);
871 				//	Elements.append(ite);
872 					break;
873 				}
874 			}
875 		}
876 	}
877 	if (!gc->startMarker.isEmpty())
878 	{
879 		QString marker = gc->startMarker;
880 		if (importedPattTrans.contains(marker))
881 			marker = importedPattTrans[marker];
882 		if (markers.contains(marker))
883 		{
884 			FPoint End = item->PoLine.point(0);
885 			for (int xx = 1; xx < item->PoLine.size(); xx += 2)
886 			{
887 				FPoint Vector = item->PoLine.point(xx);
888 				if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
889 				{
890 					double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI) - 180.0;
891 					QTransform arrowTrans;
892 					double bX = item->xPos() + End.x();
893 					double bY = item->yPos() + End.y();
894 					ScPattern pat = m_Doc->docPatterns[marker];
895 					double dX = (pat.width * item->lineWidth()) / 2.0;
896 					double dY = (pat.height * item->lineWidth()) / 2.0;
897 					arrowTrans.translate(bX, bY);
898 					arrowTrans.rotate(r);
899 					arrowTrans.translate(-dX, -dY);
900 					FPoint ba = FPoint(0.0, 0.0).transformPoint(arrowTrans, false);
901 					int z = m_Doc->itemAdd(PageItem::Symbol, PageItem::Unspecified, ba.x(), ba.y(), dX * 2.0, dY * 2.0, 0, CommonStrings::None, CommonStrings::None);
902 					startArrow = m_Doc->Items->at(z);
903 					startArrow->setPattern(marker);
904 					startArrow->setRotation(r, true);
905 					startArrow->setRedrawBounding();
906 					startArrow->OwnPage = m_Doc->OnPage(startArrow);
907 				//	Elements.append(ite);
908 					break;
909 				}
910 			}
911 		}
912 	}
913 	if ((endArrow != nullptr) || (startArrow != nullptr))
914 	{
915 		QList<PageItem*> aElements;
916 		aElements.append(item);
917 		if (startArrow != nullptr)
918 			aElements.append(startArrow);
919 		if (endArrow != nullptr)
920 			aElements.append(endArrow);
921 		return m_Doc->groupObjectsList(aElements);
922 	}
923 	return item;
924 }
925 
isIgnorableNode(const QDomElement & e)926 bool SVGPlug::isIgnorableNode(const QDomElement &e)
927 {
928 	QString nodeName(e.tagName());
929 	return isIgnorableNodeName(nodeName);
930 }
931 
isIgnorableNodeName(const QString & n)932 bool SVGPlug::isIgnorableNodeName(const QString &n)
933 {
934 	return n.startsWith("sodipodi") || n.startsWith("inkscape") || n == "metadata";
935 }
936 
parseTextPosition(const QDomElement & e,const FPoint * pos)937 FPoint SVGPlug::parseTextPosition(const QDomElement &e, const FPoint* pos)
938 {
939 	// FIXME According to spec, we should in fact return a point list
940 	double  x = pos ? pos->x() : 0.0;
941 	double  y = pos ? pos->y() : 0.0;
942 
943 	if (e.hasAttribute("x"))
944 	{
945 		QString xatt =  e.attribute("x" , "0");
946 		if (xatt.contains(',') || xatt.contains(' '))
947 		{
948 			xatt.replace(QChar(','), QChar(' '));
949 			QStringList xl(xatt.split(QChar(' '), Qt::SkipEmptyParts));
950 			xatt = xl.first();
951 		}
952 		x = parseUnit(xatt);
953 	}
954 
955 	if (e.hasAttribute("y"))
956 	{
957 		QString yatt =  e.attribute("y" , "0");
958 		if (yatt.contains(',') || yatt.contains(' '))
959 		{
960 			yatt.replace(QChar(','), QChar(' '));
961 			QStringList yl(yatt.split(QChar(' '), Qt::SkipEmptyParts));
962 			yatt = yl.first();
963 		}
964 		y = parseUnit(yatt);
965 	}
966 
967 	if (e.hasAttribute("dx"))
968 	{
969 		QString dxatt =  e.attribute("dx" , "0");
970 		if (dxatt.contains(',') || dxatt.contains(' '))
971 		{
972 			dxatt.replace(QChar(','), QChar(' '));
973 			QStringList xl(dxatt.split(QChar(' '), Qt::SkipEmptyParts));
974 			dxatt = xl.first();
975 		}
976 		x += parseUnit(dxatt);
977 	}
978 
979 	if (e.hasAttribute("dy"))
980 	{
981 		QString dyatt =  e.attribute("dy" , "0");
982 		if (dyatt.contains(',') || dyatt.contains(' '))
983 		{
984 			dyatt.replace(QChar(','), QChar(' '));
985 			QStringList xl(dyatt.split(QChar(' '), Qt::SkipEmptyParts));
986 			dyatt = xl.first();
987 		}
988 		y += parseUnit(dyatt);
989 	}
990 
991 	return FPoint(x, y);
992 }
993 
parseWidthHeight(const QDomElement & e)994 QSizeF SVGPlug::parseWidthHeight(const QDomElement &e)
995 {
996 	QSizeF size(550, 841);
997 	QString sw = e.attribute("width", "100%");
998 	QString sh = e.attribute("height", "100%");
999 	double w =  550, h = 841;
1000 	if (!sw.isEmpty())
1001 		w = sw.endsWith("%") ? fromPercentage(sw) : parseUnit(sw);
1002 	if (!sh.isEmpty())
1003 		h = sh.endsWith("%") ? fromPercentage(sh) : parseUnit(sh);
1004 	if (!e.attribute("viewBox").isEmpty())
1005 	{
1006 		QRectF viewBox = parseViewBox(e);
1007 		double scw = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.width()  : size.width();
1008 		double sch = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.height() : size.height();
1009 		w *= (sw.endsWith("%") ? scw : 1.0);
1010 		h *= (sh.endsWith("%") ? sch : 1.0);
1011 	}
1012 	else
1013 	{
1014 		w *= (sw.endsWith("%") ? size.width() : 1.0);
1015 		h *= (sh.endsWith("%") ? size.height() : 1.0);
1016 	}
1017 	// OpenOffice files may not have width and height attributes, so avoid unnecessary large dimensions
1018 	if (w > 10000 || h > 10000)
1019 	{
1020 		double m = max(w, h);
1021 		w = w / m * 842;
1022 		h = h / m * 842;
1023 	}
1024 	size.setWidth(w);
1025 	size.setHeight(h);
1026 	return size;
1027 }
1028 
parseViewBox(const QDomElement & e)1029 QRectF SVGPlug::parseViewBox(const QDomElement &e)
1030 {
1031 	QRectF box(0, 0, 0, 0);
1032 	if (!e.attribute("viewBox").isEmpty())
1033 	{
1034 		QString viewbox(e.attribute("viewBox"));
1035 		QStringList points = viewbox.replace(QRegExp(","), " ").simplified().split(' ', Qt::SkipEmptyParts);
1036 		if (points.size() > 3)
1037 		{
1038 			double left   = ScCLocale::toDoubleC(points[0]);
1039 			double bottom = ScCLocale::toDoubleC(points[1]);
1040 			double width  = ScCLocale::toDoubleC(points[2]);
1041 			double height = ScCLocale::toDoubleC(points[3]);
1042 			box.setCoords((int) left, (int) bottom, (int) (left + width), (int) (bottom + height));
1043 		}
1044 	}
1045 	return box;
1046 }
1047 
parseDefs(const QDomElement & e)1048 void SVGPlug::parseDefs(const QDomElement &e)
1049 {
1050 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1051 	{
1052 		QDomElement b = n.toElement();
1053 		if (b.isNull())
1054 			continue;
1055 		SvgStyle svgStyle;
1056 		parseStyle(&svgStyle, b);
1057 		if (!svgStyle.Display)
1058 			continue;
1059 		QString STag2 = parseTagName(b);
1060 		if (STag2 == "g")
1061 		{
1062 			QString id = b.attribute("id", "");
1063 			if (!id.isEmpty())
1064 				m_nodeMap.insert(id, b);
1065 			parseDefs(b);
1066 		}
1067 		else if (STag2 == "linearGradient" || STag2 == "radialGradient")
1068 			parseGradient(b);
1069 		else if (STag2 == "clipPath")
1070 			parseClipPath(b);
1071 		else if (STag2 == "pattern")
1072 			parsePattern(b);
1073 		else if (STag2 == "marker")
1074 			parseMarker(b);
1075 		else if (STag2 == "filter")
1076 			parseFilter(b);
1077 		else if (b.hasAttribute("id"))
1078 		{
1079 			QString id = b.attribute("id");
1080 			if (!id.isEmpty())
1081 				m_nodeMap.insert(id, b);
1082 		}
1083 	}
1084 }
1085 
parseClipPath(const QDomElement & e)1086 void SVGPlug::parseClipPath(const QDomElement &e)
1087 {
1088 	QString id(e.attribute("id"));
1089 	if (!id.isEmpty())
1090 	{
1091 		FPointArray clip;
1092 		QDomNode n2 = e.firstChild();
1093 		QDomElement b2 = n2.toElement();
1094 		while (b2.nodeName() == "use")
1095 			b2 = getReferencedNode(b2);
1096 		if (b2.nodeName() == "path")
1097 			clip.parseSVG(b2.attribute("d"));
1098 		else if (b2.nodeName() == "rect")
1099 		{
1100 			double x = parseUnit(b2.attribute("x", "0.0"));
1101 			double y = parseUnit(b2.attribute("y", "0.0"));
1102 			double width  = parseUnit(b2.attribute("width"));
1103 			double height = parseUnit(b2.attribute("height"));
1104 			clip.addQuadPoint(x, y, x, y, width+x, y, width+x, y);
1105 			clip.addQuadPoint(width+x, y, width+x, y, width+x, height+y, width+x, height+y);
1106 			clip.addQuadPoint(width+x, height+y, width+x, height+y, x, height+y, x, height+y);
1107 			clip.addQuadPoint(x, height+y, x, height+y, x, y, x, y);
1108 		}
1109 		if (b2.hasAttribute("transform"))
1110 		{
1111 			QTransform transform = parseTransform(b2.attribute("transform"));
1112 			clip.map(transform);
1113 		}
1114 		if (clip.size() >= 2)
1115 			m_clipPaths.insert(id, clip);
1116 	}
1117 }
1118 
parseClipPathAttr(const QDomElement & e,FPointArray & clipPath)1119 void SVGPlug::parseClipPathAttr(const QDomElement &e, FPointArray& clipPath)
1120 {
1121 	clipPath.resize(0);
1122 /*	if (e.hasAttribute("style"))
1123 	{
1124 		QString style = e.attribute("style").simplified();
1125 		QStringList substyles = style.split(';', Qt::SkipEmptyParts);
1126 		for (QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
1127 		{
1128 			QStringList substyle = (*it).split(':', Qt::SkipEmptyParts);
1129 			QString command(substyle[0].trimmed());
1130 			QString params(substyle[1].trimmed());
1131 			if (command == "clip-path")
1132 			{
1133 				if (params.startsWith( "url("))
1134 				{
1135 					unsigned int start = params.indexOf("#") + 1;
1136 					unsigned int end = params.lastIndexOf(")");
1137 					QString key = params.mid(start, end - start);
1138 					QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
1139 					if (it != m_clipPaths.end())
1140 						clipPath = it.value().copy();
1141 				}
1142 			}
1143 		}
1144 	} */
1145 	if (e.hasAttribute("clip-path"))
1146 	{
1147 		QString attr = e.attribute("clip-path");
1148 		if (attr.startsWith( "url("))
1149 		{
1150 			unsigned int start = attr.indexOf("#") + 1;
1151 			unsigned int end = attr.lastIndexOf(")");
1152 			QString key = attr.mid(start, end - start);
1153 			QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
1154 			if (it != m_clipPaths.end())
1155 				clipPath = it.value().copy();
1156 		}
1157 	}
1158 }
1159 
parseFilterAttr(const QDomElement & e,PageItem * item)1160 void SVGPlug::parseFilterAttr(const QDomElement &e, PageItem* item)
1161 {
1162 	QString filterName;
1163 	if (e.hasAttribute("filter"))
1164 	{
1165 		QString attr = e.attribute("filter");
1166 		if (attr.startsWith( "url("))
1167 		{
1168 			unsigned int start = attr.indexOf("#") + 1;
1169 			unsigned int end = attr.lastIndexOf(")");
1170 			filterName = attr.mid(start, end - start);
1171 			if (filterName.isEmpty())
1172 				return;
1173 		}
1174 		if (filters.contains(filterName))
1175 		{
1176 			filterSpec spec = filters[filterName];
1177 			item->setFillBlendmode(spec.blendMode);
1178 		}
1179 	}
1180 }
1181 
parseA(const QDomElement & e)1182 QList<PageItem*> SVGPlug::parseA(const QDomElement &e)
1183 {
1184 	QList<PageItem*> aElements;
1185 	setupNode(e);
1186 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1187 	{
1188 		QDomElement b = n.toElement();
1189 		if (b.isNull() || isIgnorableNode(b))
1190 			continue;
1191 		SvgStyle svgStyle;
1192 		parseStyle(&svgStyle, b);
1193 		if (!svgStyle.Display)
1194 			continue;
1195 		QList<PageItem*> el = parseElement(b);
1196 		for (int ec = 0; ec < el.count(); ++ec)
1197 			aElements.append(el.at(ec));
1198 	}
1199 	delete (m_gc.pop());
1200 	return aElements;
1201 }
1202 
parseGroup(const QDomElement & e)1203 QList<PageItem*> SVGPlug::parseGroup(const QDomElement &e)
1204 {
1205 	FPointArray clipPath;
1206 	QList<PageItem*> GElements, gElements;
1207 
1208 	if ((importerFlags & LoadSavePlugin::lfCreateDoc) && (e.hasAttribute("inkscape:groupmode")) && (e.attribute("inkscape:groupmode") == "layer"))
1209 	{
1210 		setupNode(e);
1211 		QString layerName = e.attribute("inkscape:label", "Layer");
1212 		double trans = m_gc.top()->Opacity;
1213 		int currentLayer = 0;
1214 		if (!firstLayer)
1215 			currentLayer = m_Doc->addLayer(layerName, true);
1216 		else
1217 			m_Doc->changeLayerName(currentLayer, layerName);
1218 		m_Doc->setLayerVisible(currentLayer, true);
1219 		m_Doc->setLayerLocked(currentLayer, false);
1220 		m_Doc->setLayerPrintable(currentLayer, true);
1221 		m_Doc->setLayerTransparency(currentLayer, trans);
1222 		firstLayer = false;
1223 		for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1224 		{
1225 			QDomElement b = n.toElement();
1226 			if (b.isNull() || isIgnorableNode(b))
1227 				continue;
1228 			SvgStyle svgStyle;
1229 			parseStyle(&svgStyle, b);
1230 			if (!svgStyle.Display)
1231 				continue;
1232 			QList<PageItem*> el = parseElement(b);
1233 			for (int ec = 0; ec < el.count(); ++ec)
1234 				GElements.append(el.at(ec));
1235 		}
1236 		delete (m_gc.pop());
1237 		return GElements;
1238 	}
1239 
1240 	double baseX = m_Doc->currentPage()->xOffset();
1241 	double baseY = m_Doc->currentPage()->yOffset();
1242 	groupLevel++;
1243 	setupNode(e);
1244 	parseClipPathAttr(e, clipPath);
1245 	int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, baseX, baseY, 1, 1, 0, CommonStrings::None, CommonStrings::None);
1246 	PageItem *neu = m_Doc->Items->at(z);
1247 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1248 	{
1249 		QDomElement b = n.toElement();
1250 		if (b.isNull() || isIgnorableNode(b))
1251 			continue;
1252 		SvgStyle svgStyle;
1253 		parseStyle(&svgStyle, b);
1254 		if (!svgStyle.Display)
1255 			continue;
1256 		QList<PageItem*> el = parseElement(b);
1257 		for (int ec = 0; ec < el.count(); ++ec)
1258 			gElements.append(el.at(ec));
1259 	}
1260 	groupLevel--;
1261 	SvgStyle *gc = m_gc.top();
1262 	if (clipPath.empty())
1263 	{
1264 		if (!gc->clipPath.empty())
1265 			clipPath = gc->clipPath.copy();
1266 	}
1267 	parseFilterAttr(e, neu);
1268 
1269 	if (gElements.count() == 0 || (gElements.count() < 2 && (clipPath.empty()) && (gc->Opacity == 1.0)))
1270 	{
1271 		// Unfortunately we have to take the long route here, or we risk crash on undo/redo
1272 		// FIXME : create group object after parsing grouped objects
1273 		/*m_Doc->Items->takeAt(z);
1274 		delete neu;*/
1275 		Selection tmpSelection(m_Doc, false);
1276 		tmpSelection.addItem(neu);
1277 		m_Doc->itemSelection_DeleteItem(&tmpSelection);
1278 		for (int gr = 0; gr < gElements.count(); ++gr)
1279 		{
1280 			GElements.append(gElements.at(gr));
1281 		}
1282 		delete (m_gc.pop());
1283 		return GElements;
1284 	}
1285 
1286 	double minx =  std::numeric_limits<double>::max();
1287 	double miny =  std::numeric_limits<double>::max();
1288 	double maxx = -std::numeric_limits<double>::max();
1289 	double maxy = -std::numeric_limits<double>::max();
1290 	GElements.append(neu);
1291 	for (int gr = 0; gr < gElements.count(); ++gr)
1292 	{
1293 		PageItem* currItem = gElements.at(gr);
1294 		double x1, x2, y1, y2;
1295 		currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
1296 		minx = qMin(minx, x1);
1297 		miny = qMin(miny, y1);
1298 		maxx = qMax(maxx, x2);
1299 		maxy = qMax(maxy, y2);
1300 	}
1301 	double gx = minx;
1302 	double gy = miny;
1303 	double gw = maxx - minx;
1304 	double gh = maxy - miny;
1305 	if (((gx > -9999999) && (gx < 9999999)) && ((gy > -9999999) && (gy < 9999999)) && ((gw > 0) && (gw < 9999999)) && ((gh > 0) && (gh < 9999999)))
1306 	{
1307 		neu->setXYPos(gx, gy);
1308 		neu->setWidthHeight(gw, gh);
1309 		if (!clipPath.empty())
1310 		{
1311 			QTransform mm = gc->matrix;
1312 			neu->PoLine = clipPath.copy();
1313 			neu->PoLine.map(mm);
1314 			neu->PoLine.translate(-gx + baseX, -gy + baseY);
1315 			clipPath.resize(0);
1316 			neu->Clip = flattenPath(neu->PoLine, neu->Segments);
1317 			neu->ClipEdited = true;
1318 			neu->FrameType = 3;
1319 		}
1320 		else
1321 			neu->SetRectFrame();
1322 		if (!e.attribute("id").isEmpty())
1323 			neu->setItemName(e.attribute("id"));
1324 		else
1325 			neu->setItemName( tr("Group%1").arg(m_Doc->GroupCounter));
1326 		neu->setFillTransparency(1 - gc->Opacity);
1327 		neu->gXpos = neu->xPos() - gx;
1328 		neu->gYpos = neu->yPos() - gy;
1329 		neu->groupWidth = gw;
1330 		neu->groupHeight = gh;
1331 		for (int gr = 0; gr < gElements.count(); ++gr)
1332 		{
1333 			PageItem* currItem = gElements.at(gr);
1334 			currItem->gXpos = currItem->xPos() - gx;
1335 			currItem->gYpos = currItem->yPos() - gy;
1336 			currItem->gWidth = gw;
1337 			currItem->gHeight = gh;
1338 			currItem->Parent = neu;
1339 			neu->groupItemList.append(currItem);
1340 			m_Doc->Items->removeAll(currItem);
1341 		}
1342 		neu->setRedrawBounding();
1343 		neu->setTextFlowMode(PageItem::TextFlowDisabled);
1344 		m_Doc->GroupCounter++;
1345 	}
1346 	else
1347 	{
1348 		// Group is out of valid coordinates, remove it
1349 		GElements.removeAll(neu);
1350 		Selection tmpSelection(m_Doc, false);
1351 		tmpSelection.addItem(neu);
1352 		for (int gr = 0; gr < gElements.count(); ++gr)
1353 		{
1354 			tmpSelection.addItem(gElements.at(gr));
1355 		}
1356 		m_Doc->itemSelection_DeleteItem(&tmpSelection);
1357 	}
1358 	delete (m_gc.pop());
1359 
1360 	return GElements;
1361 }
1362 
parseDoc(const QDomElement & e)1363 QList<PageItem*> SVGPlug::parseDoc(const QDomElement &e)
1364 {
1365 	QList<PageItem*> GElements;
1366 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1367 	{
1368 		QDomElement b = n.toElement();
1369 		if (b.isNull() || isIgnorableNode(b))
1370 			continue;
1371 		SvgStyle svgStyle;
1372 		parseStyle(&svgStyle, b);
1373 		if (!svgStyle.Display)
1374 			continue;
1375 		QList<PageItem*> el = parseElement(b);
1376 		for (int ec = 0; ec < el.count(); ++ec)
1377 			GElements.append(el.at(ec));
1378 	}
1379 	return GElements;
1380 }
1381 
parseElement(const QDomElement & e)1382 QList<PageItem*> SVGPlug::parseElement(const QDomElement &e)
1383 {
1384 	QList<PageItem*> GElements;
1385 	if (e.hasAttribute("id"))
1386 		m_nodeMap.insert(e.attribute("id"), e);
1387 	QString STag = parseTagName(e);
1388 	if (STag.startsWith("svg:"))
1389 		STag = STag.mid(4, - 1);
1390 	if (STag == "g" )
1391 	{
1392 		GElements = parseGroup(e);
1393 		return GElements;
1394 	}
1395 	if (STag == "defs")
1396 		parseDefs(e);
1397 	else if (STag == "a")
1398 		GElements = parseA(e);
1399 	else if (STag == "switch")
1400 		GElements = parseSwitch(e);
1401 	else if (STag == "symbol")
1402 		GElements = parseSymbol(e);
1403 	else if (STag == "use")
1404 		GElements = parseUse(e);
1405 	else if (STag == "linearGradient" || STag == "radialGradient")
1406 		parseGradient(e);
1407 	else if (STag == "rect")
1408 		GElements = parseRect(e);
1409 	else if (STag == "ellipse")
1410 		GElements = parseEllipse(e);
1411 	else if (STag == "circle")
1412 		GElements = parseCircle(e);
1413 	else if (STag == "line")
1414 		GElements = parseLine(e);
1415 	else if (STag == "path")
1416 		GElements = parsePath(e);
1417 	else if (STag == "polyline" || STag == "polygon")
1418 		GElements = parsePolyline(e);
1419 	else if (STag == "text")
1420 		GElements = parseText(e);
1421 	else if (STag == "clipPath")
1422 		parseClipPath(e);
1423 	else if (STag == "desc")
1424 	{
1425 		if (groupLevel == 1)
1426 			docDesc = e.text();
1427 	}
1428 	else if (STag == "title")
1429 	{
1430 		if (groupLevel == 1)
1431 			docTitle = e.text();
1432 	}
1433 	else if (STag == "image")
1434 		GElements = parseImage(e);
1435 /*	else if (STag == "i:pgf")
1436 	{
1437 		QByteArray cdat;
1438 		QByteArray ddat;
1439 		cdat = e.text().simplified();
1440 		QList<QByteArray> cdlist = cdat.split(' ');
1441 		for (int cd = 0; cd < cdlist.count(); cd++)
1442 		{
1443 			ddat += QByteArray::fromBase64(cdlist[cd]);
1444 		}
1445 		QFile outf("/home/franz/testdata.txt");
1446 		outf.open(QIODevice::WriteOnly);
1447 		outf.write(ddat);
1448 		outf.close();
1449 		QString f2 = "/home/franz/testdata_decom.ai";
1450 		FILE *source = fopen("/home/franz/testdata.txt", "rb");
1451 		FILE *dest = fopen(f2, "wb");
1452 		int ret;
1453 		unsigned have;
1454 		z_stream strm;
1455 		char in[4096];
1456 		char out[4096];
1457 		strm.zalloc = Z_NULL;
1458 		strm.zfree = Z_NULL;
1459 		strm.opaque = Z_NULL;
1460 		strm.avail_in = 0;
1461 		strm.next_in = Z_NULL;
1462 		ret = inflateInit(&strm);
1463 		if (ret != Z_OK)
1464 			return GElements;
1465 		do
1466 		{
1467 			strm.avail_in = fread(in, 1, 4096, source);
1468 			if (ferror(source))
1469 			{
1470 				(void)inflateEnd(&strm);
1471 				return GElements;
1472 			}
1473 			if (strm.avail_in == 0)
1474 				break;
1475 			strm.next_in = (Bytef*)in;
1476 			do
1477 			{
1478 				strm.avail_out = 4096;
1479 				strm.next_out = (Bytef*)out;
1480 				ret = inflate(&strm, Z_NO_FLUSH);
1481 				assert(ret != Z_STREAM_ERROR);
1482 				switch (ret)
1483 				{
1484 					case Z_NEED_DICT:
1485 						ret = Z_DATA_ERROR;
1486 					case Z_DATA_ERROR:
1487 					case Z_MEM_ERROR:
1488 						(void)inflateEnd(&strm);
1489 						return GElements;
1490 				}
1491 				have = 4096 - strm.avail_out;
1492 				if (fwrite(out, 1, have, dest) != have || ferror(dest))
1493 				{
1494 					(void)inflateEnd(&strm);
1495 					return GElements;
1496 				}
1497 			}
1498 			while (strm.avail_out == 0);
1499 		}
1500 		while (ret != Z_STREAM_END);
1501 		(void)inflateEnd(&strm);
1502 		fclose(source);
1503 		fclose(dest);
1504 	} */
1505 /*	else if (STag == "image")
1506 		GElements = parseImage(e);
1507 	} */
1508 	else if (!isIgnorableNodeName(STag))
1509 	{
1510 		// warn if unsupported SVG feature are encountered
1511 		if (!m_unsupportedFeatures.contains(STag))
1512 		{
1513 			m_unsupportedFeatures.insert(STag, STag);
1514 	//		qDebug() << QString("unsupported SVG feature: %1").arg(STag);
1515 			unsupported = true;
1516 		}
1517 	}
1518 	return GElements;
1519 }
1520 
parseCircle(const QDomElement & e)1521 QList<PageItem*> SVGPlug::parseCircle(const QDomElement &e)
1522 {
1523 	QList<PageItem*> CElements;
1524 	double baseX = m_Doc->currentPage()->xOffset();
1525 	double baseY = m_Doc->currentPage()->yOffset();
1526 	double r = parseUnit(e.attribute("r"));
1527 	double x = parseUnit(e.attribute("cx")) - r;
1528 	double y = parseUnit(e.attribute("cy")) - r;
1529 	setupNode(e);
1530 	SvgStyle *gc = m_gc.top();
1531 	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, baseX, baseY, r * 2.0, r * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol);
1532 	PageItem* ite = m_Doc->Items->at(z);
1533 	QTransform mm = QTransform();
1534 	mm.translate(x, y);
1535 	ite->PoLine.map(mm);
1536 	FPoint wh = getMaxClipF(&ite->PoLine);
1537 	ite->setWidthHeight(wh.x(), wh.y());
1538 	finishNode(e, ite);
1539 	CElements.append(ite);
1540 	delete (m_gc.pop());
1541 	return CElements;
1542 }
1543 
parseEllipse(const QDomElement & e)1544 QList<PageItem*> SVGPlug::parseEllipse(const QDomElement &e)
1545 {
1546 	QList<PageItem*> EElements;
1547 	double baseX = m_Doc->currentPage()->xOffset();
1548 	double baseY = m_Doc->currentPage()->yOffset();
1549 	double rx = parseUnit(e.attribute("rx"));
1550 	double ry = parseUnit(e.attribute("ry"));
1551 	double x = parseUnit(e.attribute("cx")) - rx;
1552 	double y = parseUnit(e.attribute("cy")) - ry;
1553 	setupNode(e);
1554 	SvgStyle *gc = m_gc.top();
1555 	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, baseX, baseY, rx * 2.0, ry * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol);
1556 	PageItem* ite = m_Doc->Items->at(z);
1557 	QTransform mm = QTransform();
1558 	mm.translate(x, y);
1559 	ite->PoLine.map(mm);
1560 	FPoint wh = getMaxClipF(&ite->PoLine);
1561 	ite->setWidthHeight(wh.x(), wh.y());
1562 	finishNode(e, ite);
1563 	EElements.append(ite);
1564 	delete (m_gc.pop());
1565 	return EElements;
1566 }
1567 
parseImage(const QDomElement & e)1568 QList<PageItem*> SVGPlug::parseImage(const QDomElement &e)
1569 {
1570 	FPointArray clipPath;
1571 	QList<PageItem*> IElements;
1572 	QString fname = e.attribute("xlink:href");
1573 	double baseX = m_Doc->currentPage()->xOffset();
1574 	double baseY = m_Doc->currentPage()->yOffset();
1575 	double x = e.attribute("x").isEmpty() ? 0.0 : parseUnit(e.attribute("x"));
1576 	double y = e.attribute("y").isEmpty() ? 0.0 : parseUnit(e.attribute("y"));
1577 	double w = e.attribute("width").isEmpty() ? 1.0 : parseUnit(e.attribute("width"));
1578 	double h = e.attribute("height").isEmpty() ? 1.0 : parseUnit(e.attribute("height"));
1579 	setupNode(e);
1580 	parseClipPathAttr(e, clipPath);
1581 	int z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, baseX, baseY, w, h, 1, m_Doc->itemToolPrefs().imageFillColor, m_Doc->itemToolPrefs().imageStrokeColor);
1582 	PageItem* ite = m_Doc->Items->at(z);
1583 	if (!fname.isEmpty())
1584 	{
1585 		if (!fname.startsWith("data:"))
1586 			m_Doc->loadPict(fname, ite);
1587 		else
1588 		{
1589 			int startData = fname.indexOf(",");
1590 			QString dataType = fname.left(startData);
1591 			fname.remove(0, startData + 1);
1592 			QByteArray ba;
1593 			ba.append(fname);
1594 			if (dataType.contains("base64"))
1595 				ba = QByteArray::fromBase64(ba);
1596 			QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_svg_XXXXXX.png");
1597 			tempFile->setAutoRemove(false);
1598 			tempFile->open();
1599 			QString fileName = getLongPathName(tempFile->fileName());
1600 			tempFile->close();
1601 			delete tempFile;
1602 			ite->isTempFile = true;
1603 			ite->isInlineImage = true;
1604 			QImage img;
1605 			img.loadFromData(ba);
1606 			img.save(fileName, "PNG");
1607 			m_Doc->loadPict(fileName, ite);
1608 		}
1609 	}
1610 	if (!clipPath.empty())
1611 		ite->PoLine = clipPath.copy();
1612 	clipPath.resize(0);
1613 	ite->PoLine.map(QTransform(1.0, 0.0, 0.0, 1.0, x, y));
1614 	ite->Clip = flattenPath(ite->PoLine, ite->Segments);
1615 	finishNode(e, ite);
1616 	IElements.append(ite);
1617 	delete (m_gc.pop());
1618 	return IElements;
1619 }
1620 
parseLine(const QDomElement & e)1621 QList<PageItem*> SVGPlug::parseLine(const QDomElement &e)
1622 {
1623 	QList<PageItem*> LElements;
1624 	double baseX = m_Doc->currentPage()->xOffset();
1625 	double baseY = m_Doc->currentPage()->yOffset();
1626 	double x1 = e.attribute("x1").isEmpty() ? 0.0 : parseUnit(e.attribute("x1"));
1627 	double y1 = e.attribute("y1").isEmpty() ? 0.0 : parseUnit(e.attribute("y1"));
1628 	double x2 = e.attribute("x2").isEmpty() ? 0.0 : parseUnit(e.attribute("x2"));
1629 	double y2 = e.attribute("y2").isEmpty() ? 0.0 : parseUnit(e.attribute("y2"));
1630 	setupNode(e);
1631 	SvgStyle *gc = m_gc.top();
1632 	int z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
1633 	PageItem* ite = m_Doc->Items->at(z);
1634 	ite->PoLine.resize(4);
1635 	ite->PoLine.setPoint(0, FPoint(x1, y1));
1636 	ite->PoLine.setPoint(1, FPoint(x1, y1));
1637 	ite->PoLine.setPoint(2, FPoint(x2, y2));
1638 	ite->PoLine.setPoint(3, FPoint(x2, y2));
1639 	ite = finishNode(e, ite);
1640 	LElements.append(ite);
1641 	delete (m_gc.pop());
1642 	return LElements;
1643 }
1644 
parseNumbersList(const QString & numbersStr)1645 QVector<double> SVGPlug::parseNumbersList(const QString& numbersStr)
1646 {
1647 	QVector<double> numbers;
1648 	if (numbersStr.isEmpty())
1649 		return numbers;
1650 	numbers.reserve(8);
1651 
1652 	const QChar* str = numbersStr.data();
1653 
1654 	while (str->isSpace())
1655 		++str;
1656 	while (ScCLocale::isDigit(str->unicode()) ||
1657 	       *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
1658 	       *str == QLatin1Char('.'))
1659 	{
1660 
1661 		numbers.append(ScCLocale::toDoubleC(str));
1662 
1663 		while (str->isSpace())
1664 			++str;
1665 		if (*str == QLatin1Char(','))
1666 			++str;
1667 
1668 		//eat the rest of space
1669 		while (str->isSpace())
1670 			++str;
1671 	}
1672 
1673 	return numbers;
1674 }
1675 
parsePath(const QDomElement & e)1676 QList<PageItem*> SVGPlug::parsePath(const QDomElement &e)
1677 {
1678 	FPointArray pArray;
1679 	QList<PageItem*> PElements;
1680 	double baseX = m_Doc->currentPage()->xOffset();
1681 	double baseY = m_Doc->currentPage()->yOffset();
1682 	setupNode(e);
1683 	SvgStyle *gc = m_gc.top();
1684 	PageItem::ItemType itype = pArray.parseSVG(e.attribute("d")) ? PageItem::PolyLine : PageItem::Polygon;
1685 	int z = m_Doc->itemAdd(itype, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
1686 	PageItem* ite = m_Doc->Items->at(z);
1687 	ite->fillRule = (gc->fillRule != "nonzero");
1688 	ite->PoLine = pArray;
1689 	if (ite->PoLine.size() < 4)
1690 	{
1691 		tmpSel->addItem(ite);
1692 		m_Doc->itemSelection_DeleteItem(tmpSel);
1693 	}
1694 	else
1695 	{
1696 		ite = finishNode(e, ite);
1697 		PElements.append(ite);
1698 	}
1699 	delete (m_gc.pop());
1700 	return PElements;
1701 }
1702 
parsePolyline(const QDomElement & e)1703 QList<PageItem*> SVGPlug::parsePolyline(const QDomElement &e)
1704 {
1705 	int z;
1706 	QList<PageItem*> PElements;
1707 	double baseX = m_Doc->currentPage()->xOffset();
1708 	double baseY = m_Doc->currentPage()->yOffset();
1709 	setupNode(e);
1710 	SvgStyle *gc = m_gc.top();
1711 	QString points = e.attribute("points");
1712 	if (!points.isEmpty())
1713 	{
1714 		QString STag = parseTagName(e);
1715 		points = points.simplified().replace(',', " ");
1716 		QStringList pointList = points.split(' ', Qt::SkipEmptyParts);
1717 		if ((STag == "polygon" ) && (pointList.count() > 4))
1718 			z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
1719 		else
1720 			z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol);
1721 		PageItem* ite = m_Doc->Items->at(z);
1722 		ite->fillRule = (gc->fillRule != "nonzero");
1723 		ite->PoLine.resize(0);
1724 		ite->PoLine.svgInit();
1725 		bool bFirst = true;
1726 		double x = 0.0;
1727 		double y = 0.0;
1728 		for (QStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
1729 		{
1730 			x = ScCLocale::toDoubleC(*(it++));
1731 			y = ScCLocale::toDoubleC(*it);
1732 			if (bFirst)
1733 			{
1734 				ite->PoLine.svgMoveTo(x, y);
1735 				bFirst = false;
1736 			}
1737 			else
1738 			{
1739 				ite->PoLine.svgLineTo(x, y);
1740 			}
1741 		}
1742 		if ((STag == "polygon") && (pointList.count() > 4))
1743 			ite->PoLine.svgClosePath();
1744 		else
1745 			ite->convertTo(PageItem::PolyLine);
1746 		if (ite->PoLine.size() < 4)
1747 		{
1748 			tmpSel->addItem(ite);
1749 			m_Doc->itemSelection_DeleteItem(tmpSel);
1750 		}
1751 		else
1752 		{
1753 			ite = finishNode(e, ite);
1754 			PElements.append(ite);
1755 		}
1756 	}
1757 	delete (m_gc.pop());
1758 	return PElements;
1759 }
1760 
parseRect(const QDomElement & e)1761 QList<PageItem*> SVGPlug::parseRect(const QDomElement &e)
1762 {
1763 	QList<PageItem*> RElements;
1764 	double baseX = m_Doc->currentPage()->xOffset();
1765 	double baseY = m_Doc->currentPage()->yOffset();
1766 	double x = parseUnit(e.attribute("x"));
1767 	double y = parseUnit(e.attribute("y"));
1768 	double width = parseUnit(e.attribute("width"));
1769 	double height = parseUnit(e.attribute("height"));
1770 	double rx = e.attribute("rx").isEmpty() ? 0.0 : parseUnit(e.attribute("rx"));
1771 	double ry = e.attribute("ry").isEmpty() ? 0.0 : parseUnit(e.attribute("ry"));
1772 	setupNode(e);
1773 	SvgStyle *gc = m_gc.top();
1774 	int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, baseX, baseY, width, height, gc->LWidth, gc->FillCol, gc->StrokeCol);
1775 	PageItem* ite = m_Doc->Items->at(z);
1776 	if ((rx != 0) || (ry != 0))
1777 	{
1778 		ite->setCornerRadius(qMax(rx, ry));
1779 		ite->SetFrameRound();
1780 		m_Doc->setRedrawBounding(ite);
1781 	}
1782 	QTransform mm = QTransform();
1783 	mm.translate(x, y);
1784 	ite->PoLine.map(mm);
1785 	FPoint wh = getMaxClipF(&ite->PoLine);
1786 	ite->setWidthHeight(wh.x(), wh.y());
1787 	finishNode(e, ite);
1788 	RElements.append(ite);
1789 	delete (m_gc.pop());
1790 	return RElements;
1791 }
1792 
parseText(const QDomElement & e)1793 QList<PageItem*> SVGPlug::parseText(const QDomElement &e)
1794 {
1795 	QList<PageItem*> GElements;
1796 	setupNode(e);
1797 	double chunkWidth = 0;
1798 	FPoint currentPos = parseTextPosition(e);
1799 	SvgStyle *gc      = m_gc.top();
1800 	if (gc->textAnchor != "start")
1801 		getTextChunkWidth(e, chunkWidth);
1802 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1803 	{
1804 		if (n.isElement())
1805 		{
1806 			if (parseTagName(n.toElement()) == "tspan")
1807 			{
1808 				QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkWidth);
1809 				for (int ec = 0; ec < el.count(); ++ec)
1810 					GElements.append(el.at(ec));
1811 			}
1812 			else if (parseTagName(n.toElement()) == "textPath")
1813 			{
1814 				GElements = parseText(n.toElement());
1815 				break;
1816 			}
1817 		}
1818 		if (n.isText())
1819 		{
1820 			QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkWidth);
1821 			for (int ec = 0; ec < el.count(); ++ec)
1822 				GElements.append(el.at(ec));
1823 		}
1824 	}
1825 	delete (m_gc.pop());
1826 	return GElements;
1827 }
1828 
parseTextSpan(const QDomElement & e,FPoint & currentPos,double chunkW)1829 QList<PageItem*> SVGPlug::parseTextSpan(const QDomElement& e, FPoint& currentPos, double chunkW)
1830 {
1831 	QList<PageItem*> GElements;
1832 	setupNode(e);
1833 	currentPos   = parseTextPosition(e, &currentPos);
1834 	SvgStyle *gc = m_gc.top();
1835 	if ((e.hasAttribute("x") || e.hasAttribute("y")) && (gc->textAnchor != "start"))
1836 	{
1837 		chunkW = 0;
1838 		getTextChunkWidth(e, chunkW);
1839 	}
1840 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1841 	{
1842 		if (n.isElement() && (parseTagName(n.toElement()) == "tspan"))
1843 		{
1844 			QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkW);
1845 			for (int ec = 0; ec < el.count(); ++ec)
1846 				GElements.append(el.at(ec));
1847 		}
1848 		if (n.isText())
1849 		{
1850 			QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkW);
1851 			for (int ec = 0; ec < el.count(); ++ec)
1852 				GElements.append(el.at(ec));
1853 		}
1854 	}
1855 	delete (m_gc.pop());
1856 	return GElements;
1857 }
1858 
parseTextNode(const QDomText & e,FPoint & currentPos,double chunkW)1859 QList<PageItem*> SVGPlug::parseTextNode(const QDomText& e, FPoint& currentPos, double chunkW)
1860 {
1861 	QList<PageItem*> GElements;
1862 	double baseX  = m_Doc->currentPage()->xOffset();
1863 	double baseY  = m_Doc->currentPage()->yOffset();
1864 	double startX = currentPos.x(), startY = currentPos.y();
1865 
1866 	QString textString = e.data().simplified();
1867 	if (textString.isEmpty())
1868 		return GElements;
1869 
1870 	SvgStyle *gc   = m_gc.top();
1871 	QFont textFont = getFontFromStyle(*gc);
1872 	QFontMetrics fm(textFont);
1873 	double width   = fm.horizontalAdvance(textString);
1874 
1875 	if (gc->textAnchor == "middle")
1876 		startX -= chunkW / 2.0;
1877 	else if (gc->textAnchor == "end")
1878 		startX -= chunkW;
1879 
1880 	FPointArray textPath;
1881 	QString textFillColor   = gc->FillCol;
1882 	QString textStrokeColor = gc->StrokeCol;
1883 
1884 	// Text outline is generated using a big font size
1885 	// and scaled afterwards. This is done in order to
1886 	// overcome some QPainterPath issues when using
1887 	// small font sizes, especially bad glyph advances
1888 	QFont painterFont = textFont;
1889 	painterFont.setPointSizeF(100.0);
1890 	double fontScale = textFont.pointSizeF() / 100.0;
1891 
1892 	QPainterPath painterPath;
1893 	painterPath.addText(0.0, 0.0, painterFont, textString);
1894 
1895 	QTransform textTrans;
1896 	textTrans.translate(startX, startY);
1897 	textTrans.scale(fontScale, fontScale);
1898 	painterPath = textTrans.map(painterPath);
1899 
1900 	textPath.fromQPainterPath(painterPath);
1901 	if (!textPath.empty())
1902 	{
1903 //		double  lineWidth = 0.0;
1904 		int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, gc->LWidth, textFillColor, textStrokeColor);
1905 		PageItem* ite = m_Doc->Items->at(z);
1906 		ite->PoLine = textPath;
1907 		finishNode(e, ite);
1908 		GElements.append(ite);
1909 	}
1910 	currentPos.setX(currentPos.x() + width);
1911 	return GElements;
1912 }
1913 
parseSwitch(const QDomElement & e)1914 QList<PageItem*> SVGPlug::parseSwitch(const QDomElement &e)
1915 {
1916 	QString href;
1917 	QStringList hrefs;
1918 	QList<PageItem*> SElements;
1919 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
1920 	{
1921 		QDomElement de = n.toElement();
1922 		QString STag = parseTagName(de);
1923 		if (STag == "foreignObject")
1924 		{
1925 			if (de.hasAttribute("xlink:href"))
1926 			{
1927 				href = de.attribute("xlink:href").mid(1);
1928 				if (!href.isEmpty())
1929 					hrefs.append(href);
1930 			}
1931 			for (QDomNode n1 = de.firstChild(); !n1.isNull(); n1 = n1.nextSibling())
1932 			{
1933 				QDomElement de1 = n1.toElement();
1934 				if (de1.hasAttribute("xlink:href"))
1935 				{
1936 					href = de1.attribute("xlink:href").mid(1);
1937 					if (!href.isEmpty())
1938 						hrefs.append(href);
1939 				}
1940 			}
1941 		}
1942 		else
1943 		{
1944 			if (de.hasAttribute("requiredExtensions") || de.hasAttribute("requiredFeatures"))
1945 				continue;
1946 			if (de.hasAttribute("id") && hrefs.contains(de.attribute("id")))
1947 				continue;
1948 			SElements = parseElement(de);
1949 			if (SElements.count() > 0)
1950 				break;
1951 		}
1952 	}
1953 	return SElements;
1954 }
1955 
parseSymbol(const QDomElement & e)1956 QList<PageItem*> SVGPlug::parseSymbol(const QDomElement &e)
1957 {
1958 	QList<PageItem*> SElements;
1959 	QString id = e.attribute("id");
1960 	if (!id.isEmpty())
1961 		m_nodeMap.insert(id, e);
1962 	return SElements;
1963 }
1964 
parseUse(const QDomElement & e)1965 QList<PageItem*> SVGPlug::parseUse(const QDomElement &e)
1966 {
1967 	QList<PageItem*> UElements;
1968 	setupNode(e);
1969 	if (e.hasAttribute("x") || e.hasAttribute("y"))
1970 	{
1971 		QTransform matrix;
1972 		double  xAtt = ScCLocale::toDoubleC(e.attribute("x", "0.0"));
1973 		double  yAtt = ScCLocale::toDoubleC(e.attribute("y", "0.0"));
1974 		SvgStyle *gc = m_gc.top();
1975 		gc->matrix   = QTransform(1.0, 0.0, 0.0, 1.0, xAtt, yAtt) * gc->matrix;
1976 	}
1977 	QString href = e.attribute("xlink:href").mid(1);
1978 	QMap<QString, QDomElement>::Iterator it = m_nodeMap.find(href);
1979 	if (it != m_nodeMap.end())
1980 	{
1981 		QDomElement elem = it.value().toElement();
1982 		if (parseTagName(elem) == "symbol")
1983 			UElements = parseGroup(elem);
1984 		else
1985 			UElements = parseElement(elem);
1986 	}
1987 	delete (m_gc.pop());
1988 	return UElements;
1989 }
1990 
getFontFromStyle(SvgStyle & style)1991 QFont SVGPlug::getFontFromStyle(SvgStyle& style)
1992 {
1993 	QFont font(QApplication::font());
1994 	font.setStyleStrategy(QFont::PreferOutline);
1995 
1996 	if (!style.FontFamily.isEmpty())
1997 		font.setFamily(style.FontFamily);
1998 
1999 	if (!style.FontStyle.isEmpty())
2000 	{
2001 		if (style.FontStyle == "normal")
2002 			font.setStyle(QFont::StyleNormal);
2003 		else if (style.FontStyle == "italic")
2004 			font.setStyle(QFont::StyleItalic);
2005 		else if (style.FontStyle == "oblique")
2006 			font.setStyle(QFont::StyleOblique);
2007 	}
2008 
2009 	if (!style.FontWeight.isEmpty())
2010 	{
2011 		if (style.FontWeight == "normal")
2012 			font.setWeight(QFont::Normal);
2013 		else if (style.FontWeight == "bold")
2014 			font.setWeight(QFont::Bold);
2015 		else if (style.FontWeight == "bolder")
2016 			font.setWeight(QFont::DemiBold);
2017 		else if (style.FontWeight == "lighter")
2018 			font.setWeight(QFont::Light);
2019 		else
2020 		{
2021 			bool weightIsNum = false;
2022 			int  fontWeight  = style.FontWeight.toInt(&weightIsNum);
2023 			if (weightIsNum)
2024 			{
2025 				if (fontWeight == 100 || fontWeight == 200)
2026 					font.setWeight(QFont::Light);
2027 				else if (fontWeight == 300 || fontWeight == 400)
2028 					font.setWeight(QFont::Normal);
2029 				else if (fontWeight == 500 || fontWeight == 600)
2030 					font.setWeight(QFont::DemiBold);
2031 				else if (fontWeight == 700 || fontWeight == 800)
2032 					font.setWeight(QFont::Bold);
2033 				else if (fontWeight == 900)
2034 					font.setWeight(QFont::Black);
2035 			}
2036 		}
2037 	}
2038 
2039 	if (!style.FontStretch.isEmpty())
2040 	{
2041 		if (style.FontStretch == "normal")
2042 			font.setStretch(QFont::Unstretched);
2043 		else if (style.FontStretch == "ultra-condensed")
2044 			font.setStretch(QFont::UltraCondensed);
2045 		else if (style.FontStretch == "extra-condensed")
2046 			font.setStretch(QFont::ExtraCondensed);
2047 		else if (style.FontStretch == "condensed")
2048 			font.setStretch(QFont::Condensed);
2049 		else if (style.FontStretch == "semi-condensed")
2050 			font.setStretch(QFont::SemiCondensed);
2051 		else if (style.FontStretch == "semi-expanded")
2052 			font.setStretch(QFont::SemiExpanded);
2053 		else if (style.FontStretch == "expanded")
2054 			font.setStretch(QFont::Expanded);
2055 		else if (style.FontStretch == "extra-expanded")
2056 			font.setStretch(QFont::ExtraExpanded);
2057 		else if (style.FontStretch == "ultra-expanded")
2058 			font.setStretch(QFont::UltraExpanded);
2059 		else if (style.FontStretch == "narrower")
2060 			font.setStretch(QFont::SemiCondensed);
2061 		else if (style.FontStretch == "wider")
2062 			font.setStretch(QFont::SemiExpanded);
2063 	}
2064 	if (!style.textDecoration.isEmpty())
2065 	{
2066 		bool underline = false, overline  = false;
2067 		bool strikeOut = false;
2068 		if (style.textDecoration == "underline")
2069 			underline = true;
2070 		else if (style.textDecoration == "overline")
2071 			overline = true;
2072 		else if (style.textDecoration == "line-through")
2073 			strikeOut = true;
2074 		font.setUnderline(underline);
2075 		font.setOverline(overline);
2076 		font.setStrikeOut(strikeOut);
2077 	}
2078 	font.setPointSizeF(style.FontSize);
2079 	return font;
2080 }
2081 
getReferencedNode(const QDomElement & e)2082 QDomElement SVGPlug::getReferencedNode(const QDomElement &e)
2083 {
2084 	QDomElement ret;
2085 	QMap<QString, QDomElement>::Iterator it;
2086 	QString href = e.attribute("xlink:href").mid(1);
2087 	it = m_nodeMap.find(href);
2088 	if (it != m_nodeMap.end())
2089 		ret = it.value().toElement();
2090 	return ret;
2091 }
2092 
getTextChunkWidth(const QDomElement & e,double & width)2093 bool SVGPlug::getTextChunkWidth(const QDomElement &e, double& width)
2094 {
2095 	bool doBreak = false;
2096 	setupNode(e);
2097 //	QDomNode c = e.firstChild();
2098 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
2099 	{
2100 		if (n.isElement() && (parseTagName(n.toElement()) == "tspan"))
2101 		{
2102 			QDomElement elem = n.toElement();
2103 			if (elem.hasAttribute("x") || elem.hasAttribute("y"))
2104 			{
2105 				doBreak = true;
2106 				break;
2107 			}
2108 			doBreak = getTextChunkWidth(n.toElement(), width);
2109 			if (doBreak) break;
2110 		}
2111 		if (n.isText())
2112 		{
2113 			QDomText text = n.toText();
2114 			QString  textString = text.data().simplified();
2115 			if (textString.length() > 0)
2116 			{
2117 				SvgStyle *gc   = m_gc.top();
2118 				QFont textFont = getFontFromStyle(*gc);
2119 
2120 				// This is to match the scaling done in
2121 				// parseTextNode()
2122 				double fontSize = textFont.pointSizeF();
2123 				textFont.setPointSizeF(100.0);
2124 
2125 				QFontMetrics fm(textFont);
2126 				width += fm.horizontalAdvance(textString) * (fontSize / 100.0);
2127 			}
2128 		}
2129 	}
2130 	delete (m_gc.pop());
2131 	return doBreak;
2132 }
2133 
fromPercentage(const QString & s)2134 double SVGPlug::fromPercentage(const QString &s)
2135 {
2136 	QString s1 = s;
2137 	if (s1.endsWith(";"))
2138 		s1.chop(1);
2139 	if (s1.endsWith("%"))
2140 	{
2141 		s1.chop(1);
2142 		return ScCLocale::toDoubleC(s1) / 100.0;
2143 	}
2144 	return ScCLocale::toDoubleC(s1);
2145 }
2146 
parseFontSize(const QString & fsize)2147 double SVGPlug::parseFontSize(const QString& fsize)
2148 {
2149 	bool noUnit  = true;
2150 	QString unit = fsize.right(2);
2151 	if (unit == "pt" || unit == "cm" || unit == "mm" ||
2152 		unit == "in" || unit == "px")
2153 	{
2154 		noUnit = false;
2155 	}
2156 	double value = parseUnit(fsize);
2157 	if (noUnit)
2158 		value *= 0.8;
2159 	return value;
2160 }
2161 
parseUnit(const QString & unit)2162 double SVGPlug::parseUnit(const QString &unit)
2163 {
2164 	bool noUnit = false;
2165 	QString unitval=unit;
2166 	if (unit.right(2) == "pt")
2167 		unitval.replace("pt", "");
2168 	else if (unit.right(2) == "cm")
2169 		unitval.replace("cm", "" );
2170 	else if (unit.right(2) == "mm")
2171 		unitval.replace("mm" , "");
2172 	else if (unit.right(2) == "in")
2173 		unitval.replace("in", "" );
2174 	else if (unit.right(2) == "px")
2175 		unitval.replace("px", "" );
2176 	if (unitval == unit)
2177 		noUnit = true;
2178 	double value = ScCLocale::toDoubleC(unitval);
2179 	if (unit.right(2) == "pt")
2180 		{}/* value = value; */ //no change
2181 	else if (unit.right(2) == "cm")
2182 		value = (value / 2.54) * 72;
2183 	else if (unit.right(2) == "mm")
2184 		value = (value / 25.4) * 72;
2185 	else if (unit.right(2) == "in")
2186 		value = value * 72;
2187 	else if (unit.right(2) == "px")
2188 		value = value * 0.8;
2189 	else if (noUnit)
2190 		{}/* value = value; */ //no change
2191 	return value;
2192 }
2193 
parseTransform(const QString & transform)2194 QTransform SVGPlug::parseTransform(const QString &transform)
2195 {
2196 	QTransform ret;
2197 	// Workaround for QString::split() bug when string ends with space
2198 	QString trans = transform.simplified();
2199 	// Split string for handling 1 transform statement at a time
2200 	QStringList subtransforms = trans.split(')', Qt::SkipEmptyParts);
2201 	QStringList::ConstIterator it = subtransforms.begin();
2202 	QStringList::ConstIterator end = subtransforms.end();
2203 	for (; it != end; ++it)
2204 	{
2205 		QTransform result;
2206 		QStringList subtransform = it->split('(', Qt::SkipEmptyParts);
2207 		subtransform[0] = subtransform[0].trimmed().toLower();
2208 		subtransform[1] = subtransform[1].simplified();
2209 		QVector<double> params = parseNumbersList(subtransform[1]);
2210 		if (subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
2211 			subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
2212 		if (subtransform[0] == "rotate")
2213 		{
2214 			if (params.count() == 3)
2215 			{
2216 				double x = params[1];
2217 				double y = params[2];
2218 				result.translate(x, y);
2219 				result.rotate(params[0]);
2220 				result.translate(-x, -y);
2221 			}
2222 			else
2223 				result.rotate(params[0]);
2224 		}
2225 		else if (subtransform[0] == "translate")
2226 		{
2227 			if (params.count() == 2)
2228 				result.translate(params[0], params[1]);
2229 			else    // Spec : if only one param given, assume 2nd param to be 0
2230 				result.translate(params[0], 0);
2231 		}
2232 		else if (subtransform[0] == "scale")
2233 		{
2234 			if (params.count() == 2)
2235 				result.scale(params[0], params[1]);
2236 			else    // Spec : if only one param given, assume uniform scaling
2237 				result.scale(params[0], params[0]);
2238 		}
2239 		else if (subtransform[0] == "skewx")
2240 			result.shear(tan(params[0] * 0.01745329251994329576), 0.0F);
2241 		else if (subtransform[0] == "skewy")
2242 			result.shear(0.0F, tan(params[0] * 0.01745329251994329576));
2243 		else if (subtransform[0] == "matrix")
2244 		{
2245 			if (params.count() >= 6)
2246 			{
2247 				double sx = params[0];
2248 				double sy = params[3];
2249 				double p1 = params[1];
2250 				double p2 = params[2];
2251 				double p4 = params[4];
2252 				double p5 = params[5];
2253 				result = QTransform(sx, p1, p2, sy, p4, p5);
2254 			}
2255 		}
2256 		ret = result * ret;
2257 	}
2258 	return ret;
2259 }
2260 
getCoord(const char * ptr,double & number)2261 const char * SVGPlug::getCoord(const char *ptr, double &number)
2262 {
2263 	int integer, exponent;
2264 	double decimal, frac;
2265 	int sign, expsign;
2266 
2267 	exponent = 0;
2268 	integer = 0;
2269 	frac = 1.0;
2270 	decimal = 0;
2271 	sign = 1;
2272 	expsign = 1;
2273 
2274 	// read the sign
2275 	if (*ptr == '+')
2276 		ptr++;
2277 	else if (*ptr == '-')
2278 	{
2279 		ptr++;
2280 		sign = -1;
2281 	}
2282 
2283 	// read the integer part
2284 	while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
2285 		integer = (integer * 10) + *(ptr++) - '0';
2286 	if (*ptr == '.') // read the decimals
2287 	{
2288 		ptr++;
2289 		while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
2290 			decimal += (*(ptr++) - '0') * (frac *= 0.1);
2291 	}
2292 
2293 	if (*ptr == 'e' || *ptr == 'E') // read the exponent part
2294 	{
2295 		ptr++;
2296 
2297 		// read the sign of the exponent
2298 		if (*ptr == '+')
2299 			ptr++;
2300 		else if (*ptr == '-')
2301 		{
2302 			ptr++;
2303 			expsign = -1;
2304 		}
2305 
2306 		exponent = 0;
2307 		while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
2308 		{
2309 			exponent *= 10;
2310 			exponent += *ptr - '0';
2311 			ptr++;
2312 		}
2313 	}
2314 	number = integer + decimal;
2315 	number *= sign * pow(static_cast<double>(10), static_cast<double>(expsign * exponent));
2316 	// skip the following space
2317 	if (*ptr == ' ')
2318 		ptr++;
2319 
2320 	return ptr;
2321 }
2322 
parseColor(const QString & s)2323 QString SVGPlug::parseColor(const QString &s)
2324 {
2325 	QColor c;
2326 	QString ret = CommonStrings::None;
2327 	if (s.length() > 11) // icc-color()
2328 	{
2329 		int iccColorIndex  = s.indexOf("icc-color");
2330 		if (iccColorIndex >= 0)
2331 		{
2332 			QString iccColorName = parseIccColor(s);
2333 			if (iccColorName.length() > 0)
2334 				return iccColorName;
2335 		}
2336 	}
2337 	if (s.startsWith( "rgb(" ) )
2338 	{
2339 		QString parse = s.trimmed();
2340 		QStringList colors = parse.split(',', Qt::SkipEmptyParts);
2341 		QString r = colors[0].right(colors[0].length() - 4);
2342 		QString g = colors[1];
2343 		QString b = colors[2].left(colors[2].length() - 1);
2344 		if (r.contains("%"))
2345 		{
2346 			r.chop(1);
2347 			r = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(r)) / 100.0) );
2348 		}
2349 		if (g.contains("%"))
2350 		{
2351 			g.chop(1);
2352 			g = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(g)) / 100.0) );
2353 		}
2354 		if (b.contains("%"))
2355 		{
2356 			b.chop(1);
2357 			b = QString::number( static_cast<int>((255.0 * ScCLocale::toDoubleC(b)) / 100.0) );
2358 		}
2359 		c = QColor(r.toInt(), g.toInt(), b.toInt());
2360 	}
2361 	else
2362 		c.setNamedColor(s.trimmed());
2363 
2364 	ScColor tmp;
2365 	tmp.fromQColor(c);
2366 	tmp.setSpotColor(false);
2367 	tmp.setRegistrationColor(false);
2368 	QString newColorName = "FromSVG"+c.name();
2369 	QString fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
2370 	if (fNam == newColorName)
2371 		importedColors.append(newColorName);
2372 	ret = fNam;
2373 	return ret;
2374 }
2375 
parseIccColor(const QString & s)2376 QString SVGPlug::parseIccColor(const QString &s)
2377 {
2378 	QColor color;
2379 	QString ret;
2380 	bool iccColorFound = false;
2381 	int iccColorIndex  = s.indexOf("icc-color");
2382 	if (iccColorIndex < 0)
2383 		return ret;
2384 	int iccFirst = s.indexOf("(", iccColorIndex);
2385 	int iccLast  = s.indexOf(")", iccColorIndex);
2386 	if (iccFirst >= 0 && iccLast >= 0)
2387 	{
2388 		QString iccColor = s.mid(iccFirst + 1, iccLast - iccFirst - 1);
2389 		iccColor = iccColor.trimmed();
2390 		QStringList colors = iccColor.split(',', Qt::SkipEmptyParts);
2391 		if (colors.count() == 5) // then we assume this is a cmyk color
2392 		{
2393 			QString cs = colors[1], ms = colors[2], ys = colors[3], ks = colors[4];
2394 			if (cs.contains("%"))
2395 			{
2396 				cs.chop(1);
2397 				cs = QString::number(ScCLocale::toDoubleC(cs) / 100);
2398 			}
2399 			if (ms.contains("%"))
2400 			{
2401 				ms.chop(1);
2402 				ms = QString::number(ScCLocale::toDoubleC(ms) / 100);
2403 			}
2404 			if (ys.contains("%"))
2405 			{
2406 				ys.chop(1);
2407 				ys = QString::number(ScCLocale::toDoubleC(ys) / 100);
2408 			}
2409 			if (ks.contains("%"))
2410 			{
2411 				ks.chop(1);
2412 				ks = QString::number(ScCLocale::toDoubleC(ks) / 100);
2413 			}
2414 			double cv = ScCLocale::toDoubleC(cs);
2415 			double mv = ScCLocale::toDoubleC(ms);
2416 			double yv = ScCLocale::toDoubleC(ys);
2417 			double kv = ScCLocale::toDoubleC(ks);
2418 			color.setCmykF(cv, mv, yv, kv);
2419 			iccColorFound = true;
2420 		}
2421 	}
2422 	if (!iccColorFound)
2423 		return ret;
2424 	ScColor tmp;
2425 	tmp.fromQColor(color);
2426 	tmp.setSpotColor(false);
2427 	tmp.setRegistrationColor(false);
2428 	QString newColorName = "FromSVG"+tmp.name();
2429 	QString fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
2430 	if (fNam == newColorName)
2431 		importedColors.append(newColorName);
2432 	ret = fNam;
2433 	return ret;
2434 }
2435 
parsePA(SvgStyle * obj,const QString & command,const QString & params)2436 void SVGPlug::parsePA(SvgStyle *obj, const QString &command, const QString &params)
2437 {
2438 	if (command == "display")
2439 		obj->Display = params != "none";
2440 	else if (command == "stroke-opacity")
2441 		obj->StrokeOpacity  = fromPercentage(params);
2442 	else if (command == "fill-opacity")
2443 		obj->FillOpacity = fromPercentage(params);
2444 	else if (command == "opacity")
2445 		obj->Opacity = fromPercentage(params);
2446 	else if (command == "fill")
2447 	{
2448 //		if ((obj->InherCol) && (params == "currentColor"))
2449 		if (params == "currentColor")
2450 			obj->FillCol = obj->CurCol;
2451 		else if (params == "none")
2452 		{
2453 			obj->FillCol = CommonStrings::None;
2454 			obj->FillGradientType = 0;
2455 		}
2456 		else if (params.startsWith( "url(" ))
2457 		{
2458 			unsigned int start = params.indexOf("#") + 1;
2459 			unsigned int end = params.lastIndexOf(")");
2460 			QString key = params.mid(start, end - start);
2461 			obj->FillGradientType = 0;
2462 			obj->matrixgf = QTransform();
2463 			bool firstMatrixValid = false;
2464 			if (m_gradients[key].matrixValid)
2465 			{
2466 				firstMatrixValid = true;
2467 				obj->matrixgf = m_gradients[key].matrix;
2468 			}
2469 			while (!m_gradients[key].reference.isEmpty())
2470 			{
2471 				QString key2 = m_gradients[key].reference;
2472 				const GradientHelper& gradientHelper(m_gradients[key2]);
2473 				if (gradientHelper.typeValid)
2474 					obj->FillGradientType = gradientHelper.type;
2475 				if (obj->FillGradientType != 8)
2476 				{
2477 					obj->GFillCol1 = key2;
2478 					if (gradientHelper.gradientValid)
2479 						obj->FillGradient = gradientHelper.gradient;
2480 					if (gradientHelper.cspaceValid)
2481 						obj->FillCSpace = gradientHelper.cspace;
2482 					if (gradientHelper.x1Valid)
2483 						obj->GradFillX1 = gradientHelper.x1;
2484 					if (gradientHelper.y1Valid)
2485 						obj->GradFillY1 = gradientHelper.y1;
2486 					if (gradientHelper.x2Valid)
2487 						obj->GradFillX2 = gradientHelper.x2;
2488 					if (gradientHelper.y2Valid)
2489 						obj->GradFillY2 = gradientHelper.y2;
2490 					if (gradientHelper.fxValid)
2491 						obj->GradFillFX = gradientHelper.fx;
2492 					if (gradientHelper.fyValid)
2493 						obj->GradFillFY = gradientHelper.fy;
2494 					if (gradientHelper.matrixValid)
2495 						obj->matrixgf = gradientHelper.matrix;
2496 				}
2497 				else
2498 				{
2499 					obj->GFillCol1 = key2;
2500 					if ((m_gradients[key2].matrixValid) && (!firstMatrixValid))
2501 						obj->matrixgf *= m_gradients[key2].matrix;
2502 				}
2503 				key = m_gradients[key].reference;
2504 			}
2505 			if (obj->FillGradientType != 8)
2506 			{
2507 				key = params.mid(start, end - start);
2508 				const GradientHelper& gradientHelper(m_gradients[key]);
2509 				if (gradientHelper.typeValid)
2510 					obj->FillGradientType = m_gradients[key].type;
2511 				key = m_gradients[key].reference;
2512 				if (obj->FillGradientType != 8)
2513 				{
2514 					if (gradientHelper.gradientValid)
2515 						obj->FillGradient = gradientHelper.gradient;
2516 					if (gradientHelper.cspaceValid)
2517 						obj->FillCSpace = gradientHelper.cspace;
2518 					if (gradientHelper.x1Valid)
2519 						obj->GradFillX1 = gradientHelper.x1;
2520 					if (gradientHelper.y1Valid)
2521 						obj->GradFillY1 = gradientHelper.y1;
2522 					if (gradientHelper.x2Valid)
2523 						obj->GradFillX2 = gradientHelper.x2;
2524 					if (gradientHelper.y2Valid)
2525 						obj->GradFillY2 = gradientHelper.y2;
2526 					if (gradientHelper.fxValid)
2527 						obj->GradFillFX = gradientHelper.fx;
2528 					if (gradientHelper.fyValid)
2529 						obj->GradFillFY = gradientHelper.fy;
2530 					if (gradientHelper.matrixValid)
2531 						obj->matrixgf = gradientHelper.matrix;
2532 					obj->GFillCol1 = key;
2533 				}
2534 				else
2535 				{
2536 					obj->GFillCol1 = key;
2537 					if (m_gradients[key].matrixValid)
2538 						obj->matrixgf = m_gradients[key].matrix;
2539 				}
2540 			}
2541 			obj->FillCol = CommonStrings::None;
2542 		}
2543 		else
2544 		{
2545 			obj->FillCol = parseColor(params);
2546 			obj->FillGradientType = 0;
2547 		}
2548 	}
2549 	else if (command == "fill-rule")
2550 	{
2551 		obj->fillRule = params;
2552 	}
2553 	else if (command == "color")
2554 	{
2555 		if (params == "none")
2556 			obj->CurCol = CommonStrings::None;
2557 		else if (params.startsWith( "url(" ) )
2558 			obj->CurCol = CommonStrings::None;
2559 		else if (params == "currentColor")
2560 			obj->CurCol = obj->CurCol;
2561 		else
2562 			obj->CurCol = parseColor(params);
2563 	}
2564 	else if (command == "stroke")
2565 	{
2566 //		if ((obj->InherCol) && (params == "currentColor"))
2567 		if (params == "currentColor")
2568 			obj->StrokeCol = obj->CurCol;
2569 		else if (params == "none")
2570 		{
2571 			obj->StrokeCol = CommonStrings::None;
2572 			obj->StrokeGradientType = 0;
2573 		}
2574 		else if (params.startsWith( "url(" ) )
2575 		{
2576 			unsigned int start = params.indexOf("#") + 1;
2577 			unsigned int end = params.lastIndexOf(")");
2578 			QString key = params.mid(start, end - start);
2579 			obj->StrokeGradientType = 0;
2580 			obj->matrixgs = QTransform();
2581 			bool firstMatrixValid = false;
2582 			if (m_gradients[key].matrixValid)
2583 			{
2584 				firstMatrixValid = true;
2585 				obj->matrixgs = m_gradients[key].matrix;
2586 			}
2587 			while (!m_gradients[key].reference.isEmpty())
2588 			{
2589 				QString key2 = m_gradients[key].reference;
2590 				const GradientHelper& gradientHelper(m_gradients[key2]);
2591 				if (gradientHelper.typeValid)
2592 					obj->StrokeGradientType = gradientHelper.type;
2593 				if (obj->StrokeGradientType != 8)
2594 				{
2595 					obj->GStrokeCol1 = key2;
2596 					if (gradientHelper.gradientValid)
2597 						obj->StrokeGradient = gradientHelper.gradient;
2598 					if (gradientHelper.cspaceValid)
2599 						obj->StrokeCSpace = gradientHelper.cspace;
2600 					if (gradientHelper.x1Valid)
2601 						obj->GradStrokeX1 = gradientHelper.x1;
2602 					if (gradientHelper.y1Valid)
2603 						obj->GradStrokeY1 = gradientHelper.y1;
2604 					if (gradientHelper.x2Valid)
2605 						obj->GradStrokeX2 = gradientHelper.x2;
2606 					if (gradientHelper.y2Valid)
2607 						obj->GradStrokeY2 = gradientHelper.y2;
2608 					if (gradientHelper.fxValid)
2609 						obj->GradStrokeFX = gradientHelper.fx;
2610 					if (gradientHelper.fyValid)
2611 						obj->GradStrokeFY = gradientHelper.fy;
2612 					if (gradientHelper.matrixValid)
2613 						obj->matrixgs = gradientHelper.matrix;
2614 				}
2615 				else
2616 				{
2617 					obj->GStrokeCol1 = key2;
2618 					if ((m_gradients[key2].matrixValid) && (!firstMatrixValid))
2619 						obj->matrixgs *= m_gradients[key2].matrix;
2620 				}
2621 				key = m_gradients[key].reference;
2622 			}
2623 			if (obj->StrokeGradientType != 8)
2624 			{
2625 				key = params.mid(start, end - start);
2626 				const GradientHelper& gradientHelper(m_gradients[key]);
2627 				if (gradientHelper.typeValid)
2628 					obj->StrokeGradientType = gradientHelper.type;
2629 				key = m_gradients[key].reference;
2630 				if (obj->StrokeGradientType != 8)
2631 				{
2632 					if (gradientHelper.gradientValid)
2633 						obj->StrokeGradient = gradientHelper.gradient;
2634 					if (gradientHelper.cspaceValid)
2635 						obj->StrokeCSpace = gradientHelper.cspace;
2636 					if (gradientHelper.x1Valid)
2637 						obj->GradStrokeX1 = gradientHelper.x1;
2638 					if (gradientHelper.y1Valid)
2639 						obj->GradStrokeY1 = gradientHelper.y1;
2640 					if (gradientHelper.x2Valid)
2641 						obj->GradStrokeX2 = gradientHelper.x2;
2642 					if (gradientHelper.y2Valid)
2643 						obj->GradStrokeY2 = gradientHelper.y2;
2644 					if (gradientHelper.fxValid)
2645 						obj->GradStrokeFX = gradientHelper.fx;
2646 					if (gradientHelper.fyValid)
2647 						obj->GradStrokeFY = gradientHelper.fy;
2648 					if (gradientHelper.matrixValid)
2649 						obj->matrixgs = gradientHelper.matrix;
2650 					obj->GStrokeCol1 = key;
2651 				}
2652 				else
2653 				{
2654 					obj->GStrokeCol1 = key;
2655 					if (m_gradients[key].matrixValid)
2656 						obj->matrixgs = m_gradients[key].matrix;
2657 				}
2658 			}
2659 			obj->StrokeCol = CommonStrings::None;
2660 		}
2661 		else
2662 		{
2663 			obj->StrokeCol = parseColor(params);
2664 			obj->StrokeGradientType = 0;
2665 		}
2666 	}
2667 	else if (command == "stroke-width")
2668 		obj->LWidth = parseUnit(params);
2669 	else if (command == "stroke-linejoin")
2670 	{
2671 		if (params == "miter")
2672 			obj->PLineJoin = Qt::MiterJoin;
2673 		else if (params == "round")
2674 			obj->PLineJoin = Qt::RoundJoin;
2675 		else if (params == "bevel")
2676 			obj->PLineJoin = Qt::BevelJoin;
2677 	}
2678 	else if (command == "stroke-linecap")
2679 	{
2680 		if (params == "butt")
2681 			obj->PLineEnd = Qt::FlatCap;
2682 		else if (params == "round")
2683 			obj->PLineEnd = Qt::RoundCap;
2684 		else if (params == "square")
2685 			obj->PLineEnd = Qt::SquareCap;
2686 	}
2687 	//	else if (command == "stroke-miterlimit" )
2688 	//		gc->stroke.setMiterLimit(params.todouble());
2689 	else if (command == "stroke-dasharray")
2690 	{
2691 		QVector<double> array;
2692 		if (params != "none")
2693 		{
2694 			QString params2 = params.simplified().replace(',', " ");
2695 			QStringList dashes = params2.split(' ', Qt::SkipEmptyParts);
2696 			if ((dashes.count() > 0) && (parseUnit(dashes[0]) != 0.0))
2697 			{
2698 				for (QStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it)
2699 					array.append(parseUnit(*it));
2700 			}
2701 		}
2702 		obj->dashArray = array;
2703 	}
2704 	else if (command == "stroke-dashoffset")
2705 		obj->dashOffset = ScCLocale::toDoubleC(params);
2706 	else if (command == "font-family")
2707 		obj->FontFamily = params;
2708 	else if (command == "font-style")
2709 		obj->FontStyle = params;
2710 	else if (command == "font-weight")
2711 		obj->FontWeight = params;
2712 	else if (command == "font-stretch")
2713 		obj->FontStretch = params;
2714 	else if (command == "font-size")
2715 		obj->FontSize = parseFontSize(params);
2716 	else if (command == "text-anchor")
2717 		obj->textAnchor = params;
2718 	else if (command == "text-decoration")
2719 		obj->textDecoration = params;
2720 	else if (command == "clip-path")
2721 	{
2722 		if (params.startsWith( "url("))
2723 		{
2724 			unsigned int start = params.indexOf("#") + 1;
2725 			unsigned int end = params.lastIndexOf(")");
2726 			QString key = params.mid(start, end - start);
2727 			QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
2728 			if (it != m_clipPaths.end())
2729 				obj->clipPath = it.value().copy();
2730 		}
2731 	}
2732 	else if (command == "filter")
2733 	{
2734 		if (params.startsWith( "url("))
2735 		{
2736 			unsigned int start = params.indexOf("#") + 1;
2737 			unsigned int end = params.lastIndexOf(")");
2738 			obj->filter = params.mid(start, end - start);
2739 		}
2740 	}
2741 	else if (command == "marker-end")
2742 	{
2743 		if (params.startsWith( "url("))
2744 		{
2745 			unsigned int start = params.indexOf("#") + 1;
2746 			unsigned int end = params.lastIndexOf(")");
2747 			obj->endMarker = params.mid(start, end - start);
2748 		}
2749 	}
2750 	else if (command == "marker-start")
2751 	{
2752 		if (params.startsWith( "url("))
2753 		{
2754 			unsigned int start = params.indexOf("#") + 1;
2755 			unsigned int end = params.lastIndexOf(")");
2756 			obj->startMarker = params.mid(start, end - start);
2757 		}
2758 	}
2759 	else if (!isIgnorableNodeName(command))
2760 	{
2761 		if (!m_unsupportedFeatures.contains(command))
2762 		{
2763 			m_unsupportedFeatures.insert(command, command);
2764 			qDebug() << QString("unsupported SVG feature: %1").arg(command);
2765 			unsupported = true;
2766 		}
2767 	}
2768 }
2769 
parseStyle(SvgStyle * obj,const QDomElement & e)2770 void SVGPlug::parseStyle(SvgStyle *obj, const QDomElement &e)
2771 {
2772 	SvgStyle *gc = m_gc.top();
2773 	if (!gc)
2774 		return;
2775 	if (!e.attribute("display").isEmpty())
2776 		parsePA(obj, "display", e.attribute("display"));
2777 	if (!e.attribute("color").isEmpty())
2778 	{
2779 		if (e.attribute("color") == "inherit")
2780 			gc->InherCol = true;
2781 		else
2782 			parsePA(obj, "color", e.attribute("color"));
2783 	}
2784 	if (!e.attribute("fill").isEmpty())
2785 		parsePA(obj, "fill", e.attribute("fill"));
2786 	if (!e.attribute("stroke").isEmpty())
2787 		parsePA(obj, "stroke", e.attribute("stroke"));
2788 	if (!e.attribute("stroke-width").isEmpty())
2789 		parsePA(obj, "stroke-width", e.attribute("stroke-width"));
2790 	if (!e.attribute("stroke-linejoin").isEmpty())
2791 		parsePA(obj, "stroke-linejoin", e.attribute("stroke-linejoin"));
2792 	if (!e.attribute("stroke-linecap").isEmpty())
2793 		parsePA(obj, "stroke-linecap", e.attribute("stroke-linecap"));
2794 	if (!e.attribute("stroke-dasharray").isEmpty())
2795 		parsePA(obj, "stroke-dasharray", e.attribute("stroke-dasharray"));
2796 	if (!e.attribute("stroke-dashoffset").isEmpty())
2797 		parsePA(obj, "stroke-dashoffset", e.attribute("stroke-dashoffset"));
2798 	if (!e.attribute("stroke-opacity").isEmpty())
2799 		parsePA(obj, "stroke-opacity", e.attribute("stroke-opacity"));
2800 	/*	if (!e.attribute("stroke-miterlimit").isEmpty())
2801 			parsePA(obj, "stroke-miterlimit", e.attribute("stroke-miterlimit"));   */
2802 	if (!e.attribute("fill-rule").isEmpty())
2803 		parsePA(obj, "fill-rule", e.attribute("fill-rule"));
2804 	if (!e.attribute("fill-opacity").isEmpty())
2805 		parsePA(obj, "fill-opacity", e.attribute("fill-opacity"));
2806 	if (!e.attribute("opacity").isEmpty())
2807 		parsePA(obj, "opacity", e.attribute("opacity"));
2808 	if (!e.attribute("font-family").isEmpty())
2809 		parsePA(obj, "font-family", e.attribute("font-family"));
2810 	if (!e.attribute("font-style").isEmpty())
2811 		parsePA(obj, "font-style", e.attribute("font-style"));
2812 	if (!e.attribute("font-weight").isEmpty())
2813 		parsePA(obj, "font-weight", e.attribute("font-weight"));
2814 	if (!e.attribute("font-stretch").isEmpty())
2815 		parsePA(obj, "font-stretch", e.attribute("font-stretch"));
2816 	if (!e.attribute("font-size").isEmpty())
2817 		parsePA(obj, "font-size", e.attribute("font-size"));
2818 	if (!e.attribute("text-anchor").isEmpty())
2819 		parsePA(obj, "text-anchor", e.attribute("text-anchor"));
2820 	if (!e.attribute("text-decoration").isEmpty())
2821 		parsePA(obj, "text-decoration", e.attribute("text-decoration"));
2822 	if (!e.attribute("filter").isEmpty())
2823 		parsePA(obj, "filter", e.attribute("filter"));
2824 	if (!e.attribute("marker-end").isEmpty())
2825 		parsePA(obj, "marker-end", e.attribute("marker-end"));
2826 	if (!e.attribute("marker-start").isEmpty())
2827 		parsePA(obj, "marker-start", e.attribute("marker-start"));
2828 	QString style = e.attribute("style").simplified();
2829 	QStringList substyles = style.split(';', Qt::SkipEmptyParts);
2830 	for (QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
2831 	{
2832 		QStringList substyle = it->split(':', Qt::SkipEmptyParts);
2833 		if (substyle.count() >= 2)
2834 		{
2835 			QString command(substyle.at(0).trimmed());
2836 			QString params(substyle.at(1).trimmed());
2837 			parsePA(obj, command, params);
2838 		}
2839 	}
2840 }
2841 
parseColorStops(GradientHelper * gradient,const QDomElement & e)2842 void SVGPlug::parseColorStops(GradientHelper *gradient, const QDomElement &e)
2843 {
2844 	QString Col = "Black";
2845 	double offset = 0;
2846 	double opa;
2847 	SvgStyle svgStyle;
2848 	parseStyle(&svgStyle, e);
2849 	for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling())
2850 	{
2851 		opa = 1.0;
2852 		QDomElement stop = n.toElement();
2853 		if (parseTagName(stop) == "stop")
2854 		{
2855 			QString temp = stop.attribute("offset");
2856 			if (temp.contains('%'))
2857 			{
2858 				temp.chop(1);
2859 				offset = ScCLocale::toDoubleC(temp) / 100.0;
2860 			}
2861 			else
2862 				offset = ScCLocale::toDoubleC(temp);
2863 			if (stop.hasAttribute("stop-opacity"))
2864 				opa = fromPercentage(stop.attribute("stop-opacity"));
2865 			if (stop.hasAttribute("stop-color"))
2866 			{
2867 				if (stop.attribute("stop-color") == "currentColor")
2868 					Col = svgStyle.CurCol;
2869 				else
2870 					Col = parseColor(stop.attribute("stop-color"));
2871 			}
2872 			else if (stop.hasAttribute("style"))
2873 			{
2874 				QString style = stop.attribute("style").simplified();
2875 				QStringList substyles = style.split(';', Qt::SkipEmptyParts);
2876 				for (QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
2877 				{
2878 					QStringList substyle = it->split(':', Qt::SkipEmptyParts);
2879 					if (substyle.count() >= 2)
2880 					{
2881 						QString command(substyle.at(0).trimmed());
2882 						QString params(substyle.at(1).trimmed());
2883 						if (command == "stop-color")
2884 							Col = parseColor(params);
2885 						if (command == "stop-opacity")
2886 							opa = fromPercentage(params);
2887 					}
2888 				}
2889 			}
2890 			else
2891 				Col = "Black";
2892 		}
2893 		const ScColor& gradC = m_Doc->PageColors[Col];
2894 		gradient->gradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), offset, 0.5, opa, Col, 100);
2895 		gradient->gradientValid = true;
2896 	}
2897 	if (gradient->gradientValid)
2898 		gradient->gradient.filterStops();
2899 }
2900 
parseFilter(const QDomElement & b)2901 void SVGPlug::parseFilter(const QDomElement &b)
2902 {
2903 	QString id = b.attribute("id", "");
2904 	QString origName = id;
2905 	if (id.isEmpty())
2906 		return;
2907 
2908 	filterSpec fspec;
2909 	fspec.blendMode = 0;
2910 
2911 	QDomElement child = b.firstChildElement();
2912 	if (child.isNull() || (child.tagName() != "feBlend"))
2913 	{
2914 		filters.insert(id, fspec);
2915 		m_nodeMap.insert(origName, b);
2916 		return;
2917 	}
2918 
2919 	QString blendModeStr(child.attribute("mode"));
2920 	if (blendModeStr == "normal")
2921 		fspec.blendMode = 0;
2922 	if (blendModeStr == "darken")
2923 		fspec.blendMode = 1;
2924 	if (blendModeStr == "lighten")
2925 		fspec.blendMode = 2;
2926 	if (blendModeStr == "multiply")
2927 		fspec.blendMode = 3;
2928 	if (blendModeStr == "screen")
2929 		fspec.blendMode = 4;
2930 
2931 	filters.insert(id, fspec);
2932 	m_nodeMap.insert(origName, b);
2933 }
2934 
parseMarker(const QDomElement & b)2935 void SVGPlug::parseMarker(const QDomElement &b)
2936 {
2937 	QString id = b.attribute("id", "");
2938 	if (id.isEmpty())
2939 		return;
2940 	QString origName = id;
2941 
2942 	inGroupXOrigin = 999999;
2943 	inGroupYOrigin = 999999;
2944 	markerDesc mark;
2945 	mark.xref = parseUnit(b.attribute("refX", "0"));
2946 	mark.yref = parseUnit(b.attribute("refY", "0"));
2947 	mark.wpat = parseUnit(b.attribute("markerWidth", "3"));
2948 	mark.hpat = parseUnit(b.attribute("markerHeight", "3"));
2949 
2950 	setupNode(b);
2951 	SvgStyle *gc = m_gc.top();
2952 	gc->matrix   = QTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2953 
2954 	QList<PageItem*> GElements;
2955 	GElements = parseGroup(b);
2956 	if (GElements.count() > 0)
2957 	{
2958 		ScPattern pat = ScPattern();
2959 		pat.setDoc(m_Doc);
2960 		PageItem* currItem = GElements.at(0);
2961 		m_Doc->DoDrawing = true;
2962 		double minx =  std::numeric_limits<double>::max();
2963 		double miny =  std::numeric_limits<double>::max();
2964 		double maxx = -std::numeric_limits<double>::max();
2965 		double maxy = -std::numeric_limits<double>::max();
2966 		double x1, x2, y1, y2;
2967 		currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
2968 		minx = qMin(minx, x1);
2969 		miny = qMin(miny, y1);
2970 		maxx = qMax(maxx, x2);
2971 		maxy = qMax(maxy, y2);
2972 		currItem->gXpos = currItem->xPos() - minx;
2973 		currItem->gYpos = currItem->yPos() - miny;
2974 		currItem->setXYPos(currItem->gXpos, currItem->gYpos, true);
2975 		pat.pattern = currItem->DrawObj_toImage(qMin(qMax(maxx - minx, maxy - miny), 500.0));
2976 		pat.width = maxx - minx;
2977 		pat.height = maxy - miny;
2978 		m_Doc->DoDrawing = false;
2979 		pat.items.append(currItem);
2980 		m_Doc->Items->removeAll(currItem);
2981 		m_Doc->addPattern(id, pat);
2982 		importedPatterns.append(id);
2983 		importedPattTrans.insert(origName, id);
2984 		markers.insert(id, mark);
2985 	}
2986 	m_nodeMap.insert(origName, b);
2987 	delete (m_gc.pop());
2988 }
2989 
parsePattern(const QDomElement & b)2990 void SVGPlug::parsePattern(const QDomElement &b)
2991 {
2992 	GradientHelper gradhelper;
2993 	QString href = b.attribute("xlink:href").mid(1);
2994 	if (!href.isEmpty())
2995 	{
2996 		if (m_gradients.contains(href))
2997 		{
2998 			gradhelper.type = m_gradients[href].type;
2999 			gradhelper.gradientValid = m_gradients[href].gradientValid;
3000 			gradhelper.typeValid = m_gradients[href].typeValid;
3001 			gradhelper.matrix = m_gradients[href].matrix;
3002 			gradhelper.matrixValid = m_gradients[href].matrixValid;
3003 		}
3004 		gradhelper.reference = href;
3005 	}
3006 	QString id = b.attribute("id", "");
3007 	QString origName = id;
3008 	if (!id.isEmpty())
3009 	{
3010 		inGroupXOrigin = 999999;
3011 		inGroupYOrigin = 999999;
3012 		double wpat = parseUnit(b.attribute("width", "0"));
3013 		double hpat = parseUnit(b.attribute("height", "0"));
3014 		QList<PageItem*> GElements;
3015 		GElements = parseGroup(b);
3016 		if (GElements.count() > 0)
3017 		{
3018 			ScPattern pat = ScPattern();
3019 			pat.setDoc(m_Doc);
3020 			PageItem* currItem = GElements.at(0);
3021 			m_Doc->DoDrawing = true;
3022 			pat.pattern = currItem->DrawObj_toImage(qMin(qMax(wpat, hpat), 500.0));
3023 			double xOrg = 0.0;
3024 			double yOrg = 0.0;
3025 			if (inGroupXOrigin < 0.0)
3026 				xOrg = inGroupXOrigin;
3027 			if (inGroupYOrigin < 0.0)
3028 				yOrg = inGroupYOrigin;
3029 			if ((xOrg != 0.0) || (yOrg != 0.0))
3030 				pat.pattern = pat.pattern.copy(-xOrg, -yOrg, wpat, hpat);
3031 			pat.xoffset = xOrg;
3032 			pat.yoffset = yOrg;
3033 			pat.width = wpat;
3034 			pat.height = hpat;
3035 			m_Doc->DoDrawing = false;
3036 			pat.items.append(currItem);
3037 			m_Doc->Items->removeAll(currItem);
3038 			m_Doc->addPattern(id, pat);
3039 			importedPatterns.append(id);
3040 			importedPattTrans.insert(origName, id);
3041 		}
3042 		m_nodeMap.insert(origName, b);
3043 		QString transf = b.attribute("patternTransform");
3044 		if (!transf.isEmpty())
3045 		{
3046 			gradhelper.matrix = parseTransform(b.attribute("patternTransform"));
3047 			gradhelper.matrixValid = true;
3048 		}
3049 		else
3050 			gradhelper.matrixValid = false;
3051 		gradhelper.gradientValid = true;
3052 		gradhelper.gradient.clearStops();
3053 		gradhelper.gradient.setRepeatMethod(VGradient::none);
3054 		gradhelper.type = 8;
3055 		gradhelper.typeValid = true;
3056 		m_gradients.insert(origName, gradhelper);
3057 	}
3058 }
3059 
parseGradient(const QDomElement & e)3060 void SVGPlug::parseGradient(const QDomElement &e)
3061 {
3062 	GradientHelper gradhelper;
3063 	gradhelper.gradientValid = false;
3064 	gradhelper.gradient.clearStops();
3065 	gradhelper.gradient.setRepeatMethod(VGradient::none);
3066 
3067 	QString href = e.attribute("xlink:href").mid(1);
3068 //	double x1=0, y1=0, x2=0, y2=0, fx=0, fy=0;
3069 	double x1=0, y1=0, x2=0, fx=0, fy=0;
3070 	if (!href.isEmpty())
3071 	{
3072 		if (m_gradients.contains(href))
3073 		{
3074 			gradhelper.type = m_gradients[href].type;
3075 			gradhelper.gradient = m_gradients[href].gradient;
3076 			gradhelper.x1 = m_gradients[href].x1;
3077 			gradhelper.y1 = m_gradients[href].y1;
3078 			gradhelper.x2 = m_gradients[href].x2;
3079 			gradhelper.y2 = m_gradients[href].y2;
3080 			gradhelper.fx = m_gradients[href].fx;
3081 			gradhelper.fy = m_gradients[href].fy;
3082 			gradhelper.cspace = m_gradients[href].cspace;
3083 			gradhelper.matrix = m_gradients[href].matrix;
3084 			gradhelper.x1Valid = m_gradients[href].x1Valid;
3085 			gradhelper.x2Valid = m_gradients[href].x2Valid;
3086 			gradhelper.y1Valid = m_gradients[href].y1Valid;
3087 			gradhelper.y2Valid = m_gradients[href].y2Valid;
3088 			gradhelper.fxValid = m_gradients[href].fxValid;
3089 			gradhelper.fyValid = m_gradients[href].fyValid;
3090 			gradhelper.cspaceValid = m_gradients[href].cspaceValid;
3091 			gradhelper.matrixValid = m_gradients[href].matrixValid;
3092 			gradhelper.gradientValid = m_gradients[href].gradientValid;
3093 			gradhelper.typeValid = m_gradients[href].typeValid;
3094 		}
3095 		gradhelper.reference = href;
3096 	}
3097 	QString id = e.attribute("id", "");
3098 	QString origName = id;
3099 	if (parseTagName(e) == "linearGradient")
3100 	{
3101 		if (e.hasAttribute("x1"))
3102 		{
3103 			gradhelper.x1 = parseUnit(e.attribute("x1", "0"));
3104 			gradhelper.x1Valid = true;
3105 		}
3106 		if (e.hasAttribute("y1"))
3107 		{
3108 			gradhelper.y1 = parseUnit(e.attribute("y1", "0"));
3109 			gradhelper.y1Valid = true;
3110 		}
3111 		if (e.hasAttribute("x2"))
3112 		{
3113 			gradhelper.x2 = parseUnit(e.attribute("x2", "1"));
3114 			gradhelper.x2Valid = true;
3115 		}
3116 		if (e.hasAttribute("y2"))
3117 		{
3118 			gradhelper.y2 = parseUnit(e.attribute("y2", "0"));
3119 			gradhelper.y2Valid = true;
3120 		}
3121 		gradhelper.fx = x1;
3122 		gradhelper.fy = y1;
3123 		gradhelper.type = 6;
3124 		gradhelper.typeValid = true;
3125 	}
3126 	else
3127 	{
3128 		if (e.hasAttribute("cx"))
3129 		{
3130 			x1 = parseUnit(e.attribute("cx", "0.5"));
3131 			gradhelper.x1Valid = true;
3132 		}
3133 		if (e.hasAttribute("cy"))
3134 		{
3135 			y1 = parseUnit(e.attribute("cy", "0.5"));
3136 			gradhelper.y1Valid = true;
3137 		}
3138 		if (e.hasAttribute("r"))
3139 		{
3140 			x2 = parseUnit(e.attribute("r", "0.5"));
3141 			gradhelper.x2Valid = true;
3142 		}
3143 	//	y2 = y1;
3144 		fx = e.hasAttribute("fx") ? parseUnit(e.attribute("fx", ScCLocale::toQStringC(x1))) : x1;
3145 		gradhelper.fxValid = true;
3146 		fy = e.hasAttribute("fy") ? parseUnit(e.attribute("fy", ScCLocale::toQStringC(y1))) : y1;
3147 		gradhelper.fyValid = true;
3148 		double squareRadius = (fx - x1) * (fx - x1) + (fy - y1) * (fy - y1);
3149 		if (squareRadius > (x2 * x2))
3150 		{
3151 			fx = x1 + x2 * (fx - x1) / sqrt(squareRadius);
3152 			fy = y1 + x2 * (fy - y1) / sqrt(squareRadius);
3153 		}
3154 		gradhelper.y2Valid = true;
3155 		gradhelper.x1 = x1;
3156 		gradhelper.y1 = y1;
3157 		gradhelper.x2 = x1 + x2;
3158 		gradhelper.y2 = y1;
3159 		gradhelper.fx = fx;
3160 		gradhelper.fy = fy;
3161 		gradhelper.type = 7;
3162 		gradhelper.typeValid = true;
3163 	}
3164 	if (!e.attribute("gradientUnits").isEmpty())
3165 	{
3166 		QString uni = e.attribute("gradientUnits");
3167 		gradhelper.cspace = (uni == "userSpaceOnUse");
3168 		gradhelper.cspaceValid = true;
3169 	}
3170 	else
3171 	{
3172 		gradhelper.cspace = false;
3173 		gradhelper.cspaceValid = false;
3174 	}
3175 	QString transf = e.attribute("gradientTransform");
3176 	if (!transf.isEmpty())
3177 	{
3178 		gradhelper.matrix = parseTransform(e.attribute("gradientTransform"));
3179 		gradhelper.matrixValid = true;
3180 	}
3181 	else
3182 		gradhelper.matrixValid = false;
3183 	QString spreadMethod = e.attribute("spreadMethod");
3184 	if (!spreadMethod.isEmpty())
3185 	{
3186 		if (spreadMethod == "reflect")
3187 			gradhelper.gradient.setRepeatMethod(VGradient::reflect);
3188 		else if (spreadMethod == "repeat")
3189 			gradhelper.gradient.setRepeatMethod(VGradient::repeat);
3190 	}
3191 	parseColorStops(&gradhelper, e);
3192 	m_gradients.insert(id, gradhelper);
3193 	if (gradhelper.gradientValid)
3194 	{
3195 		if (m_Doc->addGradient(id, gradhelper.gradient))
3196 			importedGradients.append(id);
3197 	}
3198 	importedGradTrans.insert(origName, id);
3199 }
3200 
parseTagName(const QDomElement & element)3201 QString SVGPlug::parseTagName(const QDomElement& element)
3202 {
3203 	QString tagName(element.tagName());
3204 	if (tagName.startsWith("svg:"))
3205 		return tagName.mid(4, -1);
3206 	return tagName;
3207 }
3208 
~SVGPlug()3209 SVGPlug::~SVGPlug()
3210 {
3211 	delete tmpSel;
3212 }
3213