Rev 25069 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/
/***************************************************************************
-------------------
begin : Sat Oct 26 2013
copyright : (C) 2013 by Franz Schmid
email : Franz.Schmid@altmuehlnet.de
***************************************************************************/
#include <QByteArray>
#include <QCursor>
#include <QDrag>
#include <QFile>
#include <QList>
#include <QMimeData>
#include <QStack>
#include <QUrl>
#include <QDebug>
#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
#define _USE_MATH_DEFINES
#endif
#include <cstdlib>
#include <climits>
#include <limits>
#include <string>
#include "commonstrings.h"
#include "importxps.h"
#include "loadsaveplugin.h"
#include "pageitem_table.h"
#include "pagesize.h"
#include "prefscontext.h"
#include "prefsfile.h"
#include "prefsmanager.h"
#include "prefstable.h"
#include "rawimage.h"
#include "scclocale.h"
#include "sccolorengine.h"
#include "scconfig.h"
#include "scmimedata.h"
#include "scpaths.h"
#include "scribusXml.h"
#include "scribuscore.h"
#include "scribusdoc.h"
#include "scribusview.h"
#include "sctextstream.h"
#include "selection.h"
#include "third_party/zip/scribus_zip.h"
#include "ui/customfdialog.h"
#include "ui/missing.h"
#include "ui/multiprogressdialog.h"
#include "ui/propertiespalette.h"
#include "undomanager.h"
#include "util.h"
#include "util_formats.h"
#include "util_math.h"
#include "xpsimportoptions.h"
XpsPlug::XpsPlug(ScribusDoc* doc, int flags)
{
tmpSel = new Selection(this, false);
m_Doc = doc;
importerFlags = flags;
interactive = (flags & LoadSavePlugin::lfInteractive);
}
QImage XpsPlug::readThumbnail(const QString& fName)
{
QImage tmp;
if (!QFile::exists(fName))
return QImage();
progressDialog = nullptr;
uz = new ScZipHandler();
if (!uz->open(fName))
{
delete uz;
if (progressDialog)
progressDialog->close();
return QImage();
}
bool found = false;
if (uz->contains("_rels/.rels"))
{
QByteArray f;
QDomDocument designMapDom;
if (!uz->read("_rels/.rels", f))
return QImage();
if (designMapDom.setContent(f))
{
QDomElement docElem = designMapDom.documentElement();
for (QDomElement drawPag = docElem.firstChildElement(); !drawPag.isNull(); drawPag = drawPag.nextSiblingElement())
{
if (drawPag.tagName() != "Relationship")
continue;
if ((!drawPag.hasAttribute("Target")) || (!drawPag.hasAttribute("Type")))
continue;
if (drawPag.attribute("Type") != "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
continue;
QString thumbRef = drawPag.attribute("Target");
if (thumbRef.startsWith("/"))
thumbRef = thumbRef.mid(1);
QByteArray im;
if (uz->read(thumbRef, im))
{
tmp = QImage::fromData(im);
found = true;
break;
}
}
}
}
if (!found)
{
QFileInfo fi(fName);
baseFile = QDir::cleanPath(QDir::toNativeSeparators(fi.absolutePath()+"/"));
docWidth = PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth;
docHeight = PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight;
m_Doc = new ScribusDoc();
m_Doc->setup(0, 1, 1, 1, 1, "Custom", "Custom");
m_Doc->setPage(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false);
m_Doc->addPage(0);
m_Doc->setGUI(false, ScCore->primaryMainWindow(), nullptr);
baseX = m_Doc->currentPage()->xOffset();
baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
Elements.clear();
m_Doc->setLoading(true);
m_Doc->DoDrawing = false;
m_Doc->scMW()->setScriptRunning(true);
QString CurDirP = QDir::currentPath();
QDir::setCurrent(fi.path());
importedColors.clear();
importedPatterns.clear();
conversionFactor = 72.0 / 96.0;
loadedFonts.clear();
linkTargets.clear();
linkSources.clear();
if (uz->contains("FixedDocSeq.fdseq"))
parseDocSequence("FixedDocSeq.fdseq");
else if (uz->contains("FixedDocumentSequence.fdseq"))
parseDocSequence("FixedDocumentSequence.fdseq");
if (Elements.count() > 0)
{
tmpSel->clear();
QDir::setCurrent(CurDirP);
if (Elements.count() > 1)
m_Doc->groupObjectsList(Elements);
m_Doc->DoDrawing = true;
m_Doc->m_Selection->delaySignalsOn();
if (Elements.count() > 0)
{
for (int dre=0; dre<Elements.count(); ++dre)
{
tmpSel->addItem(Elements.at(dre), true);
}
tmpSel->setGroupRect();
double xs = tmpSel->width();
double ys = tmpSel->height();
tmp = Elements.at(0)->DrawObj_toImage(500);
tmp.setText("XSize", QString("%1").arg(xs));
tmp.setText("YSize", QString("%1").arg(ys));
}
m_Doc->scMW()->setScriptRunning(false);
m_Doc->setLoading(false);
m_Doc->m_Selection->delaySignalsOff();
delete m_Doc;
}
else
{
QDir::setCurrent(CurDirP);
m_Doc->DoDrawing = true;
m_Doc->scMW()->setScriptRunning(false);
delete m_Doc;
}
}
uz->close();
delete uz;
return tmp;
}
bool XpsPlug::import(const QString& fNameIn, const TransactionSettings& trSettings, int flags, bool showProgress)
{
bool success = false;
interactive = (flags & LoadSavePlugin::lfInteractive);
importerFlags = flags;
cancel = false;
bool ret = false;
firstPage = true;
pagecount = 1;
QFileInfo fi = QFileInfo(fNameIn);
m_FileName = fi.fileName();
if ( !ScCore->usingGUI() )
{
interactive = false;
showProgress = false;
}
baseFile = QDir::cleanPath(QDir::toNativeSeparators(fi.absolutePath()+"/"));
if ( showProgress )
{
ScribusMainWindow* mw = (m_Doc==nullptr) ? ScCore->primaryMainWindow() : m_Doc->scMW();
progressDialog = new MultiProgressDialog( tr("Importing: %1").arg(fi.fileName()), CommonStrings::tr_Cancel, mw );
QStringList barNames, barTexts;
barNames << "GI";
barTexts << tr("Analyzing File:");
QList<bool> barsNumeric;
barsNumeric << false;
progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
progressDialog->setOverallTotalSteps(3);
progressDialog->setOverallProgress(0);
progressDialog->setProgress("GI", 0);
progressDialog->show();
connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
QApplication::processEvents();
}
else
progressDialog = nullptr;
if (progressDialog)
{
progressDialog->setOverallProgress(1);
QApplication::processEvents();
}
/* Set default Page to size defined in Preferences */
docWidth = PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth;
docHeight = PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight;
baseX = 0;
baseY = 0;
if (!interactive || (flags & LoadSavePlugin::lfInsertPage))
{
m_Doc->setPage(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false);
m_Doc->addPage(0);
m_Doc->view()->addPage(0, true);
baseX = 0;
baseY = 0;
}
else
{
if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
{
m_Doc=ScCore->primaryMainWindow()->doFileNew(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
ScCore->primaryMainWindow()->HaveNewDoc();
ret = true;
baseX = 0;
baseY = 0;
baseX = m_Doc->currentPage()->xOffset();
baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
}
}
if (!ret && interactive)
{
baseX = m_Doc->currentPage()->xOffset();
baseY = m_Doc->currentPage()->yOffset() + m_Doc->currentPage()->height() / 2.0;
}
if (ret || !interactive)
{
if (docWidth > docHeight)
m_Doc->setPageOrientation(1);
else
m_Doc->setPageOrientation(0);
m_Doc->setPageSize("Custom");
}
if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
m_Doc->view()->deselectItems();
Elements.clear();
m_Doc->setLoading(true);
m_Doc->DoDrawing = false;
if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
m_Doc->view()->updatesOn(false);
m_Doc->scMW()->setScriptRunning(true);
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
QString CurDirP = QDir::currentPath();
QDir::setCurrent(fi.path());
if (convert(fNameIn))
{
tmpSel->clear();
QDir::setCurrent(CurDirP);
if ((Elements.count() > 1) && (!(importerFlags & LoadSavePlugin::lfCreateDoc)))
m_Doc->groupObjectsList(Elements);
m_Doc->DoDrawing = true;
m_Doc->scMW()->setScriptRunning(false);
m_Doc->setLoading(false);
QApplication::changeOverrideCursor(QCursor(Qt::ArrowCursor));
if ((Elements.count() > 0) && (!ret) && (interactive))
{
if (flags & LoadSavePlugin::lfScripted)
{
bool loadF = m_Doc->isLoading();
m_Doc->setLoading(false);
m_Doc->changed();
m_Doc->setLoading(loadF);
if (!(flags & LoadSavePlugin::lfLoadAsPattern))
{
m_Doc->m_Selection->delaySignalsOn();
for (int dre=0; dre<Elements.count(); ++dre)
{
m_Doc->m_Selection->addItem(Elements.at(dre), true);
}
m_Doc->m_Selection->delaySignalsOff();
m_Doc->m_Selection->setGroupRect();
if (m_Doc->view() != nullptr)
m_Doc->view()->updatesOn(true);
}
}
else
{
m_Doc->DragP = true;
m_Doc->DraggedElem = nullptr;
m_Doc->DragElements.clear();
m_Doc->m_Selection->delaySignalsOn();
for (int dre=0; dre<Elements.count(); ++dre)
{
tmpSel->addItem(Elements.at(dre), true);
}
tmpSel->setGroupRect();
ScElemMimeData* md = ScriXmlDoc::writeToMimeData(m_Doc, tmpSel);
m_Doc->itemSelection_DeleteItem(tmpSel);
m_Doc->view()->updatesOn(true);
if ((importedColors.count() != 0) && (!((flags & LoadSavePlugin::lfKeepGradients) || (flags & LoadSavePlugin::lfKeepColors) || (flags & LoadSavePlugin::lfKeepPatterns))))
{
for (int cd = 0; cd < importedColors.count(); cd++)
{
m_Doc->PageColors.remove(importedColors[cd]);
}
}
if ((importedPatterns.count() != 0) && (!(flags & LoadSavePlugin::lfKeepPatterns)))
{
for (int cd = 0; cd < importedPatterns.count(); cd++)
{
m_Doc->docPatterns.remove(importedPatterns[cd]);
}
}
m_Doc->m_Selection->delaySignalsOff();
// We must copy the TransationSettings object as it is owned
// by handleObjectImport method afterwards
TransactionSettings* transacSettings = new TransactionSettings(trSettings);
m_Doc->view()->handleObjectImport(md, transacSettings);
m_Doc->DragP = false;
m_Doc->DraggedElem = nullptr;
m_Doc->DragElements.clear();
}
}
else
{
m_Doc->changed();
m_Doc->reformPages();
if (!(flags & LoadSavePlugin::lfLoadAsPattern))
m_Doc->view()->updatesOn(true);
}
success = true;
}
else
{
QDir::setCurrent(CurDirP);
m_Doc->DoDrawing = true;
m_Doc->scMW()->setScriptRunning(false);
if (!(flags & LoadSavePlugin::lfLoadAsPattern))
m_Doc->view()->updatesOn(true);
QApplication::changeOverrideCursor(QCursor(Qt::ArrowCursor));
success = false;
}
if (interactive)
m_Doc->setLoading(false);
//CB If we have a gui we must refresh it if we have used the progressbar
if (!(flags & LoadSavePlugin::lfLoadAsPattern))
{
if (showProgress && !interactive)
m_Doc->view()->DrawNew();
}
QApplication::restoreOverrideCursor();
return success;
}
XpsPlug::~XpsPlug()
{
delete progressDialog;
delete tmpSel;
for (int a = 0; a < tempFontFiles.count(); a++)
{
QFile::remove(tempFontFiles[a]);
}
}
bool XpsPlug::convert(const QString& fn)
{
bool retVal = true;
importedColors.clear();
importedPatterns.clear();
conversionFactor = 72.0 / 96.0;
loadedFonts.clear();
linkTargets.clear();
linkSources.clear();
pathResources.clear();
if (progressDialog)
{
progressDialog->setOverallProgress(2);
progressDialog->setLabel("GI", tr("Generating Items"));
QApplication::processEvents();
}
uz = new ScZipHandler();
if (!uz->open(fn))
{
delete uz;
if (progressDialog)
progressDialog->close();
return false;
}
retVal = false;
if (uz->contains("FixedDocSeq.fdseq"))
retVal = parseDocSequence("FixedDocSeq.fdseq");
else if (uz->contains("FixedDocumentSequence.fdseq"))
retVal = parseDocSequence("FixedDocumentSequence.fdseq");
if (retVal)
resolveLinks();
uz->close();
delete uz;
if (progressDialog)
progressDialog->close();
return retVal;
}
bool XpsPlug::parseDocSequence(const QString& designMap)
{
QByteArray f;
QDomDocument designMapDom;
if (!uz->read(designMap, f))
return false;
if (!designMapDom.setContent(f))
return false;
bool parsed = false;
QString DocumentReference;
QDomElement docElem = designMapDom.documentElement();
for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
{
QDomElement dpg = drawPag.toElement();
if (dpg.tagName() == "DocumentReference")
{
if (dpg.hasAttribute("Source"))
{
DocumentReference = dpg.attribute("Source", "");
if (DocumentReference.startsWith("/"))
DocumentReference = DocumentReference.mid(1);
parsed = parseDocReference(DocumentReference);
if (!parsed) break;
}
}
}
return parsed;
}
bool XpsPlug::parseDocReference(const QString& designMap)
{
QByteArray f;
QFileInfo fi(designMap);
QString path = fi.path();
if (!uz->read(designMap, f))
return false;
QDomDocument designMapDom;
if (!designMapDom.setContent(f))
return false;
QString PageReference;
QDomElement docElem = designMapDom.documentElement();
if (importerFlags & LoadSavePlugin::lfCreateThumbnail)
{
QDomNodeList pgList = docElem.childNodes();
QDomNode drawPag = pgList.item(0);
QDomElement dpg = drawPag.toElement();
if (dpg.tagName() == "PageContent")
{
if (dpg.hasAttribute("Source"))
{
PageReference = dpg.attribute("Source", "");
if (PageReference.startsWith("/"))
{
PageReference = PageReference.mid(1);
parsePageReference(PageReference);
}
else
{
if (!PageReference.startsWith(path))
{
PageReference = path + "/" + PageReference;
PageReference = QDir::cleanPath(PageReference);
}
parsePageReference(PageReference);
}
}
}
}
else
{
std::vector<int> pageNs;
QString pageString = "*";
int pgCount = 0;
int maxPages = docElem.childNodes().count();
if ((interactive || (importerFlags & LoadSavePlugin::lfCreateDoc)) && (maxPages > 1))
{
if (progressDialog)
progressDialog->hide();
QApplication::changeOverrideCursor(QCursor(Qt::ArrowCursor));
XpsImportOptions optImp(ScCore->primaryMainWindow());
optImp.setUpOptions(m_FileName, 1, maxPages, interactive);
if (optImp.exec() != QDialog::Accepted)
return false;
pageString = optImp.getPagesString();
QApplication::changeOverrideCursor(QCursor(Qt::WaitCursor));
if (progressDialog)
progressDialog->show();
QApplication::processEvents();
}
parsePagesString(pageString, &pageNs, maxPages);
if (pageString != "*")
maxPages = pageNs.size();
if (progressDialog)
{
progressDialog->setTotalSteps("GI", maxPages);
progressDialog->setProgress("GI", pgCount);
QApplication::processEvents();
}
QDomNodeList pgList = docElem.childNodes();
for (uint ap = 0; ap < pageNs.size(); ++ap)
{
QDomNode drawPag = pgList.item(pageNs[ap] - 1);
QDomElement dpg = drawPag.toElement();
if (dpg.tagName() == "PageContent")
{
if (dpg.hasAttribute("Source"))
{
PageReference = dpg.attribute("Source", "");
if (PageReference.startsWith("/"))
{
PageReference = PageReference.mid(1);
parsePageReference(PageReference);
}
else
{
if (!PageReference.startsWith(path))
{
PageReference = path + "/" + PageReference;
PageReference = QDir::cleanPath(PageReference);
}
parsePageReference(PageReference);
}
}
}
pgCount++;
if (progressDialog)
{
progressDialog->setProgress("GI", pgCount);
qApp->processEvents();
}
}
}
return true;
}
void XpsPlug::parsePageReference(const QString& designMap)
{
QByteArray f;
QFileInfo fi(designMap);
QString path = fi.path();
if (!uz->read(designMap, f))
return;
QDomDocument designMapDom;
if (!designMapDom.setContent(f))
return;
QDomElement docElem = designMapDom.documentElement();
docWidth = docElem.attribute("Width", QString("%1").arg(PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth)).toDouble() * conversionFactor;
docHeight = docElem.attribute("Height", QString("%1").arg(PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight)).toDouble() * conversionFactor;
if (importerFlags & LoadSavePlugin::lfCreateDoc)
{
if (firstPage)
{
topMargin = m_Doc->marginsVal().top();
leftMargin = m_Doc->marginsVal().left();
rightMargin = m_Doc->marginsVal().right();
bottomMargin = m_Doc->marginsVal().bottom();
double pgCols = m_Doc->PageSp;
double pgGap = m_Doc->PageSpa;
m_Doc->setPage(docWidth, docHeight, topMargin, leftMargin, rightMargin, bottomMargin, pgCols, pgGap, false, false);
m_Doc->setPageSize("Custom");
m_Doc->currentPage()->setSize("Custom");
m_Doc->currentPage()->setInitialHeight(docHeight);
m_Doc->currentPage()->setInitialWidth(docWidth);
m_Doc->currentPage()->setHeight(docHeight);
m_Doc->currentPage()->setWidth(docWidth);
m_Doc->currentPage()->initialMargins.setTop(topMargin);
m_Doc->currentPage()->initialMargins.setBottom(bottomMargin);
m_Doc->currentPage()->initialMargins.setLeft(leftMargin);
m_Doc->currentPage()->initialMargins.setRight(rightMargin);
m_Doc->reformPages(true);
}
else
{
m_Doc->addPage(pagecount);
m_Doc->currentPage()->setSize("Custom");
m_Doc->currentPage()->setInitialHeight(docHeight);
m_Doc->currentPage()->setInitialWidth(docWidth);
m_Doc->currentPage()->setHeight(docHeight);
m_Doc->currentPage()->setWidth(docWidth);
m_Doc->currentPage()->initialMargins.setTop(topMargin);
m_Doc->currentPage()->initialMargins.setBottom(bottomMargin);
m_Doc->currentPage()->initialMargins.setLeft(leftMargin);
m_Doc->currentPage()->initialMargins.setRight(rightMargin);
m_Doc->currentPage()->setMasterPageNameNormal();
m_Doc->view()->addPage(pagecount, true);
pagecount++;
}
}
firstPage = false;
baseX = m_Doc->currentPage()->xOffset();
baseY = m_Doc->currentPage()->yOffset();
for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
{
QDomElement dpg = drawPag.toElement();
if ((dpg.tagName() == "Path") || (dpg.tagName() == "Glyphs") || (dpg.tagName() == "Canvas"))
{
PageItem* item = parseObjectXML(dpg, path);
if (item != nullptr)
{
m_Doc->Items->append(item);
Elements.append(item);
}
}
else if (dpg.tagName() == "FixedPage.Resources")
{
for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
{
QDomElement spe = sp.toElement();
if (spe.tagName() != "ResourceDictionary")
continue;
if (spe.hasAttribute("Source"))
{
QString resFile = spe.attribute("Source", "");
if (resFile.startsWith("/"))
{
resFile = resFile.mid(1);
parseResourceFile(resFile);
}
else
{
if (!resFile.startsWith(path))
{
resFile = path + "/" + resFile;
resFile = QDir::cleanPath(resFile);
}
parseResourceFile(resFile);
}
}
else if (spe.hasChildNodes())
{
for (QDomElement dpgp = spe.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
{
if (dpgp.tagName() == "PathGeometry")
{
Coords.resize(0);
Coords.svgInit();
QString pdata;
QString key(dpg.attribute("x:Key"));
if (dpg.hasAttribute("Figures"))
pdata = dpg.attribute("Figures");
else if (dpg.hasChildNodes())
pdata = parsePathGeometryXML(dpg);
if (!pdata.isEmpty())
{
bool currentPathClosed = Coords.parseSVG(pdata);
Coords.scale(conversionFactor, conversionFactor);
QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
if (dpg.attribute("FillRule") == "NonZero")
path.setFillRule(Qt::WindingFill);
pathResources.insert(key, path);
}
}
}
}
}
}
}
}
PageItem* XpsPlug::parseObjectXML(QDomElement &dpg, const QString& path)
{
PageItem *retObj = nullptr;
ObjState obState;
obState.currentPath = QPainterPath();
obState.clipPath = QPainterPath();
obState.transform = QTransform();
obState.CurrColorFill = CommonStrings::None;
obState.fillOpacity = 0.0;
obState.CurrColorStroke = CommonStrings::None;
obState.strokeOpacity = 0.0;
obState.LineW = 1.0;
obState.fillGradientTyp = 0;
obState.gradientScale = 1.0;
obState.maskTyp = 0;
obState.strokeTyp = 0;
obState.imagePath = "";
obState.itemType = 0;
obState.patternName = "";
obState.patternMask = "";
obState.CapStyle = Qt::FlatCap;
obState.JoinStyle = Qt::MiterJoin;
obState.DashOffset = 0;
obState.DashPattern.clear();
QString itemName;
QString itemTarget;
if (dpg.hasAttribute("Name"))
itemName = dpg.attribute("Name");
if (dpg.hasAttribute("FixedPage.NavigateUri"))
{
QString iTg = dpg.attribute("FixedPage.NavigateUri");
QStringList tg = iTg.split("#");
if (tg.count() > 1)
itemTarget = tg[1];
}
if (dpg.hasAttribute("RenderTransform"))
{
QString trans = dpg.attribute("RenderTransform", "1 0 0 1 0 0");
trans.replace(",", " ");
ScTextStream list(&trans, QIODevice::ReadOnly);
double a, b, c, d, e, f;
list >> a >> b >> c >> d >> e >> f;
obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
}
if (dpg.hasAttribute("Fill"))
obState.CurrColorFill = handleColor(dpg.attribute("Fill", "#FF000000"), obState.fillOpacity);
if (dpg.hasAttribute("Stroke"))
obState.CurrColorStroke = handleColor(dpg.attribute("Stroke", "#FF000000"), obState.strokeOpacity);
if (dpg.hasAttribute("Opacity"))
{
double opacity = dpg.attribute("Opacity", "1.0").toDouble();
obState.fillOpacity = 1.0 - ((1.0 - obState.fillOpacity) * opacity);
obState.strokeOpacity = 1.0 - ((1.0 - obState.strokeOpacity) * opacity);
}
if (dpg.hasAttribute("StrokeThickness"))
obState.LineW = dpg.attribute("StrokeThickness", "1.0").toDouble() * conversionFactor;
if (dpg.hasAttribute("StrokeDashCap"))
{
if (dpg.attribute("StrokeDashCap") == "Flat")
obState.CapStyle = Qt::FlatCap;
else if (dpg.attribute("StrokeDashCap") == "Round")
obState.CapStyle = Qt::RoundCap;
else if (dpg.attribute("StrokeDashCap") == "Square")
obState.CapStyle = Qt::SquareCap;
else
obState.CapStyle = Qt::FlatCap;
}
if (dpg.hasAttribute("StrokeLineJoin"))
{
if (dpg.attribute("StrokeLineJoin") == "Miter")
obState.JoinStyle = Qt::MiterJoin;
else if (dpg.attribute("StrokeLineJoin") == "Round")
obState.JoinStyle = Qt::RoundJoin;
else if (dpg.attribute("StrokeLineJoin") == "Bevel")
obState.JoinStyle = Qt::BevelJoin;
else
obState.JoinStyle = Qt::MiterJoin;
}
if (dpg.hasAttribute("StrokeDashOffset"))
obState.DashOffset = dpg.attribute("StrokeDashOffset", "0.0").toDouble();
if (dpg.hasAttribute("StrokeDashArray"))
{
QString trans = dpg.attribute("StrokeDashArray", "");
if (!trans.isEmpty())
{
ScTextStream list(&trans, QIODevice::ReadOnly);
while (!list.atEnd())
{
double d;
list >> d;
obState.DashPattern.append(d);
}
}
}
if (dpg.hasAttribute("Clip"))
{
QString cdata = dpg.attribute("Clip");
if (!cdata.startsWith("{"))
{
Coords.resize(0);
Coords.svgInit();
Coords.parseSVG(cdata);
Coords.scale(conversionFactor, conversionFactor);
obState.clipPath = Coords.toQPainterPath(true);
obState.clipPath = obState.transform.map(obState.clipPath);
}
else
{
cdata.remove("{StaticResource ");
cdata.remove("}");
cdata = cdata.trimmed();
if (pathResources.contains(cdata))
obState.clipPath = obState.transform.map(pathResources[cdata]);
}
}
if (dpg.tagName() == "Glyphs")
{
if (dpg.hasChildNodes())
{
for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
{
QDomElement spe = sp.toElement();
if (spe.tagName() == "Glyphs.Fill")
parseFillXML(spe, path, obState);
else if (spe.tagName() == "Glyphs.OpacityMask")
parseOpacityXML(spe, path, obState);
else if (spe.tagName() == "Glyphs.Clip")
parsePathDataXML(spe, obState, true);
}
}
QString utfString = dpg.attribute("UnicodeString");
if (utfString.startsWith("{}"))
utfString = utfString.mid(2);
QString Indices = dpg.attribute("Indices");
QString tstStr = utfString.trimmed();
if (tstStr.isEmpty() && Indices.isEmpty())
return retObj;
QString fontUrl = dpg.attribute("FontUri");
if (fontUrl.startsWith("/"))
{
fontUrl = fontUrl.mid(1);
}
else
{
if (!fontUrl.startsWith(path))
{
fontUrl = path + "/" + fontUrl;
fontUrl = QDir::cleanPath(fontUrl);
}
}
ScFace iteFont = loadFontByName(fontUrl);
if (iteFont.usable())
{
double fontSize = dpg.attribute("FontRenderingEmSize", "0").toDouble() * conversionFactor;
bool bold = false;
bool italic = false;
if (dpg.hasAttribute("StyleSimulations"))
{
bold = dpg.attribute("StyleSimulations") == "BoldSimulation";
italic = dpg.attribute("StyleSimulations") == "ItalicSimulation";
if (dpg.attribute("StyleSimulations") == "BoldItalicSimulation")
{
bold = true;
italic = true;
}
}
double fontSizeEM = fontSize;
if (fontSize > 1)
{
double xPos = dpg.attribute("OriginX", "0").toDouble() * conversionFactor;
double yPos = dpg.attribute("OriginY", "0").toDouble() * conversionFactor;
QPointF origin(xPos, yPos);
QStringList glIndices = Indices.split(";");
double asc = iteFont.ascent() - iteFont.descent();
fontSize /= asc;
int glInd = 0;
if (!utfString.isEmpty())
{
std::u32string ucs4 = utfString.toStdU32String();
// FIXME HOST: this code does not do any text layout!
for (size_t sti = 0; sti < ucs4.length(); ++sti)
{
char32_t chr = ucs4.at(sti);
QString utfChar = QString::fromUcs4(&chr, 1);
uint gl = iteFont.char2CMap(chr);
if (gl != 0)
{
double adv = iteFont.glyphWidth(gl, fontSize);
double offX = 0;
double offY = 0;
FPointArray pts;
if (Indices.isEmpty())
{
if (!QChar::isSpace(chr))
pts = iteFont.glyphOutline(gl, fontSize);
}
else
{
if (glInd < glIndices.count())
{
QStringList glyInd = glIndices[glInd].split(",");
if (glyInd.count() > 1)
{
if (glyInd.count() > 2)
offX = glyInd[2].toDouble() * fontSizeEM / 100.0;
if (glyInd.count() > 3)
offY = glyInd[3].toDouble() * fontSizeEM / 100.0;
if (!glyInd[1].isEmpty())
adv = glyInd[1].toDouble() * fontSizeEM / 100.0;
}
if (glyInd.count() > 0)
{
if (!glyInd[0].isEmpty())
{
if (glyInd[0].startsWith("("))
{
qsizetype r = glyInd[0].indexOf(")");
QString comb = glyInd[0].mid(1, r - 1);
QStringList combInd = comb.split(":");
int advUtf = combInd[0].toInt() - 1;
sti += advUtf;
glyInd[0].remove(0, r+1);
}
uint gli = glyInd[0].toUInt();
pts = iteFont.glyphOutline(gli, fontSize);
if (((glyInd.count() > 1) && (glyInd[1].isEmpty())) || (glyInd.count() == 1))
adv = iteFont.glyphWidth(gli, fontSize);
}
else if (!QChar::isSpace(chr))
pts = iteFont.glyphOutline(gl, fontSize);
}
}
}
if (pts.count() > 3)
{
QTransform cht;
cht.scale(0.1, 0.1);
pts.map(cht);
pts.translate(0, -fontSize);
if (bold)
{
QTransform bot;
bot.scale(1.02, 1.02);
pts.map(bot);
adv *= 1.02;
}
if (italic)
{
QTransform bot;
bot.shear(-0.3491, 0);
pts.map(bot);
}
pts.translate(origin.x() + offX, origin.y() + offY);
obState.currentPath.addPath(pts.toQPainterPath(true));
}
origin += QPointF(adv, 0);
}
else
{
QFont eFont(iteFont.family());
eFont.setPointSizeF(fontSize);
eFont.setBold(bold);
eFont.setItalic(italic);
QFontMetricsF ft(eFont);
obState.currentPath.addText(origin, eFont, utfChar);
origin += QPointF(ft.horizontalAdvance(utfChar), 0);
}
glInd++;
}
}
else if (!Indices.isEmpty())
{
double adv = 0;
double offX = 0;
double offY = 0;
for (int glInd = 0; glInd < glIndices.count(); glInd++)
{
FPointArray pts;
QStringList glyInd = glIndices[glInd].split(",");
if (glyInd.count() > 1)
{
if (glyInd.count() > 2)
offX = glyInd[2].toDouble() * fontSizeEM / 100.0;
if (glyInd.count() > 3)
offY = glyInd[3].toDouble() * fontSizeEM / 100.0;
if (!glyInd[1].isEmpty())
adv = glyInd[1].toDouble() * fontSizeEM / 100.0;
}
if (glyInd.count() > 0)
{
if (!glyInd[0].isEmpty())
{
if (glyInd[0].startsWith("("))
{
qsizetype r = glyInd[0].indexOf(")");
QString comb = glyInd[0].mid(1, r - 1);
QStringList combInd = comb.split(":");
int advUtf = combInd[0].toInt() - 1;
glInd += advUtf;
glyInd[0].remove(0, r+1);
}
uint gli = glyInd[0].toUInt();
pts = iteFont.glyphOutline(gli, fontSize);
if (((glyInd.count() > 1) && (glyInd[1].isEmpty())) || (glyInd.count() == 1))
adv = iteFont.glyphWidth(gli, fontSize);
}
}
if (pts.count() > 3)
{
QTransform cht;
cht.scale(0.1, 0.1);
pts.map(cht);
pts.translate(0, -fontSize);
if (bold)
{
QTransform bot;
bot.scale(1.02, 1.02);
pts.map(bot);
adv *= 1.02;
}
if (italic)
{
QTransform bot;
bot.shear(-0.3491, 0);
pts.map(bot);
}
pts.translate(origin.x() + offX, origin.y() + offY);
obState.currentPath.addPath(pts.toQPainterPath(true));
}
origin += QPointF(adv, 0);
}
}
if (!obState.currentPath.isEmpty())
{
obState.currentPath = obState.transform.map(obState.currentPath);
retObj = createItem(dpg, obState);
if (!obState.clipPath.isEmpty())
retObj = addClip(retObj, obState);
}
}
}
}
else if (dpg.tagName() == "Path")
{
bool pathFromChild = false;
if (dpg.hasChildNodes())
{
for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
{
QDomElement spe = sp.toElement();
if (spe.tagName() == "Path.Fill")
parseFillXML(spe, path, obState);
else if (spe.tagName() == "Path.OpacityMask")
parseOpacityXML(spe, path, obState);
else if (spe.tagName() == "Path.Stroke")
parseStrokeXML(spe, path, obState);
else if (spe.tagName() == "Path.RenderTransform")
{
for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
{
QDomElement eog = obg.toElement();
if (eog.tagName() == "MatrixTransform")
{
QString trans = eog.attribute("Matrix", "1 0 0 1 0 0");
trans.replace(",", " ");
ScTextStream list(&trans, QIODevice::ReadOnly);
double a, b, c, d, e, f;
list >> a >> b >> c >> d >> e >> f;
obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
}
}
}
else if (spe.tagName() == "Path.Data")
{
parsePathDataXML(spe, obState);
pathFromChild = true;
}
else if (spe.tagName() == "Path.Clip")
parsePathDataXML(spe, obState, true);
}
}
if (!pathFromChild)
{
QString pdata = dpg.attribute("Data");
if (!pdata.startsWith("{"))
{
Coords.resize(0);
Coords.svgInit();
obState.currentPathClosed = Coords.parseSVG(pdata);
Coords.scale(conversionFactor, conversionFactor);
obState.currentPath = Coords.toQPainterPath(!obState.currentPathClosed);
if (obState.currentPath.isEmpty())
return retObj;
obState.currentPath = obState.transform.map(obState.currentPath);
}
else
{
pdata.remove("{StaticResource ");
pdata.remove("}");
pdata = pdata.trimmed();
if (pathResources.contains(pdata))
obState.currentPath = obState.transform.map(pathResources[pdata]);
if (obState.currentPath.isEmpty())
return retObj;
}
}
if (obState.currentPath.isEmpty())
return retObj;
retObj = createItem(dpg, obState);
if (!obState.clipPath.isEmpty())
retObj = addClip(retObj, obState);
}
else if (dpg.tagName() == "Canvas")
{
QList<PageItem*> GElements;
for (QDomNode sp = dpg.firstChild(); !sp.isNull(); sp = sp.nextSibling())
{
QDomElement spe = sp.toElement();
if (spe.tagName() == "Canvas.RenderTransform")
{
for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
{
QDomElement eog = obg.toElement();
if (eog.tagName() == "MatrixTransform")
{
QString trans = eog.attribute("Matrix", "1 0 0 1 0 0");
trans.replace(",", " ");
ScTextStream list(&trans, QIODevice::ReadOnly);
double a, b, c, d, e, f;
list >> a >> b >> c >> d >> e >> f;
obState.transform = QTransform(a, b, c, d, e * conversionFactor, f * conversionFactor);
}
}
}
else if ((spe.tagName() == "Path") || (spe.tagName() == "Glyphs") || (spe.tagName() == "Canvas"))
{
PageItem* ite = parseObjectXML(spe, path);
if (ite != nullptr)
GElements.append(ite);
}
else if (spe.tagName() == "Canvas.OpacityMask")
parseOpacityXML(spe, path, obState);
else if (spe.tagName() == "Canvas.Resources")
{
for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
{
QDomElement eog = obg.toElement();
if (eog.tagName() == "ResourceDictionary")
{
if (eog.hasAttribute("Source"))
{
QString resFile = eog.attribute("Source", "");
if (resFile.startsWith("/"))
{
resFile = resFile.mid(1);
parseResourceFile(resFile);
}
else
{
if (!resFile.startsWith(path))
{
resFile = path + "/" + resFile;
resFile = QDir::cleanPath(resFile);
}
parseResourceFile(resFile);
}
}
else if (eog.hasChildNodes())
{
for (QDomElement dpgp = eog.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
{
if (dpgp.tagName() == "PathGeometry")
{
Coords.resize(0);
Coords.svgInit();
QString pdata;
QString key = dpg.attribute("x:Key");
if (dpg.hasAttribute("Figures"))
pdata = dpg.attribute("Figures");
else if (dpg.hasChildNodes())
pdata = parsePathGeometryXML(dpg);
if (!pdata.isEmpty())
{
bool currentPathClosed = Coords.parseSVG(pdata);
Coords.scale(conversionFactor, conversionFactor);
QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
if (dpg.attribute("FillRule") == "NonZero")
path.setFillRule(Qt::WindingFill);
pathResources.insert(key, path);
}
}
}
}
}
}
}
else if (spe.tagName() == "Canvas.Clip")
parsePathDataXML(spe, obState, true);
}
if (GElements.count() > 0)
{
double minx = std::numeric_limits<double>::max();
double miny = std::numeric_limits<double>::max();
double maxx = -std::numeric_limits<double>::max();
double maxy = -std::numeric_limits<double>::max();
for (int ep = 0; ep < GElements.count(); ++ep)
{
PageItem* currItem = GElements.at(ep);
double x1, x2, y1, y2;
currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2);
minx = qMin(minx, x1);
miny = qMin(miny, y1);
maxx = qMax(maxx, x2);
maxy = qMax(maxy, y2);
}
double gx = minx;
double gy = miny;
double gw = maxx - minx;
double gh = maxy - miny;
int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, gx, gy, gw, gh, 0, CommonStrings::None, CommonStrings::None);
if (z >= 0)
{
retObj = m_Doc->Items->at(z);
retObj->ClipEdited = true;
retObj->FrameType = 3;
retObj->setFillEvenOdd(false);
retObj->OldB2 = retObj->width();
retObj->OldH2 = retObj->height();
retObj->updateClip();
m_Doc->groupObjectsToItem(retObj, GElements);
double scX = 1.0;
double scY = 1.0;
double rot = 0.0;
double dx = 0.0;
double dy = 0.0;
getTransformValuesFromMatrix( obState.transform, scX, scY, rot, dx, dy);
QLineF transp = QLineF(0, 0, retObj->xPos() - m_Doc->currentPage()->xOffset(), retObj->yPos() - m_Doc->currentPage()->yOffset());
transp = obState.transform.map(transp);
retObj->setXYPos(transp.p2().x() + m_Doc->currentPage()->xOffset(), transp.p2().y() + m_Doc->currentPage()->yOffset());
if ((scX != 1.0) || (scY != 1.0))
retObj->PoLine.scale(scX, scY);
FPoint wh = getMaxClipF(&retObj->PoLine);
retObj->setWidthHeight(wh.x(),wh.y());
m_Doc->adjustItemSize(retObj, true);
retObj->OldB2 = retObj->width();
retObj->OldH2 = retObj->height();
if (obState.maskTyp != 0)
{
double xp = retObj->xPos() - m_Doc->currentPage()->xOffset();
double yp = retObj->yPos() - m_Doc->currentPage()->yOffset();
retObj->setMaskGradient(obState.gradientMask);
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);
retObj->setMaskType(obState.maskTyp);
}
if (rot != 0)
retObj->setRotation(-rot, true);
retObj->OwnPage = m_Doc->OnPage(retObj);
m_Doc->GroupOnPage(retObj);
m_Doc->Items->removeLast();
if (!obState.clipPath.isEmpty())
retObj = addClip(retObj, obState);
}
}
}
if (retObj != nullptr)
{
if (!itemName.isEmpty())
{
linkTargets.insert(itemName, retObj);
retObj->setItemName(itemName);
}
if ((!itemTarget.isEmpty()) && (dpg.tagName() != "Canvas"))
{
linkSources.insert(retObj, itemTarget);
retObj->setIsAnnotation(true);
retObj->annotation().setType(Annotation::Link);
retObj->annotation().setZiel(m_Doc->currentPage()->pageNr());
retObj->annotation().setAction("0 0");
retObj->setTextFlowMode(PageItem::TextFlowDisabled);
}
}
return retObj;
}
void XpsPlug::parseOpacityXML(QDomElement &spe, const QString& path, ObjState &obState)
{
ObjState opaState;
opaState.CurrColorFill = CommonStrings::None;
opaState.fillOpacity = 0.0;
opaState.fillGradientTyp = 0;
opaState.gradientScale = 1.0;
opaState.imagePath = "";
opaState.patternName = "";
parseFillXML(spe, path, opaState);
if (opaState.fillGradientTyp != 0)
{
obState.gradientMask = opaState.currentGradient;
obState.maskStart = opaState.gradientStart;
obState.maskEnd = opaState.gradientEnd;
obState.maskFocus = opaState.gradientFocus;
obState.maskScale = opaState.gradientScale;
obState.maskTyp = opaState.fillGradientTyp == 6 ? 1 : 3;
}
if (!opaState.patternName.isEmpty())
{
obState.patternMask = opaState.patternName;
obState.maskTyp = 3;
}
}
void XpsPlug::parseStrokeXML(QDomElement &spe, const QString& path, ObjState &obState)
{
ObjState opaState;
opaState.CurrColorFill = CommonStrings::None;
opaState.fillOpacity = 0.0;
opaState.fillGradientTyp = 0;
opaState.gradientScale = 1.0;
opaState.imagePath = "";
opaState.patternName = "";
parseFillXML(spe, path, opaState);
if (opaState.fillGradientTyp != 0)
{
obState.gradientStroke = opaState.currentGradient;
obState.strokeStart = opaState.gradientStart;
obState.strokeEnd = opaState.gradientEnd;
obState.strokeFocus = opaState.gradientFocus;
obState.strokeScale = opaState.gradientScale;
obState.strokeTyp = opaState.fillGradientTyp;
}
if (!opaState.patternName.isEmpty())
obState.patternStroke = opaState.patternName;
}
void XpsPlug::parseFillXML(QDomElement &spe, const QString& path, ObjState &obState)
{
for (QDomNode obg = spe.firstChild(); !obg.isNull(); obg = obg.nextSibling())
{
QDomElement eog = obg.toElement();
if (eog.tagName() == "SolidColorBrush")
obState.CurrColorFill = handleColor(eog.attribute("Color", "#FF000000"), obState.fillOpacity);
else if (eog.tagName() == "ImageBrush")
{
obState.imagePath = eog.attribute("ImageSource", "");
if (obState.imagePath.startsWith("/"))
{
obState.imagePath = obState.imagePath.mid(1);
}
else
{
if (!obState.imagePath.startsWith(path))
{
obState.imagePath = path + "/" + obState.imagePath;
obState.imagePath = QDir::cleanPath(obState.imagePath);
}
}
if (eog.hasAttribute("Opacity"))
{
double opacity = eog.attribute("Opacity", "1.0").toDouble();
obState.fillOpacity = 1.0 - ((1.0 - obState.fillOpacity) * opacity);
obState.strokeOpacity = 1.0 - ((1.0 - obState.strokeOpacity) * opacity);
}
obState.itemType = 1;
}
else if (eog.tagName() == "LinearGradientBrush")
{
obState.currentGradient = VGradient(VGradient::linear);
obState.currentGradient.clearStops();
double gen_opacity = 1.0;
if (eog.hasAttribute("Opacity"))
gen_opacity = eog.attribute("Opacity", "1.0").toDouble();
for (QDomNode gr = eog.firstChild(); !gr.isNull(); gr = gr.nextSibling())
{
QDomElement grs = gr.toElement();
if (grs.tagName() == "LinearGradientBrush.GradientStops")
{
for (QDomNode spo = grs.firstChild(); !spo.isNull(); spo = spo.nextSibling())
{
QDomElement eo = spo.toElement();
if (eo.tagName() == "GradientStop")
{
double opacity = 1.0;
QString stopName = handleColor(eo.attribute("Color", "#FF000000"), opacity);
double stopOffset = eo.attribute("Offset", "0.0").toDouble();
const ScColor& gradC = m_Doc->PageColors[stopName];
obState.currentGradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), stopOffset, 0.5, 1.0 - (opacity * gen_opacity), stopName, 100);
}
}
}
}
QString vectS = eog.attribute("StartPoint", "0,0");
vectS.replace(",", " ");
ScTextStream list(&vectS, QIODevice::ReadOnly);
double x, y;
list >> x >> y;
obState.gradientStart = QPointF(x * conversionFactor, y * conversionFactor);
obState.gradientFocus = obState.gradientStart;
QString vectE = eog.attribute("EndPoint", "0,0");
vectE.replace(",", " ");
ScTextStream listE(&vectE, QIODevice::ReadOnly);
double x2, y2;
listE >> x2 >> y2;
obState.gradientEnd = QPointF(x2 * conversionFactor, y2 * conversionFactor);
obState.gradientScale = 1.0;
obState.fillGradientTyp = 6;
}
else if (eog.tagName() == "RadialGradientBrush")
{
obState.currentGradient = VGradient(VGradient::radial);
obState.currentGradient.clearStops();
double gen_opacity = 1.0;
if (eog.hasAttribute("Opacity"))
gen_opacity = eog.attribute("Opacity", "1.0").toDouble();
for (QDomElement grs = eog.firstChildElement(); !grs.isNull(); grs = grs.nextSiblingElement())
{
if (grs.tagName() == "RadialGradientBrush.GradientStops")
{
for (QDomElement eo = grs.firstChildElement(); !eo.isNull(); eo = eo.nextSiblingElement())
{
if (eo.tagName() == "GradientStop")
{
double opacity = 1.0;
QString stopName = handleColor(eo.attribute("Color", "#FF000000"), opacity);
double stopOffset = eo.attribute("Offset", "0.0").toDouble();
const ScColor& gradC = m_Doc->PageColors[stopName];
obState.currentGradient.addStop(ScColorEngine::getRGBColor(gradC, m_Doc), stopOffset, 0.5, 1.0 - (opacity * gen_opacity), stopName, 100);
}
}
}
}
QString vectS = eog.attribute("Center", "0,0");
vectS.replace(",", " ");
ScTextStream list(&vectS, QIODevice::ReadOnly);
double x, y;
list >> x >> y;
obState.gradientStart = QPointF(x * conversionFactor, y * conversionFactor);
QString vectE = eog.attribute("GradientOrigin", "0,0");
vectE.replace(",", " ");
ScTextStream listE(&vectE, QIODevice::ReadOnly);
double x2, y2;
listE >> x2 >> y2;
obState.gradientFocus = QPointF(x2 * conversionFactor, y2 * conversionFactor);
double rx = eog.attribute("RadiusX", "1").toDouble() * conversionFactor;
double ry = eog.attribute("RadiusY", "1").toDouble() * conversionFactor;
obState.gradientEnd = QPointF(obState.gradientStart.x() + rx, obState.gradientStart.y());
obState.gradientScale = rx / ry;
obState.fillGradientTyp = 7;
}
else if (eog.tagName() == "VisualBrush")
{
QString viewb = eog.attribute("Viewbox", "0,0,1,1");
viewb.replace(",", " ");
ScTextStream listb(&viewb, QIODevice::ReadOnly);
double Viewbox_x1, Viewbox_y1, Viewbox_x2, Viewbox_y2;
listb >> Viewbox_x1 >> Viewbox_y1 >> Viewbox_x2 >> Viewbox_y2;
QString vectE = eog.attribute("Viewport", "0,0,1,1");
vectE.replace(",", " ");
ScTextStream listE(&vectE, QIODevice::ReadOnly);
double Viewport_x1, Viewport_y1, Viewport_x2, Viewport_y2;
listE >> Viewport_x1 >> Viewport_y1 >> Viewport_x2 >> Viewport_y2;
double vw = (Viewport_x2 * conversionFactor) / (Viewbox_x2 - Viewbox_x1);
double vh = (Viewport_y2 * conversionFactor) / (Viewbox_y2 - Viewbox_y1);
for (QDomElement grs = eog.firstChildElement(); !grs.isNull(); grs = grs.nextSiblingElement())
{
if (grs.tagName() != "VisualBrush.Visual")
continue;
for (QDomElement eo = grs.firstChildElement(); !eo.isNull(); eo = eo.nextSiblingElement())
{
if ((eo.tagName() != "Path") && (eo.tagName() != "Glyphs") && (eo.tagName() != "Canvas"))
continue;
PageItem* item = parseObjectXML(eo, path);
if (!item)
continue;
m_Doc->sizeItem((item->width() / conversionFactor) * vw, (item->height() / conversionFactor) * vh, item, false, true, false);
ScPattern pat(m_Doc);
m_Doc->DoDrawing = true;
QImage tmpImg = item->DrawObj_toImage(qMin(qMax(item->width(), item->height()), 500.0));
if (tmpImg.isNull())
continue;
QImage retImg = QImage(qRound(Viewport_x2 * conversionFactor), qRound(Viewport_y2 * conversionFactor), QImage::Format_ARGB32_Premultiplied);
retImg.fill( qRgba(0, 0, 0, 0) );
QPainter p;
p.begin(&retImg);
p.drawImage(0, 0, tmpImg);
p.end();
pat.pattern = retImg;
pat.xoffset = 0;
pat.yoffset = 0;
m_Doc->DoDrawing = false;
pat.width = Viewport_x2 * conversionFactor;
pat.height = Viewport_y2 * conversionFactor;
item->gXpos = 0;
item->gYpos = 0;
item->setXYPos(item->gXpos, item->gYpos, true);
pat.items.append(item);
obState.patternName = QString("Pattern_from_XPS_%1").arg(m_Doc->docPatterns.count() + 1);
m_Doc->addPattern(obState.patternName, pat);
importedPatterns.append(obState.patternName);
}
}
}
}
}
void XpsPlug::parsePathDataXML(QDomElement &spe, ObjState &obState, bool forClip)
{
Coords.resize(0);
Coords.svgInit();
QString svgString;
bool windFill = false;
for (QDomElement dpgp = spe.firstChildElement(); !dpgp.isNull(); dpgp = dpgp.nextSiblingElement())
{
if (dpgp.tagName() == "PathGeometry")
svgString += parsePathGeometryXML(dpgp);
if (dpgp.attribute("FillRule") == "NonZero")
windFill = true;
}
bool currentPathClosed = Coords.parseSVG(svgString);
Coords.scale(conversionFactor, conversionFactor);
if (forClip)
{
obState.clipPath = Coords.toQPainterPath(!currentPathClosed);
if (windFill)
obState.clipPath.setFillRule(Qt::WindingFill);
}
else
{
obState.currentPathClosed = currentPathClosed;
obState.currentPath = Coords.toQPainterPath(!obState.currentPathClosed);
if (windFill)
obState.currentPath.setFillRule(Qt::WindingFill);
}
}
QString XpsPlug::parsePathGeometryXML(QDomElement &spe)
{
QString svgString;
for (QDomElement dpg = spe.firstChildElement(); !dpg.isNull(); dpg = dpg.nextSiblingElement())
{
if (dpg.tagName() != "PathFigure")
continue;
if (dpg.hasAttribute("StartPoint"))
svgString += "M " + dpg.attribute("StartPoint") + " ";
for (QDomElement dp = dpg.firstChildElement(); !dp.isNull(); dp = dp.nextSiblingElement())
{
if (dp.tagName() == "PolyLineSegment")
svgString += "L " + dp.attribute("Points") + " ";
else if (dp.tagName() == "PolyQuadraticBezierSegment")
svgString += "Q " + dp.attribute("Points") + " ";
else if (dp.tagName() == "PolyBezierSegment")
svgString += "C " + dp.attribute("Points") + " ";
else if (dp.tagName() == "ArcSegment")
{
svgString += "A " + dp.attribute("Size") + " " + dp.attribute("RotationAngle") + " ";
if (dp.hasAttribute("IsLargeArc"))
{
if (dp.attribute("IsLargeArc").toLower() == "true")
svgString += "1 ";
else
svgString += "0 ";
}
else
svgString += "0 ";
if (dp.hasAttribute("SweepDirection"))
{
if (dp.attribute("SweepDirection").toLower() == "counterclockwise")
svgString += "0 ";
else
svgString += "1 ";
}
else
svgString += "0 ";
svgString += dp.attribute("Point") + " ";
}
}
if (dpg.hasAttribute("IsClosed") && (dpg.attribute("IsClosed").toLower() == "true"))
svgString += "Z ";
}
return svgString;
}
void XpsPlug::parseResourceFile(const QString& resFile)
{
QByteArray f;
if (!uz->read(resFile, f))
return;
QDomDocument designMapDom;
if (!designMapDom.setContent(f))
return;
QDomElement docElem = designMapDom.documentElement();
for (QDomNode drawPag = docElem.firstChild(); !drawPag.isNull(); drawPag = drawPag.nextSibling())
{
QDomElement dpg = drawPag.toElement();
if (dpg.tagName() != "PathGeometry")
continue;
Coords.resize(0);
Coords.svgInit();
QString pdata;
QString key(dpg.attribute("x:Key"));
if (dpg.hasAttribute("Figures"))
pdata = dpg.attribute("Figures");
else if (dpg.hasChildNodes())
pdata = parsePathGeometryXML(dpg);
if (!pdata.isEmpty())
{
bool currentPathClosed = Coords.parseSVG(pdata);
Coords.scale(conversionFactor, conversionFactor);
QPainterPath path = Coords.toQPainterPath(!currentPathClosed);
if (dpg.attribute("FillRule") == "NonZero")
path.setFillRule(Qt::WindingFill);
pathResources.insert(key, path);
}
}
}
void XpsPlug::resolveLinks()
{
if (linkSources.isEmpty())
return;
for (auto it = linkSources.begin(); it != linkSources.end(); ++it)
{
PageItem* linkS = it.key();
QString target = it.value();
if (!linkTargets.contains(target))
continue;
PageItem* linkT = linkTargets[target];
if (!linkT)
continue;
int op = linkT->OwnPage;
if (op < 0)
continue;
QTransform tf = linkT->getTransform();
double xp = tf.dx() - m_Doc->Pages->at(op)->xOffset();
double yp = tf.dy() - m_Doc->Pages->at(op)->yOffset();
linkS->annotation().setZiel(linkT->OwnPage);
linkS->annotation().setActionType(2);
linkS->annotation().setAction(QString("%0 %1").arg(qRound(xp)).arg(qRound(m_Doc->Pages->at(op)->height() - yp)));
}
}
PageItem* XpsPlug::addClip(PageItem* retObj, ObjState &obState)
{
if (obState.clipPath.isEmpty())
return retObj;
int z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, baseX, baseY, 10, 10, 0, CommonStrings::None, CommonStrings::None);
PageItem *itemg = m_Doc->Items->at(z);
itemg->PoLine.fromQPainterPath(obState.clipPath);
FPoint wh = getMaxClipF(&itemg->PoLine);
itemg->setWidthHeight(wh.x(),wh.y());
m_Doc->adjustItemSize(itemg, true);
itemg->ClipEdited = true;
itemg->FrameType = 3;
itemg->setFillEvenOdd(false);
itemg->OldB2 = itemg->width();
itemg->OldH2 = itemg->height();
itemg->updateClip();
itemg->OwnPage = m_Doc->OnPage(itemg);
itemg->ContourLine = itemg->PoLine.copy();
QList<PageItem*> GElements;
GElements.append(retObj);
m_Doc->groupObjectsToItem(itemg, GElements);
m_Doc->resizeGroupToContents(itemg);
m_Doc->GroupOnPage(itemg);
retObj = itemg;
m_Doc->Items->removeLast();
return retObj;
}
PageItem* XpsPlug::createItem(QDomElement &dpg, ObjState &obState)
{
int z = -1;
PageItem* retObj = nullptr;
if (obState.currentPath.isEmpty())
return nullptr;
if (obState.itemType == 0)
{
if (dpg.hasAttribute("FixedPage.NavigateUri"))
z = m_Doc->itemAdd(PageItem::TextFrame, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, CommonStrings::None);
else
{
if (!obState.currentPathClosed)
z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
else
z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
}
retObj = m_Doc->Items->at(z);
finishItem(retObj, obState);
retObj = m_Doc->Items->takeAt(z);
}
else if (obState.itemType == 1)
{
z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, baseX, baseY, 10, 10, obState.LineW, obState.CurrColorFill, obState.CurrColorStroke);
retObj = m_Doc->Items->at(z);
finishItem(retObj, obState);
if (!obState.imagePath.isEmpty())
{
QByteArray f;
if (uz->read(obState.imagePath, f))
{
QFileInfo fi(obState.imagePath);
QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_xps_XXXXXX." + fi.suffix());
tempFile->setAutoRemove(false);
if (tempFile->open())
{
QString fileName = getLongPathName(tempFile->fileName());
if (!fileName.isEmpty())
{
tempFile->write(f);
tempFile->close();
retObj->isInlineImage = true;
retObj->isTempFile = true;
retObj->AspectRatio = false;
retObj->ScaleType = false;
m_Doc->loadPict(fileName, retObj);
retObj->adjustPictScale();
}
}
delete tempFile;
}
}
retObj = m_Doc->Items->takeAt(z);
}
return retObj;
}
void XpsPlug::finishItem(PageItem* item, ObjState &obState)
{
item->PoLine.fromQPainterPath(obState.currentPath, !obState.currentPathClosed);
FPoint wh = getMaxClipF(&item->PoLine);
item->setWidthHeight(wh.x(),wh.y());
m_Doc->adjustItemSize(item, true);
item->ClipEdited = true;
item->FrameType = 3;
item->setFillEvenOdd(false);
item->OldB2 = item->width();
item->OldH2 = item->height();
item->updateClip();
item->OwnPage = m_Doc->OnPage(item);
item->ContourLine = item->PoLine.copy();
item->setFillColor(obState.CurrColorFill);
item->setFillTransparency(obState.fillOpacity);
item->setLineColor(obState.CurrColorStroke);
item->setLineTransparency(obState.strokeOpacity);
item->setLineWidth(obState.LineW);
item->setLineEnd(obState.CapStyle);
item->setLineJoin(obState.JoinStyle);
double xp = item->xPos() - m_Doc->currentPage()->xOffset();
double yp = item->yPos() - m_Doc->currentPage()->yOffset();
if (obState.fillGradientTyp != 0)
{
item->fill_gradient = obState.currentGradient;
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);
item->setGradientType(obState.fillGradientTyp);
}
else if (!obState.patternName.isEmpty())
{
item->setPattern(obState.patternName);
item->GrType = Gradient_Pattern;
}
if (obState.maskTyp != 0)
{
item->setMaskGradient(obState.gradientMask);
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);
item->setMaskType(obState.maskTyp);
}
if (!obState.patternMask.isEmpty())
{
item->setPatternMask(obState.patternMask);
item->setMaskType(obState.maskTyp);
}
if (obState.strokeTyp != 0)
{
item->setStrokeGradient(obState.gradientStroke);
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);
item->setStrokeGradientType(obState.strokeTyp);
}
if (!obState.patternStroke.isEmpty())
{
item->GrTypeStroke = Gradient_Pattern;
item->setStrokePattern(obState.patternStroke);
}
if (!obState.DashPattern.isEmpty())
{
item->setDashOffset(obState.DashOffset);
QVector<double> pattern(obState.DashPattern.count());
for (int i = 0; i < obState.DashPattern.count(); ++i)
{
pattern[i] = obState.DashPattern[i] * obState.LineW;
}
item->setDashes(pattern);
}
}
QString XpsPlug::handleColor(QString rgbColor, double &opacity)
{
QString fNam = CommonStrings::None;
QString alpha = "FF";
if (rgbColor.startsWith( "sc#" ))
{
QColor c;
rgbColor.remove(0,3);
QStringList co = rgbColor.split(",");
if (co.size() == 3)
{
rgbColor.replace(",", " ");
ScTextStream list(&rgbColor, QIODevice::ReadOnly);
double r, g, b;
list >> r >> g >> b;
c.setRgbF(r, g, b);
}
else if (co.size() == 4)
{
rgbColor.replace(",", " ");
ScTextStream list(&rgbColor, QIODevice::ReadOnly);
double r, g, b;
list >> opacity >> r >> g >> b;
c.setRgbF(r, g, b);
}
else
{
opacity = 0;
return fNam;
}
ScColor tmp;
tmp.fromQColor(c);
tmp.setSpotColor(false);
tmp.setRegistrationColor(false);
QString newColorName = "FromXPS"+c.name();
fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
if (fNam == newColorName)
importedColors.append(newColorName);
}
else if (rgbColor.startsWith( "#" ))
{
QColor c;
if (rgbColor.length() == 9)
{
alpha = rgbColor.mid(1,2);
bool ok;
int hex = alpha.toInt(&ok, 16);
opacity = 1.0 - (hex / 255.0);
rgbColor.remove(1,2);
}
else
opacity = 0;
c.setNamedColor(rgbColor);
ScColor tmp;
tmp.fromQColor(c);
tmp.setSpotColor(false);
tmp.setRegistrationColor(false);
QString newColorName = "FromXPS"+c.name();
fNam = m_Doc->PageColors.tryAddColor(newColorName, tmp);
if (fNam == newColorName)
importedColors.append(newColorName);
}
return fNam;
}
int XpsPlug::hex2int(char hex)
{
QChar hexchar = QLatin1Char(hex);
int v;
if (hexchar.isDigit())
v = hexchar.digitValue();
else if (hexchar >= QLatin1Char('A') && hexchar <= QLatin1Char('F'))
v = hexchar.cell() - 'A' + 10;
else if (hexchar >= QLatin1Char('a') && hexchar <= QLatin1Char('f'))
v = hexchar.cell() - 'a' + 10;
else
v = -1;
return v;
}
bool XpsPlug::parseGUID( const QString &guidString, unsigned short guid[16])
{
if (guidString.length() <= 35)
return false;
// Maps bytes to positions in guidString
const static int indexes[] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34};
for (int i = 0; i < 16; i++)
{
int hex1 = hex2int(guidString[indexes[i]].cell());
int hex2 = hex2int(guidString[indexes[i]+1].cell());
if ((hex1 < 0) || (hex2 < 0))
return false;
guid[i] = hex1 * 16 + hex2;
}
return true;
}
ScFace XpsPlug::loadFontByName(const QString &fileName)
{
ScFace t;
if (loadedFonts.contains(fileName))
return loadedFonts[fileName];
QByteArray fontData;
if (!uz->read(fileName, fontData))
return t;
QTemporaryFile *tempImageFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_zip_XXXXXX.dat");
if (tempImageFile == nullptr)
return t;
tempImageFile->setAutoRemove(false);
tempImageFile->open();
QString fname = getLongPathName(tempImageFile->fileName());
tempImageFile->close();
delete tempImageFile;
tempFontFiles.append(fname);
QFileInfo fi(fileName);
QString ext = fi.suffix().toLower();
if (ext.startsWith("od"))
{
const QString baseName = fi.baseName();
unsigned short guid[16];
if (!parseGUID(baseName, guid))
return t;
if (fontData.length() < 32)
{
qDebug() << "Font file is too small";
return t;
}
// Obfuscation - xor bytes in font binary with bytes from guid (font's filename)
const static int mapping[] = {15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3};
for (int i = 0; i < 16; ++i)
{
fontData[i] = fontData[i] ^ guid[mapping[i]];
fontData[i+16] = fontData[i + 16] ^ guid[mapping[i]];
}
}
QFile ft(fname);
if (ft.open(QIODevice::WriteOnly))
{
ft.write(fontData);
ft.close();
t = PrefsManager::instance().appPrefs.fontPrefs.AvailFonts.loadScalableFont(fname);
loadedFonts.insert(fileName, t);
return t;
}
return t;
}