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, ¤tPos);
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 ¶ms)
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