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                           xpsexport.cpp  -  description
9                              -------------------
10     begin                : Sun Nov 24 08:00:00 CEST 2013
11     copyright            : (C) 2013 by Franz Schmid
12     email                : Franz.Schmid@altmuehlnet.de
13  ***************************************************************************/
14 
15 /***************************************************************************
16  *                                                                         *
17  *   This program is free software; you can redistribute it and/or modify  *
18  *   it under the terms of the GNU General Public License as published by  *
19  *   the Free Software Foundation; either version 2 of the License, or     *
20  *   (at your option) any later version.                                   *
21  *                                                                         *
22  ***************************************************************************/
23 
24 #include <QBuffer>
25 #include <QByteArray>
26 #include <QComboBox>
27 #include <QDataStream>
28 #include <QFile>
29 #include <QList>
30 #include <QMessageBox>
31 #include <QScopedPointer>
32 #include <QTemporaryDir>
33 #include <QTextStream>
34 #include <QUuid>
35 
36 #include "xpsexplugin.h"
37 
38 #include "scconfig.h"
39 #include "canvas.h"
40 #include "cmsettings.h"
41 #include "commonstrings.h"
42 #include "pageitem_table.h"
43 #include "prefsmanager.h"
44 #include "prefsfile.h"
45 #include "prefscontext.h"
46 #include "scpage.h"
47 #include "scpattern.h"
48 #include "scribuscore.h"
49 #include "scribusdoc.h"
50 #include "scribusview.h"
51 #include "sctextstruct.h"
52 #include "tableutils.h"
53 #include "text/textlayoutpainter.h"
54 #include "util.h"
55 #include "ui/customfdialog.h"
56 #include "ui/guidemanager.h"
57 #include "ui/scmessagebox.h"
58 #include "sccolorengine.h"
59 #include "util_formats.h"
60 #include "util_math.h"
61 #include "third_party/zip/scribus_zip.h"
62 #include "text/boxes.h"
63 
xpsexplugin_getPluginAPIVersion()64 int xpsexplugin_getPluginAPIVersion()
65 {
66 	return PLUGIN_API_VERSION;
67 }
68 
xpsexplugin_getPlugin()69 ScPlugin* xpsexplugin_getPlugin()
70 {
71 	XPSExportPlugin* plug = new XPSExportPlugin();
72 	Q_CHECK_PTR(plug);
73 	return plug;
74 }
75 
xpsexplugin_freePlugin(ScPlugin * plugin)76 void xpsexplugin_freePlugin(ScPlugin* plugin)
77 {
78 	XPSExportPlugin* plug = qobject_cast<XPSExportPlugin*>(plugin);
79 	Q_ASSERT(plug);
80 	delete plug;
81 }
82 
83 using namespace TableUtils;
84 
XPSExportPlugin()85 XPSExportPlugin::XPSExportPlugin()
86 {
87 	// Set action info in languageChange, so we only have to do
88 	// it in one place.
89 	languageChange();
90 }
91 
~XPSExportPlugin()92 XPSExportPlugin::~XPSExportPlugin() {};
93 
languageChange()94 void XPSExportPlugin::languageChange()
95 {
96 	// Note that we leave the unused members unset. They'll be initialised
97 	// with their default ctors during construction.
98 	// Action name
99 	m_actionInfo.name = "ExportAsXPS";
100 	// Action text for menu, including accel
101 	m_actionInfo.text = tr("Save as XPS...");
102 	// Menu
103 	m_actionInfo.menu = "FileExport";
104 	m_actionInfo.enabledOnStartup = false;
105 	m_actionInfo.needsNumObjects = -1;
106 }
107 
fullTrName() const108 QString XPSExportPlugin::fullTrName() const
109 {
110 	return QObject::tr("XPS Export");
111 }
112 
getAboutData() const113 const ScActionPlugin::AboutData* XPSExportPlugin::getAboutData() const
114 {
115 	AboutData* about = new AboutData;
116 	about->authors = "Franz Schmid <franz@scribus.info>";
117 	about->shortDescription = tr("Exports XPS Files");
118 	about->description = tr("Exports the current document into an XPS file.");
119 	about->license = "GPL";
120 	Q_CHECK_PTR(about);
121 	return about;
122 }
123 
deleteAboutData(const AboutData * about) const124 void XPSExportPlugin::deleteAboutData(const AboutData* about) const
125 {
126 	Q_ASSERT(about);
127 	delete about;
128 }
129 
run(ScribusDoc * doc,const QString & filename)130 bool XPSExportPlugin::run(ScribusDoc* doc, const QString& filename)
131 {
132 	Q_ASSERT(filename.isEmpty());
133 	QString fileName;
134 	if (doc!=nullptr)
135 	{
136 		PrefsContext* prefs = PrefsManager::instance().prefsFile->getPluginContext("xpsex");
137 		QString wdir = prefs->get("wdir", ".");
138 		QScopedPointer<CustomFDialog> openDia( new CustomFDialog(doc->scMW(), wdir, QObject::tr("Save as"), QObject::tr("Microsoft XPS (*.xps *.XPS);;All Files (*)"), fdHidePreviewCheckBox) );
139 
140 		QFrame *Layout = new QFrame(openDia.data());
141 		QHBoxLayout *Layout1 = new QHBoxLayout(Layout);
142 		Layout1->setSpacing(6);
143 		Layout1->setContentsMargins(0, 0, 0, 0);
144 		QLabel *text = new QLabel(QObject::tr("Output Settings:"), Layout);
145 		Layout1->addWidget(text);
146 		QComboBox* compress = new QComboBox(Layout);
147 		compress->addItem(QObject::tr("Low Resolution"));
148 		compress->addItem(QObject::tr("Medium Resolution"));
149 		compress->addItem(QObject::tr("High Resolution"));
150 		Layout1->addWidget(compress);
151 		QSpacerItem* spacer = new QSpacerItem( 2, 2, QSizePolicy::Expanding, QSizePolicy::Minimum );
152 		Layout1->addItem( spacer );
153 		compress->setCurrentIndex(1);
154 		openDia->addWidgets(Layout);
155 		QString fna;
156 		if (doc->hasName)
157 		{
158 			QFileInfo fi(doc->documentFileName());
159 			QString completeBaseName = fi.completeBaseName();
160 			if (completeBaseName.endsWith(".xps", Qt::CaseInsensitive))
161 				completeBaseName.chop(4);
162 			wdir = QDir::fromNativeSeparators( fi.path() );
163 			fna  = QDir::fromNativeSeparators( fi.path() + "/" + completeBaseName + ".xps" );
164 		}
165 		else
166 		{
167 			wdir = QDir::fromNativeSeparators( wdir );
168 			if (wdir.right(1) != "/")
169 				fna = wdir + "/";
170 			else
171 				fna = wdir;
172 			fna += doc->documentFileName() + ".xps";
173 		}
174 		openDia->setSelection(fna);
175 		openDia->setExtension("xps");
176 
177 		if (!openDia->exec())
178 			return true;
179 		fileName = openDia->selectedFile();
180 		QFileInfo fi(fileName);
181 		QString baseDir = fi.absolutePath();
182 		fileName = baseDir + "/" + fi.baseName() + ".xps";
183 		if (fileName.isEmpty())
184 			return true;
185 		prefs->set("wdir", fileName.left(fileName.lastIndexOf("/")));
186 		QFile f(fileName);
187 		if (f.exists())
188 		{
189 			int exit = ScMessageBox::warning(doc->scMW(), CommonStrings::trWarning,
190 				QObject::tr("Do you really want to overwrite the file:\n%1 ?").arg(fileName),
191 				QMessageBox::Yes | QMessageBox::No,
192 				QMessageBox::NoButton,	// GUI default
193 				QMessageBox::Yes);	// batch default
194 			if (exit == QMessageBox::No)
195 				return true;
196 		}
197 		XPSExPlug *dia = new XPSExPlug(doc, compress->currentIndex());
198 		dia->doExport(fileName);
199 		delete dia;
200 	}
201 	return true;
202 }
203 
XPSExPlug(ScribusDoc * doc,int output_res)204 XPSExPlug::XPSExPlug(ScribusDoc* doc, int output_res)
205 {
206 	m_Doc = doc;
207 	conversionFactor = 96.0 / 72.0;
208 	m_dpi = 96.0;
209 	if (output_res == 0)
210 		m_dpi = 72.0;
211 	else if (output_res == 1)
212 		m_dpi = 150.0;
213 	else if (output_res == 2)
214 		m_dpi = 300.0;
215 }
216 
doExport(const QString & fName)217 bool XPSExPlug::doExport(const QString& fName)
218 {
219 	ScZipHandler zip(true);
220 	if (!zip.open(fName))
221 		return false;
222 
223 	QTemporaryDir dir;
224 	if (!dir.isValid())
225 	{
226 		zip.close();
227 		QFile::remove(fName);
228 		return false;
229 	}
230 
231 	imageCounter = 0;
232 	fontCounter = 0;
233 	xps_fontMap.clear();
234 	baseDir = dir.path();
235 	// Create directory tree
236 	QDir outDir(baseDir);
237 	outDir.mkdir("_rels");
238 	outDir.mkdir("docProps");
239 	outDir.mkdir("Documents");
240 	outDir.cd("Documents");
241 	outDir.mkdir("1");
242 	outDir.cd("1");
243 	outDir.mkdir("_rels");
244 	outDir.mkdir("Pages");
245 	outDir.cd("Pages");
246 	outDir.mkdir("_rels");
247 	outDir.cdUp();
248 	outDir.mkdir("Structure");
249 	outDir.cdUp();
250 	outDir.cdUp();
251 	outDir.mkdir("Resources");
252 	outDir.cd("Resources");
253 	outDir.mkdir("Images");
254 	outDir.mkdir("Fonts");
255 	outDir.cdUp();
256 	writeBaseRel();
257 	writeContentType();
258 	writeCore();
259 	writeDocRels();
260 	// Write Thumbnail
261 	QImage thumb = m_Doc->view()->PageToPixmap(0, 256, Pixmap_DrawBackground);
262 	thumb.save(baseDir + "/docProps/thumbnail.jpeg", "JPG");
263 	// Write required DocStructure.struct
264 	QFile fts(baseDir + "/Documents/1/Structure/DocStructure.struct");
265 	if (fts.open(QIODevice::WriteOnly))
266 	{
267 		fts.write(QByteArray("<DocumentStructure xmlns=\"http://schemas.microsoft.com/xps/2005/06/documentstructure\">\n</DocumentStructure>"));
268 		fts.close();
269 	}
270 	// Write required FixedDocSeq.fdseq
271 	QFile ft(baseDir + "/FixedDocSeq.fdseq");
272 	if (ft.open(QIODevice::WriteOnly))
273 	{
274 		ft.write(QByteArray("<FixedDocumentSequence xmlns=\"http://schemas.microsoft.com/xps/2005/06\">\n\t<DocumentReference Source=\"/Documents/1/FixedDoc.fdoc\"/>\n</FixedDocumentSequence>"));
275 		ft.close();
276 	}
277 	// Write required FixedDoc.fdoc
278 	f_docu = QDomDocument("xpsdoc");
279 	QString st = "<FixedDocument></FixedDocument>";
280 	f_docu.setContent(st);
281 	QDomElement root  = f_docu.documentElement();
282 	root.setAttribute("xmlns", "http://schemas.microsoft.com/xps/2005/06");
283 	f_docu.appendChild(root);
284 	writePages(root);
285 	QFile fdo(baseDir + "/Documents/1/FixedDoc.fdoc");
286 	if (fdo.open(QIODevice::WriteOnly))
287 	{
288 		QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
289 		QDataStream s(&fdo);
290 		vo += f_docu.toString();
291 		QByteArray utf8wr = vo.toUtf8();
292 		s.writeRawData(utf8wr.data(), utf8wr.length());
293 		fdo.close();
294 	}
295 
296 	bool written = zip.write(baseDir);
297 	zip.close();
298 	if (!written)
299 		QFile::remove(fName);
300 	return written;
301 }
302 
writePages(QDomElement & root)303 void XPSExPlug::writePages(QDomElement &root)
304 {
305 	for (int i = 0; i < m_Doc->Pages->count(); ++i)
306 	{
307 		ScPage* Page = m_Doc->Pages->at(i);
308 
309 		p_docu.setContent(QString("<FixedPage></FixedPage>"));
310 		QDomElement droot  = p_docu.documentElement();
311 		droot.setAttribute("xmlns", "http://schemas.microsoft.com/xps/2005/06");
312 		droot.setAttribute("Width", QString("%1").arg(Page->width() * conversionFactor));
313 		droot.setAttribute("Height", QString("%1").arg(Page->height() * conversionFactor));
314 		QString lang = QLocale::system().name();
315 		lang.replace("_", "-");
316 		droot.setAttribute("xml:lang", lang);
317 
318 		r_docu.setContent(QString("<Relationships></Relationships>"));
319 		QDomElement rroot  = r_docu.documentElement();
320 		rroot.setAttribute("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
321 		xps_fontRel.clear();
322 
323 		writePage(droot, rroot, Page);
324 
325 		QFile ft(baseDir + QString("/Documents/1/Pages/%1.fpage").arg(i + 1));
326 		if (ft.open(QIODevice::WriteOnly))
327 		{
328 			QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
329 			QDataStream s(&ft);
330 			vo += p_docu.toString();
331 			QByteArray utf8wr = vo.toUtf8();
332 			s.writeRawData(utf8wr.data(), utf8wr.length());
333 			ft.close();
334 		}
335 
336 		QFile ftr(baseDir + QString("/Documents/1/Pages/_rels/%1.fpage.rels").arg(i + 1));
337 		if (ftr.open(QIODevice::WriteOnly))
338 		{
339 			QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
340 			QDataStream s(&ftr);
341 			vo += r_docu.toString();
342 			QByteArray utf8wr = vo.toUtf8();
343 			s.writeRawData(utf8wr.data(), utf8wr.length());
344 			ftr.close();
345 		}
346 
347 		QDomElement rel1 = f_docu.createElement("PageContent");
348 		rel1.setAttribute("Source", QString("Pages/%1.fpage").arg(i + 1));
349 		root.appendChild(rel1);
350 		p_docu.clear();
351 		r_docu.clear();
352 	}
353 }
354 
writePage(QDomElement & doc_root,QDomElement & rel_root,ScPage * Page)355 void XPSExPlug::writePage(QDomElement &doc_root, QDomElement &rel_root, ScPage *Page)
356 {
357 	ScLayer ll;
358 	ll.isPrintable = false;
359 	for (int i = 0; i < m_Doc->Layers.count(); i++)
360 	{
361 		m_Doc->Layers.levelToLayer(ll, i);
362 		if (ll.isPrintable)
363 		{
364 			ScPage *mpage = m_Doc->MasterPages.at(m_Doc->MasterNames[Page->masterPageName()]);
365 			writePageLayer(doc_root, rel_root, mpage, ll);
366 			writePageLayer(doc_root, rel_root, Page, ll);
367 		}
368 	}
369 }
370 
writePageLayer(QDomElement & doc_root,QDomElement & rel_root,ScPage * page,ScLayer & layer)371 void XPSExPlug::writePageLayer(QDomElement &doc_root, QDomElement &rel_root, ScPage *page, ScLayer& layer)
372 {
373 	PageItem *item;
374 	QList<PageItem*> items;
375 	ScPage* SavedAct = m_Doc->currentPage();
376 	if (page->pageNameEmpty())
377 		items = m_Doc->DocItems;
378 	else
379 		items = m_Doc->MasterItems;
380 	if (items.count() == 0)
381 		return;
382 	if (!layer.isPrintable)
383 		return;
384 	m_Doc->setCurrentPage(page);
385 	QDomElement layerGroup = p_docu.createElement("Canvas");
386 	if (layer.transparency != 1.0)
387 		layerGroup.setAttribute("Opacity", layer.transparency);
388 	for (int j = 0; j < items.count(); ++j)
389 	{
390 		item = items.at(j);
391 		if (item->m_layerID != layer.ID)
392 			continue;
393 		if (!item->printEnabled())
394 			continue;
395 		double x = page->xOffset();
396 		double y = page->yOffset();
397 		double w = page->width();
398 		double h = page->height();
399 		double lw = item->visualLineWidth();
400 		double x2 = item->BoundingX - lw / 2.0;
401 		double y2 = item->BoundingY - lw / 2.0;
402 		double w2 = item->BoundingW + lw;
403 		double h2 = item->BoundingH + lw;
404 		if (!QRectF(x2, y2, w2, h2).intersects(QRectF(x, y, w, h)))
405 			continue;
406 		if ((!page->pageNameEmpty()) && (item->OwnPage != static_cast<int>(page->pageNr())) && (item->OwnPage != -1))
407 			continue;
408 		writeItemOnPage(item->xPos() - page->xOffset(), item->yPos() - page->yOffset(), item, layerGroup, rel_root);
409 	}
410 	doc_root.appendChild(layerGroup);
411 	m_Doc->setCurrentPage(SavedAct);
412 }
413 
writeItemOnPage(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)414 void XPSExPlug::writeItemOnPage(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
415 {
416 	switch (item->itemType())
417 	{
418 		case PageItem::Arc:
419 		case PageItem::Polygon:
420 		case PageItem::PolyLine:
421 		case PageItem::RegularPolygon:
422 		case PageItem::Spiral:
423 			if (checkForFallback(item))
424 				handleImageFallBack(item, parentElem, rel_root);
425 			else
426 			{
427 				processPolyItem(xOffset, yOffset, item, parentElem, rel_root);
428 				if ((item->lineColor() != CommonStrings::None) && ((item->startArrowIndex() != 0) || (item->endArrowIndex() != 0)))
429 					processArrows(xOffset, yOffset, item, parentElem, rel_root);
430 			}
431 			break;
432 		case PageItem::Line:
433 			if (checkForFallback(item))
434 				handleImageFallBack(item, parentElem, rel_root);
435 			else
436 			{
437 				processLineItem(xOffset, yOffset, item, parentElem, rel_root);
438 				if ((item->lineColor() != CommonStrings::None) && ((item->startArrowIndex() != 0) || (item->endArrowIndex() != 0)))
439 					processArrows(xOffset, yOffset, item, parentElem, rel_root);
440 			}
441 			break;
442 		case PageItem::ImageFrame:
443 		case PageItem::LatexFrame:
444 			if (checkForFallback(item))
445 				handleImageFallBack(item, parentElem, rel_root);
446 			else
447 				processImageItem(xOffset, yOffset, item, parentElem, rel_root);
448 			break;
449 		case PageItem::PathText:
450 		case PageItem::TextFrame:
451 			if (checkForFallback(item))
452 				handleImageFallBack(item, parentElem, rel_root);
453 			else
454 				processTextItem(xOffset, yOffset, item, parentElem, rel_root);
455 			break;
456 		case PageItem::Table:
457 			if (checkForFallback(item))
458 				handleImageFallBack(item, parentElem, rel_root);
459 			else
460 				processTableItem(xOffset, yOffset, item, parentElem, rel_root);
461 			break;
462 		case PageItem::Symbol:
463 			if (checkForFallback(item))
464 				handleImageFallBack(item, parentElem, rel_root);
465 			else
466 				processSymbolItem(xOffset, yOffset, item, parentElem, rel_root);
467 			break;
468 		case PageItem::Group:
469 			if (item->groupItemList.count() > 0)
470 			{
471 				if (checkForFallback(item))
472 					handleImageFallBack(item, parentElem, rel_root);
473 				else
474 				{
475 					QDomElement ob = p_docu.createElement("Canvas");
476 					if (item->GrMask > 0)
477 						handleMask(1, item, ob, rel_root, xOffset, yOffset);
478 					else
479 					{
480 						if (item->fillTransparency() != 0)
481 							ob.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
482 					}
483 					if (item->groupClipping())
484 					{
485 						FPointArray path = item->PoLine.copy();
486 						path.scale(conversionFactor, conversionFactor);
487 						path.scale(item->groupWidth / item->width(), item->groupHeight / item->height());
488 						setClipAttr(ob, &path, item->fillRule);
489 					}
490 					QTransform mpx;
491 					mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
492 					mpx.scale(item->width() / item->groupWidth, item->height() / item->groupHeight);
493 					if ((item->rotation() != 0.0) || item->imageFlippedH() || item->imageFlippedV())
494 					{
495 						mpx.rotate(item->rotation());
496 						if (item->imageFlippedH())
497 						{
498 							mpx.translate(item->width() * conversionFactor, 0);
499 							mpx.scale(-1, 1);
500 						}
501 						if (item->imageFlippedV())
502 						{
503 							mpx.translate(0, item->height() * conversionFactor);
504 							mpx.scale(1, -1);
505 						}
506 					}
507 					ob.setAttribute("RenderTransform", MatrixToStr(mpx));
508 					for (int em = 0; em < item->groupItemList.count(); ++em)
509 					{
510 						PageItem* embed = item->groupItemList.at(em);
511 						writeItemOnPage(embed->gXpos, embed->gYpos, embed, ob, rel_root);
512 					}
513 					parentElem.appendChild(ob);
514 				}
515 			}
516 			break;
517 		default:
518 			handleImageFallBack(item, parentElem, rel_root);
519 			break;
520 	}
521 }
522 
handleImageFallBack(PageItem * item,QDomElement & parentElem,QDomElement & rel_root)523 void XPSExPlug::handleImageFallBack(PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
524 {
525 	QDomElement ob = p_docu.createElement("Path");
526 	double maxAdd = 0;
527 	if (item->hasSoftShadow())
528 		maxAdd = qMax(fabs(item->softShadowXOffset()), fabs(item->softShadowYOffset())) + item->softShadowBlurRadius();
529 	QRectF bounds = item->getVisualBoundingRect().adjusted(-maxAdd, -maxAdd, maxAdd, maxAdd);
530 	QPainterPath path;
531 	path.moveTo(0, 0);
532 	path.lineTo(bounds.width(), 0);
533 	path.lineTo(bounds.width(), bounds.height());
534 	path.lineTo(0, bounds.height());
535 	path.closeSubpath();
536 	QTransform mpp;
537 	mpp.translate((item->visualXPos() - m_Doc->currentPage()->xOffset() - maxAdd) * conversionFactor, (item->visualYPos() - m_Doc->currentPage()->yOffset() - maxAdd) * conversionFactor);
538 	mpp.scale(conversionFactor, conversionFactor);
539 	path = mpp.map(path);
540 	FPointArray fPath;
541 	fPath.fromQPainterPath(path, true);
542 	QString pa = setClipPath(&fPath, true);
543 	if (item->fillRule)
544 		pa.prepend("F 0 ");
545 	else
546 		pa.prepend("F 1 ");
547 	ob.setAttribute("Data", pa);
548 	QDomElement obf = p_docu.createElement("Path.Fill");
549 	QDomElement gr = p_docu.createElement("ImageBrush");
550 	double maxSize = qMax(bounds.width(), bounds.height());
551 	maxSize = qMin(3000.0, maxSize * (m_dpi / 72.0));
552 	QImage tmpImg = item->DrawObj_toImage(maxSize);
553 	tmpImg.save(baseDir + "/Resources/Images/" + QString("%1.png").arg(imageCounter), "PNG");
554 	gr.setAttribute("TileMode", "None");
555 	gr.setAttribute("ViewboxUnits", "Absolute");
556 	gr.setAttribute("ViewportUnits", "Absolute");
557 	gr.setAttribute("Viewport", "0,0,1,1");
558 	gr.setAttribute("Viewbox", QString("0, 0, %1, %2").arg(tmpImg.width()).arg(tmpImg.height()));
559 	gr.setAttribute("Viewport", QString("%1, %2, %3, %4").arg((item->visualXPos() - m_Doc->currentPage()->xOffset() - maxAdd) * conversionFactor).arg((item->visualYPos() - m_Doc->currentPage()->yOffset() - maxAdd) * conversionFactor).arg(bounds.width() * conversionFactor).arg(bounds.height() * conversionFactor));
560 	gr.setAttribute("ImageSource", "/Resources/Images/" + QString("%1.png").arg(imageCounter));
561 	QDomElement rel = r_docu.createElement("Relationship");
562 	rel.setAttribute("Id", QString("rIDi%1").arg(imageCounter));
563 	rel.setAttribute("Type", "http://schemas.microsoft.com/xps/2005/06/required-resource");
564 	rel.setAttribute("Target", "/Resources/Images/" + QString("%1.png").arg(imageCounter));
565 	rel_root.appendChild(rel);
566 	imageCounter++;
567 	obf.appendChild(gr);
568 	ob.appendChild(obf);
569 	parentElem.appendChild(ob);
570 }
571 
processPolyItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)572 void XPSExPlug::processPolyItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
573 {
574 	if (((item->GrType == 0) && (item->fillColor() == CommonStrings::None)) && ((item->GrTypeStroke == 0) && (item->lineColor() == CommonStrings::None) && item->NamedLStyle.isEmpty()))
575 		return;
576 
577 	if (item->GrType == Gradient_Hatch)
578 		processHatchFill(xOffset, yOffset, item, parentElem, rel_root);
579 	bool closedPath;
580 	closedPath = ((item->itemType() == PageItem::Polygon) || (item->itemType() == PageItem::RegularPolygon) || (item->itemType() == PageItem::Arc));
581 
582 	QDomElement ob = p_docu.createElement("Path");
583 	FPointArray path = item->PoLine.copy();
584 	QTransform mpx;
585 	if (item->rotation() != 0.0)
586 	{
587 		mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
588 		mpx.rotate(item->rotation());
589 		mpx.translate(-xOffset * conversionFactor, -yOffset * conversionFactor);
590 	}
591 	path.translate(xOffset, yOffset);
592 	path.scale(conversionFactor, conversionFactor);
593 	QString pa = setClipPath(&path, closedPath);
594 	if (item->fillRule)
595 		pa.prepend("F 0 ");
596 	else
597 		pa.prepend("F 1 ");
598 	ob.setAttribute("Data", pa);
599 	if (item->GrType != Gradient_Hatch)
600 	{
601 		if (item->GrMask > 0)
602 			handleMask(3, item, ob, rel_root, xOffset, yOffset);
603 		getFillStyle(item, ob, rel_root, xOffset, yOffset);
604 	}
605 	if (item->NamedLStyle.isEmpty())
606 	{
607 		if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
608 		{
609 			processSymbolStroke(xOffset, yOffset, item, parentElem, rel_root);
610 		}
611 		else
612 		{
613 			getStrokeStyle(item, ob, rel_root, xOffset, yOffset);
614 			if (item->rotation() != 0.0)
615 				ob.setAttribute("RenderTransform", MatrixToStr(mpx));
616 			parentElem.appendChild(ob);
617 		}
618 	}
619 	else
620 	{
621 		QDomElement grp2 = p_docu.createElement("Canvas");
622 		multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
623 		for (int it = ml.size()-1; it > -1; it--)
624 		{
625 			if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
626 			{
627 				QDomElement ob3 = p_docu.createElement("Path");
628 				ob3.setAttribute("Data", pa);
629 				getMultiStroke(&ml[it], ob3);
630 				grp2.appendChild(ob3);
631 			}
632 		}
633 		if (item->lineTransparency() != 0)
634 			grp2.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
635 		if (item->rotation() != 0.0)
636 			grp2.setAttribute("RenderTransform", MatrixToStr(mpx));
637 		parentElem.appendChild(grp2);
638 	}
639 }
640 
processLineItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)641 void XPSExPlug::processLineItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
642 {
643 	if ((item->GrTypeStroke != 0) || (item->lineColor() != CommonStrings::None) || !item->NamedLStyle.isEmpty())
644 	{
645 		QDomElement ob;
646 		double x1 = xOffset * conversionFactor;
647 		double y1 = yOffset * conversionFactor;
648 		double x2 = (item->width() + xOffset) * conversionFactor;
649 		double y2 = yOffset * conversionFactor;
650 		QLineF line = QLineF(x1, y1, x2, y2);
651 		line.setAngle(-item->rotation());
652 		if (item->NamedLStyle.isEmpty())
653 		{
654 			ob = p_docu.createElement("Path");
655 			ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(line.p1().x()).arg(line.p1().y()).arg(line.p2().x()).arg(line.p2().y()));
656 			getStrokeStyle(item, ob, rel_root, xOffset, yOffset);
657 		}
658 		else
659 		{
660 			ob = p_docu.createElement("Canvas");
661 			multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
662 			for (int it = ml.size()-1; it > -1; it--)
663 			{
664 				if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
665 				{
666 					QDomElement ob2 = p_docu.createElement("Path");
667 					ob2.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(line.p1().x()).arg(line.p1().y()).arg(line.p2().x()).arg(line.p2().y()));
668 					getMultiStroke(&ml[it], ob2);
669 					ob.appendChild(ob2);
670 				}
671 			}
672 			if (item->lineTransparency() != 0)
673 				ob.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
674 		}
675 		parentElem.appendChild(ob);
676 	}
677 }
678 
processImageItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)679 void XPSExPlug::processImageItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
680 {
681 	if (item->GrType == Gradient_Hatch)
682 		processHatchFill(xOffset, yOffset, item, parentElem, rel_root);
683 	FPointArray path = item->PoLine.copy();
684 	path.translate(xOffset, yOffset);
685 	path.scale(conversionFactor, conversionFactor);
686 	QString pa = setClipPath(&path, true);
687 	if (item->fillRule)
688 		pa.prepend("F 0 ");
689 	else
690 		pa.prepend("F 1 ");
691 	QDomElement grp = p_docu.createElement("Canvas");
692 	QTransform mpx;
693 	if (item->rotation() != 0.0)
694 	{
695 		mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
696 		mpx.rotate(item->rotation());
697 		mpx.translate(-xOffset * conversionFactor, -yOffset * conversionFactor);
698 		grp.setAttribute("RenderTransform", MatrixToStr(mpx));
699 	}
700 	if (item->GrType != Gradient_Hatch)
701 	{
702 		if (item->GrMask > 0)
703 			handleMask(1, item, grp, rel_root, xOffset, yOffset);
704 		else
705 		{
706 			if (item->fillTransparency() != 0)
707 				grp.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
708 		}
709 		if ((item->GrType != 0) || (item->fillColor() != CommonStrings::None))
710 		{
711 			QDomElement ob = p_docu.createElement("Path");
712 			ob.setAttribute("Data", pa);
713 			getFillStyle(item, ob, rel_root, xOffset, yOffset, false);
714 			grp.appendChild(ob);
715 		}
716 	}
717 	if ((item->imageIsAvailable) && (!item->Pfile.isEmpty()))
718 	{
719 		QDomElement ob2 = p_docu.createElement("Path");
720 		ob2.setAttribute("Data", pa);
721 		ob2.setAttribute("Clip", pa);
722 		QDomElement obf = p_docu.createElement("Path.Fill");
723 		QDomElement gr = p_docu.createElement("ImageBrush");
724 		ScImage img;
725 		CMSettings cms(m_Doc, item->ImageProfile, item->ImageIntent);
726 		cms.setUseEmbeddedProfile(item->UseEmbedded);
727 		cms.allowSoftProofing(true);
728 		img.loadPicture(item->Pfile, item->pixm.imgInfo.actualPageNumber, cms, ScImage::RGBData, 96);
729 		img.applyEffect(item->effectsInUse, m_Doc->PageColors, true);
730 		img.qImagePtr()->setDotsPerMeterX(3780);
731 		img.qImagePtr()->setDotsPerMeterY(3780);
732 		img.qImage().save(baseDir + "/Resources/Images/" + QString("%1.png").arg(imageCounter), "PNG");
733 		gr.setAttribute("TileMode", "None");
734 		gr.setAttribute("ViewboxUnits", "Absolute");
735 		gr.setAttribute("ViewportUnits", "Absolute");
736 		gr.setAttribute("Viewport", "0,0,1,1");
737 		gr.setAttribute("Viewbox", QString("0, 0, %1, %2").arg(img.width()).arg(img.height()));
738 		QTransform mpx;
739 		double xpos = item->imageXOffset() * item->imageXScale();
740 		double ypos = item->imageYOffset() * item->imageYScale();
741 		mpx.translate((xOffset + xpos) * conversionFactor, (yOffset + ypos) * conversionFactor);
742 		mpx.scale(img.width() * item->imageXScale() * conversionFactor, img.height() * item->imageYScale() * conversionFactor);
743 		if (item->imageFlippedH())
744 		{
745 			mpx.translate(item->width() / (img.width() * item->imageXScale()), 0);
746 			mpx.scale(-1, 1);
747 		}
748 		if (item->imageFlippedV())
749 		{
750 			mpx.translate(0, item->height() / (img.height() * item->imageYScale()));
751 			mpx.scale(1, -1);
752 		}
753 		mpx.rotate(item->imageRotation());
754 		gr.setAttribute("Transform", MatrixToStr(mpx));
755 		gr.setAttribute("ImageSource", "/Resources/Images/" + QString("%1.png").arg(imageCounter));
756 		QDomElement rel = r_docu.createElement("Relationship");
757 		rel.setAttribute("Id", QString("rIDi%1").arg(imageCounter));
758 		rel.setAttribute("Type", "http://schemas.microsoft.com/xps/2005/06/required-resource");
759 		rel.setAttribute("Target", "/Resources/Images/" + QString("%1.png").arg(imageCounter));
760 		rel_root.appendChild(rel);
761 		imageCounter++;
762 		obf.appendChild(gr);
763 		ob2.appendChild(obf);
764 		grp.appendChild(ob2);
765 	}
766 	parentElem.appendChild(grp);
767 	if ((item->GrTypeStroke != 0) || (item->lineColor() != CommonStrings::None) || !item->NamedLStyle.isEmpty())
768 	{
769 		if (item->NamedLStyle.isEmpty())
770 		{
771 			if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
772 			{
773 				processSymbolStroke(xOffset, yOffset, item, parentElem, rel_root);
774 			}
775 			else
776 			{
777 				QDomElement ob3 = p_docu.createElement("Path");
778 				ob3.setAttribute("Data", pa);
779 				getStrokeStyle(item, ob3, rel_root, xOffset, yOffset);
780 				if (item->rotation() != 0.0)
781 					ob3.setAttribute("RenderTransform", MatrixToStr(mpx));
782 				parentElem.appendChild(ob3);
783 			}
784 		}
785 		else
786 		{
787 			QDomElement grp2 = p_docu.createElement("Canvas");
788 			multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
789 			for (int it = ml.size()-1; it > -1; it--)
790 			{
791 				if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
792 				{
793 					QDomElement ob3 = p_docu.createElement("Path");
794 					ob3.setAttribute("Data", pa);
795 					getMultiStroke(&ml[it], ob3);
796 					grp2.appendChild(ob3);
797 				}
798 			}
799 			if (item->lineTransparency() != 0)
800 				grp2.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
801 			if (item->rotation() != 0.0)
802 				grp2.setAttribute("RenderTransform", MatrixToStr(mpx));
803 			parentElem.appendChild(grp2);
804 		}
805 	}
806 }
807 
808 class XPSPainter: public TextLayoutPainter
809 {
810 	//PageItem *m_item;
811 	QDomElement m_group;
812 	XPSExPlug *m_xps;
813 	QMap<QString, XPSResourceInfo> &m_fontMap;
814 	QSet<QString> &m_fontRel;
815 	QDomElement &m_relRoot;
816 
817 	bool    m_restart { true };
818 	double  m_current_x { 0.0 };
819 	double  m_current_y { 0.0 };
820 	double  m_fontSize { 0.0 };
821 	QString m_fontUri;
822 	TextLayoutColor m_fillColor;
823 	QTransform  m_transform;
824 	QDomElement m_glyphElem;
825 
826 public:
XPSPainter(PageItem * item,QDomElement & group,XPSExPlug * xps,QMap<QString,XPSResourceInfo> & xpsFontMap,QSet<QString> & xpsFontRel,QDomElement & rel_root)827 	XPSPainter(PageItem *item, QDomElement &group, XPSExPlug *xps, QMap<QString, XPSResourceInfo> &xpsFontMap, QSet<QString> &xpsFontRel, QDomElement &rel_root):
828 //		m_item(item),
829 		m_group(group),
830 		m_xps(xps),
831 		m_fontMap(xpsFontMap),
832 		m_fontRel(xpsFontRel),
833 		m_relRoot(rel_root)
834 	{ }
835 
drawGlyph(const GlyphCluster & gc)836 	void drawGlyph(const GlyphCluster& gc) override
837 	{
838 		if (gc.isControlGlyphs() || gc.isEmpty())
839 			return;
840 
841 		XPSResourceInfo fontInfo;
842 		QString replacementName = font().replacementName();
843 		if (!m_fontMap.contains(replacementName))
844 			m_fontMap.insert(replacementName, m_xps->embedFont(font(), m_relRoot));
845 		fontInfo = m_fontMap.value(replacementName);
846 
847 		if (!m_fontRel.contains(replacementName))
848 		{
849 			m_xps->addFontRelationship(m_relRoot, fontInfo);
850 			m_fontRel.insert(replacementName);
851 		}
852 
853 		QTransform transform = matrix();
854 		double size = fontSize() * qMax(gc.scaleV(), gc.scaleH()) * m_xps->conversionFactor;
855 		QString fontUri = fontInfo.uri;
856 
857 		if (m_restart || (size != m_fontSize) || (m_fillColor != fillColor()) || (m_fontUri != fontUri) ||
858 			(qAbs(m_current_x - x()) > 1e-6) || (m_current_y != y()) || (m_transform != transform))
859 		{
860 			m_glyphElem = m_xps->p_docu.createElement("Glyphs");
861 			m_glyphElem.setAttribute("RenderTransform", m_xps->MatrixToStr(transform, m_xps->conversionFactor));
862 			m_glyphElem.setAttribute("BidiLevel", "0");
863 			m_glyphElem.setAttribute("StyleSimulations", "None");
864 			m_glyphElem.setAttribute("FontRenderingEmSize", m_xps->FToStr(size));
865 			m_glyphElem.setAttribute("FontUri", fontUri);
866 			m_glyphElem.setAttribute("Fill", m_xps->setColor(fillColor().color,fillColor().shade, 0));
867 			m_glyphElem.setAttribute("OriginX", m_xps->FToStr(x() * m_xps->conversionFactor));
868 			m_glyphElem.setAttribute("OriginY", m_xps->FToStr(y() * m_xps->conversionFactor));
869 			m_glyphElem.setAttribute("UnicodeString", QString());
870 			m_group.appendChild(m_glyphElem);
871 		}
872 
873 		QString unicodeString = m_glyphElem.attribute("UnicodeString");
874 		unicodeString += gc.getText();
875 		m_glyphElem.setAttribute("UnicodeString", unicodeString);
876 
877 		QString gcMap;
878 		QString indices, allIndices = m_glyphElem.attribute("Indices");
879 
880 		int gcTextSize = gc.getText().size();
881 		int gcGlyphCount = gc.glyphs().size();
882 		if (gcTextSize > 1 || gcGlyphCount > 1)
883 			gcMap = QString("(%1:%2)").arg(gcTextSize).arg(gcGlyphCount);
884 
885 		double current_x = 0.0;
886 		double clusterWidth = gc.width();
887 		const auto& glyphs = gc.glyphs();
888 
889 		for (int i = 0; i < glyphs.count(); ++i)
890 		{
891 			const GlyphLayout& gl = glyphs.at(i);
892 			if (gl.glyph >= ScFace::CONTROL_GLYPHS)
893 			{
894 				current_x += gl.xadvance * gl.scaleH;
895 				continue;
896 			}
897 
898 			double glWidth = gl.xadvance * gl.scaleH;
899 			if (i == glyphs.count() - 1) // Clusters may have some extra width
900 				glWidth = clusterWidth - current_x;
901 
902 			indices += QString("%1,%2,%3,%4;").arg(gl.glyph)
903 					.arg((glWidth * m_xps->conversionFactor) / size * 100)
904 					.arg((-gl.xoffset * m_xps->conversionFactor) / size * 100)
905 					.arg((-gl.yoffset * m_xps->conversionFactor) / size * 100);
906 			current_x += glWidth;
907 		}
908 		indices.chop(1);
909 
910 		if (!allIndices.isEmpty())
911 			allIndices += ";";
912 		allIndices += QString("%1%2").arg(gcMap, indices);
913 		m_glyphElem.setAttribute("Indices", allIndices);
914 
915 		m_restart = false;
916 		m_current_x = x() + clusterWidth;
917 		m_current_y = y();
918 		m_fontSize = size;
919 		m_fontUri = fontUri;
920 		m_fillColor = fillColor();
921 		m_transform = transform;
922 	}
923 
drawGlyphOutline(const GlyphCluster & gc,bool fill)924 	void drawGlyphOutline(const GlyphCluster& gc, bool fill) override
925 	{
926 		if (gc.isControlGlyphs())
927 			return;
928 
929 		double current_x = 0.0;
930 		for (const GlyphLayout& gl : gc.glyphs())
931 		{
932 			if (gl.glyph >= ScFace::CONTROL_GLYPHS)
933 			{
934 				current_x += gl.xadvance * gl.scaleH;
935 				continue;
936 			}
937 
938 			FPointArray outline = font().glyphOutline(gl.glyph);
939 			if (outline.size() >= 4)
940 			{
941 				QTransform transform = matrix();
942 				transform.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
943 				outline.map(transform);
944 				outline.translate(gl.xoffset + current_x, -(fontSize() * gl.scaleV) + gl.yoffset);
945 				outline.translate(x(), y());
946 				outline.scale(m_xps->conversionFactor, m_xps->conversionFactor);
947 				QString pathData = m_xps->setClipPath(&outline, true);
948 				QDomElement glyph = m_xps->p_docu.createElement("Path");
949 				glyph.setAttribute("Data", pathData);
950 				if (!fill)
951 					glyph.setAttribute("Fill", m_xps->setColor("None", fillColor().shade, 0));
952 				else
953 					glyph.setAttribute("Fill", m_xps->setColor(fillColor().color, fillColor().shade, 0));
954 				glyph.setAttribute("StrokeThickness", m_xps->FToStr(strokeWidth() * m_xps->conversionFactor));
955 				glyph.setAttribute("Stroke", m_xps->setColor(strokeColor().color, strokeColor().shade, 0));
956 				m_group.appendChild(glyph);
957 				//qDebug() << "StrokeWidth XPS" << strokeWidth();
958 			}
959 			current_x += gl.xadvance * gl.scaleH;
960 		}
961 
962 		m_restart = true;
963 	}
964 
drawLine(QPointF start,QPointF end)965 	void drawLine(QPointF start, QPointF end) override
966 	{
967 		QTransform transform = matrix();
968 		QDomElement path = m_xps->p_docu.createElement("Path");
969 		path.setAttribute("RenderTransform", m_xps->MatrixToStr(transform, m_xps->conversionFactor));
970 		path.setAttribute("Data", QString("M%1,%2 L%3,%4").arg((x() + start.x()) * m_xps->conversionFactor).arg((y() + end.y()) * m_xps->conversionFactor).arg((x() + start.x() + end.x()) * m_xps->conversionFactor).arg((y() + end.y()) * m_xps->conversionFactor));
971 		path.setAttribute("Stroke", m_xps->setColor(strokeColor().color, strokeColor().shade, 0));
972 		path.setAttribute("StrokeThickness", m_xps->FToStr(strokeWidth() * m_xps->conversionFactor));
973 		m_group.appendChild(path);
974 		m_restart = true;
975 	}
976 
drawRect(QRectF rect)977 	void drawRect(QRectF rect) override
978 	{
979 		QTransform transform = matrix();
980 		double rx = (x() + rect.x()) * m_xps->conversionFactor;
981 		double ry = (y() + rect.y()) * m_xps->conversionFactor;
982 		double rw = rx + rect.width() * m_xps->conversionFactor;
983 		double rh = ry + rect.height() * m_xps->conversionFactor;
984 		QString paS = QString("M%1,%2 ").arg(rx).arg(ry);
985 		paS += QString("L%1,%2 ").arg(rw).arg(ry);
986 		paS += QString("L%1,%2 ").arg(rw).arg(rh);
987 		paS += QString("L%1,%2 ").arg(rx).arg(rh);
988 		paS += "Z";
989 		QDomElement path = m_xps->p_docu.createElement("Path");
990 		path.setAttribute("RenderTransform", m_xps->MatrixToStr(transform, m_xps->conversionFactor));
991 		path.setAttribute("Data", paS);
992 		path.setAttribute("Fill", m_xps->setColor(fillColor().color, fillColor().shade, 0));
993 		path.setAttribute("StrokeThickness", m_xps->FToStr(strokeWidth() * m_xps->conversionFactor));
994 		path.setAttribute("Stroke", m_xps->setColor(strokeColor().color, strokeColor().shade, 0));
995 		m_group.appendChild(path);
996 		m_restart = true;
997 	}
998 
drawObject(PageItem * item)999 	void drawObject(PageItem* item) override
1000 	{
1001 		QDomElement canvas = m_xps->p_docu.createElement("Canvas");
1002 		QTransform matrix = QTransform();
1003 		matrix.translate(x() * m_xps->conversionFactor, (y() - (item->height() * (scaleV() / 1000.0))) * m_xps->conversionFactor);
1004 		if (scaleH() != 1.0)
1005 			matrix.scale(scaleH(), 1);
1006 		if (scaleV() != 1.0)
1007 			matrix.scale(1, scaleV());
1008 		canvas.setAttribute("RenderTransform", m_xps->MatrixToStr(matrix));
1009 		m_xps->writeItemOnPage(item->gXpos, item->gYpos, item, canvas, m_relRoot);
1010 		m_group.appendChild(canvas);
1011 		m_restart = true;
1012 	}
1013 };
1014 
processTextItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1015 void XPSExPlug::processTextItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1016 {
1017 	if (item->isAnnotation())
1018 		return;
1019 	if (item->GrType == Gradient_Hatch)
1020 		processHatchFill(xOffset, yOffset, item, parentElem, rel_root);
1021 	FPointArray path = item->PoLine.copy();
1022 	path.scale(conversionFactor, conversionFactor);
1023 	QString pa = setClipPath(&path, true);
1024 	if (item->fillRule)
1025 		pa.prepend("F 0 ");
1026 	else
1027 		pa.prepend("F 1 ");
1028 	QDomElement grp = p_docu.createElement("Canvas");
1029 	QTransform mpx;
1030 	QTransform mpl;
1031 	mpl.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1032 	mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1033 	if ((item->rotation() != 0.0) || item->imageFlippedH() || item->imageFlippedV())
1034 	{
1035 		mpx.rotate(item->rotation());
1036 		mpl.rotate(item->rotation());
1037 		if (item->imageFlippedH())
1038 		{
1039 			mpx.translate(item->width() * conversionFactor, 0);
1040 			mpx.scale(-1, 1);
1041 		}
1042 		if (item->imageFlippedV())
1043 		{
1044 			mpx.translate(0, item->height() * conversionFactor);
1045 			mpx.scale(1, -1);
1046 		}
1047 	}
1048 	grp.setAttribute("RenderTransform", MatrixToStr(mpx));
1049 	if (item->isBookmark)
1050 		grp.setAttribute("Name", item->itemName());
1051 	if (item->GrType != Gradient_Hatch)
1052 	{
1053 		if (item->GrMask > 0)
1054 			handleMask(1, item, grp, rel_root, xOffset, yOffset);
1055 		else
1056 		{
1057 			if (item->fillTransparency() != 0)
1058 				grp.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
1059 		}
1060 		if ((item->GrType != 0) || (item->fillColor() != CommonStrings::None))
1061 		{
1062 			FPointArray pathi = item->PoLine.copy();
1063 			if (item->imageFlippedH() || item->imageFlippedV())
1064 			{
1065 				QTransform mpi;
1066 				if (item->imageFlippedH())
1067 				{
1068 					mpi.translate(item->width(), 0);
1069 					mpi.scale(-1, 1);
1070 				}
1071 				if (item->imageFlippedV())
1072 				{
1073 					mpi.translate(0, item->height());
1074 					mpi.scale(1, -1);
1075 				}
1076 				pathi.map(mpi);
1077 			}
1078 			pathi.scale(conversionFactor, conversionFactor);
1079 			QString pai = setClipPath(&pathi, true);
1080 			if (item->fillRule)
1081 				pai.prepend("F 0 ");
1082 			else
1083 				pai.prepend("F 1 ");
1084 			QDomElement ob = p_docu.createElement("Path");
1085 			ob.setAttribute("Data", pai);
1086 			getFillStyle(item, ob, rel_root, xOffset, yOffset, false);
1087 			grp.appendChild(ob);
1088 		}
1089 	}
1090 	if (item->isPathText())
1091 	{
1092 		if ((item->PoShow) && (item->lineColor() != CommonStrings::None))
1093 		{
1094 			QDomElement ob = p_docu.createElement("Path");
1095 			FPointArray path = item->PoLine.copy();
1096 			path.scale(conversionFactor, conversionFactor);
1097 			QString pa = setClipPath(&path, false);
1098 			ob.setAttribute("Data", pa);
1099 			if (item->NamedLStyle.isEmpty())
1100 			{
1101 				if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
1102 				{
1103 					processSymbolStroke(xOffset, yOffset, item, parentElem, rel_root);
1104 				}
1105 				else
1106 				{
1107 					getStrokeStyle(item, ob, rel_root, xOffset, yOffset);
1108 					grp.appendChild(ob);
1109 				}
1110 			}
1111 			else
1112 			{
1113 				QDomElement grp2 = p_docu.createElement("Canvas");
1114 				multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1115 				for (int it = ml.size()-1; it > -1; it--)
1116 				{
1117 					if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1118 					{
1119 						QDomElement ob3 = p_docu.createElement("Path");
1120 						ob3.setAttribute("Data", pa);
1121 						getMultiStroke(&ml[it], ob3);
1122 						grp2.appendChild(ob3);
1123 					}
1124 				}
1125 				if (item->lineTransparency() != 0)
1126 					grp2.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
1127 				grp.appendChild(grp2);
1128 			}
1129 		}
1130 	}
1131 
1132 	if (grp.hasChildNodes())
1133 		parentElem.appendChild(grp);
1134 
1135 	if (item->itemText.length() != 0)
1136 	{
1137 		QDomElement grp2 = p_docu.createElement("Canvas");
1138 		if (grp.hasAttribute("RenderTransform"))
1139 			grp2.setAttribute("RenderTransform", grp.attribute("RenderTransform"));
1140 		if (grp.hasAttribute("Name"))
1141 			grp2.setAttribute("Name", grp.attribute("Name"));
1142 		if (grp.hasAttribute("Opacity"))
1143 			grp2.setAttribute("Opacity", grp.attribute("Opacity"));
1144 		XPSPainter p(item, grp2, this, xps_fontMap, xps_fontRel, rel_root);
1145 		item->textLayout.renderBackground(&p);
1146 		item->textLayout.render(&p);
1147 		parentElem.appendChild(grp2);
1148 	}
1149 	if (item->isTextFrame())
1150 	{
1151 		if ((item->GrTypeStroke != 0) || (item->lineColor() != CommonStrings::None) || !item->NamedLStyle.isEmpty())
1152 		{
1153 			if (item->NamedLStyle.isEmpty())
1154 			{
1155 				if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
1156 				{
1157 					processSymbolStroke(xOffset, yOffset, item, parentElem, rel_root);
1158 				}
1159 				else
1160 				{
1161 					QDomElement ob3 = p_docu.createElement("Path");
1162 					ob3.setAttribute("Data", pa);
1163 					getStrokeStyle(item, ob3, rel_root, xOffset, yOffset);
1164 					ob3.setAttribute("RenderTransform", MatrixToStr(mpl));
1165 					parentElem.appendChild(ob3);
1166 				}
1167 			}
1168 			else
1169 			{
1170 				QDomElement grp2 = p_docu.createElement("Canvas");
1171 				multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1172 				for (int it = ml.size()-1; it > -1; it--)
1173 				{
1174 					if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1175 					{
1176 						QDomElement ob3 = p_docu.createElement("Path");
1177 						ob3.setAttribute("Data", pa);
1178 						getMultiStroke(&ml[it], ob3);
1179 						grp2.appendChild(ob3);
1180 					}
1181 				}
1182 				if (item->lineTransparency() != 0)
1183 					grp2.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
1184 				grp2.setAttribute("RenderTransform", MatrixToStr(mpl));
1185 				parentElem.appendChild(grp2);
1186 			}
1187 		}
1188 	}
1189 }
1190 
processSymbolItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1191 void XPSExPlug::processSymbolItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1192 {
1193 	QDomElement ob = p_docu.createElement("Canvas");
1194 	FPointArray path = item->PoLine.copy();
1195 	ScPattern pat = m_Doc->docPatterns[item->pattern()];
1196 	path.scale(conversionFactor, conversionFactor);
1197 	path.scale(pat.width / item->width(), pat.height / item->height());
1198 	setClipAttr(ob, &path, item->fillRule);
1199 	QTransform mpx;
1200 	mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1201 	mpx.scale(item->width() / pat.width, item->height() / pat.height);
1202 	if ((item->rotation() != 0.0) || item->imageFlippedH() || item->imageFlippedV())
1203 	{
1204 		mpx.rotate(item->rotation());
1205 		if (item->imageFlippedH())
1206 		{
1207 			mpx.translate(item->width() * conversionFactor, 0);
1208 			mpx.scale(-1, 1);
1209 		}
1210 		if (item->imageFlippedV())
1211 		{
1212 			mpx.translate(0, item->height() * conversionFactor);
1213 			mpx.scale(1, -1);
1214 		}
1215 	}
1216 	ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1217 	if (item->GrMask > 0)
1218 		handleMask(1, item, ob, rel_root, xOffset, yOffset);
1219 	else
1220 	{
1221 		if (item->fillTransparency() != 0)
1222 			ob.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
1223 	}
1224 	for (int em = 0; em < pat.items.count(); ++em)
1225 	{
1226 		PageItem* embed = pat.items.at(em);
1227 		writeItemOnPage(embed->gXpos, embed->gYpos, embed, ob, rel_root);
1228 	}
1229 	parentElem.appendChild(ob);
1230 }
1231 
processTableItem(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1232 void XPSExPlug::processTableItem(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1233 {
1234 	QDomElement ob = p_docu.createElement("Canvas");
1235 	QTransform mpx;
1236 	mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1237 	if (item->rotation() != 0.0)
1238 		mpx.rotate(item->rotation());
1239 	mpx.translate(item->asTable()->gridOffset().x() * conversionFactor, item->asTable()->gridOffset().y() * conversionFactor);
1240 	ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1241 	// Paint table fill.
1242 	if (item->asTable()->fillColor() != CommonStrings::None)
1243 	{
1244 		int lastCol = item->asTable()->columns() - 1;
1245 		int lastRow = item->asTable()->rows() - 1;
1246 		double x = item->asTable()->columnPosition(0);
1247 		double y = item->asTable()->rowPosition(0);
1248 		double width = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol) - x;
1249 		double height = item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow) - y;
1250 		FPointArray path;
1251 		path.svgInit();
1252 		path.svgMoveTo(0, 0);
1253 		path.svgLineTo(width, 0);
1254 		path.svgLineTo(width, height);
1255 		path.svgLineTo(0, height);
1256 		path.svgClosePath();
1257 		path.scale(conversionFactor, conversionFactor);
1258 		QString pa = setClipPath(&path, true);
1259 		QDomElement cl = p_docu.createElement("Path");
1260 		cl.setAttribute("Data", pa);
1261 		cl.setAttribute("Fill", setColor(item->asTable()->fillColor(), item->asTable()->fillShade(), 0));
1262 		ob.appendChild(cl);
1263 	}
1264 	// Pass 1: Paint cell fills.
1265 	for (int row = 0; row < item->asTable()->rows(); ++row)
1266 	{
1267 		int colSpan = 0;
1268 		for (int col = 0; col < item->asTable()->columns(); col += colSpan)
1269 		{
1270 			TableCell cell = item->asTable()->cellAt(row, col);
1271 			if (row == cell.row())
1272 			{
1273 				QString colorName = cell.fillColor();
1274 				if (colorName != CommonStrings::None)
1275 				{
1276 					int row = cell.row();
1277 					int col = cell.column();
1278 					int lastRow = row + cell.rowSpan() - 1;
1279 					int lastCol = col + cell.columnSpan() - 1;
1280 					double x = item->asTable()->columnPosition(col);
1281 					double y = item->asTable()->rowPosition(row);
1282 					double width = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol) - x;
1283 					double height = item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow) - y;
1284 					FPointArray path;
1285 					path.svgInit();
1286 					path.svgMoveTo(x, y);
1287 					path.svgLineTo(x + width, y);
1288 					path.svgLineTo(x + width, y + height);
1289 					path.svgLineTo(x, y + height);
1290 					path.svgClosePath();
1291 					path.scale(conversionFactor, conversionFactor);
1292 					QString pa = setClipPath(&path, true);
1293 					QDomElement cl = p_docu.createElement("Path");
1294 					cl.setAttribute("Data", pa);
1295 					cl.setAttribute("Fill", setColor(colorName, cell.fillShade(), 0));
1296 					ob.appendChild(cl);
1297 				}
1298 			}
1299 			colSpan = cell.columnSpan();
1300 		}
1301 	}
1302 	// Pass 2: Paint vertical borders.
1303 	for (int row = 0; row < item->asTable()->rows(); ++row)
1304 	{
1305 		int colSpan = 0;
1306 		for (int col = 0; col < item->asTable()->columns(); col += colSpan)
1307 		{
1308 			TableCell cell = item->asTable()->cellAt(row, col);
1309 			if (row == cell.row())
1310 			{
1311 				const int lastRow = cell.row() + cell.rowSpan() - 1;
1312 				const int lastCol = cell.column() + cell.columnSpan() - 1;
1313 				const double borderX = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol);
1314 				QPointF start(borderX, 0.0);
1315 				QPointF end(borderX, 0.0);
1316 				QPointF startOffsetFactors, endOffsetFactors;
1317 				int startRow, endRow;
1318 				for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
1319 				{
1320 					TableCell rightCell = item->asTable()->cellAt(row, lastCol + 1);
1321 					startRow = qMax(cell.row(), rightCell.row());
1322 					endRow = qMin(lastRow, rightCell.isValid() ? rightCell.row() + rightCell.rowSpan() - 1 : lastRow);
1323 					TableCell topLeftCell = item->asTable()->cellAt(startRow - 1, lastCol);
1324 					TableCell topRightCell = item->asTable()->cellAt(startRow - 1, lastCol + 1);
1325 					TableCell bottomRightCell = item->asTable()->cellAt(endRow + 1, lastCol + 1);
1326 					TableCell bottomLeftCell = item->asTable()->cellAt(endRow + 1, lastCol);
1327 					TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
1328 					resolveBordersVertical(topLeftCell, topRightCell, cell, rightCell, bottomLeftCell, bottomRightCell,
1329 										   &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, item->asTable());
1330 					if (border.isNull())
1331 						continue; // Quit early if the border to paint is null.
1332 					start.setY(item->asTable()->rowPosition(startRow));
1333 					end.setY((item->asTable()->rowPosition(endRow) + item->asTable()->rowHeight(endRow)));
1334 					joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
1335 					paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
1336 				}
1337 				if (col == 0)
1338 				{
1339 					const int lastRow = cell.row() + cell.rowSpan() - 1;
1340 					const int firstCol = cell.column();
1341 					const double borderX = item->asTable()->columnPosition(firstCol);
1342 					QPointF start(borderX, 0.0);
1343 					QPointF end(borderX, 0.0);
1344 					QPointF startOffsetFactors, endOffsetFactors;
1345 					int startRow, endRow;
1346 					for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
1347 					{
1348 						TableCell leftCell = item->asTable()->cellAt(row, firstCol - 1);
1349 						startRow = qMax(cell.row(), leftCell.row());
1350 						endRow = qMin(lastRow, leftCell.isValid() ? leftCell.row() + leftCell.rowSpan() - 1 : lastRow);
1351 						TableCell topLeftCell = item->asTable()->cellAt(startRow - 1, firstCol - 1);
1352 						TableCell topRightCell = item->asTable()->cellAt(startRow - 1, firstCol);
1353 						TableCell bottomRightCell = item->asTable()->cellAt(lastRow + 1, firstCol);
1354 						TableCell bottomLeftCell = item->asTable()->cellAt(lastRow + 1, firstCol - 1);
1355 						TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
1356 						resolveBordersVertical(topLeftCell, topRightCell, leftCell, cell, bottomLeftCell, bottomRightCell,
1357 											   &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, item->asTable());
1358 						if (border.isNull())
1359 							continue; // Quit early if the border to paint is null.
1360 						start.setY(item->asTable()->rowPosition(startRow));
1361 						end.setY((item->asTable()->rowPosition(endRow) + item->asTable()->rowHeight(endRow)));
1362 						joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
1363 						paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
1364 					}
1365 				}
1366 			}
1367 			colSpan = cell.columnSpan();
1368 		}
1369 	}
1370 	// Pass 3: Paint horizontal borders.
1371 	for (int row = 0; row < item->asTable()->rows(); ++row)
1372 	{
1373 		int colSpan = 0;
1374 		for (int col = 0; col < item->asTable()->columns(); col += colSpan)
1375 		{
1376 			TableCell cell = item->asTable()->cellAt(row, col);
1377 			if (row == cell.row())
1378 			{
1379 				const int lastRow = cell.row() + cell.rowSpan() - 1;
1380 				const int lastCol = cell.column() + cell.columnSpan() - 1;
1381 				const double borderY = (item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow));
1382 				QPointF start(0.0, borderY);
1383 				QPointF end(0.0, borderY);
1384 				QPointF startOffsetFactors, endOffsetFactors;
1385 				int startCol, endCol;
1386 				for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
1387 				{
1388 					TableCell bottomCell = item->asTable()->cellAt(lastRow + 1, col);
1389 					startCol = qMax(cell.column(), bottomCell.column());
1390 					endCol = qMin(lastCol, bottomCell.isValid() ? bottomCell.column() + bottomCell.columnSpan() - 1 : lastCol);
1391 					TableCell topLeftCell = item->asTable()->cellAt(lastRow, startCol - 1);
1392 					TableCell topRightCell = item->asTable()->cellAt(lastRow, endCol + 1);
1393 					TableCell bottomRightCell = item->asTable()->cellAt(lastRow + 1, endCol + 1);
1394 					TableCell bottomLeftCell = item->asTable()->cellAt(lastRow + 1, startCol - 1);
1395 					TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
1396 					resolveBordersHorizontal(topLeftCell, cell, topRightCell, bottomLeftCell, bottomCell,
1397 											 bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, item->asTable());
1398 					if (border.isNull())
1399 						continue; // Quit early if the border is null.
1400 					start.setX(item->asTable()->columnPosition(startCol));
1401 					end.setX(item->asTable()->columnPosition(endCol) + item->asTable()->columnWidth(endCol));
1402 					joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
1403 					paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
1404 				}
1405 				if (row == 0)
1406 				{
1407 					const int firstRow = cell.row();
1408 					const int lastCol = cell.column() + cell.columnSpan() - 1;
1409 					const double borderY = item->asTable()->rowPosition(firstRow);
1410 					QPointF start(0.0, borderY);
1411 					QPointF end(0.0, borderY);
1412 					QPointF startOffsetFactors, endOffsetFactors;
1413 					int startCol, endCol;
1414 					for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
1415 					{
1416 						TableCell topCell = item->asTable()->cellAt(firstRow - 1, col);
1417 						startCol = qMax(cell.column(), topCell.column());
1418 						endCol = qMin(lastCol, topCell.isValid() ? topCell.column() + topCell.columnSpan() - 1 : lastCol);
1419 						TableCell topLeftCell = item->asTable()->cellAt(firstRow - 1, startCol - 1);
1420 						TableCell topRightCell = item->asTable()->cellAt(firstRow - 1, endCol + 1);
1421 						TableCell bottomRightCell = item->asTable()->cellAt(firstRow, endCol + 1);
1422 						TableCell bottomLeftCell = item->asTable()->cellAt(firstRow, startCol - 1);
1423 						TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
1424 						resolveBordersHorizontal(topLeftCell, topCell, topRightCell, bottomLeftCell, cell,
1425 												 bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, item->asTable());
1426 						if (border.isNull())
1427 							continue; // Quit early if the border is null.
1428 						start.setX(item->asTable()->columnPosition(startCol));
1429 						end.setX(item->asTable()->columnPosition(endCol) + item->asTable()->columnWidth(endCol));
1430 						joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
1431 						paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
1432 					}
1433 				}
1434 			}
1435 			colSpan = cell.columnSpan();
1436 		}
1437 	}
1438 	// Pass 4: Paint cell content.
1439 	for (int row = 0; row < item->asTable()->rows(); ++row)
1440 	{
1441 		for (int col = 0; col < item->asTable()->columns(); col ++)
1442 		{
1443 			TableCell cell = item->asTable()->cellAt(row, col);
1444 			if (cell.row() == row && cell.column() == col)
1445 			{
1446 				PageItem* textFrame = cell.textFrame();
1447 				processTextItem(cell.contentRect().x(), cell.contentRect().y(), textFrame, ob, rel_root);
1448 			}
1449 		}
1450 	}
1451 	if (item->GrMask > 0)
1452 		handleMask(1, item, ob, rel_root, xOffset, yOffset);
1453 	else
1454 	{
1455 		if (item->fillTransparency() != 0)
1456 			ob.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
1457 	}
1458 	parentElem.appendChild(ob);
1459 }
1460 
paintBorder(const TableBorder & border,const QPointF & start,const QPointF & end,const QPointF & startOffsetFactors,const QPointF & endOffsetFactors,QDomElement & ob)1461 void XPSExPlug::paintBorder(const TableBorder& border, const QPointF& start, const QPointF& end, const QPointF& startOffsetFactors, const QPointF& endOffsetFactors, QDomElement &ob)
1462 {
1463 	QPointF lineStart, lineEnd;
1464 	for (const TableBorderLine& line : border.borderLines())
1465 	{
1466 		lineStart.setX(start.x() + line.width() * startOffsetFactors.x());
1467 		lineStart.setY(start.y() + line.width() * startOffsetFactors.y());
1468 		lineEnd.setX(end.x() + line.width() * endOffsetFactors.x());
1469 		lineEnd.setY(end.y() + line.width() * endOffsetFactors.y());
1470 		QDomElement cl = p_docu.createElement("Path");
1471 		cl.setAttribute("Data", "M" + FToStr(lineStart.x() * conversionFactor) + "," + FToStr(lineStart.y() * conversionFactor) + " L" + FToStr(lineEnd.x() * conversionFactor) + " " + FToStr(lineEnd.y() * conversionFactor));
1472 		QString dashVals = "";
1473 		if (line.style() != Qt::SolidLine)
1474 			dashVals = getDashString(line.style(), qMax(line.width(), 1.0));
1475 		if (!dashVals.isEmpty())
1476 			cl.setAttribute("StrokeDashArray", dashVals);
1477 		if (line.color() != CommonStrings::None)
1478 			cl.setAttribute("Stroke", setColor(line.color(), line.shade(), 0));
1479 		if (line.width() != 0.0)
1480 			cl.setAttribute("StrokeThickness", FToStr(line.width() * conversionFactor));
1481 		else
1482 			cl.setAttribute("StrokeThickness", FToStr(1.0 * conversionFactor));
1483 		ob.appendChild(cl);
1484 	}
1485 }
1486 
processHatchFill(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1487 void XPSExPlug::processHatchFill(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1488 {
1489 	QDomElement obC = p_docu.createElement("Canvas");
1490 	FPointArray path = item->PoLine.copy();
1491 	path.scale(conversionFactor, conversionFactor);
1492 	setClipAttr(obC, &path, item->fillRule);
1493 	if (item->GrMask > 0)
1494 		handleMask(1, item, obC, rel_root, xOffset, yOffset);
1495 	else
1496 	{
1497 		if (item->fillTransparency() != 0)
1498 			obC.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
1499 	}
1500 	QTransform mpo;
1501 	mpo.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1502 	if (item->rotation() != 0.0)
1503 		mpo.rotate(item->rotation());
1504 	obC.setAttribute("RenderTransform", MatrixToStr(mpo));
1505 	if (item->hatchUseBackground)
1506 	{
1507 		FPointArray path;
1508 		path.svgInit();
1509 		path.svgMoveTo(0, 0);
1510 		path.svgLineTo(item->width(), 0);
1511 		path.svgLineTo(item->width(), item->height());
1512 		path.svgLineTo(0, item->height());
1513 		path.svgClosePath();
1514 		path.scale(conversionFactor, conversionFactor);
1515 		QString pa = setClipPath(&path, true);
1516 		QDomElement cl = p_docu.createElement("Path");
1517 		cl.setAttribute("Data", pa);
1518 		cl.setAttribute("Fill", setColor(item->hatchBackground, 100, 0));
1519 		obC.appendChild(cl);
1520 	}
1521 	double lineLen = sqrt((item->width() / 2.0) * (item->width() / 2.0) + (item->height() / 2.0) * (item->height() / 2.0)) * conversionFactor;
1522 	double dist = 0.0;
1523 	while (dist < lineLen)
1524 	{
1525 		QTransform mpx;
1526 		mpx.translate((item->width() / 2.0) * conversionFactor, (item->height() / 2.0) * conversionFactor);
1527 		if (item->hatchAngle != 0.0)
1528 			mpx.rotate(-item->hatchAngle);
1529 		QDomElement ob = p_docu.createElement("Path");
1530 		ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1531 		ob.setAttribute("StrokeDashCap", "Flat");
1532 		ob.setAttribute("StrokeEndLineCap", "Flat");
1533 		ob.setAttribute("StrokeStartLineCap", "Flat");
1534 		ob.setAttribute("StrokeLineJoin", "Miter");
1535 		ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1536 		ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dist).arg(lineLen).arg(dist));
1537 		ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1538 		obC.appendChild(ob);
1539 		if (dist > 0)
1540 		{
1541 			QDomElement ob = p_docu.createElement("Path");
1542 			ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1543 			ob.setAttribute("StrokeDashCap", "Flat");
1544 			ob.setAttribute("StrokeEndLineCap", "Flat");
1545 			ob.setAttribute("StrokeStartLineCap", "Flat");
1546 			ob.setAttribute("StrokeLineJoin", "Miter");
1547 			ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1548 			ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dist).arg(lineLen).arg(-dist));
1549 			ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1550 			obC.appendChild(ob);
1551 		}
1552 		dist += item->hatchDistance * conversionFactor;
1553 	}
1554 	if ((item->hatchType == 1) || (item->hatchType == 2))
1555 	{
1556 		dist = 0.0;
1557 		while (dist < lineLen)
1558 		{
1559 			QTransform mpx;
1560 			mpx.translate((item->width() / 2.0) * conversionFactor, (item->height() / 2.0) * conversionFactor);
1561 			if (item->hatchAngle != 0.0)
1562 				mpx.rotate(-item->hatchAngle + 90);
1563 			QDomElement ob = p_docu.createElement("Path");
1564 			ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1565 			ob.setAttribute("StrokeDashCap", "Flat");
1566 			ob.setAttribute("StrokeEndLineCap", "Flat");
1567 			ob.setAttribute("StrokeStartLineCap", "Flat");
1568 			ob.setAttribute("StrokeLineJoin", "Miter");
1569 			ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1570 			ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dist).arg(lineLen).arg(dist));
1571 			ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1572 			obC.appendChild(ob);
1573 			if (dist > 0)
1574 			{
1575 				QDomElement ob = p_docu.createElement("Path");
1576 				ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1577 				ob.setAttribute("StrokeDashCap", "Flat");
1578 				ob.setAttribute("StrokeEndLineCap", "Flat");
1579 				ob.setAttribute("StrokeStartLineCap", "Flat");
1580 				ob.setAttribute("StrokeLineJoin", "Miter");
1581 				ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1582 				ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dist).arg(lineLen).arg(-dist));
1583 				ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1584 				obC.appendChild(ob);
1585 			}
1586 			dist += item->hatchDistance * conversionFactor;
1587 		}
1588 	}
1589 	if (item->hatchType == 2)
1590 	{
1591 		dist = 0.0;
1592 		while (dist < lineLen)
1593 		{
1594 			double dDist = dist * sqrt(2.0);
1595 			QTransform mpx;
1596 			mpx.translate((item->width() / 2.0) * conversionFactor, (item->height() / 2.0) * conversionFactor);
1597 			if (item->hatchAngle != 0.0)
1598 				mpx.rotate(-item->hatchAngle + 45);
1599 			QDomElement ob = p_docu.createElement("Path");
1600 			ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1601 			ob.setAttribute("StrokeDashCap", "Flat");
1602 			ob.setAttribute("StrokeEndLineCap", "Flat");
1603 			ob.setAttribute("StrokeStartLineCap", "Flat");
1604 			ob.setAttribute("StrokeLineJoin", "Miter");
1605 			ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1606 			ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dDist).arg(lineLen).arg(dDist));
1607 			ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1608 			obC.appendChild(ob);
1609 			if (dist > 0)
1610 			{
1611 				QDomElement ob = p_docu.createElement("Path");
1612 				ob.setAttribute("StrokeThickness", FToStr(conversionFactor));
1613 				ob.setAttribute("StrokeDashCap", "Flat");
1614 				ob.setAttribute("StrokeEndLineCap", "Flat");
1615 				ob.setAttribute("StrokeStartLineCap", "Flat");
1616 				ob.setAttribute("StrokeLineJoin", "Miter");
1617 				ob.setAttribute("Stroke", setColor(item->hatchForeground, 100, 0));
1618 				ob.setAttribute("Data", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dDist).arg(lineLen).arg(-dDist));
1619 				ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1620 				obC.appendChild(ob);
1621 			}
1622 			dist += item->hatchDistance * conversionFactor;
1623 		}
1624 	}
1625 	parentElem.appendChild(obC);
1626 }
1627 
processSymbolStroke(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1628 void XPSExPlug::processSymbolStroke(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1629 {
1630 	QDomElement ob = p_docu.createElement("Canvas");
1631 	QTransform mpx;
1632 	mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1633 	ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1634 	QPainterPath path = item->PoLine.toQPainterPath(false);
1635 	ScPattern pat = m_Doc->docPatterns[item->strokePattern()];
1636 	double pLen = path.length() - ((pat.width / 2.0) * (item->patternStrokeScaleX / 100.0));
1637 	double adv = pat.width * item->patternStrokeScaleX / 100.0 * item->patternStrokeSpace;
1638 	double xpos = item->patternStrokeOffsetX * item->patternStrokeScaleX / 100.0;
1639 	while (xpos < pLen)
1640 	{
1641 		double currPerc = path.percentAtLength(xpos);
1642 		double currAngle = path.angleAtPercent(currPerc);
1643 		if (currAngle <= 180.0)
1644 			currAngle *= -1.0;
1645 		else
1646 			currAngle = 360.0 - currAngle;
1647 		QPointF currPoint = path.pointAtPercent(currPerc);
1648 		QTransform trans;
1649 		trans.translate(currPoint.x() * conversionFactor, currPoint.y() * conversionFactor);
1650 		trans.rotate(currAngle);
1651 		trans.translate(0.0, item->patternStrokeOffsetY);
1652 		trans.rotate(-item->patternStrokeRotation);
1653 		trans.shear(item->patternStrokeSkewX, -item->patternStrokeSkewY);
1654 		trans.scale(item->patternStrokeScaleX / 100.0, item->patternStrokeScaleY / 100.0);
1655 		trans.translate(-pat.width / 2.0, -pat.height / 2.0);
1656 		if (item->patternStrokeMirrorX)
1657 		{
1658 			trans.translate(pat.width, 0);
1659 			trans.scale(-1, 1);
1660 		}
1661 		if (item->patternStrokeMirrorY)
1662 		{
1663 			trans.translate(0, pat.height);
1664 			trans.scale(1, -1);
1665 		}
1666 		QDomElement obS = p_docu.createElement("Canvas");
1667 		obS.setAttribute("RenderTransform", MatrixToStr(trans));
1668 		for (int em = 0; em < pat.items.count(); ++em)
1669 		{
1670 			PageItem* embed = pat.items.at(em);
1671 			writeItemOnPage(embed->gXpos, embed->gYpos, embed, obS, rel_root);
1672 		}
1673 		ob.appendChild(obS);
1674 		xpos += adv;
1675 	}
1676 	parentElem.appendChild(ob);
1677 }
1678 
processArrows(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root)1679 void XPSExPlug::processArrows(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root)
1680 {
1681 	if (item->startArrowIndex() != 0)
1682 	{
1683 		QTransform arrowTrans;
1684 		FPointArray arrow = m_Doc->arrowStyles().at(item->startArrowIndex()-1).points.copy();
1685 		if (item->itemType() == PageItem::Line)
1686 		{
1687 			arrowTrans.translate(0, 0);
1688 			arrowTrans.scale(item->startArrowScale() / 100.0, item->startArrowScale() / 100.0);
1689 			if (item->NamedLStyle.isEmpty())
1690 			{
1691 				if (item->lineWidth() != 0.0)
1692 					arrowTrans.scale(item->lineWidth(), item->lineWidth());
1693 			}
1694 			else
1695 			{
1696 				multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1697 				if (ml[ml.size()-1].Width != 0.0)
1698 					arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1699 			}
1700 			arrowTrans.scale(-1,1);
1701 		}
1702 		else
1703 		{
1704 			FPoint Start = item->PoLine.point(0);
1705 			for (int xx = 1; xx < item->PoLine.size(); xx += 2)
1706 			{
1707 				FPoint Vector = item->PoLine.point(xx);
1708 				if ((Start.x() != Vector.x()) || (Start.y() != Vector.y()))
1709 				{
1710 					double r = atan2(Start.y()-Vector.y(),Start.x()-Vector.x())*(180.0/M_PI);
1711 					arrowTrans.translate(Start.x(), Start.y());
1712 					arrowTrans.rotate(r);
1713 					arrowTrans.scale(item->startArrowScale() / 100.0, item->startArrowScale() / 100.0);
1714 					if (item->NamedLStyle.isEmpty())
1715 					{
1716 						if (item->lineWidth() != 0.0)
1717 							arrowTrans.scale(item->lineWidth(), item->lineWidth());
1718 					}
1719 					else
1720 					{
1721 						multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1722 						if (ml[ml.size()-1].Width != 0.0)
1723 							arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1724 					}
1725 					break;
1726 				}
1727 			}
1728 		}
1729 		arrow.map(arrowTrans);
1730 		drawArrow(xOffset, yOffset, item, parentElem, rel_root, arrow);
1731 	}
1732 	if (item->endArrowIndex() != 0)
1733 	{
1734 		QTransform arrowTrans;
1735 		FPointArray arrow = m_Doc->arrowStyles().at(item->endArrowIndex()-1).points.copy();
1736 		if (item->itemType() == PageItem::Line)
1737 		{
1738 			arrowTrans.translate(item->width(), 0);
1739 			arrowTrans.scale(item->endArrowScale() / 100.0, item->endArrowScale() / 100.0);
1740 			if (item->NamedLStyle.isEmpty())
1741 			{
1742 				if (item->lineWidth() != 0.0)
1743 					arrowTrans.scale(item->lineWidth(), item->lineWidth());
1744 			}
1745 			else
1746 			{
1747 				multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1748 				if (ml[ml.size()-1].Width != 0.0)
1749 					arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1750 			}
1751 		}
1752 		else
1753 		{
1754 			FPoint End = item->PoLine.point(item->PoLine.size()-2);
1755 			for (uint xx = item->PoLine.size()-1; xx > 0; xx -= 2)
1756 			{
1757 				FPoint Vector = item->PoLine.point(xx);
1758 				if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
1759 				{
1760 					double r = atan2(End.y()-Vector.y(),End.x()-Vector.x())*(180.0/M_PI);
1761 					arrowTrans.translate(End.x(), End.y());
1762 					arrowTrans.rotate(r);
1763 					arrowTrans.scale(item->endArrowScale() / 100.0, item->endArrowScale() / 100.0);
1764 					if (item->NamedLStyle.isEmpty())
1765 					{
1766 						if (item->lineWidth() != 0.0)
1767 							arrowTrans.scale(item->lineWidth(), item->lineWidth());
1768 					}
1769 					else
1770 					{
1771 						multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1772 						if (ml[ml.size()-1].Width != 0.0)
1773 							arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1774 					}
1775 					break;
1776 				}
1777 			}
1778 		}
1779 		arrow.map(arrowTrans);
1780 		drawArrow(xOffset, yOffset, item, parentElem, rel_root, arrow);
1781 	}
1782 }
1783 
drawArrow(double xOffset,double yOffset,PageItem * item,QDomElement & parentElem,QDomElement & rel_root,FPointArray & arrow)1784 void XPSExPlug::drawArrow(double xOffset, double yOffset, PageItem *item, QDomElement &parentElem, QDomElement &rel_root, FPointArray &arrow)
1785 {
1786 	QTransform mpx;
1787 	if (item->rotation() != 0.0)
1788 	{
1789 		mpx.translate(xOffset * conversionFactor, yOffset * conversionFactor);
1790 		mpx.rotate(item->rotation());
1791 		mpx.translate(-xOffset * conversionFactor, -yOffset * conversionFactor);
1792 	}
1793 	arrow.translate(xOffset, yOffset);
1794 	arrow.scale(conversionFactor, conversionFactor);
1795 	QString pa = setClipPath(&arrow, true);
1796 	if (item->NamedLStyle.isEmpty())
1797 	{
1798 		QDomElement ob = p_docu.createElement("Path");
1799 		ob.setAttribute("Data", pa);
1800 		ob.setAttribute("RenderTransform", MatrixToStr(mpx));
1801 		getStrokeStyle(item, ob, rel_root, xOffset, yOffset, true);
1802 		parentElem.appendChild(ob);
1803 	}
1804 	else
1805 	{
1806 		QDomElement grp2 = p_docu.createElement("Canvas");
1807 		grp2.setAttribute("RenderTransform", MatrixToStr(mpx));
1808 		multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1809 		if (ml[0].Color != CommonStrings::None)
1810 		{
1811 			QDomElement ob3 = p_docu.createElement("Path");
1812 			ob3.setAttribute("Data", pa);
1813 			ob3.setAttribute("Fill", setColor(ml[0].Color, ml[0].Shade, 0));
1814 			getMultiStroke(&ml[0], ob3);
1815 			grp2.appendChild(ob3);
1816 		}
1817 		for (int it = ml.size()-1; it > 0; it--)
1818 		{
1819 			if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1820 			{
1821 				QDomElement ob3 = p_docu.createElement("Path");
1822 				ob3.setAttribute("Data", pa);
1823 				getMultiStroke(&ml[it], ob3);
1824 				grp2.appendChild(ob3);
1825 			}
1826 		}
1827 		parentElem.appendChild(grp2);
1828 	}
1829 }
1830 
addFontRelationship(QDomElement & rel_root,const XPSResourceInfo & fontInfo)1831 void XPSExPlug::addFontRelationship(QDomElement &rel_root, const XPSResourceInfo& fontInfo)
1832 {
1833 	QDomElement rel = r_docu.createElement("Relationship");
1834 	rel.setAttribute("Id", fontInfo.id);
1835 	rel.setAttribute("Type", "http://schemas.microsoft.com/xps/2005/06/required-resource");
1836 	rel.setAttribute("Target", fontInfo.uri);
1837 	rel_root.appendChild(rel);
1838 }
1839 
embedFont(const ScFace & font,QDomElement & rel_root)1840 XPSResourceInfo XPSExPlug::embedFont(const ScFace& font, QDomElement &rel_root)
1841 {
1842 	QByteArray fontData;
1843 	loadRawText(font.fontFilePath(), fontData);
1844 	QUuid id = QUuid::createUuid();
1845 	QString guidString = id.toString();
1846 	guidString = guidString.toUpper();
1847 	guidString.remove("{");
1848 	guidString.remove("}");
1849 	unsigned short guid[16];
1850 	const static int indexes[] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34};
1851 	for (int i = 0; i < 16; i++)
1852 	{
1853 		int hex1 = hex2int(guidString[indexes[i]].cell());
1854 		int hex2 = hex2int(guidString[indexes[i]+1].cell());
1855 		guid[i] = hex1 * 16 + hex2;
1856 	}
1857 	// Obfuscation - xor bytes in font binary with bytes from guid (font's filename)
1858 	const static int mapping[] = {15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3};
1859 	for (int i = 0; i < 16; i++)
1860 	{
1861 		fontData[i] = fontData[i] ^ guid[mapping[i]];
1862 		fontData[i+16] = fontData[i+16] ^ guid[mapping[i]];
1863 	}
1864 	QFile ft(baseDir + "/Resources/Fonts/" + guidString + ".odttf");
1865 	if (ft.open(QIODevice::WriteOnly))
1866 	{
1867 		ft.write(fontData);
1868 		ft.close();
1869 	}
1870 
1871 	XPSResourceInfo rsrcInfo;
1872 	rsrcInfo.id =  QString("rIDf%1").arg(fontCounter);
1873 	rsrcInfo.uri = "/Resources/Fonts/" + guidString + ".odttf";
1874 	fontCounter++;
1875 
1876 	return rsrcInfo;
1877 }
1878 
getMultiStroke(struct SingleLine * sl,QDomElement & parentElem)1879 void XPSExPlug::getMultiStroke(struct SingleLine *sl, QDomElement &parentElem)
1880 {
1881 	parentElem.setAttribute("StrokeThickness", FToStr(sl->Width * conversionFactor));
1882 	switch (static_cast<Qt::PenCapStyle>(sl->LineEnd))
1883 	{
1884 		case Qt::FlatCap:
1885 			parentElem.setAttribute("StrokeDashCap", "Flat");
1886 			parentElem.setAttribute("StrokeEndLineCap", "Flat");
1887 			parentElem.setAttribute("StrokeStartLineCap", "Flat");
1888 			break;
1889 		case Qt::SquareCap:
1890 			parentElem.setAttribute("StrokeDashCap", "Square");
1891 			parentElem.setAttribute("StrokeEndLineCap", "Square");
1892 			parentElem.setAttribute("StrokeStartLineCap", "Square");
1893 			break;
1894 		case Qt::RoundCap:
1895 			parentElem.setAttribute("StrokeDashCap", "Round");
1896 			parentElem.setAttribute("StrokeEndLineCap", "Round");
1897 			parentElem.setAttribute("StrokeStartLineCap", "Round");
1898 			break;
1899 		default:
1900 			parentElem.setAttribute("StrokeDashCap", "Flat");
1901 			parentElem.setAttribute("StrokeEndLineCap", "Flat");
1902 			parentElem.setAttribute("StrokeStartLineCap", "Flat");
1903 			break;
1904 	}
1905 	switch (static_cast<Qt::PenJoinStyle>(sl->LineJoin))
1906 	{
1907 		case Qt::MiterJoin:
1908 			parentElem.setAttribute("StrokeLineJoin", "Miter");
1909 			break;
1910 		case Qt::BevelJoin:
1911 			parentElem.setAttribute("StrokeLineJoin", "Bevel");
1912 			break;
1913 		case Qt::RoundJoin:
1914 			parentElem.setAttribute("StrokeLineJoin", "Round");
1915 			break;
1916 		default:
1917 			parentElem.setAttribute("StrokeLineJoin", "Miter");
1918 			break;
1919 	}
1920 	QString dashVals = "";
1921 	if (static_cast<Qt::PenStyle>(sl->Dash) != Qt::SolidLine)
1922 		dashVals = getDashString(sl->Dash, 1);
1923 	if (!dashVals.isEmpty())
1924 		parentElem.setAttribute("StrokeDashArray", dashVals);
1925 	if (sl->Color != CommonStrings::None)
1926 		parentElem.setAttribute("Stroke", setColor(sl->Color, sl->Shade, 0));
1927 }
1928 
getStrokeStyle(PageItem * item,QDomElement & parentElem,QDomElement & rel_root,double xOffset,double yOffset,bool forArrow)1929 void XPSExPlug::getStrokeStyle(PageItem *item, QDomElement &parentElem, QDomElement &rel_root, double xOffset, double yOffset, bool forArrow)
1930 {
1931 	parentElem.setAttribute("StrokeThickness", FToStr(item->lineWidth() * conversionFactor));
1932 	switch (item->PLineEnd)
1933 	{
1934 		case Qt::FlatCap:
1935 			parentElem.setAttribute("StrokeDashCap", "Flat");
1936 			parentElem.setAttribute("StrokeEndLineCap", "Flat");
1937 			parentElem.setAttribute("StrokeStartLineCap", "Flat");
1938 			break;
1939 		case Qt::SquareCap:
1940 			parentElem.setAttribute("StrokeDashCap", "Square");
1941 			parentElem.setAttribute("StrokeEndLineCap", "Square");
1942 			parentElem.setAttribute("StrokeStartLineCap", "Square");
1943 			break;
1944 		case Qt::RoundCap:
1945 			parentElem.setAttribute("StrokeDashCap", "Round");
1946 			parentElem.setAttribute("StrokeEndLineCap", "Round");
1947 			parentElem.setAttribute("StrokeStartLineCap", "Round");
1948 			break;
1949 		default:
1950 			parentElem.setAttribute("StrokeDashCap", "Flat");
1951 			parentElem.setAttribute("StrokeEndLineCap", "Flat");
1952 			parentElem.setAttribute("StrokeStartLineCap", "Flat");
1953 			break;
1954 	}
1955 	switch (item->PLineJoin)
1956 	{
1957 		case Qt::MiterJoin:
1958 			parentElem.setAttribute("StrokeLineJoin", "Miter");
1959 			break;
1960 		case Qt::BevelJoin:
1961 			parentElem.setAttribute("StrokeLineJoin", "Bevel");
1962 			break;
1963 		case Qt::RoundJoin:
1964 			parentElem.setAttribute("StrokeLineJoin", "Round");
1965 			break;
1966 		default:
1967 			parentElem.setAttribute("StrokeLineJoin", "Miter");
1968 			break;
1969 	}
1970 	QString dashVals = "";
1971 	if (item->DashValues.count() != 0)
1972 	{
1973 		for (auto it = item->DashValues.cbegin(); it != item->DashValues.cend(); ++it)
1974 		{
1975 			dashVals += FToStr((*it) / item->lineWidth()) + " ";
1976 		}
1977 	}
1978 	else
1979 	{
1980 		if (item->PLineArt != Qt::SolidLine)
1981 			dashVals = getDashString(item->PLineArt, 1);
1982 	}
1983 	if (!dashVals.isEmpty())
1984 	{
1985 		parentElem.setAttribute("StrokeDashArray", dashVals);
1986 		if (item->DashValues.count() != 0)
1987 			parentElem.setAttribute("StrokeDashOffset", FToStr(item->DashOffset));
1988 	}
1989 	if (item->GrTypeStroke == 0)
1990 	{
1991 		if (item->lineColor() != CommonStrings::None)
1992 		{
1993 			if (forArrow)
1994 				parentElem.setAttribute("Fill", setColor(item->lineColor(), item->lineShade(), item->lineTransparency()));
1995 			else
1996 				parentElem.setAttribute("Stroke", setColor(item->lineColor(), item->lineShade(), item->lineTransparency()));
1997 			return;
1998 		}
1999 	}
2000 	else
2001 	{
2002 		if ((!item->strokePattern().isEmpty()) && (!item->patternStrokePath))
2003 		{
2004 			ScPattern pa = m_Doc->docPatterns[item->strokePattern()];
2005 			QDomElement ob;
2006 			if (forArrow)
2007 				ob = p_docu.createElement("Path.Fill");
2008 			else
2009 				ob = p_docu.createElement("Path.Stroke");
2010 			QDomElement gr = p_docu.createElement("VisualBrush");
2011 			gr.setAttribute("TileMode", "Tile");
2012 			gr.setAttribute("ViewboxUnits", "Absolute");
2013 			gr.setAttribute("ViewportUnits", "Absolute");
2014 			gr.setAttribute("Viewbox", QString("0, 0, %1, %2").arg(pa.width * conversionFactor).arg(pa.height * conversionFactor));
2015 			double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace;
2016 			item->strokePatternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace);
2017 			patternScaleX /= 100.0;
2018 			patternScaleY /= 100.0;
2019 			double lw2 = item->lineWidth() / 2.0;
2020 			gr.setAttribute("Viewport", QString("%1, %2, %3, %4").arg((xOffset + patternOffsetX - lw2) * conversionFactor).arg((yOffset + patternOffsetY - lw2) * conversionFactor).arg((pa.width * patternScaleX) * conversionFactor).arg((pa.height * patternScaleY) * conversionFactor));
2021 			bool mirrorX, mirrorY;
2022 			item->strokePatternFlip(mirrorX, mirrorY);
2023 			if ((patternRotation != 0) || (patternSkewX != 0) || (patternSkewY != 0) || mirrorX || mirrorY)
2024 			{
2025 				QTransform mpa;
2026 				mpa.rotate(patternRotation);
2027 				mpa.shear(-patternSkewX, patternSkewY);
2028 				mpa.scale(pa.scaleX, pa.scaleY);
2029 				if (mirrorX)
2030 					mpa.scale(-1, 1);
2031 				if (mirrorY)
2032 					mpa.scale(1, -1);
2033 				gr.setAttribute("Transform", MatrixToStr(mpa));
2034 			}
2035 			if (item->lineTransparency() != 0)
2036 				gr.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
2037 			QDomElement grp = p_docu.createElement("VisualBrush.Visual");
2038 			for (int em = 0; em < pa.items.count(); ++em)
2039 			{
2040 				PageItem* embed = pa.items.at(em);
2041 				writeItemOnPage(embed->gXpos, embed->gYpos, embed, grp, rel_root);
2042 			}
2043 			gr.appendChild(grp);
2044 			ob.appendChild(gr);
2045 			parentElem.appendChild(ob);
2046 		}
2047 		else if ((item->GrTypeStroke == Gradient_Linear) || (item->GrTypeStroke == Gradient_Radial))
2048 		{
2049 			QDomElement ob;
2050 			if (forArrow)
2051 				ob = p_docu.createElement("Path.Fill");
2052 			else
2053 				ob = p_docu.createElement("Path.Stroke");
2054 			QDomElement gr;
2055 			double GrStartX = (item->GrStrokeStartX + xOffset) * conversionFactor;
2056 			double GrStartY = (item->GrStrokeStartY + yOffset) * conversionFactor;
2057 			double GrFocalX = (item->GrStrokeFocalX + xOffset) * conversionFactor;
2058 			double GrFocalY = (item->GrStrokeFocalY + yOffset) * conversionFactor;
2059 			double GrEndX = (item->GrStrokeEndX + xOffset) * conversionFactor;
2060 			double GrEndY = (item->GrStrokeEndY + yOffset) * conversionFactor;
2061 			if (item->GrTypeStroke == Gradient_Linear)
2062 			{
2063 				gr = p_docu.createElement("LinearGradientBrush");
2064 				gr.setAttribute("MappingMode", "Absolute");
2065 				gr.setAttribute("StartPoint", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2066 				gr.setAttribute("EndPoint", FToStr(GrEndX) + ", " + FToStr(GrEndY));
2067 			}
2068 			else
2069 			{
2070 				gr = p_docu.createElement("RadialGradientBrush");
2071 				double rad = sqrt(pow(GrEndX - GrStartX, 2) + pow(GrEndY - GrStartY,2));
2072 				gr.setAttribute("MappingMode", "Absolute");
2073 				gr.setAttribute("RadiusX", FToStr(rad));
2074 				gr.setAttribute("RadiusY", FToStr(rad));
2075 				gr.setAttribute("Center", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2076 				gr.setAttribute("GradientOrigin", FToStr(GrFocalX) + ", " + FToStr(GrFocalY));
2077 			}
2078 			double gradientSkew;
2079 			if (item->GrStrokeSkew == 90)
2080 				gradientSkew = 1;
2081 			else if (item->GrStrokeSkew == 180)
2082 				gradientSkew = 0;
2083 			else if (item->GrStrokeSkew == 270)
2084 				gradientSkew = -1;
2085 			else if (item->GrStrokeSkew == 390)
2086 				gradientSkew = 0;
2087 			else
2088 				gradientSkew = tan(M_PI / 180.0 * item->GrStrokeSkew);
2089 			QTransform qmatrix;
2090 			if (item->GrTypeStroke == Gradient_Linear)
2091 			{
2092 				qmatrix.translate(GrStartX, GrStartY);
2093 				qmatrix.shear(-gradientSkew, 0);
2094 				qmatrix.translate(-GrStartX, -GrStartY);
2095 			}
2096 			else
2097 			{
2098 				double rotEnd = xy2Deg(GrEndX - GrStartX, GrEndY - GrStartY);
2099 				qmatrix.translate(GrStartX, GrStartY);
2100 				qmatrix.rotate(rotEnd);
2101 				qmatrix.shear(gradientSkew, 0);
2102 				qmatrix.translate(0, GrStartY * (1.0 - item->GrStrokeScale));
2103 				qmatrix.translate(-GrStartX, -GrStartY);
2104 				qmatrix.scale(1, item->GrStrokeScale);
2105 			}
2106 			gr.setAttribute("Transform", MatrixToStr(qmatrix));
2107 			if (item->lineTransparency() != 0)
2108 				gr.setAttribute("Opacity", FToStr(1.0 - item->lineTransparency()));
2109 			QDomElement grs;
2110 			if (item->GrTypeStroke == Gradient_Linear)
2111 				grs = p_docu.createElement("LinearGradientBrush.GradientStops");
2112 			else
2113 				grs = p_docu.createElement("RadialGradientBrush.GradientStops");
2114 			bool   isFirst = true;
2115 			double actualStop = 0.0, lastStop = 0.0;
2116 			QList<VColorStop*> cstops = item->stroke_gradient.colorStops();
2117 			for (int cst = 0; cst < item->stroke_gradient.stops(); ++cst)
2118 			{
2119 				actualStop = cstops.at(cst)->rampPoint;
2120 				if ((actualStop != lastStop) || (isFirst))
2121 				{
2122 					QDomElement itcl = p_docu.createElement("GradientStop");
2123 					itcl.setAttribute("Offset", FToStr(cstops.at(cst)->rampPoint));
2124 					itcl.setAttribute("Color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade, 1.0 - cstops.at(cst)->opacity));
2125 					grs.appendChild(itcl);
2126 					lastStop = actualStop;
2127 					isFirst  = false;
2128 				}
2129 			}
2130 			gr.appendChild(grs);
2131 			ob.appendChild(gr);
2132 			parentElem.appendChild(ob);
2133 		}
2134 	}
2135 }
2136 
getFillStyle(PageItem * item,QDomElement & parentElem,QDomElement & rel_root,double xOffset,double yOffset,bool withTransparency)2137 void XPSExPlug::getFillStyle(PageItem *item, QDomElement &parentElem, QDomElement &rel_root, double xOffset, double yOffset, bool withTransparency)
2138 {
2139 	if (item->GrType == 0)
2140 	{
2141 		if (item->fillColor() != CommonStrings::None)
2142 		{
2143 			if ((withTransparency) || (item->GrMask == 0))
2144 				parentElem.setAttribute("Fill", setColor(item->fillColor(), item->fillShade(), item->fillTransparency()));
2145 			else
2146 				parentElem.setAttribute("Fill", setColor(item->fillColor(), item->fillShade(), 0));
2147 		}
2148 		return;
2149 	}
2150 
2151 	if ((item->GrType == Gradient_Linear) || (item->GrType == Gradient_Radial))
2152 	{
2153 		QDomElement ob = p_docu.createElement("Path.Fill");
2154 		QDomElement gr;
2155 		double GrStartX = (item->GrStartX + xOffset) * conversionFactor;
2156 		double GrStartY = (item->GrStartY + yOffset) * conversionFactor;
2157 		double GrFocalX = (item->GrFocalX + xOffset) * conversionFactor;
2158 		double GrFocalY = (item->GrFocalY + yOffset) * conversionFactor;
2159 		double GrEndX = (item->GrEndX + xOffset) * conversionFactor;
2160 		double GrEndY = (item->GrEndY + yOffset) * conversionFactor;
2161 		if (item->GrType == Gradient_Linear)
2162 		{
2163 			gr = p_docu.createElement("LinearGradientBrush");
2164 			gr.setAttribute("MappingMode", "Absolute");
2165 			gr.setAttribute("StartPoint", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2166 			gr.setAttribute("EndPoint", FToStr(GrEndX) + ", " + FToStr(GrEndY));
2167 		}
2168 		else
2169 		{
2170 			gr = p_docu.createElement("RadialGradientBrush");
2171 			double rad = sqrt(pow(GrEndX - GrStartX, 2) + pow(GrEndY - GrStartY,2));
2172 			gr.setAttribute("MappingMode", "Absolute");
2173 			gr.setAttribute("RadiusX", FToStr(rad));
2174 			gr.setAttribute("RadiusY", FToStr(rad));
2175 			gr.setAttribute("Center", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2176 			gr.setAttribute("GradientOrigin", FToStr(GrFocalX) + ", " + FToStr(GrFocalY));
2177 		}
2178 		double gradientSkew;
2179 		if (item->GrSkew == 90)
2180 			gradientSkew = 1;
2181 		else if (item->GrSkew == 180)
2182 			gradientSkew = 0;
2183 		else if (item->GrSkew == 270)
2184 			gradientSkew = -1;
2185 		else if (item->GrSkew == 390)
2186 			gradientSkew = 0;
2187 		else
2188 			gradientSkew = tan(M_PI / 180.0 * item->GrSkew);
2189 		QTransform qmatrix;
2190 		if (item->GrType == Gradient_Linear)
2191 		{
2192 			qmatrix.translate(GrStartX, GrStartY);
2193 			qmatrix.shear(-gradientSkew, 0);
2194 			qmatrix.translate(-GrStartX, -GrStartY);
2195 		}
2196 		else
2197 		{
2198 			double rotEnd = xy2Deg(GrEndX - GrStartX, GrEndY - GrStartY);
2199 			qmatrix.translate(GrStartX, GrStartY);
2200 			qmatrix.rotate(rotEnd);
2201 			qmatrix.shear(gradientSkew, 0);
2202 			qmatrix.translate(0, GrStartY * (1.0 - item->GrScale));
2203 			qmatrix.translate(-GrStartX, -GrStartY);
2204 			qmatrix.scale(1, item->GrScale);
2205 		}
2206 		gr.setAttribute("Transform", MatrixToStr(qmatrix));
2207 		if ((item->fillTransparency() != 0) && ((withTransparency) || (item->GrMask == 0)))
2208 			gr.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
2209 		QDomElement grs;
2210 		if (item->GrType == Gradient_Linear)
2211 			grs = p_docu.createElement("LinearGradientBrush.GradientStops");
2212 		else
2213 			grs = p_docu.createElement("RadialGradientBrush.GradientStops");
2214 		bool   isFirst = true;
2215 		double actualStop = 0.0, lastStop = 0.0;
2216 		QList<VColorStop*> cstops = item->fill_gradient.colorStops();
2217 		for (int cst = 0; cst < item->fill_gradient.stops(); ++cst)
2218 		{
2219 			actualStop = cstops.at(cst)->rampPoint;
2220 			if ((actualStop != lastStop) || (isFirst))
2221 			{
2222 				QDomElement itcl = p_docu.createElement("GradientStop");
2223 				itcl.setAttribute("Offset", FToStr(cstops.at(cst)->rampPoint));
2224 				itcl.setAttribute("Color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade, 1.0 - cstops.at(cst)->opacity));
2225 				grs.appendChild(itcl);
2226 				lastStop = actualStop;
2227 				isFirst  = false;
2228 			}
2229 		}
2230 		gr.appendChild(grs);
2231 		ob.appendChild(gr);
2232 		parentElem.appendChild(ob);
2233 	}
2234 	else if (item->GrType == 8)
2235 	{
2236 		ScPattern pa = m_Doc->docPatterns[item->pattern()];
2237 		QDomElement ob = p_docu.createElement("Path.Fill");
2238 		QDomElement gr = p_docu.createElement("VisualBrush");
2239 		gr.setAttribute("TileMode", "Tile");
2240 		gr.setAttribute("ViewboxUnits", "Absolute");
2241 		gr.setAttribute("ViewportUnits", "Absolute");
2242 		gr.setAttribute("Viewbox", QString("0, 0, %1, %2").arg(pa.width * conversionFactor).arg(pa.height * conversionFactor));
2243 		double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY;
2244 		item->patternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY);
2245 		patternScaleX /= 100.0;
2246 		patternScaleY /= 100.0;
2247 		gr.setAttribute("Viewport", QString("%1, %2, %3, %4").arg((xOffset + patternOffsetX) * conversionFactor).arg((yOffset + patternOffsetY) * conversionFactor).arg((pa.width * patternScaleX) * conversionFactor).arg((pa.height * patternScaleY) * conversionFactor));
2248 		bool mirrorX, mirrorY;
2249 		item->patternFlip(mirrorX, mirrorY);
2250 		if ((patternRotation != 0) || (patternSkewX != 0) || (patternSkewY != 0) || mirrorX || mirrorY)
2251 		{
2252 			QTransform mpa;
2253 			mpa.rotate(patternRotation);
2254 			mpa.shear(-patternSkewX, patternSkewY);
2255 			mpa.scale(pa.scaleX, pa.scaleY);
2256 			if (mirrorX)
2257 				mpa.scale(-1, 1);
2258 			if (mirrorY)
2259 				mpa.scale(1, -1);
2260 			gr.setAttribute("Transform", MatrixToStr(mpa));
2261 		}
2262 		if ((item->fillTransparency() != 0) && ((withTransparency) || (item->GrMask == 0)))
2263 			gr.setAttribute("Opacity", FToStr(1.0 - item->fillTransparency()));
2264 		QDomElement grp = p_docu.createElement("VisualBrush.Visual");
2265 		for (int em = 0; em < pa.items.count(); ++em)
2266 		{
2267 			PageItem* embed = pa.items.at(em);
2268 			writeItemOnPage(embed->gXpos, embed->gYpos, embed, grp, rel_root);
2269 		}
2270 		gr.appendChild(grp);
2271 		ob.appendChild(gr);
2272 		parentElem.appendChild(ob);
2273 	}
2274 
2275 }
2276 
handleMask(int type,PageItem * item,QDomElement & parentElem,QDomElement & rel_root,double xOffset,double yOffset)2277 void XPSExPlug::handleMask(int type, PageItem *item, QDomElement &parentElem, QDomElement &rel_root, double xOffset, double yOffset)
2278 {
2279 	QDomElement ob;
2280 	if (type == 1)
2281 		ob = p_docu.createElement("Canvas.OpacityMask");
2282 	else if (type == 2)
2283 		ob = p_docu.createElement("Glyph.OpacityMask");
2284 	else
2285 		ob = p_docu.createElement("Path.OpacityMask");
2286 	if ((item->GrMask == GradMask_Linear) || (item->GrMask == GradMask_Radial))
2287 	{
2288 		QDomElement gr;
2289 		double GrStartX = (item->GrMaskStartX + xOffset) * conversionFactor;
2290 		double GrStartY = (item->GrMaskStartY + yOffset) * conversionFactor;
2291 		double GrFocalX = (item->GrMaskFocalX + xOffset) * conversionFactor;
2292 		double GrFocalY = (item->GrMaskFocalY + yOffset) * conversionFactor;
2293 		double GrEndX = (item->GrMaskEndX + xOffset) * conversionFactor;
2294 		double GrEndY = (item->GrMaskEndY + yOffset) * conversionFactor;
2295 		if ((item->GrMask == GradMask_Linear) || (item->GrMask == GradMask_LinearLumAlpha))
2296 		{
2297 			gr = p_docu.createElement("LinearGradientBrush");
2298 			gr.setAttribute("MappingMode", "Absolute");
2299 			gr.setAttribute("StartPoint", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2300 			gr.setAttribute("EndPoint", FToStr(GrEndX) + ", " + FToStr(GrEndY));
2301 		}
2302 		else
2303 		{
2304 			gr = p_docu.createElement("RadialGradientBrush");
2305 			double rad = sqrt(pow(GrEndX - GrStartX, 2) + pow(GrEndY - GrStartY,2));
2306 			gr.setAttribute("MappingMode", "Absolute");
2307 			gr.setAttribute("RadiusX", FToStr(rad));
2308 			gr.setAttribute("RadiusY", FToStr(rad));
2309 			gr.setAttribute("Center", FToStr(GrStartX) + ", " + FToStr(GrStartY));
2310 			gr.setAttribute("GradientOrigin", FToStr(GrFocalX) + ", " + FToStr(GrFocalY));
2311 		}
2312 		double gradientSkew;
2313 		if (item->GrMaskSkew == 90)
2314 			gradientSkew = 1;
2315 		else if (item->GrMaskSkew == 180)
2316 			gradientSkew = 0;
2317 		else if (item->GrMaskSkew == 270)
2318 			gradientSkew = -1;
2319 		else if (item->GrMaskSkew == 390)
2320 			gradientSkew = 0;
2321 		else
2322 			gradientSkew = tan(M_PI / 180.0 * item->GrMaskSkew);
2323 		QTransform qmatrix;
2324 		if (item->GrMask == GradMask_Linear)
2325 		{
2326 			qmatrix.translate(GrStartX, GrStartY);
2327 			qmatrix.shear(-gradientSkew, 0);
2328 			qmatrix.translate(-GrStartX, -GrStartY);
2329 		}
2330 		else
2331 		{
2332 			double rotEnd = xy2Deg(GrEndX - GrStartX, GrEndY - GrStartY);
2333 			qmatrix.translate(GrStartX, GrStartY);
2334 			qmatrix.rotate(rotEnd);
2335 			qmatrix.shear(gradientSkew, 0);
2336 			qmatrix.translate(0, GrStartY * (1.0 - item->GrMaskScale));
2337 			qmatrix.translate(-GrStartX, -GrStartY);
2338 			qmatrix.scale(1, item->GrMaskScale);
2339 		}
2340 		gr.setAttribute("Transform", MatrixToStr(qmatrix));
2341 		QDomElement grs;
2342 		if (item->GrMask == GradMask_Linear)
2343 			grs = p_docu.createElement("LinearGradientBrush.GradientStops");
2344 		else
2345 			grs = p_docu.createElement("RadialGradientBrush.GradientStops");
2346 		bool   isFirst = true;
2347 		double actualStop = 0.0, lastStop = 0.0;
2348 		QList<VColorStop*> cstops = item->mask_gradient.colorStops();
2349 		for (int cst = 0; cst < item->mask_gradient.stops(); ++cst)
2350 		{
2351 			actualStop = cstops.at(cst)->rampPoint;
2352 			if ((actualStop != lastStop) || (isFirst))
2353 			{
2354 				QDomElement itcl = p_docu.createElement("GradientStop");
2355 				itcl.setAttribute("Offset", FToStr(cstops.at(cst)->rampPoint));
2356 				itcl.setAttribute("Color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade, 1.0 - cstops.at(cst)->opacity));
2357 				grs.appendChild(itcl);
2358 				lastStop = actualStop;
2359 				isFirst  = false;
2360 			}
2361 		}
2362 		gr.appendChild(grs);
2363 		ob.appendChild(gr);
2364 	}
2365 	else if (item->GrMask == GradMask_Pattern)
2366 	{
2367 		ScPattern pa = m_Doc->docPatterns[item->patternMask()];
2368 		QDomElement gr = p_docu.createElement("VisualBrush");
2369 		gr.setAttribute("TileMode", "Tile");
2370 		gr.setAttribute("ViewboxUnits", "Absolute");
2371 		gr.setAttribute("ViewportUnits", "Absolute");
2372 		gr.setAttribute("Viewbox", QString("0, 0, %1, %2").arg(pa.width * conversionFactor).arg(pa.height * conversionFactor));
2373 		double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY;
2374 		item->maskTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY);
2375 		patternScaleX /= 100.0;
2376 		patternScaleY /= 100.0;
2377 		gr.setAttribute("Viewport", QString("%1, %2, %3, %4").arg((xOffset + patternOffsetX) * conversionFactor).arg((yOffset + patternOffsetY) * conversionFactor).arg((pa.width * patternScaleX) * conversionFactor).arg((pa.height * patternScaleY) * conversionFactor));
2378 		bool mirrorX, mirrorY;
2379 		item->maskFlip(mirrorX, mirrorY);
2380 		if ((patternRotation != 0) || (patternSkewX != 0) || (patternSkewY != 0) || mirrorX || mirrorY)
2381 		{
2382 			QTransform mpa;
2383 			mpa.rotate(patternRotation);
2384 			mpa.shear(-patternSkewX, patternSkewY);
2385 			mpa.scale(pa.scaleX, pa.scaleY);
2386 			if (mirrorX)
2387 				mpa.scale(-1, 1);
2388 			if (mirrorY)
2389 				mpa.scale(1, -1);
2390 			gr.setAttribute("Transform", MatrixToStr(mpa));
2391 		}
2392 		QDomElement grp = p_docu.createElement("VisualBrush.Visual");
2393 		for (int em = 0; em < pa.items.count(); ++em)
2394 		{
2395 			PageItem* embed = pa.items.at(em);
2396 			writeItemOnPage(embed->gXpos, embed->gYpos, embed, grp, rel_root);
2397 		}
2398 		gr.appendChild(grp);
2399 		ob.appendChild(gr);
2400 	}
2401 	parentElem.appendChild(ob);
2402 }
2403 
setColor(const QString & farbe,int shad,double transparency)2404 QString XPSExPlug::setColor(const QString& farbe, int shad, double transparency)
2405 {
2406 	if (farbe == CommonStrings::None)
2407 		return "#00FFFFFF";
2408 	const ScColor& col = m_Doc->PageColors[farbe];
2409 	QString color =  ScColorEngine::getShadeColorProof(col, m_Doc, shad).name().mid(1);
2410 	color = color.toUpper();
2411 	QString alpha = "";
2412 	alpha.setNum(qRound((1.0 - transparency) * 255), 16);
2413 	alpha = alpha.toUpper();
2414 	if (alpha.length() == 1)
2415 		alpha.prepend("0");
2416 	return "#" + alpha + color;
2417 }
2418 
setClipAttr(QDomElement & elem,FPointArray * ite,bool fillRule)2419 void XPSExPlug::setClipAttr(QDomElement &elem, FPointArray *ite, bool fillRule)
2420 {
2421 	QString pathStr = setClipPath(ite, true);
2422 	if (pathStr.length() > 0)
2423 	{
2424 		if (fillRule)
2425 			pathStr.prepend("F 0 ");
2426 		else
2427 			pathStr.prepend("F 1 ");
2428 		elem.setAttribute("Clip", pathStr);
2429 	}
2430 }
2431 
setClipPath(FPointArray * ite,bool closed)2432 QString XPSExPlug::setClipPath(FPointArray *ite, bool closed)
2433 {
2434 	QString tmp;
2435 	FPoint np, np1, np2, np3, np4, firstP;
2436 	bool nPath = true;
2437 	bool first = true;
2438 	if (ite->size() <= 3)
2439 		return tmp;
2440 
2441 	for (int poi=0; poi<ite->size()-3; poi += 4)
2442 	{
2443 		if (ite->isMarker(poi))
2444 		{
2445 			nPath = true;
2446 			continue;
2447 		}
2448 		if (nPath)
2449 		{
2450 			np = ite->point(poi);
2451 			if ((!first) && (closed) && (np4 == firstP))
2452 				tmp += "Z ";
2453 			tmp += QString("M%1,%2 ").arg(np.x()).arg(np.y());
2454 			nPath = false;
2455 			first = false;
2456 			firstP = np;
2457 			np4 = np;
2458 		}
2459 		np = ite->point(poi);
2460 		np1 = ite->point(poi+1);
2461 		np2 = ite->point(poi+3);
2462 		np3 = ite->point(poi+2);
2463 		if ((np == np1) && (np2 == np3))
2464 			tmp += QString("L%1,%2 ").arg(np3.x()).arg(np3.y());
2465 		else
2466 			tmp += QString("C%1,%2 %3,%4 %5,%6 ").arg(np1.x()).arg(np1.y()).arg(np2.x()).arg(np2.y()).arg(np3.x()).arg(np3.y());
2467 		np4 = np3;
2468 	}
2469 	if (closed)
2470 		tmp += "Z";
2471 	return tmp;
2472 }
2473 
writeDocRels()2474 void XPSExPlug::writeDocRels()
2475 {
2476 	// Create and write required "Documents/1/_rels/FixedDoc.fdoc.rels" file
2477 	QDomDocument doc("rels");
2478 	QString st = "<Relationships></Relationships>";
2479 	doc.setContent(st);
2480 	QDomElement root  = doc.documentElement();
2481 	root.setAttribute("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
2482 	doc.appendChild(root);
2483 	QFile ft(baseDir + "/Documents/1/_rels/FixedDoc.fdoc.rels");
2484 	if (ft.open(QIODevice::WriteOnly))
2485 	{
2486 		QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
2487 		QDataStream s(&ft);
2488 		vo += doc.toString();
2489 		QByteArray utf8wr = vo.toUtf8();
2490 		s.writeRawData(utf8wr.data(), utf8wr.length());
2491 		ft.close();
2492 	}
2493 }
2494 
writeCore()2495 void XPSExPlug::writeCore()
2496 {
2497 	// Create and write required "docProps/core.xml" file
2498 	QDomDocument doc("rels");
2499 	QString st = "<cp:coreProperties></cp:coreProperties>";
2500 	doc.setContent(st);
2501 	QDomElement root  = doc.documentElement();
2502 	root.setAttribute("xmlns:cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
2503 	root.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
2504 	root.setAttribute("xmlns:dcterms", "http://purl.org/dc/terms/");
2505 	root.setAttribute("xmlns:dcmitype", "http://purl.org/dc/dcmitype/");
2506 	root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2507 	QDomElement rel1 = doc.createElement("dc:creator");
2508 	rel1.setNodeValue("");
2509 	root.appendChild(rel1);
2510 	QDomElement rel2 = doc.createElement("dcterms:created");
2511 	rel2.setAttribute("xsi:type", "dcterms:W3CDTF");
2512 	rel2.setNodeValue("");
2513 	root.appendChild(rel2);
2514 	QDomElement rel3 = doc.createElement("dcterms:modified");
2515 	rel3.setNodeValue("");
2516 	rel3.setAttribute("xsi:type", "dcterms:W3CDTF");
2517 	root.appendChild(rel3);
2518 	doc.appendChild(root);
2519 	QFile ft(baseDir + "/docProps/core.xml");
2520 	if (ft.open(QIODevice::WriteOnly))
2521 	{
2522 		QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
2523 		QDataStream s(&ft);
2524 		vo += doc.toString();
2525 		QByteArray utf8wr = vo.toUtf8();
2526 		s.writeRawData(utf8wr.data(), utf8wr.length());
2527 		ft.close();
2528 	}
2529 }
2530 
writeContentType()2531 void XPSExPlug::writeContentType()
2532 {
2533 	// Create and write required "[Content_Type].xml" file
2534 	QDomDocument doc("Content_Type");
2535 	QString st = "<Types></Types>";
2536 	doc.setContent(st);
2537 	QDomElement root  = doc.documentElement();
2538 	root.setAttribute("xmlns", "http://schemas.openxmlformats.org/package/2006/content-types");
2539 	QDomElement rel1 = doc.createElement("Default");
2540 	rel1.setAttribute("Extension", "png");
2541 	rel1.setAttribute("ContentType", "image/png");
2542 	root.appendChild(rel1);
2543 	QDomElement rel2 = doc.createElement("Default");
2544 	rel2.setAttribute("Extension", "jpeg");
2545 	rel2.setAttribute("ContentType", "image/jpeg");
2546 	root.appendChild(rel2);
2547 	QDomElement rel3 = doc.createElement("Default");
2548 	rel3.setAttribute("Extension", "jpg");
2549 	rel3.setAttribute("ContentType", "image/jpeg");
2550 	root.appendChild(rel3);
2551 	QDomElement rel4 = doc.createElement("Default");
2552 	rel4.setAttribute("Extension", "rels");
2553 	rel4.setAttribute("ContentType", "application/vnd.openxmlformats-package.relationships+xml");
2554 	root.appendChild(rel4);
2555 	QDomElement rel5 = doc.createElement("Default");
2556 	rel5.setAttribute("Extension", "xml");
2557 	rel5.setAttribute("ContentType", "application/xml");
2558 	root.appendChild(rel5);
2559 	QDomElement rel6 = doc.createElement("Default");
2560 	rel6.setAttribute("Extension", "fdseq");
2561 	rel6.setAttribute("ContentType", "application/vnd.ms-package.xps-fixeddocumentsequence+xml");
2562 	root.appendChild(rel6);
2563 	QDomElement rel7 = doc.createElement("Default");
2564 	rel7.setAttribute("Extension", "fpage");
2565 	rel7.setAttribute("ContentType", "application/vnd.ms-package.xps-fixedpage+xml");
2566 	root.appendChild(rel7);
2567 	QDomElement rel8 = doc.createElement("Default");
2568 	rel8.setAttribute("Extension", "struct");
2569 	rel8.setAttribute("ContentType", "application/vnd.ms-package.xps-documentstructure+xml");
2570 	root.appendChild(rel8);
2571 	QDomElement rel9 = doc.createElement("Default");
2572 	rel9.setAttribute("Extension", "fdoc");
2573 	rel9.setAttribute("ContentType", "application/vnd.ms-package.xps-fixeddocument+xml");
2574 	root.appendChild(rel9);
2575 	QDomElement rel10 = doc.createElement("Default");
2576 	rel10.setAttribute("Extension", "odttf");
2577 	rel10.setAttribute("ContentType", "application/vnd.ms-package.obfuscated-opentype");
2578 	root.appendChild(rel10);
2579 	QDomElement rel11 = doc.createElement("Default");
2580 	rel11.setAttribute("Extension", "dict");
2581 	rel11.setAttribute("ContentType", "application/vnd.ms-package.xps-resourcedictionary+xml");
2582 	root.appendChild(rel11);
2583 	QDomElement rel12 = doc.createElement("Override");
2584 	rel12.setAttribute("PartName", "/docProps/core.xml");
2585 	rel12.setAttribute("ContentType", "application/vnd.openxmlformats-package.core-properties+xml");
2586 	root.appendChild(rel12);
2587 	doc.appendChild(root);
2588 	QFile ft(baseDir + "/[Content_Types].xml");
2589 	if (ft.open(QIODevice::WriteOnly))
2590 	{
2591 		QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
2592 		QDataStream s(&ft);
2593 		vo += doc.toString();
2594 		QByteArray utf8wr = vo.toUtf8();
2595 		s.writeRawData(utf8wr.data(), utf8wr.length());
2596 		ft.close();
2597 	}
2598 }
2599 
writeBaseRel()2600 void XPSExPlug::writeBaseRel()
2601 {
2602 	// Create and write required "_rels/.rels" file
2603 	QDomDocument doc("rels");
2604 	QString st = "<Relationships></Relationships>";
2605 	doc.setContent(st);
2606 	QDomElement root  = doc.documentElement();
2607 	root.setAttribute("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
2608 	QDomElement rel1 = doc.createElement("Relationship");
2609 	rel1.setAttribute("Id", "rID1");
2610 	rel1.setAttribute("Type", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties");
2611 	rel1.setAttribute("Target", "docProps/core.xml");
2612 	root.appendChild(rel1);
2613 	QDomElement rel2 = doc.createElement("Relationship");
2614 	rel2.setAttribute("Id", "rID2");
2615 	rel2.setAttribute("Type", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail");
2616 	rel2.setAttribute("Target", "docProps/thumbnail.jpeg");
2617 	root.appendChild(rel2);
2618 	QDomElement rel3 = doc.createElement("Relationship");
2619 	rel3.setAttribute("Id", "rID3");
2620 	rel3.setAttribute("Type", "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation");
2621 	rel3.setAttribute("Target", "FixedDocSeq.fdseq");
2622 	root.appendChild(rel3);
2623 	doc.appendChild(root);
2624 	QFile ft(baseDir + "/_rels/.rels");
2625 	if (ft.open(QIODevice::WriteOnly))
2626 	{
2627 		QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
2628 		QDataStream s(&ft);
2629 		vo += doc.toString();
2630 		QByteArray utf8wr = vo.toUtf8();
2631 		s.writeRawData(utf8wr.data(), utf8wr.length());
2632 		ft.close();
2633 	}
2634 }
2635 
FToStr(double c)2636 QString XPSExPlug::FToStr(double c)
2637 {
2638 	QString cc;
2639 	return cc.setNum(c);
2640 }
2641 
IToStr(int c)2642 QString XPSExPlug::IToStr(int c)
2643 {
2644 	QString cc;
2645 	return cc.setNum(c);
2646 }
2647 
MatrixToStr(QTransform & mat,double factor)2648 QString XPSExPlug::MatrixToStr(QTransform &mat, double factor)
2649 {
2650 	QString cc("%1, %2, %3, %4, %5, %6");
2651 	return  cc.arg(mat.m11()).arg(mat.m12()).arg(mat.m21()).arg(mat.m22()).arg(mat.dx() * factor).arg(mat.dy() * factor);
2652 }
2653 
MatrixToStr(QTransform & mat)2654 QString XPSExPlug::MatrixToStr(QTransform &mat)
2655 {
2656 	QString cc("%1, %2, %3, %4, %5, %6");
2657 	return  cc.arg(mat.m11()).arg(mat.m12()).arg(mat.m21()).arg(mat.m22()).arg(mat.dx()).arg(mat.dy());
2658 }
2659 
hex2int(char hex)2660 int XPSExPlug::hex2int(char hex)
2661 {
2662 	QChar hexchar = QLatin1Char(hex);
2663 	int v;
2664 	if (hexchar.isDigit())
2665 		v = hexchar.digitValue();
2666 	else if (hexchar >= QLatin1Char('A') && hexchar <= QLatin1Char('F'))
2667 		v = hexchar.cell() - 'A' + 10;
2668 	else if (hexchar >= QLatin1Char('a') && hexchar <= QLatin1Char('f'))
2669 		v = hexchar.cell() - 'a' + 10;
2670 	else
2671 		v = -1;
2672 	return v;
2673 }
2674 
checkForFallback(PageItem * item)2675 bool XPSExPlug::checkForFallback(PageItem *item)
2676 {
2677 	bool ret = false;
2678 	int GrType = item->GrType;
2679 	int GrMask = item->GrMask;
2680 	if ((GrType == Gradient_4Colors) || (GrType == Gradient_Diamond) || (GrType == Gradient_Mesh) || (GrType == Gradient_PatchMesh) || (GrType == Gradient_Conical))
2681 		ret = true;
2682 	if ((GrMask == GradMask_LinearLumAlpha) || (GrMask == GradMask_RadialLumAlpha) || (GrMask == GradMask_PatternLumAlpha) || (GrMask == GradMask_PatternLumAlphaInverted) || (GrMask == GradMask_PatternInverted))
2683 		ret = true;
2684 	if (item->fillBlendmode() != 0)
2685 		ret = true;
2686 	if (item->lineBlendmode() != 0)
2687 		ret = true;
2688 	if (item->hasSoftShadow())
2689 		ret = true;
2690 	return ret;
2691 }
2692 
~XPSExPlug()2693 XPSExPlug::~XPSExPlug()
2694 {
2695 }
2696