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 							 -------------------
9 	begin                : Sat Oct 26 2013
10 	copyright            : (C) 2013 by Franz Schmid
11 	email                : Franz.Schmid@altmuehlnet.de
12  ***************************************************************************/
13 
14 #include <QByteArray>
15 #include <QCursor>
16 #include <QDrag>
17 #include <QFile>
18 #include <QList>
19 #include <QMimeData>
20 #include <QRegExp>
21 #include <QStack>
22 #include <QUrl>
23 #include <QDebug>
24 
25 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
26 #define _USE_MATH_DEFINES
27 #endif
28 
29 #include <cstdlib>
30 #include <climits>
31 #include <limits>
32 
33 #include "commonstrings.h"
34 
35 #include "importxps.h"
36 
37 #include "loadsaveplugin.h"
38 #include "pageitem_table.h"
39 #include "pagesize.h"
40 #include "prefscontext.h"
41 #include "prefsfile.h"
42 #include "prefsmanager.h"
43 #include "prefstable.h"
44 #include "rawimage.h"
45 #include "scclocale.h"
46 #include "sccolorengine.h"
47 #include "scconfig.h"
48 #include "scmimedata.h"
49 #include "scpaths.h"
50 #include "scribusXml.h"
51 #include "scribuscore.h"
52 #include "scribusdoc.h"
53 #include "scribusview.h"
54 #include "sctextstream.h"
55 #include "selection.h"
56 #include "third_party/zip/scribus_zip.h"
57 #include "ui/customfdialog.h"
58 #include "ui/missing.h"
59 #include "ui/multiprogressdialog.h"
60 #include "ui/propertiespalette.h"
61 #include "undomanager.h"
62 #include "util.h"
63 #include "util_formats.h"
64 #include "util_math.h"
65 #include "xpsimportoptions.h"
66 
XpsPlug(ScribusDoc * doc,int flags)67 XpsPlug::XpsPlug(ScribusDoc* doc, int flags)
68 {
69 	tmpSel = new Selection(this, false);
70 	m_Doc = doc;
71 	importerFlags = flags;
72 	interactive = (flags & LoadSavePlugin::lfInteractive);
73 	progressDialog = nullptr;
74 	uz = nullptr;
75 }
76 
readThumbnail(const QString & fName)77 QImage XpsPlug::readThumbnail(const QString& fName)
78 {
79 	QImage tmp;
80 	if (!QFile::exists(fName))
81 		return QImage();
82 	progressDialog = nullptr;
83 	uz = new ScZipHandler();
84 	if (!uz->open(fName))
85 	{
86 		delete uz;
87 		if (progressDialog)
88 			progressDialog->close();
89 		return QImage();
90 	}
91 	bool found = false;
92 	if (uz->contains("_rels/.rels"))
93 	{
94 		QByteArray f;
95 		QDomDocument designMapDom;
96 		if (!uz->read("_rels/.rels", f))
97 			return QImage();
98 		if (designMapDom.setContent(f))
99 		{
100 			QDomElement docElem = designMapDom.documentElement();
101 			for (QDomElement drawPag = docElem.firstChildElement(); !drawPag.isNull(); drawPag = drawPag.nextSiblingElement())
102 			{
103 				if (drawPag.tagName() != "Relationship")
104 					continue;
105 				if ((!drawPag.hasAttribute("Target")) || (!drawPag.hasAttribute("Type")))
106 					continue;
107 				if (drawPag.attribute("Type") != "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
108 					continue;
109 				QString thumbRef = drawPag.attribute("Target");
110 				if (thumbRef.startsWith("/"))
111 					thumbRef = thumbRef.mid(1);
112 				QByteArray im;
113 				if (uz->read(thumbRef, im))
114 				{
115 					tmp = QImage::fromData(im);
116 					found = true;
117 					break;
118 				}
119 			}
120 		}
121 	}
122 	if (!found)
123 	{
124 		QFileInfo fi = QFileInfo(fName);
125 		baseFile = QDir::cleanPath(QDir::toNativeSeparators(fi.absolutePath()+"/"));
126 		docWidth = PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth;
127 		docHeight = PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight;
128 		m_Doc = new ScribusDoc();
129 		m_Doc->setup(0, 1, 1, 1, 1, "Custom", "Custom");
130 		m_Doc->setPage(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false);
131 		m_Doc->addPage(0);
132 		m_Doc->setGUI(false, ScCore->primaryMainWindow(), nullptr);
133 		baseX = m_Doc->currentPage()->xOffset();
134 		baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
135 		Elements.clear();
136 		m_Doc->setLoading(true);
137 		m_Doc->DoDrawing = false;
138 		m_Doc->scMW()->setScriptRunning(true);
139 		QString CurDirP = QDir::currentPath();
140 		QDir::setCurrent(fi.path());
141 		importedColors.clear();
142 		importedPatterns.clear();
143 		conversionFactor = 72.0 / 96.0;
144 		loadedFonts.clear();
145 		linkTargets.clear();
146 		linkSources.clear();
147 		if (uz->contains("FixedDocSeq.fdseq"))
148 			parseDocSequence("FixedDocSeq.fdseq");
149 		else if (uz->contains("FixedDocumentSequence.fdseq"))
150 			parseDocSequence("FixedDocumentSequence.fdseq");
151 		if (Elements.count() > 0)
152 		{
153 			tmpSel->clear();
154 			QDir::setCurrent(CurDirP);
155 			if (Elements.count() > 1)
156 				m_Doc->groupObjectsList(Elements);
157 			m_Doc->DoDrawing = true;
158 			m_Doc->m_Selection->delaySignalsOn();
159 			if (Elements.count() > 0)
160 			{
161 				for (int dre=0; dre<Elements.count(); ++dre)
162 				{
163 					tmpSel->addItem(Elements.at(dre), true);
164 				}
165 				tmpSel->setGroupRect();
166 				double xs = tmpSel->width();
167 				double ys = tmpSel->height();
168 				tmp = Elements.at(0)->DrawObj_toImage(500);
169 				tmp.setText("XSize", QString("%1").arg(xs));
170 				tmp.setText("YSize", QString("%1").arg(ys));
171 			}
172 			m_Doc->scMW()->setScriptRunning(false);
173 			m_Doc->setLoading(false);
174 			m_Doc->m_Selection->delaySignalsOff();
175 			delete m_Doc;
176 		}
177 		else
178 		{
179 			QDir::setCurrent(CurDirP);
180 			m_Doc->DoDrawing = true;
181 			m_Doc->scMW()->setScriptRunning(false);
182 			delete m_Doc;
183 		}
184 	}
185 	uz->close();
186 	delete uz;
187 	return tmp;
188 }
189 
import(const QString & fNameIn,const TransactionSettings & trSettings,int flags,bool showProgress)190 bool XpsPlug::import(const QString& fNameIn, const TransactionSettings& trSettings, int flags, bool showProgress)
191 {
192 	bool success = false;
193 	interactive = (flags & LoadSavePlugin::lfInteractive);
194 	importerFlags = flags;
195 	cancel = false;
196 	bool ret = false;
197 	firstPage = true;
198 	pagecount = 1;
199 	QFileInfo fi = QFileInfo(fNameIn);
200 	m_FileName = fi.fileName();
201 	if ( !ScCore->usingGUI() )
202 	{
203 		interactive = false;
204 		showProgress = false;
205 	}
206 	baseFile = QDir::cleanPath(QDir::toNativeSeparators(fi.absolutePath()+"/"));
207 	if ( showProgress )
208 	{
209 		ScribusMainWindow* mw=(m_Doc==nullptr) ? ScCore->primaryMainWindow() : m_Doc->scMW();
210 		progressDialog = new MultiProgressDialog( tr("Importing: %1").arg(fi.fileName()), CommonStrings::tr_Cancel, mw );
211 		QStringList barNames, barTexts;
212 		barNames << "GI";
213 		barTexts << tr("Analyzing File:");
214 		QList<bool> barsNumeric;
215 		barsNumeric << false;
216 		progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
217 		progressDialog->setOverallTotalSteps(3);
218 		progressDialog->setOverallProgress(0);
219 		progressDialog->setProgress("GI", 0);
220 		progressDialog->show();
221 		connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
222 		qApp->processEvents();
223 	}
224 	else
225 		progressDialog = nullptr;
226 	if (progressDialog)
227 	{
228 		progressDialog->setOverallProgress(1);
229 		qApp->processEvents();
230 	}
231 	/* Set default Page to size defined in Preferences */
232 	docWidth = PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth;
233 	docHeight = PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight;
234 	baseX = 0;
235 	baseY = 0;
236 	if (!interactive || (flags & LoadSavePlugin::lfInsertPage))
237 	{
238 		m_Doc->setPage(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false);
239 		m_Doc->addPage(0);
240 		m_Doc->view()->addPage(0, true);
241 		baseX = 0;
242 		baseY = 0;
243 	}
244 	else
245 	{
246 		if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
247 		{
248 			m_Doc=ScCore->primaryMainWindow()->doFileNew(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
249 			ScCore->primaryMainWindow()->HaveNewDoc();
250 			ret = true;
251 			baseX = 0;
252 			baseY = 0;
253 			baseX = m_Doc->currentPage()->xOffset();
254 			baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
255 		}
256 	}
257 	if ((!ret) && (interactive))
258 	{
259 		baseX = m_Doc->currentPage()->xOffset();
260 		baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
261 	}
262 	if ((ret) || (!interactive))
263 	{
264 		if (docWidth > docHeight)
265 			m_Doc->setPageOrientation(1);
266 		else
267 			m_Doc->setPageOrientation(0);
268 		m_Doc->setPageSize("Custom");
269 	}
270 	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
271 		m_Doc->view()->deselectItems();
272 	Elements.clear();
273 	m_Doc->setLoading(true);
274 	m_Doc->DoDrawing = false;
275 	if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
276 		m_Doc->view()->updatesOn(false);
277 	m_Doc->scMW()->setScriptRunning(true);
278 	qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
279 	QString CurDirP = QDir::currentPath();
280 	QDir::setCurrent(fi.path());
281 	if (convert(fNameIn))
282 	{
283 		tmpSel->clear();
284 		QDir::setCurrent(CurDirP);
285 		if ((Elements.count() > 1) && (!(importerFlags & LoadSavePlugin::lfCreateDoc)))
286 			m_Doc->groupObjectsList(Elements);
287 		m_Doc->DoDrawing = true;
288 		m_Doc->scMW()->setScriptRunning(false);
289 		m_Doc->setLoading(false);
290 		qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
291 		if ((Elements.count() > 0) && (!ret) && (interactive))
292 		{
293 			if (flags & LoadSavePlugin::lfScripted)
294 			{
295 				bool loadF = m_Doc->isLoading();
296 				m_Doc->setLoading(false);
297 				m_Doc->changed();
298 				m_Doc->setLoading(loadF);
299 				if (!(flags & LoadSavePlugin::lfLoadAsPattern))
300 				{
301 					m_Doc->m_Selection->delaySignalsOn();
302 					for (int dre=0; dre<Elements.count(); ++dre)
303 					{
304 						m_Doc->m_Selection->addItem(Elements.at(dre), true);
305 					}
306 					m_Doc->m_Selection->delaySignalsOff();
307 					m_Doc->m_Selection->setGroupRect();
308 					if (m_Doc->view() != nullptr)
309 						m_Doc->view()->updatesOn(true);
310 				}
311 			}
312 			else
313 			{
314 				m_Doc->DragP = true;
315 				m_Doc->DraggedElem = nullptr;
316 				m_Doc->DragElements.clear();
317 				m_Doc->m_Selection->delaySignalsOn();
318 				for (int dre=0; dre<Elements.count(); ++dre)
319 				{
320 					tmpSel->addItem(Elements.at(dre), true);
321 				}
322 				tmpSel->setGroupRect();
323 				ScElemMimeData* md = ScriXmlDoc::writeToMimeData(m_Doc, tmpSel);
324 				m_Doc->itemSelection_DeleteItem(tmpSel);
325 				m_Doc->view()->updatesOn(true);
326 				if ((importedColors.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns))))
327 				{
328 					for (int cd = 0; cd < importedColors.count(); cd++)
329 					{
330 						m_Doc->PageColors.remove(importedColors[cd]);
331 					}
332 				}
333 				if ((importedPatterns.count() != 0) && (!(flags & LoadSavePlugin::lfKeepPatterns)))
334 				{
335 					for (int cd = 0; cd < importedPatterns.count(); cd++)
336 					{
337 						m_Doc->docPatterns.remove(importedPatterns[cd]);
338 					}
339 				}
340 				m_Doc->m_Selection->delaySignalsOff();
341 				// We must copy the TransationSettings object as it is owned
342 				// by handleObjectImport method afterwards
343 				TransactionSettings* transacSettings = new TransactionSettings(trSettings);
344 				m_Doc->view()->handleObjectImport(md, transacSettings);
345 				m_Doc->DragP = false;
346 				m_Doc->DraggedElem = nullptr;
347 				m_Doc->DragElements.clear();
348 			}
349 		}
350 		else
351 		{
352 			m_Doc->changed();
353 			m_Doc->reformPages();
354 			if (!(flags & LoadSavePlugin::lfLoadAsPattern))
355 				m_Doc->view()->updatesOn(true);
356 		}
357 		success = true;
358 	}
359 	else
360 	{
361 		QDir::setCurrent(CurDirP);
362 		m_Doc->DoDrawing = true;
363 		m_Doc->scMW()->setScriptRunning(false);
364 		if (!(flags & LoadSavePlugin::lfLoadAsPattern))
365 			m_Doc->view()->updatesOn(true);
366 		qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
367 		success = false;
368 	}
369 	if (interactive)
370 		m_Doc->setLoading(false);
371 	//CB If we have a gui we must refresh it if we have used the progressbar
372 	if (!(flags & LoadSavePlugin::lfLoadAsPattern))
373 	{
374 		if ((showProgress) && (!interactive))
375 			m_Doc->view()->DrawNew();
376 	}
377 	qApp->restoreOverrideCursor();
378 	return success;
379 }
380 
~XpsPlug()381 XpsPlug::~XpsPlug()
382 {
383 	delete progressDialog;
384 	delete tmpSel;
385 	for (int a = 0; a < tempFontFiles.count(); a++)
386 	{
387 		QFile::remove(tempFontFiles[a]);
388 	}
389 }
390 
convert(const QString & fn)391 bool XpsPlug::convert(const QString& fn)
392 {
393 	bool retVal = true;
394 	importedColors.clear();
395 	importedPatterns.clear();
396 	conversionFactor = 72.0 / 96.0;
397 	loadedFonts.clear();
398 	linkTargets.clear();
399 	linkSources.clear();
400 	pathResources.clear();
401 	if (progressDialog)
402 	{
403 		progressDialog->setOverallProgress(2);
404 		progressDialog->setLabel("GI", tr("Generating Items"));
405 		qApp->processEvents();
406 	}
407 
408 	uz = new ScZipHandler();
409 	if (!uz->open(fn))
410 	{
411 		delete uz;
412 		if (progressDialog)
413 			progressDialog->close();
414 		return false;
415 	}
416 
417 	retVal = false;
418 	if (uz->contains("FixedDocSeq.fdseq"))
419 		retVal = parseDocSequence("FixedDocSeq.fdseq");
420 	else if (uz->contains("FixedDocumentSequence.fdseq"))
421 		retVal = parseDocSequence("FixedDocumentSequence.fdseq");
422 	if (retVal)
423 		resolveLinks();
424 
425 	uz->close();
426 	delete uz;
427 	if (progressDialog)
428 		progressDialog->close();
429 	return retVal;
430 }
431 
parseDocSequence(const QString & designMap)432 bool XpsPlug::parseDocSequence(const QString& designMap)
433 {
434 	QByteArray f;
435 	QDomDocument designMapDom;
436 	if (!uz->read(designMap, f))
437 		return false;
438 	if (!designMapDom.setContent(f))
439 		return false;
440 
441 	bool parsed = false;
442 	QString DocumentReference = "";
443 	QDomElement docElem = designMapDom.documentElement();
444 	for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
445 	{
446 		QDomElement dpg = drawPag.toElement();
447 		if (dpg.tagName() == "DocumentReference")
448 		{
449 			if (dpg.hasAttribute("Source"))
450 			{
451 				DocumentReference = dpg.attribute("Source", "");
452 				if (DocumentReference.startsWith("/"))
453 					DocumentReference = DocumentReference.mid(1);
454 				parsed = parseDocReference(DocumentReference);
455 				if (!parsed) break;
456 			}
457 		}
458 	}
459 	return parsed;
460 }
461 
parseDocReference(const QString & designMap)462 bool XpsPlug::parseDocReference(const QString& designMap)
463 {
464 	QByteArray f;
465 	QFileInfo fi(designMap);
466 	QString path = fi.path();
467 	if (!uz->read(designMap, f))
468 		return false;
469 
470 	QDomDocument designMapDom;
471 	if (!designMapDom.setContent(f))
472 		return false;
473 
474 	QString PageReference = "";
475 	QDomElement docElem = designMapDom.documentElement();
476 	if (importerFlags & LoadSavePlugin::lfCreateThumbnail)
477 	{
478 		QDomNodeList pgList = docElem.childNodes();
479 		QDomNode drawPag = pgList.item(0);
480 		QDomElement dpg = drawPag.toElement();
481 		if (dpg.tagName() == "PageContent")
482 		{
483 			if (dpg.hasAttribute("Source"))
484 			{
485 				PageReference = dpg.attribute("Source", "");
486 				if (PageReference.startsWith("/"))
487 				{
488 					PageReference = PageReference.mid(1);
489 					parsePageReference(PageReference);
490 				}
491 				else
492 				{
493 					if (!PageReference.startsWith(path))
494 					{
495 						PageReference = path + "/" + PageReference;
496 						PageReference = QDir::cleanPath(PageReference);
497 					}
498 					parsePageReference(PageReference);
499 				}
500 			}
501 		}
502 	}
503 	else
504 	{
505 		std::vector<int> pageNs;
506 		QString pageString = "*";
507 		int pgCount = 0;
508 		int maxPages = docElem.childNodes().count();
509 		if (((interactive) || (importerFlags & LoadSavePlugin::lfCreateDoc)) && (maxPages > 1))
510 		{
511 			if (progressDialog)
512 				progressDialog->hide();
513 			qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
514 			XpsImportOptions optImp(ScCore->primaryMainWindow());
515 			optImp.setUpOptions(m_FileName, 1, maxPages, interactive);
516 			if (optImp.exec() != QDialog::Accepted)
517 				return false;
518 			pageString = optImp.getPagesString();
519 			qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
520 			if (progressDialog)
521 				progressDialog->show();
522 			qApp->processEvents();
523 		}
524 		parsePagesString(pageString, &pageNs, maxPages);
525 		if (pageString != "*")
526 			maxPages = pageNs.size();
527 		if (progressDialog)
528 		{
529 			progressDialog->setTotalSteps("GI", maxPages);
530 			progressDialog->setProgress("GI", pgCount);
531 			qApp->processEvents();
532 		}
533 		QDomNodeList pgList = docElem.childNodes();
534 		for (uint ap = 0; ap < pageNs.size(); ++ap)
535 		{
536 			QDomNode drawPag = pgList.item(pageNs[ap] - 1);
537 			QDomElement dpg = drawPag.toElement();
538 			if (dpg.tagName() == "PageContent")
539 			{
540 				if (dpg.hasAttribute("Source"))
541 				{
542 					PageReference = dpg.attribute("Source", "");
543 					if (PageReference.startsWith("/"))
544 					{
545 						PageReference = PageReference.mid(1);
546 						parsePageReference(PageReference);
547 					}
548 					else
549 					{
550 						if (!PageReference.startsWith(path))
551 						{
552 							PageReference = path + "/" + PageReference;
553 							PageReference = QDir::cleanPath(PageReference);
554 						}
555 						parsePageReference(PageReference);
556 					}
557 				}
558 			}
559 			pgCount++;
560 			if (progressDialog)
561 			{
562 				progressDialog->setProgress("GI", pgCount);
563 				qApp->processEvents();
564 			}
565 		}
566 	}
567 	return true;
568 }
569 
parsePageReference(const QString & designMap)570 void XpsPlug::parsePageReference(const QString& designMap)
571 {
572 	QByteArray f;
573 	QFileInfo fi(designMap);
574 	QString path = fi.path();
575 	if (!uz->read(designMap, f))
576 		return;
577 
578 	QDomDocument designMapDom;
579 	if (!designMapDom.setContent(f))
580 		return;
581 
582 	QDomElement docElem = designMapDom.documentElement();
583 	docWidth = docElem.attribute("Width", QString("%1").arg(PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth)).toDouble() * conversionFactor;
584 	docHeight = docElem.attribute("Height", QString("%1").arg(PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight)).toDouble() * conversionFactor;
585 	if (importerFlags & LoadSavePlugin::lfCreateDoc)
586 	{
587 		if (firstPage)
588 		{
589 			topMargin = m_Doc->marginsVal().top();
590 			leftMargin = m_Doc->marginsVal().left();
591 			rightMargin = m_Doc->marginsVal().right();
592 			bottomMargin = m_Doc->marginsVal().bottom();
593 			double pgCols = m_Doc->PageSp;
594 			double pgGap = m_Doc->PageSpa;
595 			m_Doc->setPage(docWidth, docHeight, topMargin, leftMargin, rightMargin, bottomMargin, pgCols, pgGap, false, false);
596 			m_Doc->setPageSize("Custom");
597 			m_Doc->currentPage()->setSize("Custom");
598 			m_Doc->currentPage()->setInitialHeight(docHeight);
599 			m_Doc->currentPage()->setInitialWidth(docWidth);
600 			m_Doc->currentPage()->setHeight(docHeight);
601 			m_Doc->currentPage()->setWidth(docWidth);
602 			m_Doc->currentPage()->initialMargins.setTop(topMargin);
603 			m_Doc->currentPage()->initialMargins.setBottom(bottomMargin);
604 			m_Doc->currentPage()->initialMargins.setLeft(leftMargin);
605 			m_Doc->currentPage()->initialMargins.setRight(rightMargin);
606 			m_Doc->reformPages(true);
607 		}
608 		else
609 		{
610 			m_Doc->addPage(pagecount);
611 			m_Doc->currentPage()->setSize("Custom");
612 			m_Doc->currentPage()->setInitialHeight(docHeight);
613 			m_Doc->currentPage()->setInitialWidth(docWidth);
614 			m_Doc->currentPage()->setHeight(docHeight);
615 			m_Doc->currentPage()->setWidth(docWidth);
616 			m_Doc->currentPage()->initialMargins.setTop(topMargin);
617 			m_Doc->currentPage()->initialMargins.setBottom(bottomMargin);
618 			m_Doc->currentPage()->initialMargins.setLeft(leftMargin);
619 			m_Doc->currentPage()->initialMargins.setRight(rightMargin);
620 			m_Doc->currentPage()->setMasterPageNameNormal();
621 			m_Doc->view()->addPage(pagecount, true);
622 			pagecount++;
623 		}
624 	}
625 	firstPage = false;
626 	baseX = m_Doc->currentPage()->xOffset();
627 	baseY = m_Doc->currentPage()->yOffset();
628 	for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
629 	{
630 		QDomElement dpg = drawPag.toElement();
631 		if ((dpg.tagName() == "Path") || (dpg.tagName() == "Glyphs") || (dpg.tagName() == "Canvas"))
632 		{
633 			PageItem* item = parseObjectXML(dpg, path);
634 			if (item != nullptr)
635 			{
636 				m_Doc->Items->append(item);
637 				Elements.append(item);
638 			}
639 		}
640 		else if (dpg.tagName() == "FixedPage.Resources")
641 		{
642 			for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
643 			{
644 				QDomElement spe = sp.toElement();
645 				if (spe.tagName() != "ResourceDictionary")
646 					continue;
647 				if (spe.hasAttribute("Source"))
648 				{
649 					QString resFile = spe.attribute("Source", "");
650 					if (resFile.startsWith("/"))
651 					{
652 						resFile = resFile.mid(1);
653 						parseResourceFile(resFile);
654 					}
655 					else
656 					{
657 						if (!resFile.startsWith(path))
658 						{
659 							resFile = path + "/" + resFile;
660 							resFile = QDir::cleanPath(resFile);
661 						}
662 						parseResourceFile(resFile);
663 					}
664 				}
665 				else if (spe.hasChildNodes())
666 				{
667 					for (QDomElement dpgp = spe.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
668 					{
669 						if (dpgp.tagName() == "PathGeometry")
670 						{
671 							Coords.resize(0);
672 							Coords.svgInit();
673 							QString pdata = "";
674 							QString key = dpg.attribute("x:Key");
675 							if (dpg.hasAttribute("Figures"))
676 								pdata = dpg.attribute("Figures");
677 							else if (dpg.hasChildNodes())
678 								pdata = parsePathGeometryXML(dpg);
679 							if (!pdata.isEmpty())
680 							{
681 								bool currentPathClosed = Coords.parseSVG(pdata);
682 								Coords.scale(conversionFactor, conversionFactor);
683 								QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
684 								if (dpg.attribute("FillRule") == "NonZero")
685 									path.setFillRule(Qt::WindingFill);
686 								pathResources.insert(key, path);
687 							}
688 						}
689 					}
690 				}
691 			}
692 		}
693 	}
694 }
695 
parseObjectXML(QDomElement & dpg,const QString & path)696 PageItem* XpsPlug::parseObjectXML(QDomElement &dpg, const QString& path)
697 {
698 	PageItem *retObj = nullptr;
699 	ObjState obState;
700 	obState.currentPath = QPainterPath();
701 	obState.clipPath = QPainterPath();
702 	obState.transform = QTransform();
703 	obState.CurrColorFill = CommonStrings::None;
704 	obState.fillOpacity = 0.0;
705 	obState.CurrColorStroke = CommonStrings::None;
706 	obState.strokeOpacity = 0.0;
707 	obState.LineW = 1.0;
708 	obState.fillGradientTyp = 0;
709 	obState.gradientScale = 1.0;
710 	obState.maskTyp = 0;
711 	obState.strokeTyp = 0;
712 	obState.imagePath = "";
713 	obState.itemType = 0;
714 	obState.patternName = "";
715 	obState.patternMask = "";
716 	obState.CapStyle = Qt::FlatCap;
717 	obState.JoinStyle = Qt::MiterJoin;
718 	obState.DashOffset = 0;
719 	obState.DashPattern.clear();
720 	QString itemName = "";
721 	QString itemTarget = "";
722 	if (dpg.hasAttribute("Name"))
723 		itemName = dpg.attribute("Name");
724 	if (dpg.hasAttribute("FixedPage.NavigateUri"))
725 	{
726 		QString iTg = dpg.attribute("FixedPage.NavigateUri");
727 		QStringList tg = iTg.split("#");
728 		if (tg.count() > 1)
729 			itemTarget = tg[1];
730 	}
731 	if (dpg.hasAttribute("RenderTransform"))
732 	{
733 		QString trans = dpg.attribute("RenderTransform", "1 0 0 1 0 0");
734 		trans.replace(",", " ");
735 		ScTextStream list(&trans, QIODevice::ReadOnly);
736 		double a, b, c, d, e, f;
737 		list >> a >> b >> c >> d >> e >> f;
738 		obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
739 	}
740 	if (dpg.hasAttribute("Fill"))
741 		obState.CurrColorFill = handleColor(dpg.attribute("Fill", "#FF000000"), obState.fillOpacity);
742 	if (dpg.hasAttribute("Stroke"))
743 		obState.CurrColorStroke = handleColor(dpg.attribute("Stroke", "#FF000000"), obState.strokeOpacity);
744 	if (dpg.hasAttribute("Opacity"))
745 	{
746 		double opacity = dpg.attribute("Opacity", "1.0").toDouble();
747 		obState.fillOpacity = 1.0 - ((1.0 - obState.fillOpacity) * opacity);
748 		obState.strokeOpacity = 1.0 - ((1.0 - obState.strokeOpacity) * opacity);
749 	}
750 	if (dpg.hasAttribute("StrokeThickness"))
751 		obState.LineW = dpg.attribute("StrokeThickness", "1.0").toDouble() * conversionFactor;
752 	if (dpg.hasAttribute("StrokeDashCap"))
753 	{
754 		if (dpg.attribute("StrokeDashCap") == "Flat")
755 			obState.CapStyle = Qt::FlatCap;
756 		else if (dpg.attribute("StrokeDashCap") == "Round")
757 			obState.CapStyle = Qt::RoundCap;
758 		else if (dpg.attribute("StrokeDashCap") == "Square")
759 			obState.CapStyle = Qt::SquareCap;
760 		else
761 			obState.CapStyle = Qt::FlatCap;
762 	}
763 	if (dpg.hasAttribute("StrokeLineJoin"))
764 	{
765 		if (dpg.attribute("StrokeLineJoin") == "Miter")
766 			obState.JoinStyle = Qt::MiterJoin;
767 		else if (dpg.attribute("StrokeLineJoin") == "Round")
768 			obState.JoinStyle = Qt::RoundJoin;
769 		else if (dpg.attribute("StrokeLineJoin") == "Bevel")
770 			obState.JoinStyle = Qt::BevelJoin;
771 		else
772 			obState.JoinStyle = Qt::MiterJoin;
773 	}
774 	if (dpg.hasAttribute("StrokeDashOffset"))
775 		obState.DashOffset = dpg.attribute("StrokeDashOffset", "0.0").toDouble();
776 	if (dpg.hasAttribute("StrokeDashArray"))
777 	{
778 		QString trans = dpg.attribute("StrokeDashArray", "");
779 		if (!trans.isEmpty())
780 		{
781 			ScTextStream list(&trans, QIODevice::ReadOnly);
782 			while (!list.atEnd())
783 			{
784 				double d;
785 				list >> d;
786 				obState.DashPattern.append(d);
787 			}
788 		}
789 	}
790 	if (dpg.hasAttribute("Clip"))
791 	{
792 		QString cdata = dpg.attribute("Clip");
793 		if (!cdata.startsWith("{"))
794 		{
795 			Coords.resize(0);
796 			Coords.svgInit();
797 			Coords.parseSVG(cdata);
798 			Coords.scale(conversionFactor, conversionFactor);
799 			obState.clipPath = Coords.toQPainterPath(true);
800 			obState.clipPath = obState.transform.map(obState.clipPath);
801 		}
802 		else
803 		{
804 			cdata.remove("{StaticResource ");
805 			cdata.remove("}");
806 			cdata = cdata.trimmed();
807 			if (pathResources.contains(cdata))
808 				obState.clipPath = obState.transform.map(pathResources[cdata]);
809 		}
810 	}
811 	if (dpg.tagName() == "Glyphs")
812 	{
813 		if (dpg.hasChildNodes())
814 		{
815 			for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
816 			{
817 				QDomElement spe = sp.toElement();
818 				if (spe.tagName() == "Glyphs.Fill")
819 					parseFillXML(spe, path, obState);
820 				else if (spe.tagName() == "Glyphs.OpacityMask")
821 					parseOpacityXML(spe, path, obState);
822 				else if (spe.tagName() == "Glyphs.Clip")
823 					parsePathDataXML(spe, obState, true);
824 			}
825 		}
826 		QString utfString = dpg.attribute("UnicodeString");
827 		if (utfString.startsWith("{}"))
828 			utfString = utfString.mid(2);
829 		QString Indices = dpg.attribute("Indices");
830 		QString tstStr = utfString.trimmed();
831 		if (tstStr.isEmpty() && Indices.isEmpty())
832 			return retObj;
833 		QString fontUrl = dpg.attribute("FontUri");
834 		if (fontUrl.startsWith("/"))
835 		{
836 			fontUrl = fontUrl.mid(1);
837 		}
838 		else
839 		{
840 			if (!fontUrl.startsWith(path))
841 			{
842 				fontUrl = path + "/" + fontUrl;
843 				fontUrl = QDir::cleanPath(fontUrl);
844 			}
845 		}
846 		ScFace iteFont = loadFontByName(fontUrl);
847 		if (iteFont.usable())
848 		{
849 			double fontSize = dpg.attribute("FontRenderingEmSize", "0").toDouble() * conversionFactor;
850 			bool bold = false;
851 			bool italic = false;
852 			if (dpg.hasAttribute("StyleSimulations"))
853 			{
854 				bold = dpg.attribute("StyleSimulations") == "BoldSimulation";
855 				italic = dpg.attribute("StyleSimulations") == "ItalicSimulation";
856 				if (dpg.attribute("StyleSimulations") == "BoldItalicSimulation")
857 				{
858 					bold = true;
859 					italic = true;
860 				}
861 			}
862 			double fontSizeEM = fontSize;
863 			if (fontSize > 1)
864 			{
865 				double xPos = dpg.attribute("OriginX", "0").toDouble() * conversionFactor;
866 				double yPos = dpg.attribute("OriginY", "0").toDouble() * conversionFactor;
867 				QPointF origin(xPos, yPos);
868 				QStringList glIndices = Indices.split(";");
869 				double asc = iteFont.ascent() - iteFont.descent();
870 				fontSize /= asc;
871 				int glInd = 0;
872 				if (!utfString.isEmpty())
873 				{
874 					QVector<uint> ucs4 = utfString.toUcs4();
875 					// FIXME HOST: this code does not do any text layout!
876 					for (int sti = 0; sti < ucs4.length(); sti++)
877 					{
878 						uint chr = ucs4[sti];
879 						QString utfChar = QString::fromUcs4(&chr, 1);
880 						uint gl = iteFont.char2CMap(chr);
881 						if (gl != 0)
882 						{
883 							double adv = iteFont.glyphWidth(gl, fontSize);
884 							double offX = 0;
885 							double offY = 0;
886 							FPointArray pts;
887 							if (Indices.isEmpty())
888 							{
889 								if (!QChar::isSpace(chr))
890 									pts = iteFont.glyphOutline(gl, fontSize);
891 							}
892 							else
893 							{
894 								if (glInd < glIndices.count())
895 								{
896 									QStringList glyInd = glIndices[glInd].split(",");
897 									if (glyInd.count() > 1)
898 									{
899 										if (glyInd.count() > 2)
900 											offX = glyInd[2].toDouble() * fontSizeEM / 100.0;
901 										if (glyInd.count() > 3)
902 											offY = glyInd[3].toDouble() * fontSizeEM / 100.0;
903 										if (!glyInd[1].isEmpty())
904 											adv = glyInd[1].toDouble() * fontSizeEM / 100.0;
905 									}
906 									if (glyInd.count() > 0)
907 									{
908 										if (!glyInd[0].isEmpty())
909 										{
910 											if (glyInd[0].startsWith("("))
911 											{
912 												int r = glyInd[0].indexOf(")");
913 												QString comb = glyInd[0].mid(1, r-1);
914 												QStringList combInd = comb.split(":");
915 												int advUtf = combInd[0].toInt() - 1;
916 												sti += advUtf;
917 												glyInd[0].remove(0, r+1);
918 											}
919 											uint gli = glyInd[0].toUInt();
920 											pts = iteFont.glyphOutline(gli, fontSize);
921 											if (((glyInd.count() > 1) && (glyInd[1].isEmpty())) || (glyInd.count() == 1))
922 												adv = iteFont.glyphWidth(gli, fontSize);
923 										}
924 										else if (!QChar::isSpace(chr))
925 											pts = iteFont.glyphOutline(gl, fontSize);
926 									}
927 								}
928 							}
929 							if (pts.count() > 3)
930 							{
931 								QTransform cht;
932 								cht.scale(0.1, 0.1);
933 								pts.map(cht);
934 								pts.translate(0, -fontSize);
935 								if (bold)
936 								{
937 									QTransform bot;
938 									bot.scale(1.02, 1.02);
939 									pts.map(bot);
940 									adv *= 1.02;
941 								}
942 								if (italic)
943 								{
944 									QTransform bot;
945 									bot.shear(-0.3491, 0);
946 									pts.map(bot);
947 								}
948 								pts.translate(origin.x() + offX, origin.y() + offY);
949 								obState.currentPath.addPath(pts.toQPainterPath(true));
950 							}
951 							origin += QPointF(adv, 0);
952 						}
953 						else
954 						{
955 							QFont eFont(iteFont.family());
956 							eFont.setPointSizeF(fontSize);
957 							eFont.setBold(bold);
958 							eFont.setItalic(italic);
959 							QFontMetricsF ft(eFont);
960 							obState.currentPath.addText(origin, eFont, utfChar);
961 							origin += QPointF(ft.horizontalAdvance(utfChar), 0);
962 						}
963 						glInd++;
964 					}
965 				}
966 				else if (!Indices.isEmpty())
967 				{
968 					double adv = 0;
969 					double offX = 0;
970 					double offY = 0;
971 					for (int glInd = 0; glInd < glIndices.count(); glInd++)
972 					{
973 						FPointArray pts;
974 						QStringList glyInd = glIndices[glInd].split(",");
975 						if (glyInd.count() > 1)
976 						{
977 							if (glyInd.count() > 2)
978 								offX = glyInd[2].toDouble() * fontSizeEM / 100.0;
979 							if (glyInd.count() > 3)
980 								offY = glyInd[3].toDouble() * fontSizeEM / 100.0;
981 							if (!glyInd[1].isEmpty())
982 								adv = glyInd[1].toDouble() * fontSizeEM / 100.0;
983 						}
984 						if (glyInd.count() > 0)
985 						{
986 							if (!glyInd[0].isEmpty())
987 							{
988 								if (glyInd[0].startsWith("("))
989 								{
990 									int r = glyInd[0].indexOf(")");
991 									QString comb = glyInd[0].mid(1, r-1);
992 									QStringList combInd = comb.split(":");
993 									int advUtf = combInd[0].toInt() - 1;
994 									glInd += advUtf;
995 									glyInd[0].remove(0, r+1);
996 								}
997 								uint gli = glyInd[0].toUInt();
998 								pts = iteFont.glyphOutline(gli, fontSize);
999 								if (((glyInd.count() > 1) && (glyInd[1].isEmpty())) || (glyInd.count() == 1))
1000 									adv = iteFont.glyphWidth(gli, fontSize);
1001 							}
1002 						}
1003 						if (pts.count() > 3)
1004 						{
1005 							QTransform cht;
1006 							cht.scale(0.1, 0.1);
1007 							pts.map(cht);
1008 							pts.translate(0, -fontSize);
1009 							if (bold)
1010 							{
1011 								QTransform bot;
1012 								bot.scale(1.02, 1.02);
1013 								pts.map(bot);
1014 								adv *= 1.02;
1015 							}
1016 							if (italic)
1017 							{
1018 								QTransform bot;
1019 								bot.shear(-0.3491, 0);
1020 								pts.map(bot);
1021 							}
1022 							pts.translate(origin.x() + offX, origin.y() + offY);
1023 							obState.currentPath.addPath(pts.toQPainterPath(true));
1024 						}
1025 						origin += QPointF(adv, 0);
1026 					}
1027 				}
1028 				if (!obState.currentPath.isEmpty())
1029 				{
1030 					obState.currentPath = obState.transform.map(obState.currentPath);
1031 					retObj = createItem(dpg, obState);
1032 					if (!obState.clipPath.isEmpty())
1033 						retObj = addClip(retObj, obState);
1034 				}
1035 			}
1036 		}
1037 	}
1038 	else if (dpg.tagName() == "Path")
1039 	{
1040 		bool pathFromChild = false;
1041 		if (dpg.hasChildNodes())
1042 		{
1043 			for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
1044 			{
1045 				QDomElement spe = sp.toElement();
1046 				if (spe.tagName() == "Path.Fill")
1047 					parseFillXML(spe, path, obState);
1048 				else if (spe.tagName() == "Path.OpacityMask")
1049 					parseOpacityXML(spe, path, obState);
1050 				else if (spe.tagName() == "Path.Stroke")
1051 					parseStrokeXML(spe, path, obState);
1052 				else if (spe.tagName() == "Path.RenderTransform")
1053 				{
1054 					for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
1055 					{
1056 						QDomElement eog = obg.toElement();
1057 						if (eog.tagName() == "MatrixTransform")
1058 						{
1059 							QString trans = eog.attribute("Matrix", "1 0 0 1 0 0");
1060 							trans.replace(",", " ");
1061 							ScTextStream list(&trans, QIODevice::ReadOnly);
1062 							double a, b, c, d, e, f;
1063 							list >> a >> b >> c >> d >> e >> f;
1064 							obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
1065 						}
1066 					}
1067 				}
1068 				else if (spe.tagName() == "Path.Data")
1069 				{
1070 					parsePathDataXML(spe, obState);
1071 					pathFromChild = true;
1072 				}
1073 				else if (spe.tagName() == "Path.Clip")
1074 					parsePathDataXML(spe, obState, true);
1075 			}
1076 		}
1077 		if (!pathFromChild)
1078 		{
1079 			QString pdata = dpg.attribute("Data");
1080 			if (!pdata.startsWith("{"))
1081 			{
1082 				Coords.resize(0);
1083 				Coords.svgInit();
1084 				obState.currentPathClosed = Coords.parseSVG(pdata);
1085 				Coords.scale(conversionFactor, conversionFactor);
1086 				obState.currentPath = Coords.toQPainterPath(!obState.currentPathClosed);
1087 				if (obState.currentPath.isEmpty())
1088 					return retObj;
1089 				obState.currentPath = obState.transform.map(obState.currentPath);
1090 			}
1091 			else
1092 			{
1093 				pdata.remove("{StaticResource ");
1094 				pdata.remove("}");
1095 				pdata = pdata.trimmed();
1096 				if (pathResources.contains(pdata))
1097 					obState.currentPath = obState.transform.map(pathResources[pdata]);
1098 				if (obState.currentPath.isEmpty())
1099 					return retObj;
1100 			}
1101 		}
1102 		if (obState.currentPath.isEmpty())
1103 			return retObj;
1104 		retObj = createItem(dpg, obState);
1105 		if (!obState.clipPath.isEmpty())
1106 			retObj = addClip(retObj, obState);
1107 	}
1108 	else if (dpg.tagName() == "Canvas")
1109 	{
1110 		QList<PageItem*> GElements;
1111 		for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
1112 		{
1113 			QDomElement spe = sp.toElement();
1114 			if (spe.tagName() == "Canvas.RenderTransform")
1115 			{
1116 				for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
1117 				{
1118 					QDomElement eog = obg.toElement();
1119 					if (eog.tagName() == "MatrixTransform")
1120 					{
1121 						QString trans = eog.attribute("Matrix", "1 0 0 1 0 0");
1122 						trans.replace(",", " ");
1123 						ScTextStream list(&trans, QIODevice::ReadOnly);
1124 						double a, b, c, d, e, f;
1125 						list >> a >> b >> c >> d >> e >> f;
1126 						obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
1127 					}
1128 				}
1129 			}
1130 			else if ((spe.tagName() == "Path") || (spe.tagName() == "Glyphs") || (spe.tagName() == "Canvas"))
1131 			{
1132 				PageItem* ite = parseObjectXML(spe, path);
1133 				if (ite != nullptr)
1134 					GElements.append(ite);
1135 			}
1136 			else if (spe.tagName() == "Canvas.OpacityMask")
1137 				parseOpacityXML(spe, path, obState);
1138 			else if (spe.tagName() == "Canvas.Resources")
1139 			{
1140 				for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
1141 				{
1142 					QDomElement eog = obg.toElement();
1143 					if (eog.tagName() == "ResourceDictionary")
1144 					{
1145 						if (eog.hasAttribute("Source"))
1146 						{
1147 							QString resFile = eog.attribute("Source", "");
1148 							if (resFile.startsWith("/"))
1149 							{
1150 								resFile = resFile.mid(1);
1151 								parseResourceFile(resFile);
1152 							}
1153 							else
1154 							{
1155 								if (!resFile.startsWith(path))
1156 								{
1157 									resFile = path + "/" + resFile;
1158 									resFile = QDir::cleanPath(resFile);
1159 								}
1160 								parseResourceFile(resFile);
1161 							}
1162 						}
1163 						else if (eog.hasChildNodes())
1164 						{
1165 							for (QDomElement dpgp = eog.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
1166 							{
1167 								if (dpgp.tagName() == "PathGeometry")
1168 								{
1169 									Coords.resize(0);
1170 									Coords.svgInit();
1171 									QString pdata = "";
1172 									QString key = dpg.attribute("x:Key");
1173 									if (dpg.hasAttribute("Figures"))
1174 										pdata = dpg.attribute("Figures");
1175 									else if (dpg.hasChildNodes())
1176 										pdata = parsePathGeometryXML(dpg);
1177 									if (!pdata.isEmpty())
1178 									{
1179 										bool currentPathClosed = Coords.parseSVG(pdata);
1180 										Coords.scale(conversionFactor, conversionFactor);
1181 										QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
1182 										if (dpg.attribute("FillRule") == "NonZero")
1183 											path.setFillRule(Qt::WindingFill);
1184 										pathResources.insert(key, path);
1185 									}
1186 								}
1187 							}
1188 						}
1189 					}
1190 				}
1191 			}
1192 			else if (spe.tagName() == "Canvas.Clip")
1193 				parsePathDataXML(spe, obState, true);
1194 		}
1195 		if (GElements.count() > 0)
1196 		{
1197 			double minx =  std::numeric_limits<double>::max();
1198 			double miny =  std::numeric_limits<double>::max();
1199 			double maxx = -std::numeric_limits<double>::max();
1200 			double maxy = -std::numeric_limits<double>::max();
1201 			for (int ep = 0; ep < GElements.count(); ++ep)
1202 			{
1203 				PageItem* currItem = GElements.at(ep);
1204 				double x1, x2, y1, y2;
1205 				currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
1206 				minx = qMin(minx, x1);
1207 				miny = qMin(miny, y1);
1208 				maxx = qMax(maxx, x2);
1209 				maxy = qMax(maxy, y2);
1210 			}
1211 			double gx = minx;
1212 			double gy = miny;
1213 			double gw = maxx - minx;
1214 			double gh = maxy - miny;
1215 			int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, gx, gy, gw, gh, 0, CommonStrings::None, CommonStrings::None);
1216 			if (z >= 0)
1217 			{
1218 				retObj = m_Doc->Items->at(z);
1219 				retObj->ClipEdited = true;
1220 				retObj->FrameType = 3;
1221 				retObj->setFillEvenOdd(false);
1222 				retObj->OldB2 = retObj->width();
1223 				retObj->OldH2 = retObj->height();
1224 				retObj->updateClip();
1225 				m_Doc->groupObjectsToItem(retObj, GElements);
1226 				double scX = 1.0;
1227 				double scY = 1.0;
1228 				double rot = 0.0;
1229 				double dx = 0.0;
1230 				double dy = 0.0;
1231 				getTransformValuesFromMatrix( obState.transform, scX, scY, rot, dx, dy);
1232 				QLineF transp = QLineF(0, 0, retObj->xPos() - m_Doc->currentPage()->xOffset(), retObj->yPos() - m_Doc->currentPage()->yOffset());
1233 				transp = obState.transform.map(transp);
1234 				retObj->setXYPos(transp.p2().x() + m_Doc->currentPage()->xOffset(), transp.p2().y() + m_Doc->currentPage()->yOffset());
1235 				if ((scX != 1.0) || (scY != 1.0))
1236 					retObj->PoLine.scale(scX, scY);
1237 				FPoint wh = getMaxClipF(&retObj->PoLine);
1238 				retObj->setWidthHeight(wh.x(),wh.y());
1239 				m_Doc->adjustItemSize(retObj, true);
1240 				retObj->OldB2 = retObj->width();
1241 				retObj->OldH2 = retObj->height();
1242 				if (obState.maskTyp != 0)
1243 				{
1244 					double xp = retObj->xPos() - m_Doc->currentPage()->xOffset();
1245 					double yp = retObj->yPos() - m_Doc->currentPage()->yOffset();
1246 					retObj->setMaskGradient(obState.gradientMask);
1247 					retObj->setMaskVector(obState.maskStart.x() - xp, obState.maskStart.y() - yp, obState.maskEnd.x() - xp, obState.maskEnd.y() - yp, obState.maskFocus.x() - xp, obState.maskFocus.y() - yp, obState.maskScale, 0);
1248 					retObj->setMaskType(obState.maskTyp);
1249 				}
1250 				if (rot != 0)
1251 					retObj->setRotation(-rot, true);
1252 				retObj->OwnPage = m_Doc->OnPage(retObj);
1253 				m_Doc->GroupOnPage(retObj);
1254 				m_Doc->Items->removeLast();
1255 				if (!obState.clipPath.isEmpty())
1256 					retObj = addClip(retObj, obState);
1257 			}
1258 		}
1259 	}
1260 	if (retObj != nullptr)
1261 	{
1262 		if (!itemName.isEmpty())
1263 		{
1264 			linkTargets.insert(itemName, retObj);
1265 			retObj->setItemName(itemName);
1266 		}
1267 		if ((!itemTarget.isEmpty()) && (dpg.tagName() != "Canvas"))
1268 		{
1269 			linkSources.insert(retObj, itemTarget);
1270 			retObj->setIsAnnotation(true);
1271 			retObj->annotation().setType(Annotation::Link);
1272 			retObj->annotation().setZiel(m_Doc->currentPage()->pageNr());
1273 			retObj->annotation().setAction("0 0");
1274 			retObj->setTextFlowMode(PageItem::TextFlowDisabled);
1275 		}
1276 	}
1277 	return retObj;
1278 }
1279 
parseOpacityXML(QDomElement & spe,const QString & path,ObjState & obState)1280 void XpsPlug::parseOpacityXML(QDomElement &spe, const QString& path, ObjState &obState)
1281 {
1282 	ObjState opaState;
1283 	opaState.CurrColorFill = CommonStrings::None;
1284 	opaState.fillOpacity = 0.0;
1285 	opaState.fillGradientTyp = 0;
1286 	opaState.gradientScale = 1.0;
1287 	opaState.imagePath = "";
1288 	opaState.patternName = "";
1289 	parseFillXML(spe, path, opaState);
1290 	if (opaState.fillGradientTyp != 0)
1291 	{
1292 		obState.gradientMask = opaState.currentGradient;
1293 		obState.maskStart = opaState.gradientStart;
1294 		obState.maskEnd = opaState.gradientEnd;
1295 		obState.maskFocus = opaState.gradientFocus;
1296 		obState.maskScale = opaState.gradientScale;
1297 		obState.maskTyp = opaState.fillGradientTyp == 6 ? 1 : 3;
1298 	}
1299 	if (!opaState.patternName.isEmpty())
1300 	{
1301 		obState.patternMask = opaState.patternName;
1302 		obState.maskTyp = 3;
1303 	}
1304 }
1305 
parseStrokeXML(QDomElement & spe,const QString & path,ObjState & obState)1306 void XpsPlug::parseStrokeXML(QDomElement &spe, const QString& path, ObjState &obState)
1307 {
1308 	ObjState opaState;
1309 	opaState.CurrColorFill = CommonStrings::None;
1310 	opaState.fillOpacity = 0.0;
1311 	opaState.fillGradientTyp = 0;
1312 	opaState.gradientScale = 1.0;
1313 	opaState.imagePath = "";
1314 	opaState.patternName = "";
1315 	parseFillXML(spe, path, opaState);
1316 	if (opaState.fillGradientTyp != 0)
1317 	{
1318 		obState.gradientStroke = opaState.currentGradient;
1319 		obState.strokeStart = opaState.gradientStart;
1320 		obState.strokeEnd = opaState.gradientEnd;
1321 		obState.strokeFocus = opaState.gradientFocus;
1322 		obState.strokeScale = opaState.gradientScale;
1323 		obState.strokeTyp = opaState.fillGradientTyp;
1324 	}
1325 	if (!opaState.patternName.isEmpty())
1326 		obState.patternStroke = opaState.patternName;
1327 }
1328 
parseFillXML(QDomElement & spe,const QString & path,ObjState & obState)1329 void XpsPlug::parseFillXML(QDomElement &spe, const QString& path, ObjState &obState)
1330 {
1331 	for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
1332 	{
1333 		QDomElement eog = obg.toElement();
1334 		if (eog.tagName() == "SolidColorBrush")
1335 			obState.CurrColorFill = handleColor(eog.attribute("Color", "#FF000000"), obState.fillOpacity);
1336 		else if (eog.tagName() == "ImageBrush")
1337 		{
1338 			obState.imagePath = eog.attribute("ImageSource", "");
1339 			if (obState.imagePath.startsWith("/"))
1340 			{
1341 				obState.imagePath = obState.imagePath.mid(1);
1342 			}
1343 			else
1344 			{
1345 				if (!obState.imagePath.startsWith(path))
1346 				{
1347 					obState.imagePath = path + "/" + obState.imagePath;
1348 					obState.imagePath = QDir::cleanPath(obState.imagePath);
1349 				}
1350 			}
1351 			if (eog.hasAttribute("Opacity"))
1352 			{
1353 				double opacity = eog.attribute("Opacity", "1.0").toDouble();
1354 				obState.fillOpacity = 1.0 - ((1.0 - obState.fillOpacity) * opacity);
1355 				obState.strokeOpacity = 1.0 - ((1.0 - obState.strokeOpacity) * opacity);
1356 			}
1357 			obState.itemType = 1;
1358 		}
1359 		else if (eog.tagName() == "LinearGradientBrush")
1360 		{
1361 			obState.currentGradient = VGradient(VGradient::linear);
1362 			obState.currentGradient.clearStops();
1363 			double gen_opacity = 1.0;
1364 			if (eog.hasAttribute("Opacity"))
1365 				gen_opacity = eog.attribute("Opacity", "1.0").toDouble();
1366 			for (QDomNode gr = eog.firstChild(); !gr.isNull(); gr = gr.nextSibling())
1367 			{
1368 				QDomElement grs = gr.toElement();
1369 				if (grs.tagName() == "LinearGradientBrush.GradientStops")
1370 				{
1371 					for (QDomNode spo = grs.firstChild(); !spo.isNull(); spo = spo.nextSibling())
1372 					{
1373 						QDomElement eo = spo.toElement();
1374 						if (eo.tagName() == "GradientStop")
1375 						{
1376 							double opacity = 1.0;
1377 							QString stopName = handleColor(eo.attribute("Color", "#FF000000"), opacity);
1378 							double stopOffset = eo.attribute("Offset", "0.0").toDouble();
1379 							const ScColor& gradC = m_Doc->PageColors[stopName];
1380 							obState.currentGradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), stopOffset, 0.5, 1.0 - (opacity * gen_opacity), stopName, 100);
1381 						}
1382 					}
1383 				}
1384 			}
1385 			QString vectS = eog.attribute("StartPoint", "0,0");
1386 			vectS.replace(",", " ");
1387 			ScTextStream list(&vectS, QIODevice::ReadOnly);
1388 			double x, y;
1389 			list >> x >> y;
1390 			obState.gradientStart = QPointF(x * conversionFactor, y * conversionFactor);
1391 			obState.gradientFocus = obState.gradientStart;
1392 			QString vectE = eog.attribute("EndPoint", "0,0");
1393 			vectE.replace(",", " ");
1394 			ScTextStream listE(&vectE, QIODevice::ReadOnly);
1395 			double x2, y2;
1396 			listE >> x2 >> y2;
1397 			obState.gradientEnd = QPointF(x2 * conversionFactor, y2 * conversionFactor);
1398 			obState.gradientScale = 1.0;
1399 			obState.fillGradientTyp = 6;
1400 		}
1401 		else if (eog.tagName() == "RadialGradientBrush")
1402 		{
1403 			obState.currentGradient = VGradient(VGradient::radial);
1404 			obState.currentGradient.clearStops();
1405 			double gen_opacity = 1.0;
1406 			if (eog.hasAttribute("Opacity"))
1407 				gen_opacity = eog.attribute("Opacity", "1.0").toDouble();
1408 			for (QDomElement grs = eog.firstChildElement(); !grs.isNull(); grs = grs.nextSiblingElement())
1409 			{
1410 				if (grs.tagName() == "RadialGradientBrush.GradientStops")
1411 				{
1412 					for (QDomElement eo = grs.firstChildElement(); !eo.isNull(); eo = eo.nextSiblingElement())
1413 					{
1414 						if (eo.tagName() == "GradientStop")
1415 						{
1416 							double opacity = 1.0;
1417 							QString stopName = handleColor(eo.attribute("Color", "#FF000000"), opacity);
1418 							double stopOffset = eo.attribute("Offset", "0.0").toDouble();
1419 							const ScColor& gradC = m_Doc->PageColors[stopName];
1420 							obState.currentGradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), stopOffset, 0.5, 1.0 - (opacity * gen_opacity), stopName, 100);
1421 						}
1422 					}
1423 				}
1424 			}
1425 			QString vectS = eog.attribute("Center", "0,0");
1426 			vectS.replace(",", " ");
1427 			ScTextStream list(&vectS, QIODevice::ReadOnly);
1428 			double x, y;
1429 			list >> x >> y;
1430 			obState.gradientStart = QPointF(x * conversionFactor, y * conversionFactor);
1431 			QString vectE = eog.attribute("GradientOrigin", "0,0");
1432 			vectE.replace(",", " ");
1433 			ScTextStream listE(&vectE, QIODevice::ReadOnly);
1434 			double x2, y2;
1435 			listE >> x2 >> y2;
1436 			obState.gradientFocus = QPointF(x2 * conversionFactor, y2 * conversionFactor);
1437 			double rx = eog.attribute("RadiusX", "1").toDouble() * conversionFactor;
1438 			double ry = eog.attribute("RadiusY", "1").toDouble() * conversionFactor;
1439 			obState.gradientEnd = QPointF(obState.gradientStart.x() + rx, obState.gradientStart.y());
1440 			obState.gradientScale = rx / ry;
1441 			obState.fillGradientTyp = 7;
1442 		}
1443 		else if (eog.tagName() == "VisualBrush")
1444 		{
1445 			QString viewb = eog.attribute("Viewbox", "0,0,1,1");
1446 			viewb.replace(",", " ");
1447 			ScTextStream listb(&viewb, QIODevice::ReadOnly);
1448 			double Viewbox_x1, Viewbox_y1, Viewbox_x2, Viewbox_y2;
1449 			listb >> Viewbox_x1 >> Viewbox_y1 >> Viewbox_x2 >> Viewbox_y2;
1450 			QString vectE = eog.attribute("Viewport", "0,0,1,1");
1451 			vectE.replace(",", " ");
1452 			ScTextStream listE(&vectE, QIODevice::ReadOnly);
1453 			double Viewport_x1, Viewport_y1, Viewport_x2, Viewport_y2;
1454 			listE >> Viewport_x1 >> Viewport_y1 >> Viewport_x2 >> Viewport_y2;
1455 			double vw = (Viewport_x2 * conversionFactor) / (Viewbox_x2 - Viewbox_x1);
1456 			double vh = (Viewport_y2 * conversionFactor) / (Viewbox_y2 - Viewbox_y1);
1457 			for (QDomElement grs = eog.firstChildElement(); !grs.isNull(); grs = grs.nextSiblingElement())
1458 			{
1459 				if (grs.tagName() != "VisualBrush.Visual")
1460 					continue;
1461 				for (QDomElement eo = grs.firstChildElement(); !eo.isNull(); eo = eo.nextSiblingElement())
1462 				{
1463 					if ((eo.tagName() != "Path") && (eo.tagName() != "Glyphs") && (eo.tagName() != "Canvas"))
1464 						continue;
1465 					PageItem* item = parseObjectXML(eo, path);
1466 					if (!item)
1467 						continue;
1468 					m_Doc->sizeItem((item->width() / conversionFactor) * vw, (item->height() / conversionFactor) * vh, item, false, true, false);
1469 					ScPattern pat = ScPattern();
1470 					pat.setDoc(m_Doc);
1471 					m_Doc->DoDrawing = true;
1472 					QImage tmpImg = item->DrawObj_toImage(qMin(qMax(item->width(), item->height()), 500.0));
1473 					if (tmpImg.isNull())
1474 						continue;
1475 					QImage retImg = QImage(qRound(Viewport_x2 * conversionFactor), qRound(Viewport_y2 * conversionFactor), QImage::Format_ARGB32_Premultiplied);
1476 					retImg.fill( qRgba(0, 0, 0, 0) );
1477 					QPainter p;
1478 					p.begin(&retImg);
1479 					p.drawImage(0, 0, tmpImg);
1480 					p.end();
1481 					pat.pattern = retImg;
1482 					pat.xoffset = 0;
1483 					pat.yoffset = 0;
1484 					m_Doc->DoDrawing = false;
1485 					pat.width = Viewport_x2 * conversionFactor;
1486 					pat.height = Viewport_y2 * conversionFactor;
1487 					item->gXpos = 0;
1488 					item->gYpos = 0;
1489 					item->setXYPos(item->gXpos, item->gYpos, true);
1490 					pat.items.append(item);
1491 					obState.patternName = QString("Pattern_from_XPS_%1").arg(m_Doc->docPatterns.count() + 1);
1492 					m_Doc->addPattern(obState.patternName, pat);
1493 					importedPatterns.append(obState.patternName);
1494 				}
1495 			}
1496 		}
1497 	}
1498 }
1499 
parsePathDataXML(QDomElement & spe,ObjState & obState,bool forClip)1500 void XpsPlug::parsePathDataXML(QDomElement &spe, ObjState &obState, bool forClip)
1501 {
1502 	Coords.resize(0);
1503 	Coords.svgInit();
1504 	QString svgString = "";
1505 	bool windFill = false;
1506 	for (QDomElement dpgp = spe.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
1507 	{
1508 		if (dpgp.tagName() == "PathGeometry")
1509 			svgString += parsePathGeometryXML(dpgp);
1510 		if (dpgp.attribute("FillRule") == "NonZero")
1511 			windFill = true;
1512 	}
1513 	bool currentPathClosed = Coords.parseSVG(svgString);
1514 	Coords.scale(conversionFactor, conversionFactor);
1515 	if (forClip)
1516 	{
1517 		obState.clipPath = Coords.toQPainterPath(!currentPathClosed);
1518 		if (windFill)
1519 			obState.clipPath.setFillRule(Qt::WindingFill);
1520 	}
1521 	else
1522 	{
1523 		obState.currentPathClosed = currentPathClosed;
1524 		obState.currentPath = Coords.toQPainterPath(!obState.currentPathClosed);
1525 		if (windFill)
1526 			obState.currentPath.setFillRule(Qt::WindingFill);
1527 	}
1528 }
1529 
parsePathGeometryXML(QDomElement & spe)1530 QString XpsPlug::parsePathGeometryXML(QDomElement &spe)
1531 {
1532 	QString svgString = "";
1533 	for (QDomElement dpg = spe.firstChildElement(); !dpg.isNull(); dpg = dpg.nextSiblingElement())
1534 	{
1535 		if (dpg.tagName() != "PathFigure")
1536 			continue;
1537 
1538 		if (dpg.hasAttribute("StartPoint"))
1539 			svgString += "M " + dpg.attribute("StartPoint") + " ";
1540 		for (QDomElement dp = dpg.firstChildElement(); !dp.isNull(); dp = dp.nextSiblingElement())
1541 		{
1542 			if (dp.tagName() == "PolyLineSegment")
1543 				svgString += "L " + dp.attribute("Points") + " ";
1544 			else if (dp.tagName() == "PolyQuadraticBezierSegment")
1545 				svgString += "Q " + dp.attribute("Points") + " ";
1546 			else if (dp.tagName() == "PolyBezierSegment")
1547 				svgString += "C " + dp.attribute("Points") + " ";
1548 			else if (dp.tagName() == "ArcSegment")
1549 			{
1550 				svgString += "A " + dp.attribute("Size") + " "  + dp.attribute("RotationAngle") + " ";
1551 				if (dp.hasAttribute("IsLargeArc"))
1552 				{
1553 					if (dp.attribute("IsLargeArc").toLower() == "true")
1554 						svgString += "1 ";
1555 					else
1556 						svgString += "0 ";
1557 				}
1558 				else
1559 					svgString += "0 ";
1560 				if (dp.hasAttribute("SweepDirection"))
1561 				{
1562 					if (dp.attribute("SweepDirection").toLower() == "counterclockwise")
1563 						svgString += "0 ";
1564 					else
1565 						svgString += "1 ";
1566 				}
1567 				else
1568 					svgString += "0 ";
1569 				svgString += dp.attribute("Point") + " ";
1570 			}
1571 		}
1572 		if (dpg.hasAttribute("IsClosed") && (dpg.attribute("IsClosed").toLower() == "true"))
1573 			svgString += "Z ";
1574 	}
1575 	return svgString;
1576 }
1577 
parseResourceFile(const QString & resFile)1578 void XpsPlug::parseResourceFile(const QString& resFile)
1579 {
1580 	QByteArray f;
1581 	if (!uz->read(resFile, f))
1582 		return;
1583 
1584 	QDomDocument designMapDom;
1585 	if (!designMapDom.setContent(f))
1586 		return;
1587 
1588 	QDomElement docElem = designMapDom.documentElement();
1589 	for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
1590 	{
1591 		QDomElement dpg = drawPag.toElement();
1592 		if (dpg.tagName() != "PathGeometry")
1593 			continue;
1594 		Coords.resize(0);
1595 		Coords.svgInit();
1596 		QString pdata = "";
1597 		QString key = dpg.attribute("x:Key");
1598 		if (dpg.hasAttribute("Figures"))
1599 			pdata = dpg.attribute("Figures");
1600 		else if (dpg.hasChildNodes())
1601 			pdata = parsePathGeometryXML(dpg);
1602 		if (!pdata.isEmpty())
1603 		{
1604 			bool currentPathClosed = Coords.parseSVG(pdata);
1605 			Coords.scale(conversionFactor, conversionFactor);
1606 			QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
1607 			if (dpg.attribute("FillRule") == "NonZero")
1608 				path.setFillRule(Qt::WindingFill);
1609 			pathResources.insert(key, path);
1610 		}
1611 	}
1612 }
1613 
resolveLinks()1614 void XpsPlug::resolveLinks()
1615 {
1616 	if (linkSources.isEmpty())
1617 		return;
1618 
1619 	for (auto it = linkSources.begin(); it != linkSources.end(); ++it)
1620 	{
1621 		PageItem* linkS = it.key();
1622 		QString target = it.value();
1623 		if (!linkTargets.contains(target))
1624 			continue;
1625 
1626 		PageItem* linkT = linkTargets[target];
1627 		if (!linkT)
1628 			continue;
1629 
1630 		int op = linkT->OwnPage;
1631 		if (op < 0)
1632 			continue;
1633 
1634 		QTransform tf = linkT->getTransform();
1635 		double xp = tf.dx() - m_Doc->Pages->at(op)->xOffset();
1636 		double yp = tf.dy() - m_Doc->Pages->at(op)->yOffset();
1637 		linkS->annotation().setZiel(linkT->OwnPage);
1638 		linkS->annotation().setActionType(2);
1639 		linkS->annotation().setAction(QString("%0 %1").arg(qRound(xp)).arg(qRound(m_Doc->Pages->at(op)->height() - yp)));
1640 	}
1641 }
1642 
addClip(PageItem * retObj,ObjState & obState)1643 PageItem* XpsPlug::addClip(PageItem* retObj, ObjState &obState)
1644 {
1645 	if (obState.clipPath.isEmpty())
1646 		return retObj;
1647 
1648 	int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, baseX, baseY, 10, 10, 0, CommonStrings::None, CommonStrings::None);
1649 	PageItem *itemg = m_Doc->Items->at(z);
1650 	itemg->PoLine.fromQPainterPath(obState.clipPath);
1651 	FPoint wh = getMaxClipF(&itemg->PoLine);
1652 	itemg->setWidthHeight(wh.x(),wh.y());
1653 	m_Doc->adjustItemSize(itemg, true);
1654 	itemg->ClipEdited = true;
1655 	itemg->FrameType = 3;
1656 	itemg->setFillEvenOdd(false);
1657 	itemg->OldB2 = itemg->width();
1658 	itemg->OldH2 = itemg->height();
1659 	itemg->updateClip();
1660 	itemg->OwnPage = m_Doc->OnPage(itemg);
1661 	itemg->ContourLine = itemg->PoLine.copy();
1662 	QList<PageItem*> GElements;
1663 	GElements.append(retObj);
1664 	m_Doc->groupObjectsToItem(itemg, GElements);
1665 	m_Doc->resizeGroupToContents(itemg);
1666 	m_Doc->GroupOnPage(itemg);
1667 	retObj = itemg;
1668 	m_Doc->Items->removeLast();
1669 
1670 	return retObj;
1671 }
1672 
createItem(QDomElement & dpg,ObjState & obState)1673 PageItem* XpsPlug::createItem(QDomElement &dpg, ObjState &obState)
1674 {
1675 	int z = -1;
1676 	PageItem* retObj = nullptr;
1677 
1678 	if (obState.currentPath.isEmpty())
1679 		return nullptr;
1680 
1681 	if (obState.itemType == 0)
1682 	{
1683 		if (dpg.hasAttribute("FixedPage.NavigateUri"))
1684 			z = m_Doc->itemAdd(PageItem::TextFrame, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, CommonStrings::None);
1685 		else
1686 		{
1687 			if (!obState.currentPathClosed)
1688 				z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
1689 			else
1690 				z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
1691 		}
1692 		retObj = m_Doc->Items->at(z);
1693 		finishItem(retObj, obState);
1694 		retObj = m_Doc->Items->takeAt(z);
1695 	}
1696 	else if (obState.itemType == 1)
1697 	{
1698 		z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
1699 		retObj = m_Doc->Items->at(z);
1700 		finishItem(retObj, obState);
1701 		if (!obState.imagePath.isEmpty())
1702 		{
1703 			QByteArray f;
1704 			if (uz->read(obState.imagePath, f))
1705 			{
1706 				QFileInfo fi(obState.imagePath);
1707 				QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_xps_XXXXXX." + fi.suffix());
1708 				tempFile->setAutoRemove(false);
1709 				if (tempFile->open())
1710 				{
1711 					QString fileName = getLongPathName(tempFile->fileName());
1712 					if (!fileName.isEmpty())
1713 					{
1714 						tempFile->write(f);
1715 						tempFile->close();
1716 						retObj->isInlineImage = true;
1717 						retObj->isTempFile = true;
1718 						retObj->AspectRatio = false;
1719 						retObj->ScaleType   = false;
1720 						m_Doc->loadPict(fileName, retObj);
1721 						retObj->adjustPictScale();
1722 					}
1723 				}
1724 				delete tempFile;
1725 			}
1726 		}
1727 		retObj = m_Doc->Items->takeAt(z);
1728 	}
1729 
1730 	return retObj;
1731 }
1732 
finishItem(PageItem * item,ObjState & obState)1733 void XpsPlug::finishItem(PageItem* item, ObjState &obState)
1734 {
1735 	item->PoLine.fromQPainterPath(obState.currentPath, !obState.currentPathClosed);
1736 	FPoint wh = getMaxClipF(&item->PoLine);
1737 	item->setWidthHeight(wh.x(),wh.y());
1738 	m_Doc->adjustItemSize(item, true);
1739 	item->ClipEdited = true;
1740 	item->FrameType = 3;
1741 	item->setFillEvenOdd(false);
1742 	item->OldB2 = item->width();
1743 	item->OldH2 = item->height();
1744 	item->updateClip();
1745 	item->OwnPage = m_Doc->OnPage(item);
1746 	item->ContourLine = item->PoLine.copy();
1747 	item->setFillColor(obState.CurrColorFill);
1748 	item->setFillTransparency(obState.fillOpacity);
1749 	item->setLineColor(obState.CurrColorStroke);
1750 	item->setLineTransparency(obState.strokeOpacity);
1751 	item->setLineWidth(obState.LineW);
1752 	item->setLineEnd(obState.CapStyle);
1753 	item->setLineJoin(obState.JoinStyle);
1754 	double xp = item->xPos() - m_Doc->currentPage()->xOffset();
1755 	double yp = item->yPos() - m_Doc->currentPage()->yOffset();
1756 	if (obState.fillGradientTyp != 0)
1757 	{
1758 		item->fill_gradient = obState.currentGradient;
1759 		item->setGradientVector(obState.gradientStart.x() - xp, obState.gradientStart.y() - yp, obState.gradientEnd.x() - xp, obState.gradientEnd.y() - yp, obState.gradientFocus.x() - xp, obState.gradientFocus.y() - yp, obState.gradientScale, 0);
1760 		item->setGradientType(obState.fillGradientTyp);
1761 	}
1762 	else if (!obState.patternName.isEmpty())
1763 	{
1764 		item->setPattern(obState.patternName);
1765 		item->GrType = Gradient_Pattern;
1766 	}
1767 	if (obState.maskTyp != 0)
1768 	{
1769 		item->setMaskGradient(obState.gradientMask);
1770 		item->setMaskVector(obState.maskStart.x() - xp, obState.maskStart.y() - yp, obState.maskEnd.x() - xp, obState.maskEnd.y() - yp, obState.maskFocus.x() - xp, obState.maskFocus.y() - yp, obState.maskScale, 0);
1771 		item->setMaskType(obState.maskTyp);
1772 	}
1773 	if (!obState.patternMask.isEmpty())
1774 	{
1775 		item->setPatternMask(obState.patternMask);
1776 		item->setMaskType(obState.maskTyp);
1777 	}
1778 	if (obState.strokeTyp != 0)
1779 	{
1780 		item->setStrokeGradient(obState.gradientStroke);
1781 		item->setStrokeGradientVector(obState.strokeStart.x() - xp, obState.strokeStart.y() - yp, obState.strokeEnd.x() - xp, obState.strokeEnd.y() - yp, obState.strokeFocus.x() - xp, obState.strokeFocus.y() - yp, obState.strokeScale, 0);
1782 		item->setStrokeGradientType(obState.strokeTyp);
1783 	}
1784 	if (!obState.patternStroke.isEmpty())
1785 	{
1786 		item->GrTypeStroke = Gradient_Pattern;
1787 		item->setStrokePattern(obState.patternStroke);
1788 	}
1789 	if (!obState.DashPattern.isEmpty())
1790 	{
1791 		item->setDashOffset(obState.DashOffset);
1792 		QVector<double> pattern(obState.DashPattern.count());
1793 		for (int i = 0; i < obState.DashPattern.count(); ++i)
1794 		{
1795 			pattern[i] = obState.DashPattern[i] * obState.LineW;
1796 		}
1797 		item->setDashes(pattern);
1798 	}
1799 }
1800 
handleColor(QString rgbColor,double & opacity)1801 QString XpsPlug::handleColor(QString rgbColor, double &opacity)
1802 {
1803 	QString fNam = CommonStrings::None;
1804 	QString alpha = "FF";
1805 	if (rgbColor.startsWith( "sc#" ))
1806 	{
1807 		QColor c;
1808 		rgbColor = rgbColor.remove(0,3);
1809 		QStringList co = rgbColor.split(",");
1810 		if (co.size() == 3)
1811 		{
1812 			rgbColor.replace(",", " ");
1813 			ScTextStream list(&rgbColor, QIODevice::ReadOnly);
1814 			double r, g, b;
1815 			list >> r >> g >> b;
1816 			c.setRgbF(r, g, b);
1817 		}
1818 		else if (co.size() == 4)
1819 		{
1820 			rgbColor.replace(",", " ");
1821 			ScTextStream list(&rgbColor, QIODevice::ReadOnly);
1822 			double r, g, b;
1823 			list >> opacity >> r >> g >> b;
1824 			c.setRgbF(r, g, b);
1825 		}
1826 		else
1827 		{
1828 			opacity = 0;
1829 			return fNam;
1830 		}
1831 		ScColor tmp;
1832 		tmp.fromQColor(c);
1833 		tmp.setSpotColor(false);
1834 		tmp.setRegistrationColor(false);
1835 		QString newColorName = "FromXPS"+c.name();
1836 		fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
1837 		if (fNam == newColorName)
1838 			importedColors.append(newColorName);
1839 	}
1840 	else if (rgbColor.startsWith( "#" ))
1841 	{
1842 		QColor c;
1843 		if (rgbColor.length() == 9)
1844 		{
1845 			alpha = rgbColor.mid(1,2);
1846 			bool ok;
1847 			int hex = alpha.toInt(&ok, 16);
1848 			opacity = 1.0 - (hex / 255.0);
1849 			rgbColor = rgbColor.remove(1,2);
1850 		}
1851 		else
1852 			opacity = 0;
1853 		c.setNamedColor(rgbColor);
1854 		ScColor tmp;
1855 		tmp.fromQColor(c);
1856 		tmp.setSpotColor(false);
1857 		tmp.setRegistrationColor(false);
1858 		QString newColorName = "FromXPS"+c.name();
1859 		fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
1860 		if (fNam == newColorName)
1861 			importedColors.append(newColorName);
1862 	}
1863 	return fNam;
1864 }
1865 
hex2int(char hex)1866 int XpsPlug::hex2int(char hex)
1867 {
1868 	QChar hexchar = QLatin1Char(hex);
1869 	int v;
1870 	if (hexchar.isDigit())
1871 		v = hexchar.digitValue();
1872 	else if (hexchar >= QLatin1Char('A') && hexchar <= QLatin1Char('F'))
1873 		v = hexchar.cell() - 'A' + 10;
1874 	else if (hexchar >= QLatin1Char('a') && hexchar <= QLatin1Char('f'))
1875 		v = hexchar.cell() - 'a' + 10;
1876 	else
1877 		v = -1;
1878 	return v;
1879 }
1880 
parseGUID(const QString & guidString,unsigned short guid[16])1881 bool XpsPlug::parseGUID( const QString &guidString, unsigned short guid[16])
1882 {
1883 	if (guidString.length() <= 35)
1884 		return false;
1885 	// Maps bytes to positions in guidString
1886 	const static int indexes[] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34};
1887 	for (int i = 0; i < 16; i++)
1888 	{
1889 		int hex1 = hex2int(guidString[indexes[i]].cell());
1890 		int hex2 = hex2int(guidString[indexes[i]+1].cell());
1891 		if ((hex1 < 0) || (hex2 < 0))
1892 			return false;
1893 		guid[i] = hex1 * 16 + hex2;
1894 	}
1895 	return true;
1896 }
1897 
1898 
loadFontByName(const QString & fileName)1899 ScFace XpsPlug::loadFontByName(const QString &fileName)
1900 {
1901 	ScFace t;
1902 	if (loadedFonts.contains(fileName))
1903 		return loadedFonts[fileName];
1904 	QByteArray fontData;
1905 	if (!uz->read(fileName, fontData))
1906 		return t;
1907 	QTemporaryFile *tempImageFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_zip_XXXXXX.dat");
1908 	if (tempImageFile == nullptr)
1909 		return t;
1910 	tempImageFile->setAutoRemove(false);
1911 	tempImageFile->open();
1912 	QString fname = getLongPathName(tempImageFile->fileName());
1913 	tempImageFile->close();
1914 	delete tempImageFile;
1915 	tempFontFiles.append(fname);
1916 	QFileInfo fi(fileName);
1917 	QString ext = fi.suffix().toLower();
1918 	if (ext.startsWith("od"))
1919 	{
1920 		const QString baseName = fi.baseName();
1921 		unsigned short guid[16];
1922 		if (!parseGUID(baseName, guid))
1923 			return t;
1924 		if (fontData.length() < 32)
1925 		{
1926 			qDebug() << "Font file is too small";
1927 			return t;
1928 		}
1929 		// Obfuscation - xor bytes in font binary with bytes from guid (font's filename)
1930 		const static int mapping[] = {15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3};
1931 		for (int i = 0; i < 16; i++)
1932 		{
1933 			fontData[i] = fontData[i] ^ guid[mapping[i]];
1934 			fontData[i+16] = fontData[i+16] ^ guid[mapping[i]];
1935 		}
1936 	}
1937 	QFile ft(fname);
1938 	if (ft.open(QIODevice::WriteOnly))
1939 	{
1940 		ft.write(fontData);
1941 		ft.close();
1942 		t = PrefsManager::instance().appPrefs.fontPrefs.AvailFonts.loadScalableFont(fname);
1943 		loadedFonts.insert(fileName, t);
1944 		return t;
1945 	}
1946 	return t;
1947 }
1948