1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7
8 #include <QByteArray>
9 #include <QCursor>
10 #include <QDebug>
11 #include <QDrag>
12 #include <QFile>
13 #include <QList>
14 #include <QMessageBox>
15 #include <QMimeData>
16 #include <QRegExp>
17 #include <QStack>
18
19 #include <cstdlib>
20
21 #include "importps.h"
22
23
24 #include "commonstrings.h"
25 #include "loadsaveplugin.h"
26 #include "prefscontext.h"
27 #include "prefsfile.h"
28 #include "prefsmanager.h"
29 #include "prefstable.h"
30 #include "scclocale.h"
31 #include "scconfig.h"
32 #include "scmimedata.h"
33 #include "scpaths.h"
34 #include "scribusXml.h"
35 #include "scribuscore.h"
36 #include "scribusdoc.h"
37 #include "scribusview.h"
38 #include "sctextstream.h"
39 #include "selection.h"
40 #include "ui/customfdialog.h"
41 #include "ui/multiprogressdialog.h"
42 #include "ui/propertiespalette.h"
43 #include "ui/scmessagebox.h"
44 #include "undomanager.h"
45 #include "util.h"
46 #include "util_color.h"
47 #include "util_formats.h"
48 #include "util_math.h"
49
50 #ifdef HAVE_PODOFO
51 #include <podofo/podofo.h>
52 #endif
53
54
55 extern SCRIBUS_API ScribusQApp * ScQApp;
56
EPSPlug(ScribusDoc * doc,int flags)57 EPSPlug::EPSPlug(ScribusDoc* doc, int flags)
58 {
59 tmpSel = new Selection(this, false);
60 m_Doc = doc;
61 progressDialog = nullptr;
62 interactive = (flags & LoadSavePlugin::lfInteractive);
63 }
64
import(QString fName,const TransactionSettings & trSettings,int flags,bool showProgress)65 bool EPSPlug::import(QString fName, const TransactionSettings &trSettings, int flags, bool showProgress)
66 {
67 #ifdef Q_OS_MACOS
68 #if QT_VERSION >= 0x050300
69 showProgress = false;
70 #endif
71 #endif
72
73 bool success = false;
74 interactive = (flags & LoadSavePlugin::lfInteractive);
75 cancel = false;
76 double x, y, b, h;
77 bool ret = false;
78 bool found = false;
79 CustColors.clear();
80 QFileInfo fi = QFileInfo(fName);
81 QString ext = fi.suffix().toLower();
82 if ( !ScCore->usingGUI() ) {
83 interactive = false;
84 showProgress = false;
85 }
86 if ( showProgress )
87 {
88 ScribusMainWindow* mw=(m_Doc==0) ? ScCore->primaryMainWindow() : m_Doc->scMW();
89 progressDialog = new MultiProgressDialog( tr("Importing: %1").arg(fi.fileName()), CommonStrings::tr_Cancel, mw);
90 QStringList barNames, barTexts;
91 barNames << "GI";
92 barTexts << tr("Analyzing PostScript:");
93 QList<bool> barsNumeric;
94 barsNumeric << false;
95 progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
96 progressDialog->setOverallTotalSteps(3);
97 progressDialog->setOverallProgress(0);
98 progressDialog->setProgress("GI", 0);
99 progressDialog->show();
100 connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
101 qApp->processEvents();
102 }
103 else {
104 progressDialog = nullptr;
105 }
106
107 /* Set default Page to size defined in Preferences */
108 x = 0.0;
109 y = 0.0;
110 b = PrefsManager::instance()->appPrefs.docSetupPrefs.pageWidth;
111 h = PrefsManager::instance()->appPrefs.docSetupPrefs.pageHeight;
112 if (extensionIndicatesEPSorPS(ext))
113 {
114 QString tmp, BBox, tmp2, FarNam;
115 ScColor cc;
116 QFile f(fName);
117 if (f.open(QIODevice::ReadOnly))
118 {
119 /* Try to find Bounding Box */
120 QDataStream ts(&f);
121 while (!ts.atEnd())
122 {
123 tmp = readLinefromDataStream(ts);
124 if (tmp.startsWith("%%BoundingBox:"))
125 {
126 found = true;
127 BBox = tmp.remove("%%BoundingBox:");
128 }
129 if (!found)
130 {
131 if (tmp.startsWith("%%BoundingBox"))
132 {
133 found = true;
134 BBox = tmp.remove("%%BoundingBox");
135 }
136 }
137 if (tmp.startsWith("%%EndComments"))
138 break;
139 }
140 f.close();
141 if (found)
142 {
143 QStringList bb = BBox.split(" ", QString::SkipEmptyParts);
144 if (bb.count() == 4)
145 {
146 x = ScCLocale::toDoubleC(bb[0]);
147 y = ScCLocale::toDoubleC(bb[1]);
148 b = ScCLocale::toDoubleC(bb[2]);
149 h = ScCLocale::toDoubleC(bb[3]);
150 }
151 }
152 }
153 importColorsFromFile(fName, CustColors);
154 }
155 #ifdef HAVE_PODOFO
156 else if (extensionIndicatesPDF(ext))
157 {
158 try
159 {
160 PoDoFo::PdfError::EnableDebug( false );
161 #if (PODOFO_VERSION == 0 && PODOFO_MINOR > 6)
162 PoDoFo::PdfError::EnableLogging( false );
163 #endif
164 #if (PODOFO_VERSION == 0 && PODOFO_MINOR == 5 && PODOFO_REVISION == 99) || PODOFO_MINOR > 5
165 PoDoFo::PdfMemDocument doc( fName.toLocal8Bit().data() );
166 #else
167 PoDoFo::PdfDocument doc( fName.toLocal8Bit().data() );
168 #endif
169 PoDoFo::PdfPage *curPage = doc.GetPage(0);
170 if (curPage != nullptr)
171 {
172 PoDoFo::PdfRect rect = curPage->GetMediaBox();
173 b = rect.GetWidth() - rect.GetLeft();
174 h = rect.GetHeight() - rect.GetBottom();
175 }
176 }
177 catch(PoDoFo::PdfError& e)
178 {
179 qDebug("%s", "PoDoFo error while reading page size!");
180 e.PrintErrorMsg();
181 }
182 }
183 #endif
184 baseX = 0;
185 baseY = 0;
186 if (!interactive || (flags & LoadSavePlugin::lfInsertPage))
187 {
188 m_Doc->setPage(b-x, h-y, 0, 0, 0, 0, 0, 0, false, false);
189 m_Doc->addPage(0);
190 m_Doc->view()->addPage(0, true);
191 baseX = 0;
192 baseY = 0;
193 }
194 else
195 {
196 if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
197 {
198 m_Doc=ScCore->primaryMainWindow()->doFileNew(b-x, h-y, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
199 ScCore->primaryMainWindow()->HaveNewDoc();
200 ret = true;
201 baseX = 0;
202 baseY = 0;
203 }
204 }
205 if ((!ret) && (interactive))
206 {
207 baseX = m_Doc->currentPage()->xOffset();
208 baseY = m_Doc->currentPage()->yOffset();
209 }
210 if ((ret) || (!interactive))
211 {
212 if (b-x > h-y)
213 m_Doc->setPageOrientation(1);
214 else
215 m_Doc->setPageOrientation(0);
216 m_Doc->setPageSize("Custom");
217 }
218 ColorList::Iterator it;
219 for (it = CustColors.begin(); it != CustColors.end(); ++it)
220 {
221 if (!m_Doc->PageColors.contains(it.key()))
222 m_Doc->PageColors.insert(it.key(), it.value());
223 }
224 boundingBoxRect.addRect(0, 0, b-x, h-y);
225 Elements.clear();
226 m_Doc->setLoading(true);
227 m_Doc->DoDrawing = false;
228 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
229 m_Doc->view()->updatesOn(false);
230 m_Doc->scMW()->setScriptRunning(true);
231 qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
232 QString CurDirP = QDir::currentPath();
233 QDir::setCurrent(fi.path());
234 if (convert(fName, x, y, b, h))
235 {
236 // m_Doc->m_Selection->clear();
237 tmpSel->clear();
238 QDir::setCurrent(CurDirP);
239 // if ((Elements.count() > 1) && (interactive))
240 if (Elements.count() > 1)
241 m_Doc->groupObjectsList(Elements);
242 m_Doc->DoDrawing = true;
243 m_Doc->scMW()->setScriptRunning(false);
244 m_Doc->setLoading(false);
245 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
246 if ((Elements.count() > 0) && (!ret) && (interactive))
247 {
248 if (flags & LoadSavePlugin::lfScripted)
249 {
250 bool loadF = m_Doc->isLoading();
251 m_Doc->setLoading(false);
252 m_Doc->changed();
253 m_Doc->setLoading(loadF);
254 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
255 {
256 m_Doc->m_Selection->delaySignalsOn();
257 for (int dre=0; dre<Elements.count(); ++dre)
258 {
259 m_Doc->m_Selection->addItem(Elements.at(dre), true);
260 }
261 m_Doc->m_Selection->delaySignalsOff();
262 m_Doc->m_Selection->setGroupRect();
263 m_Doc->view()->updatesOn(true);
264 }
265 }
266 else
267 {
268 m_Doc->DragP = true;
269 m_Doc->DraggedElem = 0;
270 m_Doc->DragElements.clear();
271 m_Doc->m_Selection->delaySignalsOn();
272 for (int dre=0; dre<Elements.count(); ++dre)
273 {
274 tmpSel->addItem(Elements.at(dre), true);
275 }
276 tmpSel->setGroupRect();
277 ScriXmlDoc *ss = new ScriXmlDoc();
278 ScElemMimeData* md = new ScElemMimeData();
279 md->setScribusElem(ss->WriteElem(m_Doc, tmpSel));
280 delete ss;
281 /*#ifndef Q_OS_MACOS */
282 // see #2196
283 m_Doc->itemSelection_DeleteItem(tmpSel);
284 /*#else
285 qDebug() << "psimport: leaving items on page";
286 #endif*/
287 m_Doc->view()->updatesOn(true);
288 m_Doc->m_Selection->delaySignalsOff();
289 // We must copy the TransationSettings object as it is owned
290 // by handleObjectImport method afterwards
291 TransactionSettings* transacSettings = new TransactionSettings(trSettings);
292 m_Doc->view()->handleObjectImport(md, transacSettings);
293 m_Doc->DragP = false;
294 m_Doc->DraggedElem = 0;
295 m_Doc->DragElements.clear();
296 }
297 }
298 else
299 {
300 m_Doc->changed();
301 m_Doc->reformPages();
302 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
303 m_Doc->view()->updatesOn(true);
304 }
305 success = true;
306 }
307 else
308 {
309 QDir::setCurrent(CurDirP);
310 m_Doc->DoDrawing = true;
311 m_Doc->scMW()->setScriptRunning(false);
312 m_Doc->view()->updatesOn(true);
313 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
314 }
315 if (interactive)
316 m_Doc->setLoading(false);
317 //CB If we have a gui we must refresh it if we have used the progressbar
318 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
319 {
320 if ((showProgress) && (!interactive))
321 m_Doc->view()->DrawNew();
322 }
323 return success;
324 }
325
~EPSPlug()326 EPSPlug::~EPSPlug()
327 {
328 if (progressDialog)
329 delete progressDialog;
330 delete tmpSel;
331 }
332
333
convert(QString fn,double x,double y,double b,double h)334 bool EPSPlug::convert(QString fn, double x, double y, double b, double h)
335 {
336 QStringList args;
337 QString cmd, cmd1, cmd2, cmd3, tmp, tmp2, tmp3, tmp4;
338 // import.prolog do not cope with filenames containing blank spaces
339 // so take care that output filename does not (win32 compatibility)
340 QString tmpFile = getShortPathName(ScPaths::tempFileDir())+ "/ps.out";
341 QString errFile = getShortPathName(ScPaths::tempFileDir())+ "/ps.err";
342 QString pfad = ScPaths::instance().libDir();
343 QString pfad2 = QDir::toNativeSeparators(pfad + "import.prolog");
344 QFileInfo fi = QFileInfo(fn);
345 QString ext = fi.suffix().toLower();
346
347 if (progressDialog) {
348 progressDialog->setOverallProgress(1);
349 qApp->processEvents();
350 }
351 /*
352 // Destill the eps with ghostscript to get a clean eps file
353 QString cleanFile = getShortPathName(ScPaths::tempFileDir())+ "/clean.eps";
354 args.append( "-q" );
355 args.append( "-dNOPAUSE" );
356 args.append( "-sDEVICE=epswrite" );
357 args.append( "-dBATCH" );
358 args.append( "-dSAFER" );
359 args.append( "-dDEVICEWIDTH=250000" );
360 args.append( "-dDEVICEHEIGHT=250000" );
361 args.append( QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(cleanFile)) );
362 args.append( QDir::toNativeSeparators(fn) );
363 System(getShortPathName(PrefsManager::instance()->ghostscriptExecutable()), args, errFile, errFile, &cancel);
364 args.clear();
365 */
366 args.append( "-q" );
367 args.append( "-dNOPAUSE" );
368 args.append( "-dNODISPLAY" );
369 args.append( "-dBATCH" );
370 args.append( "-dDELAYBIND" );
371 // Add any extra font paths being used by Scribus to gs's font search
372 // path We have to use Scribus's prefs context, not a plugin context, to
373 // get to the required information.
374 PrefsContext *pc = PrefsManager::instance()->prefsFile->getContext("Fonts");
375 PrefsTable *extraFonts = pc->getTable("ExtraFontDirs");
376 const char sep = ScPaths::envPathSeparator;
377 if (extraFonts->getRowCount() >= 1)
378 cmd = QString("-sFONTPATH=%1").arg(extraFonts->get(0,0));
379 for (int i = 1; i < extraFonts->getRowCount(); ++i)
380 cmd += QString("%1%2").arg(sep).arg(extraFonts->get(i,0));
381 if( !cmd.isEmpty() )
382 args.append( cmd );
383 // then finish building the command and call gs
384 args.append( QString("-g%1x%2").arg(tmp2.setNum(qRound((b-x)*4))).arg(tmp3.setNum(qRound((h-y)*4))) );
385 args.append( "-r288");
386 args.append( "-dTextAlphaBits=4" );
387 args.append( "-dGraphicsAlphaBits=4" );
388 args.append( "-c" );
389 args.append( tmp.setNum(-x) );
390 args.append( tmp.setNum(-y) );
391 args.append( "translate" );
392 args.append( QString("-sTraceFile=%1").arg(QDir::toNativeSeparators(tmpFile)) );
393 QString exportPath = m_Doc->DocName + "-" + fi.baseName();
394 QFileInfo exportFi(exportPath);
395 if ( !exportFi.isWritable() ) {
396 PrefsContext* docContext = PrefsManager::instance()->prefsFile->getContext("docdirs", false);
397 QString docDir = ".";
398 QString prefsDocDir=PrefsManager::instance()->documentDir();
399 if (!prefsDocDir.isEmpty())
400 docDir = docContext->get("docsopen", prefsDocDir);
401 else
402 docDir = docContext->get("docsopen", ".");
403 exportFi.setFile(docDir + "/" + exportFi.baseName());
404 }
405 //qDebug() << QString("using export path %1").arg(exportFi.absFilePath());
406 args.append( QString("-sExportFiles=%1").arg(QDir::toNativeSeparators(exportFi.absoluteFilePath())) );
407 args.append( pfad2 );
408 args.append( QDir::toNativeSeparators(fn) );
409 args.append( "-c" );
410 args.append( "flush" );
411 args.append( "cfile" );
412 args.append( "closefile" );
413 args.append( "quit" );
414 QByteArray finalCmd = args.join(" ").toLocal8Bit();
415 int ret = System(getShortPathName(PrefsManager::instance()->ghostscriptExecutable()), args, errFile, errFile, &cancel);
416 if (ret != 0 && !cancel)
417 {
418 qDebug("PostScript import failed when calling gs as: \n%s\n", finalCmd.data());
419 qDebug("%s", "Ghostscript diagnostics:\n");
420 QFile diag(errFile);
421 if (diag.open(QIODevice::ReadOnly) && !diag.atEnd() ) {
422 char buf[121];
423 while (diag.readLine(buf, 120) > 0) {
424 qDebug("\t%s", buf);
425 }
426 diag.close();
427 }
428 else {
429 qDebug("%s", "-- no output --");
430 }
431 if (progressDialog)
432 progressDialog->close();
433 QString mess = tr("Importing File:\n%1\nfailed!").arg(fn);
434 ScMessageBox::critical(0, tr("Fatal Error"), mess);
435 return false;
436 }
437 if(progressDialog && !cancel) {
438 progressDialog->setOverallProgress(2);
439 progressDialog->setLabel("GI", tr("Generating Items"));
440 qApp->processEvents();
441 }
442 if (!cancel) {
443 parseOutput(tmpFile, extensionIndicatesEPSorPS(ext));
444 }
445 QFile::remove(tmpFile);
446 // QFile::remove(cleanFile);
447 if (progressDialog)
448 progressDialog->close();
449 return true;
450 }
451
parseOutput(QString fn,bool eps)452 void EPSPlug::parseOutput(QString fn, bool eps)
453 {
454 QString tmp, token, params, lasttoken, lastPath, currPath;
455 int z, lcap, ljoin, dc, pagecount;
456 int failedImages = 0;
457 double dcp;
458 bool fillRuleEvenOdd = true;
459 PageItem* ite;
460 QStack<PageItem*> groupStack;
461 QStack< QList<PageItem*> > groupStackP;
462 QStack<int> gsStack;
463 QStack<uint> gsStackMarks;
464 QFile f(fn);
465 lasttoken = "";
466 pagecount = 1;
467 if (f.open(QIODevice::ReadOnly))
468 {
469 int fProgress = 0;
470 int fSize = (int) f.size();
471 if (progressDialog) {
472 progressDialog->setTotalSteps("GI", fSize);
473 qApp->processEvents();
474 }
475 lastPath = "";
476 currPath = "";
477 LineW = 0;
478 Opacity = 1;
479 CurrColor = CommonStrings::None;
480 JoinStyle = Qt::MiterJoin;
481 CapStyle = Qt::FlatCap;
482 DashPattern.clear();
483 ScTextStream ts(&f);
484 int line_cnt = 0;
485 while (!ts.atEnd() && !cancel)
486 {
487 tmp = "";
488 tmp = ts.readLine();
489 if (progressDialog && (++line_cnt % 100 == 0)) {
490 int fPos = f.pos();
491 int progress = static_cast<int>(ceil(fPos / (double) fSize * 100));
492 if (progress > fProgress)
493 {
494 progressDialog->setProgress("GI", fPos);
495 qApp->processEvents();
496 fProgress = progress;
497 }
498 }
499 token = tmp.section(' ', 0, 0);
500 params = tmp.section(' ', 1, -1, QString::SectionIncludeTrailingSep);
501 if (lasttoken == "sp" && !eps && token != "sp" ) //av: messes up anyway: && (!interactive))
502 {
503 m_Doc->addPage(pagecount);
504 m_Doc->view()->addPage(pagecount, true);
505 pagecount++;
506 }
507 if (token == "n")
508 {
509 Coords.resize(0);
510 FirstM = true;
511 WasM = false;
512 ClosedPath = false;
513 }
514 else if (token == "m")
515 WasM = true;
516 else if (token == "c")
517 {
518 Curve(&Coords, params);
519 currPath += params;
520 }
521 else if (token == "l")
522 {
523 LineTo(&Coords, params);
524 currPath += params;
525 }
526 else if (token == "fill-winding")
527 {
528 fillRuleEvenOdd = false;
529 }
530 else if (token == "fill-evenodd")
531 {
532 fillRuleEvenOdd = true;
533 }
534 else if (token == "f")
535 {
536 //TODO: pattern -> Imageframe + Clip
537 if (Coords.size() != 0)
538 {
539 if ((Elements.count() != 0) && (lastPath == currPath))
540 {
541 ite = Elements.last();
542 ite->setFillColor(CurrColor);
543 ite->setFillTransparency(1.0 - Opacity);
544 lastPath = "";
545 }
546 else
547 {
548 if (ClosedPath)
549 z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, LineW, CurrColor, CommonStrings::None);
550 else
551 z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, LineW, CurrColor, CommonStrings::None);
552 ite = m_Doc->Items->at(z);
553 ite->PoLine = Coords.copy(); //FIXME: try to avoid copy if FPointArray when properly shared
554 ite->PoLine.translate(m_Doc->currentPage()->xOffset(), m_Doc->currentPage()->yOffset());
555 ite->ClipEdited = true;
556 ite->FrameType = 3;
557 ite->fillRule = (fillRuleEvenOdd);
558 FPoint wh = getMaxClipF(&ite->PoLine);
559 ite->setWidthHeight(wh.x(),wh.y());
560 ite->Clip = FlattenPath(ite->PoLine, ite->Segments);
561 ite->setFillTransparency(1.0 - Opacity);
562 ite->setTextFlowMode(PageItem::TextFlowDisabled);
563 m_Doc->adjustItemSize(ite);
564 if (ite->itemType() == PageItem::Polygon)
565 ite->ContourLine = ite->PoLine.copy();
566 if ((groupStack.count() != 0) && (groupStackP.count() != 0))
567 groupStackP.top().append(ite);
568 Elements.append(ite);
569 lastPath = currPath;
570 }
571 currPath = "";
572 }
573 }
574 else if (token == "s")
575 {
576 if (Coords.size() != 0)
577 {
578 // LineW = qMax(LineW, 0.01); // Set Linewidth to be a least 0.01 pts, a Stroke without a Linewidth makes no sense
579 if ((Elements.count() != 0) && (lastPath == currPath))
580 {
581 ite = Elements.last();
582 ite->setLineColor(CurrColor);
583 ite->setLineWidth(LineW);
584 ite->PLineEnd = CapStyle;
585 ite->PLineJoin = JoinStyle;
586 ite->setLineTransparency(1.0 - Opacity);
587 ite->DashOffset = DashOffset;
588 ite->DashValues = DashPattern;
589 }
590 else
591 {
592 if (ClosedPath)
593 z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, baseX, baseY, 10, 10, LineW, CommonStrings::None, CurrColor);
594 else
595 z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, baseX, baseY, 10, 10, LineW, CommonStrings::None, CurrColor);
596 ite = m_Doc->Items->at(z);
597 ite->PoLine = Coords.copy(); //FIXME: try to avoid copy when FPointArray is properly shared
598 ite->PoLine.translate(m_Doc->currentPage()->xOffset(), m_Doc->currentPage()->yOffset());
599 ite->ClipEdited = true;
600 ite->FrameType = 3;
601 ite->PLineEnd = CapStyle;
602 ite->PLineJoin = JoinStyle;
603 ite->DashOffset = DashOffset;
604 ite->DashValues = DashPattern;
605 FPoint wh = getMaxClipF(&ite->PoLine);
606 ite->setWidthHeight(wh.x(), wh.y());
607 ite->Clip = FlattenPath(ite->PoLine, ite->Segments);
608 ite->setLineTransparency(1.0 - Opacity);
609 m_Doc->adjustItemSize(ite);
610 if (ite->itemType() == PageItem::Polygon)
611 ite->ContourLine = ite->PoLine.copy();
612 ite->setLineWidth(LineW);
613 ite->setTextFlowMode(PageItem::TextFlowDisabled);
614 if ((groupStack.count() != 0) && (groupStackP.count() != 0))
615 groupStackP.top().append(ite);
616 Elements.append(ite);
617 }
618 lastPath = "";
619 currPath = "";
620 }
621 }
622 else if (token == "co")
623 CurrColor = parseColor(params, eps);
624 else if (token == "corgb")
625 CurrColor = parseColor(params, eps, colorModelRGB);
626 else if (token == "ci")
627 {
628 if (Coords.size() != 0)
629 {
630 QPainterPath tmpPath = Coords.toQPainterPath(true);
631 tmpPath = boundingBoxRect.intersected(tmpPath);
632 if ((tmpPath.boundingRect().width() != 0) && (tmpPath.boundingRect().height() != 0))
633 {
634 clipCoords.fromQPainterPath(tmpPath);
635 z = m_Doc->itemAdd(PageItem::Group, PageItem::Rectangle, baseX, baseY, 10, 10, 0, CommonStrings::None, CommonStrings::None);
636 ite = m_Doc->Items->at(z);
637 ite->PoLine = clipCoords.copy(); //FIXME: try to avoid copy if FPointArray when properly shared
638 ite->PoLine.translate(m_Doc->currentPage()->xOffset(), m_Doc->currentPage()->yOffset());
639 ite->ClipEdited = true;
640 ite->FrameType = 3;
641 FPoint wh = getMaxClipF(&ite->PoLine);
642 ite->setWidthHeight(wh.x(),wh.y());
643 ite->Clip = FlattenPath(ite->PoLine, ite->Segments);
644 m_Doc->adjustItemSize(ite, true);
645 ite->ContourLine = ite->PoLine.copy();
646 ite->setItemName( tr("Group%1").arg(m_Doc->GroupCounter));
647 ite->setTextFlowMode(PageItem::TextFlowDisabled);
648 Elements.append(ite);
649 if ((groupStack.count() != 0) && (groupStackP.count() != 0))
650 groupStackP.top().append(ite);
651 groupStack.push(ite);
652 QList<PageItem*> gElements;
653 groupStackP.push(gElements);
654 gsStackMarks.push(gsStack.count());
655 m_Doc->GroupCounter++;
656 }
657 }
658 Coords = FPointArray(0);
659 lastPath = "";
660 currPath = "";
661 }
662 else if (token == "gs")
663 {
664 gsStack.push(1);
665 }
666 else if (token == "gr")
667 {
668 // #6834 : self defense against incorrectly balanced save/restore
669 if (gsStack.count() > 0)
670 gsStack.pop();
671 if ((groupStack.count() != 0) && (groupStackP.count() != 0))
672 {
673 if (gsStack.count() < static_cast<int>(gsStackMarks.top()))
674 {
675 PageItem *ite = groupStack.pop();
676 QList<PageItem*> gList = groupStackP.pop();
677 for (int d = 0; d < gList.count(); d++)
678 {
679 Elements.removeAll(gList.at(d));
680 }
681 m_Doc->groupObjectsToItem(ite, gList);
682 gsStackMarks.pop();
683 }
684 }
685 }
686 else if (token == "w")
687 {
688 ScTextStream Lw(¶ms, QIODevice::ReadOnly);
689 Lw >> LineW;
690 }
691 else if (token == "ld")
692 {
693 ScTextStream Lw(¶ms, QIODevice::ReadOnly);
694 Lw >> dc;
695 Lw >> DashOffset;
696 DashPattern.clear();
697 if (dc != 0)
698 {
699 for (int dcc = 0; dcc < dc; ++dcc)
700 {
701 Lw >> dcp;
702 DashPattern.append(dcp);
703 }
704 }
705 }
706 else if (token == "lc")
707 {
708 ScTextStream Lw(¶ms, QIODevice::ReadOnly);
709 Lw >> lcap;
710 switch (lcap)
711 {
712 case 0:
713 CapStyle = Qt::FlatCap;
714 break;
715 case 1:
716 CapStyle = Qt::RoundCap;
717 break;
718 case 2:
719 CapStyle = Qt::SquareCap;
720 break;
721 default:
722 CapStyle = Qt::FlatCap;
723 break;
724 }
725 }
726 else if (token == "lj")
727 {
728 ScTextStream Lw(¶ms, QIODevice::ReadOnly);
729 Lw >> ljoin;
730 switch (ljoin)
731 {
732 case 0:
733 JoinStyle = Qt::MiterJoin;
734 break;
735 case 1:
736 JoinStyle = Qt::RoundJoin;
737 break;
738 case 2:
739 JoinStyle = Qt::BevelJoin;
740 break;
741 default:
742 JoinStyle = Qt::MiterJoin;
743 break;
744 }
745 }
746 else if (token == "cp") {
747 ClosedPath = true;
748 }
749 else if (token == "im") {
750 if ( !Image(params) )
751 ++failedImages;
752 }
753 lasttoken = token;
754 }
755 f.close();
756 if (groupStack.count() != 0)
757 {
758 while (!groupStack.isEmpty())
759 {
760 PageItem *ite = groupStack.pop();
761 QList<PageItem*> gList = groupStackP.pop();
762 for (int d = 0; d < gList.count(); d++)
763 {
764 Elements.removeAll(gList.at(d));
765 }
766 m_Doc->groupObjectsToItem(ite, gList);
767 }
768 }
769 }
770 if (failedImages > 0)
771 {
772 QString mess = tr("Converting of %1 images failed!").arg(failedImages);
773 ScMessageBox::critical(0, tr("Error"), mess);
774 }
775 }
776
Image(QString vals)777 bool EPSPlug::Image(QString vals)
778 {
779 double x, y, w, h, angle;
780 int horpix, verpix;
781 QString filename, device;
782 ScTextStream Code(&vals, QIODevice::ReadOnly);
783 Code >> x;
784 Code >> y;
785 Code >> w;
786 Code >> h;
787 Code >> angle;
788 Code >> horpix;
789 Code >> verpix;
790 Code >> device;
791 filename = Code.readAll().trimmed();
792 if (device.startsWith("psd")) {
793 filename = filename.mid(0, filename.length()-3) + "psd";
794 }
795
796 qDebug("%s", QString("import %7 image %1: %2x%3 @ (%4,%5) °%6").arg(filename).arg(w).arg(h).arg(x).arg(y).arg(angle).arg(device).toLocal8Bit().data());
797 QString rawfile = filename.mid(0, filename.length()-3) + "dat";
798 QStringList args;
799 args.append( "-q" );
800 args.append( "-dNOPAUSE" );
801 args.append( QString("-sDEVICE=%1").arg(device) );
802 args.append( "-dBATCH" );
803 args.append( QString("-g%1x%2").arg(horpix).arg(verpix) );
804 args.append( QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(filename)) );
805 args.append( QDir::toNativeSeparators(rawfile) );
806 args.append( "-c" );
807 args.append( "showpage" );
808 args.append( "quit" );
809 QByteArray finalCmd = args.join(" ").toLocal8Bit();
810 int ret = System(getShortPathName(PrefsManager::instance()->ghostscriptExecutable()), args);
811 if (ret != 0)
812 {
813 qDebug("PostScript image conversion failed when calling gs as: \n%s\n", finalCmd.data());
814 qDebug("Ghostscript diagnostics: %d\n", ret);
815 QFile diag(filename);
816 if (diag.open(QIODevice::ReadOnly)) {
817 char buf[121];
818 long int len;
819 bool gs_error = false;
820 do {
821 len = diag.readLine(buf, 120);
822 gs_error |= (strstr(buf,"Error")==nullptr);
823 if (gs_error)
824 qDebug("\t%s", buf);
825 }
826 while (len > 0);
827 diag.close();
828 }
829 else {
830 qDebug("%s", "-- no output --");
831 }
832 qDebug("%s", "Failed file was:\n");
833 QFile dat(rawfile);
834 if (dat.open(QIODevice::ReadOnly)) {
835 char buf[121];
836 long int len;
837 do {
838 len = dat.readLine(buf, 120);
839 qDebug("\t%s", buf);
840 }
841 while ( len > 0 && !(strstr(buf, "image")==nullptr) );
842 dat.close();
843 }
844 else {
845 qDebug("%s", "-- empty --");
846 }
847 }
848 QFile::remove(rawfile);
849 int z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, m_Doc->currentPage()->xOffset(), m_Doc->currentPage()->yOffset(), w, h, LineW, CommonStrings::None, CurrColor);
850 PageItem * ite = m_Doc->Items->at(z);
851 ite->setXYPos(m_Doc->currentPage()->xOffset() + x, m_Doc->currentPage()->yOffset() + y);
852 ite->setWidthHeight(w, h);
853 ite->clearContents();
854 /* FPoint a(x, y);
855 FPoint b(x+w, y);
856 FPoint c(x+w, y-h);
857 FPoint d(x, y-h);
858 ite->PoLine.resize(0);
859 ite->PoLine.addQuadPoint(a, a, b, b);
860 ite->PoLine.addQuadPoint(b, b, c, c);
861 ite->PoLine.addQuadPoint(c, c, d, d);
862 ite->PoLine.addQuadPoint(d, d, a, a);
863 ite->PoLine.translate(m_Doc->currentPage->xOffset() - x, m_Doc->currentPage->yOffset() - y);
864 ite->ClipEdited = true;
865 ite->Clip = FlattenPath(ite->PoLine, ite->Segments);
866 */
867 m_Doc->loadPict(filename, ite, -1);
868 ite->setRotation(angle);
869 ite->setImageScalingMode(false, true); // fit to frame, keep ratio
870 // m_Doc->view()->adjustItemSize(ite);
871 Elements.append(ite);
872 return ret == 0;
873 }
874
875
LineTo(FPointArray * i,QString vals)876 void EPSPlug::LineTo(FPointArray *i, QString vals)
877 {
878 if (vals.isEmpty())
879 return;
880 double x1, x2, y1, y2;
881 x1 = ScCLocale::toDoubleC(vals.section(' ', 0, 0, QString::SectionSkipEmpty));
882 y1 = ScCLocale::toDoubleC(vals.section(' ', 1, 1, QString::SectionSkipEmpty));
883 x2 = ScCLocale::toDoubleC(vals.section(' ', 2, 2, QString::SectionSkipEmpty));
884 y2 = ScCLocale::toDoubleC(vals.section(' ', 3, 3, QString::SectionSkipEmpty));
885 if ((!FirstM) && (WasM))
886 i->setMarker();
887 FirstM = false;
888 WasM = false;
889 i->addPoint(FPoint(x1, y1));
890 i->addPoint(FPoint(x1, y1));
891 i->addPoint(FPoint(x2, y2));
892 i->addPoint(FPoint(x2, y2));
893 }
894
Curve(FPointArray * i,QString vals)895 void EPSPlug::Curve(FPointArray *i, QString vals)
896 {
897 if (vals.isEmpty())
898 return;
899 double x1, x2, y1, y2, x3, y3, x4, y4;
900 x1 = ScCLocale::toDoubleC(vals.section(' ', 0, 0, QString::SectionSkipEmpty));
901 y1 = ScCLocale::toDoubleC(vals.section(' ', 1, 1, QString::SectionSkipEmpty));
902 x2 = ScCLocale::toDoubleC(vals.section(' ', 2, 2, QString::SectionSkipEmpty));
903 y2 = ScCLocale::toDoubleC(vals.section(' ', 3, 3, QString::SectionSkipEmpty));
904 x3 = ScCLocale::toDoubleC(vals.section(' ', 4, 4, QString::SectionSkipEmpty));
905 y3 = ScCLocale::toDoubleC(vals.section(' ', 5, 5, QString::SectionSkipEmpty));
906 x4 = ScCLocale::toDoubleC(vals.section(' ', 6, 6, QString::SectionSkipEmpty));
907 y4 = ScCLocale::toDoubleC(vals.section(' ', 7, 7, QString::SectionSkipEmpty));
908 if ((!FirstM) && (WasM))
909 i->setMarker();
910 FirstM = false;
911 WasM = false;
912 i->addPoint(FPoint(x1, y1));
913 i->addPoint(FPoint(x2, y2));
914 i->addPoint(FPoint(x4, y4));
915 i->addPoint(FPoint(x3, y3));
916 }
917
parseColor(QString vals,bool eps,colorModel model)918 QString EPSPlug::parseColor(QString vals, bool eps, colorModel model)
919 {
920 QString ret = CommonStrings::None;
921 if (vals.isEmpty())
922 return ret;
923 double c, m, y, k, r, g, b;
924 ScColor tmp;
925 ScTextStream Code(&vals, QIODevice::ReadOnly);
926 if (model == colorModelRGB)
927 {
928 Code >> r;
929 Code >> g;
930 Code >> b;
931 Code >> Opacity;
932 // Why adding 0.5 here color values range from 0 to 255 not 1 to 256 ??
933 /* int Rc = static_cast<int>(r * 255 + 0.5);
934 int Gc = static_cast<int>(g * 255 + 0.5);
935 int Bc = static_cast<int>(b * 255 + 0.5); */
936 int Rc = qRound(r * 255);
937 int Gc = qRound(g * 255);
938 int Bc = qRound(b * 255);
939 tmp.setColorRGB(Rc, Gc, Bc);
940 }
941 else
942 {
943 Code >> c;
944 Code >> m;
945 Code >> y;
946 Code >> k;
947 Code >> Opacity;
948 int Cc = qRound(c * 255);
949 int Mc = qRound(m * 255);
950 int Yc = qRound(y * 255);
951 int Kc = qRound(k * 255);
952 tmp.setColor(Cc, Mc, Yc, Kc);
953 }
954 tmp.setSpotColor(false);
955 tmp.setRegistrationColor(false);
956 QString namPrefix = "FromEPS";
957 if (!eps)
958 namPrefix = "FromPS";
959 QString fNam = m_Doc->PageColors.tryAddColor(namPrefix+tmp.name(), tmp);
960 ret = fNam;
961 return ret;
962 }
963