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