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 <algorithm>
9
10 #include <QApplication>
11 #include <QDesktopWidget>
12 #include <QMap>
13 #include <QTransform>
14 #include <QTemporaryFile>
15
16 #include "outputpreview_ps.h"
17 #include "commonstrings.h"
18 #include "cmsettings.h"
19 #include "filewatcher.h"
20 #include "iconmanager.h"
21 #include "pageitemiterator.h"
22 #include "pslib.h"
23 #include "prefsfile.h"
24 #include "prefsmanager.h"
25 #include "prefstable.h"
26 #include "sccolor.h"
27 #include "sccolorengine.h"
28 #include "scpaths.h"
29 #include "scribus.h"
30 #include "scribuscore.h"
31 #include "scribusdoc.h"
32 #include "util.h"
33 #include "util_ghostscript.h"
34 #include "util_printer.h"
35
36 #include "ui_outputpreviewbase.h"
37 #include "ui_outputpreview_ps.h"
38
OutputPreview_PS(QWidget * parent,ScribusDoc * doc)39 OutputPreview_PS::OutputPreview_PS(QWidget* parent, ScribusDoc* doc) :
40 OutputPreviewBase(parent, doc),
41 m_printOptions(doc->Print_Options),
42 m_prefsManager(PrefsManager::instance())
43 {
44 int inkTableWidth = 0;
45
46 setModal(true);
47 setWindowIcon(IconManager::instance().loadIcon("AppIcon.png"));
48
49 QString caption = tr("PostScript Output Preview");
50 setWindowTitle(caption);
51
52 m_havePngAlpha = ScCore->havePNGAlpha();
53 m_haveTiffSep = ScCore->haveTIFFSep();
54 getNumericGSVersion(m_gsVersion);
55
56 m_optionsUi = new Ui::OutputPreview_PS();
57 m_optionsUi->setupUi(m_uiBase->optionWidget);
58
59 m_optionsUi->psLevelCombo->addItem(CommonStrings::trPostScript1);
60 m_optionsUi->psLevelCombo->addItem(CommonStrings::trPostScript2);
61 m_optionsUi->psLevelCombo->addItem(CommonStrings::trPostScript3);
62
63 if (m_haveTiffSep)
64 {
65 ColorList usedSpots;
66 doc->getUsedColors(usedSpots, true);
67 QStringList spots = usedSpots.keys();
68
69 m_inkMax = (spots.count() + 4) * 255;
70 m_optionsUi->coverThresholdValue->setMaximum((spots.count() + 4) * 100.0);
71
72 m_optionsUi->inkTable->setColumnCount(2);
73 m_optionsUi->inkTable->setRowCount(4 + spots.count());
74 m_optionsUi->inkTable->setHorizontalHeaderItem(0, new QTableWidgetItem(IconManager::instance().loadIcon("16/show-object.png"), ""));
75 m_optionsUi->inkTable->setHorizontalHeaderItem(1, new QTableWidgetItem( tr("Separation Name")));
76
77 QHeaderView *header = m_optionsUi->inkTable->horizontalHeader();
78 header->setStretchLastSection(true);
79 header->setSectionsMovable(false);
80 header->setSectionResizeMode(QHeaderView::Fixed);
81
82 m_optionsUi->inkTable->setColumnWidth(0, 24);
83 m_optionsUi->inkTable->verticalHeader()->hide();
84 m_optionsUi->inkTable->setFocusPolicy(Qt::NoFocus);
85 m_optionsUi->inkTable->setSizeAdjustPolicy(QTableWidget::AdjustToContentsOnFirstShow);
86
87 m_optionsUi->inkTable->setItem(0, 1, new QTableWidgetItem( tr("Cyan")));
88 QCheckBox *cp = new QCheckBox(this);
89 cp->setFocusPolicy(Qt::NoFocus);
90 cp->setChecked(true);
91 connect(cp, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
92 m_optionsUi->inkTable->setCellWidget(0, 0, cp);
93 m_inkVisibilities.insert("Cyan", cp);
94
95 m_optionsUi->inkTable->setItem(1, 1, new QTableWidgetItem( tr("Magenta")));
96 cp = new QCheckBox(this);
97 cp->setFocusPolicy(Qt::NoFocus);
98 cp->setChecked(true);
99 connect(cp, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
100 m_optionsUi->inkTable->setCellWidget(1, 0, cp);
101 m_inkVisibilities.insert("Magenta", cp);
102
103 m_optionsUi->inkTable->setItem(2, 1, new QTableWidgetItem( tr("Yellow")));
104 cp = new QCheckBox(this);
105 cp->setFocusPolicy(Qt::NoFocus);
106 cp->setChecked(true);
107 connect(cp, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
108 m_optionsUi->inkTable->setCellWidget(2, 0, cp);
109 m_inkVisibilities.insert("Yellow", cp);
110
111 m_optionsUi->inkTable->setItem(3, 1, new QTableWidgetItem( tr("Black")));
112 cp = new QCheckBox(this);
113 cp->setFocusPolicy(Qt::NoFocus);
114 cp->setChecked(true);
115 connect(cp, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
116 m_optionsUi->inkTable->setCellWidget(3, 0, cp);
117 m_inkVisibilities.insert("Black", cp);
118
119 for (int sp = 0; sp < spots.count(); ++sp)
120 {
121 const QString& spotName = spots.at(sp);
122 m_optionsUi->inkTable->setItem(sp + 4, 1, new QTableWidgetItem(spotName));
123 cp = new QCheckBox(this);
124 cp->setFocusPolicy(Qt::NoFocus);
125 cp->setChecked(true);
126 connect(cp, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
127 m_optionsUi->inkTable->setCellWidget(sp + 4, 0, cp);
128 m_inkVisibilities.insert(spotName, cp);
129 }
130
131 inkTableWidth = m_optionsUi->inkTable->columnWidth(1);
132
133 bool inkCoverageEnabled = (m_optionsUi->enableCMYK->isChecked()) && (m_optionsUi->displayInkCoverage->isChecked());
134 m_optionsUi->coverThresholdLabel->setEnabled(inkCoverageEnabled);
135 m_optionsUi->coverThresholdValue->setEnabled(inkCoverageEnabled);
136 m_optionsUi->coverThresholdValue->setSuffix( tr(" %"));
137
138 connect(header, SIGNAL(sectionClicked(int)), this, SLOT(toggleAllFromHeader()));
139 connect(m_optionsUi->enableCMYK, SIGNAL(clicked()), this, SLOT(toggleCMYK()));
140 connect(m_optionsUi->displayInkCoverage, SIGNAL(clicked()), this, SLOT(toggleCMYK_Colour()));
141 connect(m_optionsUi->inkTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(onInkTableCellDoubleClicked(int)));
142 }
143 else
144 {
145 m_optionsUi->enableCMYK->setChecked(false);
146 m_optionsUi->enableCMYK->setVisible(false);
147 m_optionsUi->inkTable->setVisible(false);
148 m_optionsUi->displayInkCoverage->setChecked(false);
149 m_optionsUi->displayInkCoverage->setVisible(false);
150 m_optionsUi->coverThresholdLabel->setVisible(false);
151 m_optionsUi->coverThresholdValue->setVisible(false);
152 }
153
154 bool cmykEnabled = m_optionsUi->enableCMYK->isChecked();
155 m_optionsUi->inkTable->setEnabled(cmykEnabled);
156 m_optionsUi->displayInkCoverage->setEnabled(cmykEnabled);
157 bool isInkCoverageEnabled = m_optionsUi->displayInkCoverage->isChecked();
158 m_optionsUi->coverThresholdLabel->setEnabled(cmykEnabled && isInkCoverageEnabled);
159 m_optionsUi->coverThresholdValue->setEnabled(cmykEnabled && isInkCoverageEnabled);
160
161 if (m_optionsUi->inkTable->isVisible())
162 m_optionsUi->inkTable->setEnabled(m_optionsUi->enableCMYK->isChecked());
163
164 // Restore display settings from preferences
165 m_optionsUi->antiAliasing->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.enableAntiAliasing);
166 m_optionsUi->showTransparency->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.showTransparency);
167 if (m_optionsUi->inkTable->isVisible() && m_optionsUi->inkTable->isEnabled())
168 {
169 m_optionsUi->enableCMYK->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.cmykPreviewMode);
170 m_inkVisibilities["Cyan"]->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.isCyanVisible);
171 m_inkVisibilities["Magenta"]->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.isMagentaVisible);
172 m_inkVisibilities["Magenta"]->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.isYellowVisible);
173 m_inkVisibilities["Yellow"]->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.isBlackVisible);
174 }
175 m_optionsUi->displayInkCoverage->setChecked(m_prefsManager.appPrefs.psOutputPreviewPrefs.displayInkCoverage);
176 m_optionsUi->coverThresholdValue->setValue(m_prefsManager.appPrefs.psOutputPreviewPrefs.inkCoverageThreshold);
177
178 // Generate a template name for temporary files
179 QTemporaryFile *tempFile = new QTemporaryFile(ScPaths::tempFileDir() + "/scpreview_XXXXXX.ps");
180 if (tempFile->open())
181 {
182 QString tempFileBase = tempFile->fileName();
183 tempFile->setAutoRemove(false);
184 tempFile->close();
185 m_tempBaseName = QFileInfo(tempFileBase).completeBaseName();
186 }
187 if (m_tempBaseName.isEmpty())
188 m_tempBaseName = "scpreview";
189 delete tempFile;
190
191 if (m_printOptions.firstUse)
192 PrinterUtil::getDefaultPrintOptions(m_printOptions, m_doc->bleedsVal());
193 m_printOptions.prnLanguage = PrintLanguage::PostScript3;
194 m_printOptions.outputSeparations = false;
195 m_printOptions.separationName = "All";
196 m_printOptions.allSeparations = QStringList();
197 m_printOptions.setDevParam = false;
198 m_printOptions.cropMarks = false;
199 m_printOptions.bleedMarks = false;
200 m_printOptions.registrationMarks = false;
201 m_printOptions.colorMarks = false;
202 m_printOptions.markLength = 20.0;
203 m_printOptions.markOffset = 0.0;
204 m_printOptions.bleeds.set(0, 0, 0, 0);
205 setPrintOptionsToUi(m_printOptions);
206
207 // Display preview
208 QPixmap previewPix = createPreview(m_doc->currentPageNumber(), qRound(72 * m_scaleFactor));
209 m_previewLabel->setPixmap(previewPix);
210 m_previewLabel->resize(previewPix.size());
211
212 m_uiBase->pageSelector->setGUIForPage(m_doc->currentPage()->pageNr());
213
214 int w = m_previewLabel->width() + inkTableWidth + 50;
215 resize(qMin(QApplication::desktop()->width() - 30, w), 500);
216
217 //signals and slots
218 connect(m_uiBase->pageSelector, SIGNAL(pageChanged(int)), this, SLOT(jumpToPage(int)));
219 connect(m_uiBase->closeButton, SIGNAL(clicked()), this, SLOT(close()));
220 connect(m_uiBase->exportButton, SIGNAL(clicked()), this, SIGNAL(doExport()));
221 connect(m_uiBase->scaleBox, SIGNAL(activated(int)), this, SLOT(onScaleBoxValueChanged(int)));
222
223 connect(m_optionsUi->psLevelCombo, SIGNAL(activated(int)), this, SLOT(onPSLevelChanged(int)));
224 connect(m_optionsUi->colorOutputMode, SIGNAL(activated(int)), this, SLOT(onColorOutputModeChanged(int)));
225 connect(m_optionsUi->mirrorH, SIGNAL(clicked()), this, SLOT(redisplay()));
226 connect(m_optionsUi->mirrorV, SIGNAL(clicked()), this, SLOT(redisplay()));
227 connect(m_optionsUi->clipToMargins, SIGNAL(clicked()), this, SLOT(redisplay()));
228 connect(m_optionsUi->convertSpots, SIGNAL(clicked()), this, SLOT(redisplay()));
229 connect(m_optionsUi->enableGCR, SIGNAL(clicked()), this, SLOT(redisplay()));
230
231 connect(m_optionsUi->antiAliasing, SIGNAL(clicked()), this, SLOT(redisplay()));
232 connect(m_optionsUi->showTransparency, SIGNAL(clicked()), this, SLOT(redisplay()));
233
234 connect(m_optionsUi->coverThresholdValue, SIGNAL(valueChanged(double)), this, SLOT(toggleCMYK_Colour()));
235 }
236
~OutputPreview_PS()237 OutputPreview_PS::~OutputPreview_PS()
238 {
239 cleanupTemporaryFiles();
240 delete m_optionsUi;
241 }
242
postscriptLevel() const243 int OutputPreview_PS::postscriptLevel() const
244 {
245 return m_optionsUi->psLevelCombo->currentIndex() + 1;
246 }
247
isCMYKPreviewEnabled() const248 bool OutputPreview_PS::isCMYKPreviewEnabled() const
249 {
250 return m_optionsUi->enableCMYK->isChecked();
251 }
252
isAntialiasingEnabled() const253 bool OutputPreview_PS::isAntialiasingEnabled() const
254 {
255 return m_optionsUi->antiAliasing->isChecked();
256 }
257
isTransparencyEnabled() const258 bool OutputPreview_PS::isTransparencyEnabled() const
259 {
260 return m_optionsUi->showTransparency->isChecked();
261 }
262
isInkChannelVisible(const QString & ink)263 bool OutputPreview_PS::isInkChannelVisible(const QString& ink)
264 {
265 if (m_inkVisibilities.contains(ink))
266 return m_inkVisibilities[ink]->isChecked();
267 return false;
268 }
269
isInkCoverageEnabled() const270 bool OutputPreview_PS::isInkCoverageEnabled() const
271 {
272 return m_optionsUi->displayInkCoverage->isChecked();
273 }
274
inkCoverageThreshold() const275 double OutputPreview_PS::inkCoverageThreshold() const
276 {
277 return m_optionsUi->coverThresholdValue->value();
278 }
279
cleanupTemporaryFiles()280 void OutputPreview_PS::cleanupTemporaryFiles()
281 {
282 QString tempFileDir = ScPaths::tempFileDir();
283 QFile::remove(tempFileDir + "/" + m_tempBaseName + ".ps");
284 QFile::remove(tempFileDir + "/" + m_tempBaseName + ".png");
285 QDir d(tempFileDir + "/", m_tempBaseName + "*", QDir::Name, QDir::Files | QDir::NoSymLinks);
286 if ((d.exists()) && (d.count() != 0))
287 {
288 for (uint i = 0; i < d.count(); i++)
289 QFile::remove(tempFileDir + "/" + d[i]);
290 }
291 }
292
createPreview(int pageIndex,int res)293 QPixmap OutputPreview_PS::createPreview(int pageIndex, int res)
294 {
295 int ret = -1;
296 int gsRes = qRound(res * devicePixelRatioF());
297 int w = qRound(m_doc->Pages->at(pageIndex)->width() * gsRes / 72.0);
298 int h = qRound(m_doc->Pages->at(pageIndex)->height() * gsRes / 72.0);
299
300 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
301
302 QPixmap pixmap;
303 if (psOptionsHaveChanged(pageIndex))
304 {
305 bool success = createPreviewFile(pageIndex);
306 if (!success)
307 {
308 imageLoadError(pixmap);
309 return pixmap;
310 }
311 }
312
313 if (optionsHaveChanged(pageIndex))
314 {
315 if (m_optionsUi->enableCMYK->isChecked() && m_haveTiffSep)
316 ret = renderPreviewSep(pageIndex, gsRes);
317 else
318 ret = renderPreview(pageIndex, gsRes);
319 if (ret > 0)
320 {
321 imageLoadError(pixmap);
322 return pixmap;
323 }
324 }
325
326 QImage image;
327 if (m_optionsUi->enableCMYK->isChecked() && m_haveTiffSep)
328 {
329 bool loadError;
330 int cyan, magenta, yellow, black;
331
332 ScImage im;
333 bool mode;
334 int w2 = w;
335 int h2 = h;
336 if (m_doc->Pages->at(pageIndex)->orientation() == 1)
337 std::swap(w2, h2);
338 image = QImage(w2, h2, QImage::Format_ARGB32);
339 image.fill(qRgba(0, 0, 0, 0));
340
341 QStringList separationNames { "Cyan", "Magenta", "Yellow" };
342 CMSettings cms(m_doc, "", Intent_Perceptual);
343 cms.allowColorManagement(false);
344 for (int i = 0; i < separationNames.count(); ++i)
345 {
346 QString separationName = separationNames.at(i);
347 if (!m_inkVisibilities[separationName]->isChecked())
348 continue;
349 if (m_gsVersion < 854)
350 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif." + separationName + ".tif", 1, cms, ScImage::RGBData, 72, &mode);
351 else if (m_gsVersion <= 905)
352 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + "." + separationName + ".tif", 1, cms, ScImage::RGBData, 72, &mode);
353 else
354 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + "(" + separationName + ").tif", 1, cms, ScImage::RGBData, 72, &mode);
355 if (!loadError)
356 {
357 imageLoadError(pixmap);
358 return pixmap;
359 }
360 if (m_optionsUi->displayInkCoverage->isChecked())
361 blendImagesSumUp(image, im);
362 else
363 {
364 int c = (i == 0) ? 255 : 0;
365 int m = (i == 1) ? 255 : 0;
366 int j = (i == 2) ? 255 : 0;
367 blendImages(image, im, ScColor(c, m, j, 0));
368 }
369 }
370
371 for (auto sepit = m_sepsToFileNum.begin(); sepit != m_sepsToFileNum.end(); ++sepit)
372 {
373 const QCheckBox* checkBox = m_inkVisibilities.value(sepit.key(), nullptr);
374 if (!checkBox || !checkBox->isChecked())
375 continue;
376 QString sepFileName;
377 if (m_gsVersion < 854)
378 sepFileName = QString(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif.s%1.tif").arg(sepit.value());
379 else if (m_gsVersion <= 905)
380 sepFileName = QString(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".s%1.tif").arg(sepit.value());
381 else
382 sepFileName = QString(ScPaths::tempFileDir() + "/" + m_tempBaseName + "(%1).tif").arg(sepit.key());
383 if (!im.loadPicture(sepFileName, 1, cms, ScImage::RGBData, 72, &mode))
384 {
385 imageLoadError(pixmap);
386 return pixmap;
387 }
388 if (m_optionsUi->displayInkCoverage->isChecked())
389 blendImagesSumUp(image, im);
390 else
391 blendImages(image, im, m_doc->PageColors[sepit.key()]);
392 }
393
394 if (m_inkVisibilities["Black"]->isChecked())
395 {
396 CMSettings cms(m_doc, "", Intent_Perceptual);
397 cms.allowColorManagement(false);
398 if (m_gsVersion < 854)
399 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif.Black.tif", 1, cms, ScImage::RGBData, 72, &mode);
400 else if (m_gsVersion <= 905)
401 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".Black.tif", 1, cms, ScImage::RGBData, 72, &mode);
402 else
403 loadError = im.loadPicture(ScPaths::tempFileDir() + "/" + m_tempBaseName + "(Black).tif", 1, cms, ScImage::RGBData, 72, &mode);
404 if (!loadError)
405 {
406 imageLoadError(pixmap);
407 return pixmap;
408 }
409 if (m_optionsUi->displayInkCoverage->isChecked())
410 blendImagesSumUp(image, im);
411 else
412 blendImages(image, im, ScColor(0, 0, 0, 255));
413 }
414
415 if (m_optionsUi->displayInkCoverage->isChecked())
416 {
417 uint limitVal = (m_optionsUi->coverThresholdValue->value() * 255) / 100;
418 for (int yi = 0; yi < h2; ++yi)
419 {
420 QRgb *q = (QRgb*) image.scanLine(yi);
421 for (int xi = 0; xi < w2; ++xi)
422 {
423 uint greyVal = *q;
424 if (greyVal != 0)
425 {
426 if (limitVal == 0)
427 {
428 QColor tmpC;
429 tmpC.setHsv((greyVal * 359) / m_inkMax, 255, 255);
430 *q = tmpC.rgba();
431 }
432 else
433 {
434 int col = qMin(255 - static_cast<int>(((greyVal * 128) / m_inkMax) * 2), 255);
435 if ((*q > 0) && (*q < limitVal))
436 *q = qRgba(col, col, col, 255);
437 else
438 *q = qRgba(col, 0, 0, 255);
439 }
440 }
441 else
442 {
443 if (!m_optionsUi->showTransparency->isChecked())
444 *q = qRgba(255, 255, 255, 255);
445 }
446 q++;
447 }
448 }
449 }
450 else if (m_doc->HasCMS || ScCore->haveCMS())
451 {
452 QRgb alphaFF = qRgba(0,0,0,255);
453 QRgb alphaOO = qRgba(255,255,255,0);
454 ScColorMgmtEngine engine = m_doc->colorEngine;
455 ScColorProfile cmykProfile = m_doc->HasCMS ? m_doc->DocPrinterProf : ScCore->defaultCMYKProfile;
456 ScColorProfile rgbProfile = m_doc->HasCMS ? m_doc->DocDisplayProf : ScCore->defaultRGBProfile;
457 ScColorTransform transCMYK = engine.createTransform(cmykProfile, Format_YMCK_8, rgbProfile, Format_BGRA_8, Intent_Relative_Colorimetric, 0);
458 for (int yi = 0; yi < h2; ++yi)
459 {
460 uchar* ptr = image.scanLine( yi );
461 transCMYK.apply(ptr, ptr, image.width());
462 QRgb *q = (QRgb *) ptr;
463 for (int xi = 0; xi < image.width(); xi++, q++)
464 {
465 if (m_optionsUi->showTransparency->isChecked())
466 {
467 cyan = qRed(*q);
468 magenta = qGreen(*q);
469 yellow = qBlue(*q);
470 if ((cyan == 255) && (magenta == 255) && (yellow == 255))
471 *q = alphaOO;
472 else
473 *q |= alphaFF;
474 }
475 else
476 *q |= alphaFF;
477 }
478 }
479 }
480 else
481 {
482 for (int yi = 0; yi < h2; ++yi)
483 {
484 QRgb *q = (QRgb*) image.scanLine(yi);
485 for (int xi = 0; xi < w2; ++xi)
486 {
487 cyan = qRed(*q);
488 magenta = qGreen(*q);
489 yellow = qBlue(*q);
490 black = qAlpha(*q);
491 if ((cyan != 0) || (magenta != 0) || (yellow != 0 ) || (black != 0))
492 *q = qRgba(255 - qMin(255, cyan + black), 255 - qMin(255, magenta + black), 255 - qMin(255, yellow + black), 255);
493 else
494 {
495 if (!m_optionsUi->showTransparency->isChecked())
496 *q = qRgba(255, 255, 255, 255);
497 }
498 q++;
499 }
500 }
501 }
502 }
503 else
504 {
505 QString previewFile;
506 if (m_optionsUi->showTransparency->isChecked() && m_havePngAlpha)
507 previewFile = ScPaths::tempFileDir() + "/" + m_tempBaseName + ".png";
508 else
509 previewFile = ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif";
510 if (!image.load(previewFile))
511 {
512 imageLoadError(pixmap);
513 return pixmap;
514 }
515 image = image.convertToFormat(QImage::Format_ARGB32);
516 if (m_optionsUi->showTransparency->isChecked() && m_havePngAlpha)
517 {
518 int wi = image.width();
519 int hi = image.height();
520 for (int yi = 0; yi < hi; ++yi)
521 {
522 QRgb *s = (QRgb*) image.scanLine(yi);
523 for (int xi = 0; xi < wi; ++xi)
524 {
525 if ((*s) == 0xffffffff)
526 (*s) &= 0x00ffffff;
527 s++;
528 }
529 }
530 }
531 }
532
533 const ScPage* page = m_doc->Pages->at(pageIndex);
534 if ((page->orientation() == 1) && (image.width() < image.height()))
535 image = image.transformed( QTransform(0, 1, -1, 0, 0, 0) );
536
537 image.setDevicePixelRatio(devicePixelRatioF());
538 if (m_optionsUi->showTransparency->isChecked())
539 {
540 pixmap = QPixmap(image.width(), image.height());
541 pixmap.setDevicePixelRatio(devicePixelRatioF());
542 QPainter p;
543 QBrush b(QColor(205,205,205), IconManager::instance().loadPixmap("testfill.png"));
544 p.begin(&pixmap);
545 p.fillRect(0, 0, image.width(), image.height(), b);
546 p.drawImage(0, 0, image);
547 p.end();
548 }
549 else
550 pixmap = QPixmap::fromImage(image);
551 pixmap.setDevicePixelRatio(devicePixelRatioF());
552
553 qApp->restoreOverrideCursor();
554 updateOptionsFromUI();
555 return pixmap;
556 }
557
createPreviewFile(int pageIndex)558 bool OutputPreview_PS::createPreviewFile(int pageIndex)
559 {
560 std::vector<int> pageNumbers { pageIndex + 1 };
561
562 setUiOptionsToPrintOptions(m_printOptions);
563 m_printOptions.pageNumbers = pageNumbers;
564 m_printOptions.outputSeparations = false;
565 m_printOptions.separationName = "All";
566 m_printOptions.allSeparations = QStringList();
567 m_printOptions.setDevParam = false;
568
569 // Disable crop marks
570 m_printOptions.cropMarks = false;
571 m_printOptions.bleedMarks = false;
572 m_printOptions.registrationMarks = false;
573 m_printOptions.colorMarks = false;
574 m_printOptions.markLength = 20.0;
575 m_printOptions.markOffset = 0.0;
576 m_printOptions.bleeds.set(0, 0, 0, 0);
577
578 // Generate PostScript
579 QString psFileName = ScPaths::tempFileDir() + "/" + m_tempBaseName + ".ps";
580
581 PSLib *psLib = new PSLib(m_doc, m_printOptions, PSLib::OutputPS, &m_doc->PageColors);
582 if (!psLib)
583 return false;
584 bool success = (psLib->createPS(psFileName) == 0);
585 delete psLib;
586
587 // TODO : Postscript level < 3
588 if (success && (m_printOptions.prnLanguage != PrintLanguage::PostScript3))
589 {
590 // use gs to convert our PS to a lower version
591 QStringList opts;
592 const ScPage* page = m_doc->Pages->at(pageIndex);
593 double pageWidth = page->width();
594 double pageHeight = page->height();
595 if (page->orientation() == 1)
596 std::swap(pageWidth, pageHeight);
597 opts.append( QString("-dDEVICEWIDTHPOINTS=%1").arg(QString::number(pageWidth)) );
598 opts.append( QString("-dDEVICEHEIGHTPOINTS=%1").arg(QString::number(pageHeight)) );
599
600 QString outFileName = ScPaths::tempFileDir() + "/" + m_tempBaseName + ".ps" + QString::number((int) m_printOptions.prnLanguage);
601 success = (convertPS2PS(psFileName, outFileName, opts, (int) m_printOptions.prnLanguage) == 0);
602 if (!success)
603 return false;
604 success &= QFile::remove(psFileName);
605 success &= QFile::rename(outFileName, psFileName);
606 }
607
608 return success;
609 }
610
renderPreview(int pageIndex,int res)611 int OutputPreview_PS::renderPreview(int pageIndex, int res)
612 {
613 int ret = -1;
614 QString cmd1;
615
616 QStringList args;
617 QString tmp, tmp2, tmp3;
618 int w = qRound(m_doc->Pages->at(pageIndex)->width() * res / 72.0);
619 int h = qRound(m_doc->Pages->at(pageIndex)->height() * res / 72.0);
620 if (m_doc->Pages->at(pageIndex)->orientation() == 1)
621 std::swap(w, h);
622
623 args.append( "-q" );
624 args.append( "-dNOPAUSE" );
625 args.append( "-dPARANOIDSAFER" );
626 args.append( QString("-r%1").arg(tmp.setNum(res)) );
627 args.append( QString("-g%1x%2").arg(tmp2.setNum(w), tmp3.setNum(h)) );
628 if (m_optionsUi->enableCMYK->isChecked())
629 {
630 if (!m_haveTiffSep)
631 return 1;
632 args.append("-sDEVICE=tiffsep");
633 }
634 else
635 {
636 if (m_optionsUi->showTransparency->isChecked() && m_havePngAlpha)
637 args.append("-sDEVICE=pngalpha");
638 else
639 args.append("-sDEVICE=tiff24nc");
640 }
641 if (m_optionsUi->antiAliasing->isChecked())
642 {
643 args.append("-dTextAlphaBits=4");
644 args.append("-dGraphicsAlphaBits=4");
645 }
646 if ((m_doc->HasCMS) && (m_gsVersion >= 900))
647 {
648 args.append("-sDefaultCMYKProfile=" + QDir::toNativeSeparators(m_doc->DocPrinterProf.profilePath()));
649 if (m_optionsUi->enableCMYK->isChecked())
650 args.append("-sOutputICCProfile=" + QDir::toNativeSeparators(m_doc->DocPrinterProf.profilePath()));
651 else
652 args.append("-sOutputICCProfile=" + QDir::toNativeSeparators(m_doc->DocDisplayProf.profilePath()));
653 }
654 else if (ScCore->haveCMS() && (m_gsVersion >= 900))
655 {
656 args.append("-sDefaultCMYKProfile=" + QDir::toNativeSeparators(ScCore->defaultCMYKProfile.profilePath()));
657 if (m_optionsUi->enableCMYK->isChecked())
658 args.append("-sOutputICCProfile=" + QDir::toNativeSeparators(ScCore->defaultCMYKProfile.profilePath()));
659 else
660 args.append("-sOutputICCProfile=" + QDir::toNativeSeparators(ScCore->defaultRGBProfile.profilePath()));
661 }
662
663 // Add any extra font paths being used by Scribus to gs's font search path
664 PrefsContext *pc = m_prefsManager.prefsFile->getContext("Fonts");
665 PrefsTable *extraFonts = pc->getTable("ExtraFontDirs");
666 const char sep = ScPaths::envPathSeparator;
667 if (extraFonts->getRowCount() >= 1)
668 cmd1 = QString("-sFONTPATH=%1").arg(QDir::toNativeSeparators(extraFonts->get(0,0)));
669 for (int i = 1; i < extraFonts->getRowCount(); ++i)
670 cmd1 += QString("%1%2").arg(sep).arg(QDir::toNativeSeparators(extraFonts->get(i,0)));
671 if (!cmd1.isEmpty())
672 args.append( cmd1 );
673
674 // then add any final args and call gs
675 QString tempFileDir = ScPaths::tempFileDir() ;
676 if (m_optionsUi->enableCMYK->isChecked())
677 args.append( QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(tempFileDir + "/" + m_tempBaseName + ".tif")) );
678 else if (m_optionsUi->showTransparency->isChecked() && m_havePngAlpha)
679 args.append( QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(tempFileDir + "/" + m_tempBaseName + ".png")) );
680 else
681 args.append(QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(tempFileDir + "/" + m_tempBaseName + ".tif")));
682 args.append( QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".ps") );
683 args.append( "-c" );
684 args.append( "showpage" );
685 args.append( "-c" );
686 args.append( "quit" );
687 ret = System(m_prefsManager.ghostscriptExecutable(), args);
688 return ret;
689 }
690
renderPreviewSep(int pageIndex,int res)691 int OutputPreview_PS::renderPreviewSep(int pageIndex, int res)
692 {
693 int ret = -1;
694 QString cmd;
695 QStringList args, args1, args2, args3;
696
697 QString tmp, tmp2, tmp3;
698 int w = qRound(m_doc->Pages->at(pageIndex)->width() * res / 72.0);
699 int h = qRound(m_doc->Pages->at(pageIndex)->height() * res / 72.0);
700 if (m_doc->Pages->at(pageIndex)->orientation() == 1)
701 std::swap(w, h);
702
703 args1.append( "-q" );
704 args1.append( "-dNOPAUSE" );
705 args1.append( "-dPARANOIDSAFER" );
706 args1.append( QString("-r%1").arg(tmp.setNum(res)) );
707 args1.append( QString("-g%1x%2").arg(tmp2.setNum(w), tmp3.setNum(h)) );
708 if (m_optionsUi->antiAliasing->isChecked())
709 {
710 args1.append("-dTextAlphaBits=4");
711 args1.append("-dGraphicsAlphaBits=4");
712 }
713 if ((m_doc->HasCMS) && (m_gsVersion >= 900))
714 {
715 args1.append("-sDefaultCMYKProfile=" + QDir::toNativeSeparators(m_doc->DocPrinterProf.profilePath()));
716 args1.append("-sOutputICCProfile=" + QDir::toNativeSeparators(m_doc->DocPrinterProf.profilePath()));
717 }
718 else if (ScCore->haveCMS() && (m_gsVersion >= 900))
719 {
720 args.append("-sDefaultCMYKProfile=" + QDir::toNativeSeparators(ScCore->defaultCMYKProfile.profilePath()));
721 args.append("-sOutputICCProfile=" + QDir::toNativeSeparators(ScCore->defaultCMYKProfile.profilePath()));
722 }
723
724 // Add any extra font paths being used by Scribus to gs's font search path
725 PrefsContext *pc = m_prefsManager.prefsFile->getContext("Fonts");
726 PrefsTable *extraFonts = pc->getTable("ExtraFontDirs");
727 const char sep = ScPaths::envPathSeparator;
728 if (extraFonts->getRowCount() >= 1)
729 cmd = QString("-sFONTPATH=%1").arg(QDir::toNativeSeparators(extraFonts->get(0,0)));
730 for (int i = 1; i < extraFonts->getRowCount(); ++i)
731 cmd += QString("%1%2").arg(sep).arg(QDir::toNativeSeparators(extraFonts->get(i,0)));
732 if (!cmd.isEmpty())
733 args1.append(cmd);
734 args1.append( QString("-sOutputFile=%1").arg(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif")) );
735
736 args2.append( QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".ps") );
737 args2.append("-c");
738 args2.append("quit");
739
740 ColorList usedSpots;
741 m_doc->getUsedColors(usedSpots, true);
742 QStringList spots = usedSpots.keys();
743 args3.append( "-sDEVICE=tiffsep" );
744
745 // args3.append( "-c" );
746 cmd = "<< /SeparationColorNames ";
747 QString allSeps ="[ /Cyan /Magenta /Yellow /Black ";
748 for (int sp = 0; sp < spots.count(); ++sp)
749 {
750 allSeps += "(" + spots[sp] + ") ";
751 }
752 allSeps += "]";
753 cmd += allSeps + " /SeparationOrder [ /Cyan /Magenta /Yellow /Black] >> setpagedevice";
754 QFile fx(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".sep.ps"));
755 if (fx.open(QIODevice::WriteOnly))
756 {
757 QTextStream tsx(&fx);
758 tsx << cmd;
759 fx.close();
760 }
761
762 QString gsExe(getShortPathName(m_prefsManager.ghostscriptExecutable()));
763 ret = System(gsExe, args1 + args3 + args2, ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif.txt" );
764
765 QFile sepInfo(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".tif.txt"));
766 m_sepsToFileNum.clear();
767 if (sepInfo.open(QIODevice::ReadOnly))
768 {
769 QString Sname;
770 QTextStream tsC(&sepInfo);
771 int counter = 0;
772 while (!tsC.atEnd())
773 {
774 Sname = tsC.readLine();
775 QString tt = Sname.remove("%%SeparationName:").trimmed();
776 if (!tt.isEmpty())
777 {
778 m_sepsToFileNum.insert(tt, counter);
779 counter++;
780 }
781 }
782 }
783 sepInfo.close();
784 QString currSeps = "";
785 uint spc = 0;
786 for (int sp = 0; sp < spots.count(); ++sp)
787 {
788 currSeps += "(" + spots[sp] + ") ";
789 spc++;
790 if (sp > 6)
791 {
792 args3.clear();
793 args3.append("-sDEVICE=tiffsep");
794 QFile fx(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".sep.ps"));
795 if (fx.open(QIODevice::WriteOnly))
796 {
797 QTextStream tsx(&fx);
798 tsx << QString("<< /SeparationColorNames " + allSeps + " /SeparationOrder [ " + currSeps + " ] >> setpagedevice");
799 fx.close();
800 }
801 args3.append("-f");
802 args3.append(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".sep.ps"));
803 ret = System(gsExe, args1 + args3 + args2);
804 currSeps = "";
805 spc = 0;
806 }
807 }
808 if (spc != 0)
809 {
810 args3.clear();
811 args3.append("-sDEVICE=tiffsep");
812 QFile fx(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".sep.ps"));
813 if (fx.open(QIODevice::WriteOnly))
814 {
815 QTextStream tsx(&fx);
816 tsx << QString("<< /SeparationColorNames " + allSeps + " /SeparationOrder [ " + currSeps + " ] >> setpagedevice");
817 fx.close();
818 }
819 args3.append("-f");
820 args3.append(QDir::toNativeSeparators(ScPaths::tempFileDir() + "/" + m_tempBaseName + ".sep.ps"));
821 ret = System(gsExe, args1 + args3 + args2);
822 }
823 return ret;
824 }
825
optionsHaveChanged(int pageIndex) const826 bool OutputPreview_PS::optionsHaveChanged(int pageIndex) const
827 {
828 if (m_currentPage != pageIndex)
829 return true;
830 if (m_scaleMode != m_uiBase->scaleBox->currentIndex())
831 return true;
832 if (m_cmykPreviewMode != m_optionsUi->enableCMYK->isChecked())
833 return true;
834 if (m_useAntialiasing != m_optionsUi->antiAliasing->isChecked())
835 return true;
836 if (m_showTransparency != m_optionsUi->showTransparency->isChecked())
837 return true;
838
839 if (m_psLevel != m_optionsUi->psLevelCombo->currentIndex() + 1)
840 return true;
841 if (m_colorOutputMode != m_optionsUi->colorOutputMode->currentIndex())
842 return true;
843 if (m_mirrorH != m_optionsUi->mirrorH->isChecked())
844 return true;
845 if (m_mirrorV != m_optionsUi->mirrorV->isChecked())
846 return true;
847 if (m_clipToMargins != m_optionsUi->clipToMargins->isChecked())
848 return true;
849 if (m_convertSpots != m_optionsUi->convertSpots->isChecked())
850 return true;
851 if (m_enableGCR != m_optionsUi->enableGCR->isChecked())
852 return true;
853
854 return false;
855 }
856
psOptionsHaveChanged(int pageIndex) const857 bool OutputPreview_PS::psOptionsHaveChanged(int pageIndex) const
858 {
859 if (m_currentPage != pageIndex)
860 return true;
861
862 if (m_psLevel != m_optionsUi->psLevelCombo->currentIndex() + 1)
863 return true;
864 if (m_colorOutputMode != m_optionsUi->colorOutputMode->currentIndex())
865 return true;
866 if (m_mirrorH != m_optionsUi->mirrorH->isChecked())
867 return true;
868 if (m_mirrorV != m_optionsUi->mirrorV->isChecked())
869 return true;
870 if (m_clipToMargins != m_optionsUi->clipToMargins->isChecked())
871 return true;
872 if (m_convertSpots != m_optionsUi->convertSpots->isChecked())
873 return true;
874 if (m_enableGCR != m_optionsUi->enableGCR->isChecked())
875 return true;
876
877 return false;
878 }
879
setPrintOptionsToOptions(PrintOptions & prnOptions)880 void OutputPreview_PS::setPrintOptionsToOptions(PrintOptions& prnOptions)
881 {
882 // TODO : finish
883 m_psLevel = qMax(1, qMin((int) prnOptions.prnLanguage, 3));
884
885 m_colorOutputMode = prnOptions.useColor ? 0 : 1;
886 m_mirrorH = prnOptions.mirrorH;
887 m_mirrorV = prnOptions.mirrorV;
888 m_clipToMargins = prnOptions.doClip;
889 m_convertSpots = !prnOptions.useSpotColors;
890 m_enableGCR = prnOptions.doGCR;
891 }
892
setPrintOptionsToUi(PrintOptions & prnOptions)893 void OutputPreview_PS::setPrintOptionsToUi(PrintOptions& prnOptions)
894 {
895 // TODO : finish
896 int psLevel = qMax(1, qMin((int) prnOptions.prnLanguage, 3));
897 m_optionsUi->psLevelCombo->setCurrentIndex(psLevel - 1);
898
899 m_optionsUi->colorOutputMode->setCurrentIndex(prnOptions.useColor ? 0 : 1);
900 m_optionsUi->mirrorH->setChecked(prnOptions.mirrorH);
901 m_optionsUi->mirrorV->setChecked(prnOptions.mirrorV);
902 m_optionsUi->clipToMargins->setChecked(prnOptions.doClip);
903 m_optionsUi->convertSpots->setChecked(!prnOptions.useSpotColors);
904 m_optionsUi->enableGCR->setChecked(prnOptions.doGCR);
905
906 bool cmykEnabled = m_optionsUi->enableCMYK->isChecked();
907 m_optionsUi->inkTable->setEnabled(cmykEnabled);
908 m_optionsUi->displayInkCoverage->setEnabled(cmykEnabled);
909 bool isInkCoverageEnabled = m_optionsUi->displayInkCoverage->isChecked();
910 m_optionsUi->coverThresholdLabel->setEnabled(cmykEnabled && isInkCoverageEnabled);
911 m_optionsUi->coverThresholdValue->setEnabled(cmykEnabled && isInkCoverageEnabled);
912 }
913
setUiOptionsToPrintOptions(PrintOptions & prnOptions)914 void OutputPreview_PS::setUiOptionsToPrintOptions(PrintOptions& prnOptions)
915 {
916 // TODO : finish
917 prnOptions.prnLanguage = (PrintLanguage) (m_optionsUi->psLevelCombo->currentIndex() + 1);
918
919 prnOptions.useColor = (m_optionsUi->colorOutputMode->currentIndex() == 0);
920 prnOptions.mirrorH = m_optionsUi->mirrorH->isChecked();
921 prnOptions.mirrorV = m_optionsUi->mirrorV->isChecked();
922 prnOptions.doClip = m_optionsUi->clipToMargins->isChecked();
923 prnOptions.useSpotColors = !m_optionsUi->convertSpots->isChecked();
924 prnOptions.doGCR = m_optionsUi->enableGCR->isChecked();
925 }
926
updateOptionsFromUI()927 void OutputPreview_PS::updateOptionsFromUI()
928 {
929 // TODO : finish
930 m_psLevel = m_optionsUi->psLevelCombo->currentIndex() + 1;
931 m_colorOutputMode = m_optionsUi->colorOutputMode->currentIndex();
932 m_mirrorH = m_optionsUi->mirrorH->isChecked();
933 m_mirrorV = m_optionsUi->mirrorV->isChecked();
934 m_clipToMargins = m_optionsUi->clipToMargins->isChecked();
935 m_convertSpots = m_optionsUi->convertSpots->isChecked();
936 m_enableGCR = m_optionsUi->enableGCR->isChecked();
937
938 m_currentPage = m_uiBase->pageSelector->getCurrentPage() - 1;
939 m_scaleMode = m_uiBase->scaleBox->currentIndex();
940 m_cmykPreviewMode = m_optionsUi->enableCMYK->isChecked();
941 m_useAntialiasing = m_optionsUi->antiAliasing->isChecked();
942 m_showTransparency = m_optionsUi->showTransparency->isChecked();
943 }
944
onPSLevelChanged(int)945 void OutputPreview_PS::onPSLevelChanged(int /*index*/)
946 {
947 redisplay();
948 }
949
onColorOutputModeChanged(int)950 void OutputPreview_PS::onColorOutputModeChanged(int /*index*/)
951 {
952 redisplay();
953 }
954
onInkTableCellDoubleClicked(int row)955 void OutputPreview_PS::onInkTableCellDoubleClicked(int row)
956 {
957 if (!m_haveTiffSep)
958 return;
959
960 for (auto sepIt = m_inkVisibilities.begin(); sepIt != m_inkVisibilities.end(); ++sepIt)
961 sepIt.value()->setChecked(false);
962 ((QCheckBox*)(m_optionsUi->inkTable->cellWidget(row, 0)))->setChecked(true);
963
964 if (m_optionsUi->enableCMYK->isChecked())
965 m_previewLabel->setPixmap(createPreview(m_currentPage, qRound(72 * m_scaleFactor)));
966 m_previewLabel->resize(m_previewLabel->pixmap()->size());
967 }
968
toggleAllFromHeader()969 void OutputPreview_PS::toggleAllFromHeader()
970 {
971 if (!m_haveTiffSep)
972 return;
973
974 for (auto sepIt = m_inkVisibilities.begin(); sepIt != m_inkVisibilities.end(); ++sepIt)
975 sepIt.value()->setChecked(true);
976
977 if (m_optionsUi->enableCMYK->isChecked())
978 m_previewLabel->setPixmap(createPreview(m_currentPage, qRound(72 * m_scaleFactor)));
979 m_previewLabel->resize(m_previewLabel->pixmap()->size());
980 }
981
toggleCMYK()982 void OutputPreview_PS::toggleCMYK()
983 {
984 if (!m_haveTiffSep)
985 return;
986
987 bool cmykEnabled = m_optionsUi->enableCMYK->isChecked();
988 m_optionsUi->inkTable->setEnabled(cmykEnabled);
989 m_optionsUi->displayInkCoverage->setEnabled(cmykEnabled);
990 bool isInkCoverageEnabled = m_optionsUi->displayInkCoverage->isChecked();
991 m_optionsUi->coverThresholdLabel->setEnabled(cmykEnabled && isInkCoverageEnabled);
992 m_optionsUi->coverThresholdValue->setEnabled(cmykEnabled && isInkCoverageEnabled);
993
994 redisplay();
995 }
996
toggleCMYK_Colour()997 void OutputPreview_PS::toggleCMYK_Colour()
998 {
999 if (m_haveTiffSep)
1000 {
1001 bool inkCoverageEnabled = (m_optionsUi->enableCMYK->isChecked()) && (m_optionsUi->displayInkCoverage->isChecked());
1002 m_optionsUi->coverThresholdLabel->setEnabled(inkCoverageEnabled);
1003 m_optionsUi->coverThresholdValue->setEnabled(inkCoverageEnabled);
1004 }
1005 if (m_optionsUi->enableCMYK->isChecked())
1006 m_previewLabel->setPixmap(createPreview(m_currentPage, qRound(72 * m_scaleFactor)));
1007 m_previewLabel->resize(m_previewLabel->pixmap()->size());
1008 }
1009
imageLoadError(QPixmap & pixmap)1010 void OutputPreview_PS::imageLoadError(QPixmap &pixmap)
1011 {
1012 pixmap = QPixmap(1, 1);
1013 qApp->restoreOverrideCursor();
1014 updateOptionsFromUI();
1015 }
1016