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 #include <QByteArray>
8 #include <QDataStream>
9 #include <QDebug>
10 #include <QFile>
11 #include <QFileInfo>
12 #include <QRegExp>
13 
14 #include "cmsettings.h"
15 #include "colormgmt/sccolormgmtengine.h"
16 #include "scclocale.h"
17 #include "scpaths.h"
18 #include "scribuscore.h"
19 #include "scimgdataloader_ps.h"
20 #include "sctextstream.h"
21 #include "prefsmanager.h"
22 #include "util.h"
23 #include "util_formats.h"
24 #include "util_ghostscript.h"
25 #include "util_math.h"
26 #include "scimage.h"
27 
28 extern "C"
29 {
30 #define XMD_H           // shut JPEGlib up
31 #if defined(Q_OS_UNIXWARE)
32 #  define HAVE_BOOLEAN  // libjpeg under Unixware seems to need this
33 #endif
34 #include <jpeglib.h>
35 #include <jerror.h>
36 #include <csetjmp>
37 #undef HAVE_STDLIB_H
38 #ifdef const
39 #  undef const          // remove crazy C hackery in jconfig.h
40 #endif
41 }
42 
ScImgDataLoader_PS()43 ScImgDataLoader_PS::ScImgDataLoader_PS() :
44 	 m_isDCS1(false),
45 	 m_isDCS2(false),
46 	 m_isDCS2multi(false),
47 	 m_isPhotoshop(false),
48 	 m_hasPhotoshopImageData(false),
49 	 m_doThumbnail(false),
50 	 m_hasThumbnail(false),
51 	 m_inTrailer(false),
52 	 m_BBoxInTrailer(false),
53 	 m_isRotated(false),
54 	 m_psXSize(0),
55 	 m_psYSize(0),
56 	 m_psDepth(0),
57 	 m_psMode(0),
58 	 m_psChannel(0),
59 	 m_psBlock(0),
60 	 m_psDataType(0)
61 {
62 	initSupportedFormatList();
63 }
64 
initialize()65 void ScImgDataLoader_PS::initialize()
66 {
67 	m_doThumbnail = false;
68 	m_hasThumbnail = false;
69 
70 	ScImgDataLoader::initialize();
71 }
72 
initSupportedFormatList()73 void ScImgDataLoader_PS::initSupportedFormatList()
74 {
75 	m_supportedFormats.clear();
76 	m_supportedFormats<<"ps"<<"eps"<<"epsi";
77 }
78 
loadEmbeddedProfile(const QString & fn,int)79 void ScImgDataLoader_PS::loadEmbeddedProfile(const QString& fn, int /* page */)
80 {
81 	m_embeddedProfile.resize(0);
82 	m_profileComponents = 0;
83 	if ( !QFile::exists(fn) )
84 		return;
85 	QFile f(fn);
86 	if (!f.open(QIODevice::ReadOnly))
87 		return;
88 
89 	QString tmp;
90 	QDataStream ts(&f);
91 	while (!ts.atEnd())
92 	{
93 		tmp = readLineFromDataStream(ts);
94 		if (tmp.startsWith("%%BeginICCProfile:"))
95 		{
96 			QByteArray psdata;
97 			while (!ts.atEnd())
98 			{
99 				tmp = readLineFromDataStream(ts);
100 				for (int a = 2; a < tmp.length(); a += 2)
101 				{
102 					bool ok;
103 					ushort data = tmp.midRef(a, 2).toUShort(&ok, 16);
104 					psdata.resize(psdata.size()+1);
105 					psdata[psdata.size()-1] = data;
106 				}
107 				if (tmp.startsWith("%%EndICCProfile"))
108 				{
109 					ScColorMgmtEngine engine(ScCore->defaultEngine);
110 					ScColorProfile prof = engine.openProfileFromMem(psdata);
111 					if (prof)
112 					{
113 						if (prof.colorSpace() == ColorSpace_Rgb)
114 							m_profileComponents = 3;
115 						if (prof.colorSpace() == ColorSpace_Cmyk)
116 							m_profileComponents = 4;
117 						m_imageInfoRecord.profileName = prof.productDescription();
118 						m_imageInfoRecord.embeddedProfileName = m_imageInfoRecord.profileName;
119 						m_imageInfoRecord.isEmbedded = true;
120 						m_embeddedProfile = psdata;
121 					}
122 					break;
123 				}
124 			}
125 		}
126 	}
127 }
128 
scanForFonts(const QString & fn)129 void ScImgDataLoader_PS::scanForFonts(const QString& fn)
130 {
131 	QFile f(fn);
132 	if (!f.open(QIODevice::ReadOnly))
133 		return;
134 	QDataStream ts(&f);
135 	QString tmp;
136 	while (!ts.atEnd())
137 	{
138 		tmp = readLineFromDataStream(ts);
139 		if (tmp.startsWith("%%BeginFont:"))
140 		{
141 			tmp = tmp.remove("%%BeginFont:");
142 			ScTextStream ts2(&tmp, QIODevice::ReadOnly);
143 			QString tmp2;
144 			ts2 >> tmp2;
145 			m_FontListe.removeAll(tmp2);
146 		}
147 	}
148 }
149 
parseData(const QString & fn)150 bool ScImgDataLoader_PS::parseData(const QString& fn)
151 {
152 	QString tmp, FarNam;
153 	ScColor cc;
154 	double x, y, b, h, c, m, k;
155 	bool found = false;
156 	m_isDCS1 = false;
157 	m_isDCS2 = false;
158 	m_isDCS2multi = false;
159 	m_isPhotoshop = false;
160 	m_hasPhotoshopImageData = false;
161 	m_hasThumbnail = false;
162 	m_inTrailer = false;
163 	m_BBoxInTrailer = false;
164 	m_isRotated = false;
165 	int plateCount = 0;
166 	uint startPos = 0;
167 	m_FontListe.clear();
168 	QFile f(fn);
169 	if (f.open(QIODevice::ReadOnly))
170 	{
171 		QByteArray tempBuf(9, ' ');
172 		f.read(tempBuf.data(), 8);
173 		if (getDouble(tempBuf.mid(0, 4), true) == 0xC5D0D3C6)
174 		{
175 			startPos = getDouble(tempBuf.mid(4, 4), false);
176 			if (m_doThumbnail)
177 			{
178 				f.seek(0);
179 				QByteArray tmp2buf(29, ' ');
180 				f.read(tmp2buf.data(), 28);
181 				uint thumbStart = 0;
182 				thumbStart = tmp2buf[20] & 0xff;
183 				thumbStart |= (tmp2buf[21] << 8) & 0xff00;
184 				thumbStart |= (tmp2buf[22] << 16) & 0xff0000;
185 				thumbStart |= (tmp2buf[23] << 24) & 0xff000000;
186 				uint thumbLen = 0;
187 				thumbLen = tmp2buf[24] & 0xff;
188 				thumbLen |= (tmp2buf[25] << 8) & 0xff00;
189 				thumbLen |= (tmp2buf[26] << 16) & 0xff0000;
190 				thumbLen |= (tmp2buf[27] << 24) & 0xff000000;
191 				if (thumbLen != 0)
192 				{
193 					QByteArray imgc(thumbLen, ' ');
194 					f.seek(thumbStart);
195 					f.read(imgc.data(), thumbLen);
196 					QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + "preview.tiff");
197 					QFile f2(tmpFile);
198 					if (f2.open(QIODevice::WriteOnly))
199 						f2.write(imgc.data(), thumbLen);
200 					f2.close();
201 					imgc.resize(0);
202 					ScImage thum;
203 					CMSettings cms(nullptr, "", Intent_Perceptual);
204 					cms.allowColorManagement(false);
205 					bool mode = true;
206 					if (thum.loadPicture(tmpFile, 1, cms, ScImage::RGBData, 72, &mode))
207 					{
208 						m_imageInfoRecord.exifDataValid = true;
209 						m_imageInfoRecord.exifInfo.thumbnail = thum.qImage().copy();
210 					}
211 					QFile::remove(tmpFile);
212 					m_hasThumbnail = true;
213 				}
214 			}
215 		}
216 		bool psFound = false;
217 		bool isAtend = false;
218 		QDataStream ts(&f);
219 		ts.device()->seek(startPos);
220 		while (!ts.atEnd())
221 		{
222 			tmp = readLineFromDataStream(ts);
223 			if (tmp.startsWith("%%Creator: "))
224 				m_Creator = tmp.remove("%%Creator: ");
225 			if (tmp.startsWith("%%Pages: "))
226 			{
227 				tmp = tmp.remove("%%Pages: ");
228 				bool ok;
229 				int pages = tmp.toInt( &ok );
230 				if (ok)
231 					m_imageInfoRecord.numberOfPages = pages;
232 			}
233 			if (tmp.startsWith("%%Trailer"))
234 				m_inTrailer = true;
235 			if (tmp.startsWith("%%BoundingBox:"))
236 			{
237 				found = true;
238 				if (m_inTrailer)
239 					m_BBoxInTrailer = true;
240 				m_BBox = tmp.remove("%%BoundingBox:");
241 			}
242 			if (!found)
243 			{
244 				if (tmp.startsWith("%%BoundingBox"))
245 				{
246 					found = true;
247 					if (m_inTrailer)
248 						m_BBoxInTrailer = true;
249 					m_BBox = tmp.remove("%%BoundingBox");
250 				}
251 			}
252 			if (tmp.startsWith("%%Orientation:"))
253 			{
254 				if (tmp.contains("Landscape"))
255 					m_isRotated = true;
256 			}
257 			if (tmp.startsWith("%%CyanPlate:"))
258 			{
259 				m_colorPlates.insert("Cyan", tmp.remove("%%CyanPlate: "));
260 				m_isDCS1 = true;
261 			}
262 			if (tmp.startsWith("%%MagentaPlate:"))
263 			{
264 				m_colorPlates.insert("Magenta", tmp.remove("%%MagentaPlate: "));
265 				m_isDCS1 = true;
266 			}
267 			if (tmp.startsWith("%%YellowPlate:"))
268 			{
269 				m_colorPlates.insert("Yellow", tmp.remove("%%YellowPlate: "));
270 				m_isDCS1 = true;
271 			}
272 			if (tmp.startsWith("%%BlackPlate:"))
273 			{
274 				m_colorPlates.insert("Black", tmp.remove("%%BlackPlate: "));
275 				m_isDCS1 = true;
276 			}
277 			if (tmp.startsWith("%%PlateFile: ("))
278 			{
279 				tmp = tmp.remove("%%PlateFile: (");
280 				int endNam = tmp.indexOf(")");
281 				QString plateNam = tmp.left(endNam);
282 				tmp = tmp.remove(plateNam+")");
283 				ScTextStream ts2(&tmp, QIODevice::ReadOnly);
284 				QString posStr, dummy, lenStr;
285 				ts2 >> dummy >> posStr >> lenStr;
286 				if (dummy == "EPS")
287 				{
288 					if (posStr.startsWith("#"))
289 					{
290 						posStr = posStr.remove(0, 1);
291 						uint pos = posStr.toUInt();
292 						uint len = lenStr.toUInt();
293 						struct plateOffsets offs;
294 						if (m_Creator.contains("Photoshop Version 9"))	// This is very strange, it seems that there is a bug in PS 9 which writes weird entries
295 						{
296 							pos -= (191 + plateCount * 83);
297 							len -= 83;
298 						}
299 						offs.pos = pos;
300 						offs.len = len;
301 						m_colorPlates2.insert(plateNam, offs);
302 						m_isDCS2 = true;
303 						plateCount++;
304 					}
305 					else
306 					{
307 						m_colorPlates.insert(plateNam, lenStr);
308 						m_isDCS2 = true;
309 						m_isDCS2multi = true;
310 					}
311 				}
312 			}
313 			if (tmp.startsWith("%%DocumentFonts:"))
314 			{
315 				tmp = tmp.remove("%%DocumentFonts:");
316 				ScTextStream ts2(&tmp, QIODevice::ReadOnly);
317 				QString tmp2;
318 				ts2 >> tmp2;
319 				if (!tmp2.contains("(atend)"))
320 				{
321 					if (!tmp2.isEmpty())
322 						m_FontListe.append(tmp2);
323 					while (!ts.atEnd())
324 					{
325 						uint oldPos = ts.device()->pos();
326 						tmp = readLineFromDataStream(ts);
327 						if (!tmp.startsWith("%%+"))
328 						{
329 							ts.device()->seek(oldPos);
330 							break;
331 						}
332 						tmp = tmp.remove(0,3);
333 						ScTextStream ts2(&tmp, QIODevice::ReadOnly);
334 						QString tmp2;
335 						ts2 >> tmp2;
336 						if (!tmp2.isEmpty())
337 							m_FontListe.append(tmp2);
338 					}
339 				}
340 				else
341 					isAtend = true;
342 			}
343 			if (tmp.startsWith("%%CMYKCustomColor"))
344 			{
345 				tmp = tmp.remove(0,18);
346 				ScTextStream ts2(&tmp, QIODevice::ReadOnly);
347 				ts2 >> c >> m >> y >> k;
348 				FarNam = ts2.readAll();
349 				FarNam = FarNam.trimmed();
350 				FarNam = FarNam.remove(0,1);
351 				FarNam = FarNam.remove(FarNam.length()-1,1);
352 				cc = ScColor(static_cast<int>(255 * c), static_cast<int>(255 * m), static_cast<int>(255 * y), static_cast<int>(255 * k));
353 				cc.setSpotColor(true);
354 				m_CustColors.insert(FarNam, cc);
355 				while (!ts.atEnd())
356 				{
357 					uint oldPos = ts.device()->pos();
358 					tmp = readLineFromDataStream(ts);
359 					if (!tmp.startsWith("%%+"))
360 					{
361 						ts.device()->seek(oldPos);
362 						break;
363 					}
364 					tmp = tmp.remove(0,3);
365 					ScTextStream ts2(&tmp, QIODevice::ReadOnly);
366 					ts2 >> c >> m >> y >> k;
367 					FarNam = ts2.readAll();
368 					FarNam = FarNam.trimmed();
369 					FarNam = FarNam.remove(0,1);
370 					FarNam = FarNam.remove(FarNam.length()-1,1);
371 					cc = ScColor(static_cast<int>(255 * c), static_cast<int>(255 * m), static_cast<int>(255 * y), static_cast<int>(255 * k));
372 					cc.setSpotColor(true);
373 					m_CustColors.insert(FarNam, cc);
374 				}
375 			}
376 			if (tmp.startsWith("%%EndComments"))
377 			{
378 				while (!ts.atEnd())
379 				{
380 					tmp = readLineFromDataStream(ts);
381 					if ((!tmp.isEmpty()) && (!tmp.startsWith("%")))
382 					{
383 						psFound = true;
384 						break;
385 					}
386 					if (tmp.startsWith("%ImageData: "))
387 					{
388 						m_hasPhotoshopImageData = true;
389 						tmp.remove("%ImageData: ");
390 						ScTextStream ts2(&tmp, QIODevice::ReadOnly);
391 						ts2 >> m_psXSize >> m_psYSize >> m_psDepth >> m_psMode >> m_psChannel >> m_psBlock >> m_psDataType >> m_psCommand;
392 						m_psCommand = m_psCommand.remove(0,1);
393 						m_psCommand = m_psCommand.remove(m_psCommand.length()-1,1);
394 					}
395 					if (tmp.startsWith("%BeginPhotoshop"))
396 					{
397 						QByteArray psdata;
398 						while (!ts.atEnd())
399 						{
400 							tmp = readLineFromDataStream(ts);
401 							if (tmp.startsWith("%EndPhotoshop"))
402 							{
403 								QDataStream strPhot( &psdata, QIODevice::ReadOnly);
404 								strPhot.setByteOrder( QDataStream::BigEndian );
405 								PSDHeader fakeHeader;
406 								ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
407 								ts2 >> x >> y >> b >> h;
408 								fakeHeader.width = qRound(b);
409 								fakeHeader.height = qRound(h);
410 								parseResourceData(strPhot, fakeHeader, psdata.size());
411 								m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size()) > 0;
412 								if (!m_imageInfoRecord.PDSpathData.empty())
413 								{
414 									QTransform mm;
415 									mm.scale(m_imageInfoRecord.xres / 72.0, m_imageInfoRecord.yres / 72.0);
416 									QMap<QString, FPointArray>::Iterator it;
417 									for (it = m_imageInfoRecord.PDSpathData.begin(); it != m_imageInfoRecord.PDSpathData.end(); ++it)
418 									{
419 										it.value().map(mm);
420 									}
421 								}
422 								m_isPhotoshop = true;
423 								break;
424 							}
425 							for (int a = 2; a < tmp.length(); a += 2)
426 							{
427 								bool ok;
428 								ushort data = tmp.midRef(a, 2).toUShort(&ok, 16);
429 								psdata.resize(psdata.size()+1);
430 								psdata[psdata.size()-1] = data;
431 							}
432 						}
433 						if ((m_doThumbnail) && ((m_hasThumbnail) || (!m_imageInfoRecord.exifInfo.thumbnail.isNull())))
434 							return true;
435 					}
436 					if (tmp.startsWith("%%BeginICCProfile:"))
437 					{
438 						QByteArray psdata;
439 						while (!ts.atEnd())
440 						{
441 							tmp = readLineFromDataStream(ts);
442 							for (int a = 2; a < tmp.length(); a += 2)
443 							{
444 								bool ok;
445 								ushort data = tmp.midRef(a, 2).toUShort(&ok, 16);
446 								psdata.resize(psdata.size()+1);
447 								psdata[psdata.size()-1] = data;
448 							}
449 							if (tmp.startsWith("%%EndICCProfile"))
450 							{
451 								ScColorMgmtEngine engine(ScCore->defaultEngine);
452 								ScColorProfile prof = engine.openProfileFromMem(psdata);
453 								if (prof)
454 								{
455 									if (prof.colorSpace() == ColorSpace_Rgb)
456 										m_profileComponents = 3;
457 									if (prof.colorSpace() == ColorSpace_Cmyk)
458 										m_profileComponents = 4;
459 									m_imageInfoRecord.profileName = prof.productDescription();
460 									m_imageInfoRecord.isEmbedded = true;
461 									m_embeddedProfile = psdata;
462 								}
463 								break;
464 							}
465 						}
466 					}
467 					if (psFound)
468 						break;
469 				}
470 			}
471 			if ((psFound) && (!isAtend))
472 				break;
473 		}
474 	}
475 	f.close();
476 	return true;
477 }
478 
loadPicture(const QString & fn,int page,int gsRes,bool thumbnail)479 bool ScImgDataLoader_PS::loadPicture(const QString& fn, int page, int gsRes, bool thumbnail)
480 {
481 	double x = 0;
482 	double y = 0;
483 	double b = 0;
484 	double h = 0;
485 	bool found = false;
486 	QFileInfo fi = QFileInfo(fn);
487 	if (!fi.exists())
488 		return false;
489 	QString ext = fi.suffix().toLower();
490 	if (ext.isEmpty())
491 		ext = getImageType(fn);
492 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + QString("sc%1.png").arg(qMax(1, page)));
493 	QString tmpFiles = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc%d.png");
494 	QString picFile = QDir::toNativeSeparators(fn);
495 	float xres = gsRes;
496 	float yres = gsRes;
497 
498 	initialize();
499 
500 	m_imageInfoRecord.type = ImageTypeEPS;
501 	m_imageInfoRecord.exifDataValid = false;
502 	m_imageInfoRecord.numberOfPages = 1; // will be overwritten by parse()
503 	m_doThumbnail = thumbnail;
504 	m_colorPlates2.clear();
505 	m_colorPlates.clear();
506 	m_CustColors.clear();
507 	m_CustColors.insert("Cyan", ScColor(255, 0, 0, 0));
508 	m_CustColors.insert("Magenta", ScColor(0, 255, 0, 0));
509 	m_CustColors.insert("Yellow", ScColor(0, 0, 255, 0));
510 	m_CustColors.insert("Black", ScColor(0, 0, 0, 255));
511 	found = parseData(fn);
512 	if (m_FontListe.count() != 0)
513 	{
514 		scanForFonts(fn);
515 		if (m_FontListe.count() != 0)
516 		{
517 			bool missing = false;
518 			QString missingF = "";
519 			for (int fo = 0; fo < m_FontListe.count(); fo++)
520 			{
521 				if (!PrefsManager::instance().appPrefs.fontPrefs.AvailFonts.contains(m_FontListe[fo]))
522 				{
523 					missing = true;
524 					missingF += m_FontListe[fo]+"\n";
525 				}
526 			}
527 			if (missing)
528 			{
529 				m_message = QObject::tr("The Font(s):\n%1 are not embedded or available for Scribus.\nThey might be replaced by \"Courier\", depending how your Ghostscript is configured.\nTherefore the image may be not correct").arg(missingF);
530 				m_msgType = warningMsg;
531 			}
532 		}
533 	}
534 	if ((thumbnail) && (m_imageInfoRecord.exifDataValid) && (!m_imageInfoRecord.exifInfo.thumbnail.isNull()))
535 	{
536 		ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
537 		ts2 >> x >> y >> b >> h;
538 		m_imageInfoRecord.exifInfo.width = qRound(b);
539 		m_imageInfoRecord.exifInfo.height = qRound(h);
540 		m_image = m_imageInfoRecord.exifInfo.thumbnail;
541 		if ((m_isPhotoshop) && (m_hasPhotoshopImageData))
542 		{
543 			m_imageInfoRecord.exifInfo.width = m_psXSize;
544 			m_imageInfoRecord.exifInfo.height = m_psYSize;
545 			m_imageInfoRecord.type = ImageType7;
546 			if (m_psMode == 4)
547 			{
548 				m_imageInfoRecord.colorspace = ColorSpaceCMYK;
549 				QRgb *s;
550 				unsigned char cc, cm, cy, ck;
551 				for (int yit = 0; yit < m_image.height(); ++yit)
552 				{
553 					s = (QRgb*)(m_image.scanLine( yit ));
554 					for (int xit = 0; xit < m_image.width(); ++xit)
555 					{
556 						cc = 255 - qRed(*s);
557 						cm = 255 - qGreen(*s);
558 						cy = 255 - qBlue(*s);
559 						ck = qMin(qMin(cc, cm), cy);
560 						*s = qRgba(cc-ck,cm-ck,cy-ck,ck);
561 						s++;
562 					}
563 				}
564 			}
565 			else
566 				m_imageInfoRecord.colorspace = ColorSpaceRGB;
567 		}
568 		else
569 			m_imageInfoRecord.colorspace = ColorSpaceRGB;
570 		m_imageInfoRecord.actualPageNumber = page;
571 		m_pixelFormat = Format_BGRA_8;
572 		return true;
573 	}
574 	if (found)
575 	{
576 		if (m_isDCS1)
577 			loadDCS1(fn, gsRes);
578 		else if (m_isDCS2)
579 			loadDCS2(fn, gsRes);
580 		else if ((m_isPhotoshop) && (m_hasPhotoshopImageData))
581 			loadPhotoshop(fn, gsRes);
582 		else if ((!m_imageInfoRecord.isEmbedded) || ((m_imageInfoRecord.isEmbedded) && (m_profileComponents == 3)))
583 		{
584 			ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
585 			ts2 >> x >> y >> b >> h;
586 			QStringList args;
587 			xres = gsRes;
588 			yres = gsRes;
589 			if (extensionIndicatesEPS(ext))
590 			{
591 				if (!m_BBoxInTrailer)
592 					args.append("-dEPSCrop");
593 			}
594 			args.append("-r"+QString::number(gsRes));
595 			args.append("-sOutputFile="+tmpFiles);
596 			args.append(picFile);
597 			h = h * gsRes / 72.0;
598 			int retg = callGS(args);
599 			if (retg == 0)
600 			{
601 				m_image.load(tmpFile);
602 				if ((extensionIndicatesEPS(ext) && m_BBoxInTrailer) || (m_isRotated))
603 				{
604 					int ex = qRound(x * gsRes / 72.0);
605 					int ey = qRound(m_image.height() - h);
606 					int ew = qRound((b - x) * gsRes / 72.0);
607 					int eh = qRound(h - y * gsRes / 72.0);
608 					m_image = m_image.copy(ex, ey, ew, eh);
609 				}
610 				if ((!ScCore->havePNGAlpha()) || (m_isRotated))
611 				{
612 					int wi = m_image.width();
613 					int hi = m_image.height();
614 					QRgb alphaFF = qRgba(255,255,255,255);
615 					QRgb alpha00 = qRgba(255,255,255,  0);
616 					QRgb *s;
617 					for (int yi = 0; yi < hi; ++yi)
618 					{
619 						s = (QRgb*)(m_image.scanLine( yi ));
620 						for (int xi = 0; xi < wi; ++xi)
621 						{
622 							if ((*s) == alphaFF)
623 								(*s) &= alpha00;
624 							s++;
625 						}
626 					}
627 				}
628 
629 				QStringList files = QStringList("sc*.png");
630 				files = QDir(ScPaths::tempFileDir()).entryList(files);
631 				for (int i=0; i < files.count(); ++i)
632 					QFile::remove(QDir::toNativeSeparators(ScPaths::tempFileDir() + files[i]));
633 
634 				if (extensionIndicatesEPS(ext))
635 				{
636 					m_imageInfoRecord.BBoxX = static_cast<int>(x);
637 					m_imageInfoRecord.BBoxH = static_cast<int>(h);
638 				}
639 				else
640 				{
641 					m_imageInfoRecord.BBoxX = 0;
642 					m_imageInfoRecord.BBoxH = m_image.height();
643 				}
644 				m_imageInfoRecord.xres = gsRes;
645 				m_imageInfoRecord.yres = gsRes;
646 				if ((m_imageInfoRecord.isEmbedded) && (m_profileComponents == 3))
647 					m_imageInfoRecord.type = ImageType7;
648 				m_imageInfoRecord.colorspace = ColorSpaceRGB;
649 				m_image.setDotsPerMeterX ((int) (xres / 0.0254));
650 				m_image.setDotsPerMeterY ((int) (yres / 0.0254));
651 				m_pixelFormat = Format_BGRA_8;
652 			}
653 		}
654 		else
655 		{
656 			ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
657 			ts2 >> x >> y >> b >> h;
658 			h = h * gsRes / 72.0;
659 			QStringList args;
660 			xres = gsRes;
661 			yres = gsRes;
662 			if (extensionIndicatesEPS(ext))
663 				args.append("-dEPSCrop");
664 			args.append("-dGrayValues=256");
665 			args.append("-r"+QString::number(gsRes));
666 			args.append("-sOutputFile="+tmpFiles);
667 			args.append(picFile);
668 //			qDebug() << "scimgdataloader_ps:" << args;
669 			int retg = callGS(args);
670 			if (retg == 0)
671 			{
672 				m_image.load(tmpFile);
673 				x = 0;
674 				b = m_image.width() / gsRes * 72.0;
675 				h = m_image.height() / gsRes * 72.0;
676 			}
677 			retg = callGS(args, "bitcmyk");
678 			if (retg == 0)
679 			{
680 				m_image = QImage( qRound(b * gsRes / 72.0), qRound(h * gsRes / 72.0), QImage::Format_ARGB32 );
681 				m_image.fill(qRgba(0, 0, 0, 0));
682 				int w = qRound(b * gsRes / 72.0);
683 				int w2 = 4*w;
684 				int h2 = qRound(h * gsRes / 72.0);
685 				uint *p;
686 				int cyan, magenta, yellow, black;
687 				QByteArray imgc(w2, ' ');
688 				QFile f(tmpFile);
689 				if (f.open(QIODevice::ReadOnly))
690 				{
691 					for (int y=0; y < h2; ++y )
692 					{
693 						p = (uint *)m_image.scanLine( y );
694 						f.read(imgc.data(), w2);
695 						for (int x=0; x < w2; x += 4 )
696 						{
697 							cyan = uchar(imgc[x]);
698 							magenta = uchar(imgc[x + 1]);
699 							yellow = uchar(imgc[x + 2]);
700 							black = uchar(imgc[x + 3]);
701 							*p = qRgba(cyan, magenta, yellow, black);
702 							p++;
703 						}
704 					}
705 					f.close();
706 				}
707 
708 				QStringList files = QStringList("sc*.png");
709 				files = QDir(ScPaths::tempFileDir()).entryList(files);
710 				for (int i=0; i < files.count(); ++i)
711 					QFile::remove(QDir::toNativeSeparators(ScPaths::tempFileDir() + files[i]));
712 
713 				if (extensionIndicatesEPS(ext))
714 				{
715 					m_imageInfoRecord.BBoxX = static_cast<int>(x);
716 					m_imageInfoRecord.BBoxH = static_cast<int>(h);
717 				}
718 				else
719 				{
720 					m_imageInfoRecord.BBoxX = 0;
721 					m_imageInfoRecord.BBoxH = m_image.height();
722 				}
723 				m_imageInfoRecord.xres = gsRes;
724 				m_imageInfoRecord.yres = gsRes;
725 				m_imageInfoRecord.colorspace = ColorSpaceCMYK;
726 				m_imageInfoRecord.type = ImageType7;
727 				m_image.setDotsPerMeterX ((int) (xres / 0.0254));
728 				m_image.setDotsPerMeterY ((int) (yres / 0.0254));
729 				m_pixelFormat = Format_YMCK_8;
730 			}
731 			else
732 			{
733 				qDebug() << "Ghostscript returned result" << retg;
734 			}
735 		}
736 		m_imageInfoRecord.actualPageNumber = page;
737 		return true;
738 	}
739 	return false;
740 }
741 
loadPhotoshop(const QString & fn,int gsRes)742 void ScImgDataLoader_PS::loadPhotoshop(const QString& fn, int gsRes)
743 {
744 	if ((m_psDataType >= 1) && (m_psDataType <= 6) && ((m_psMode == 3) || (m_psMode == 4)))
745 	{
746 		loadPhotoshopBinary(fn);
747 		return;
748 	}
749 	QStringList args;
750 	QFileInfo fi = QFileInfo(fn);
751 	QString ext = fi.suffix().toLower();
752 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc1.png");
753 	int retg;
754 	int GsVersion;
755 	getNumericGSVersion(GsVersion);
756 	ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
757 	double x, y, b, h;
758 	ts2 >> x >> y >> b >> h;
759 	h = h * gsRes / 72.0;
760 	if (extensionIndicatesEPS(ext))
761 		args.append("-dEPSCrop");
762 	if (m_psMode == 4)
763 		args.append("-dGrayValues=256");
764 	if (GsVersion >= 853)
765 		args.append("-dNOPSICC");		// prevent GS from applying an embedded ICC profile as it will be applied later on in ScImage.
766 	args.append("-r"+QString::number(gsRes));
767 	args.append("-sOutputFile=" + tmpFile);
768 	args.append(QDir::toNativeSeparators(fn));
769 	if (m_psMode == 4)
770 		retg = callGS(args, "bitcmyk");
771 	else
772 		retg = callGS(args);
773 	if (retg == 0)
774 	{
775 		if (m_psMode == 4)
776 		{
777 			m_image = QImage( qRound(b * gsRes / 72.0), qRound(h * gsRes / 72.0), QImage::Format_ARGB32 );
778 			m_image.fill(qRgba(0, 0, 0, 0));
779 			int w = qRound(b * gsRes / 72.0);
780 			int w2 = 4*w;
781 			int h2 = qRound(h * gsRes / 72.0);
782 			uint *p;
783 			int cyan, magenta, yellow, black;
784 			QByteArray imgc(w2, ' ');
785 			QFile f(tmpFile);
786 			if (f.open(QIODevice::ReadOnly))
787 			{
788 				for (int y=0; y < h2; ++y )
789 				{
790 					p = (uint *)m_image.scanLine( y );
791 					f.read(imgc.data(), w2);
792 					for (int x=0; x < w2; x += 4 )
793 					{
794 						cyan = uchar(imgc[x]);
795 						magenta = uchar(imgc[x + 1]);
796 						yellow = uchar(imgc[x + 2]);
797 						black = uchar(imgc[x + 3]);
798 						*p = qRgba(cyan, magenta, yellow, black);
799 						p++;
800 					}
801 				}
802 				f.close();
803 			}
804 			m_imageInfoRecord.colorspace = ColorSpaceCMYK;
805 			m_imageInfoRecord.type = ImageType7;
806 			m_pixelFormat = Format_YMCK_8;
807 		}
808 		else
809 		{
810 			m_image.load(tmpFile);
811 			if (!ScCore->havePNGAlpha())
812 			{
813 				int wi = m_image.width();
814 				int hi = m_image.height();
815 				QRgb alphaFF = qRgba(255,255,255,255);
816 				QRgb alpha00 = qRgba(255,255,255,  0);
817 				QRgb *s;
818 				for (int yi = 0; yi < hi; ++yi)
819 				{
820 					s = (QRgb*)(m_image.scanLine( yi ));
821 					for (int xi = 0; xi < wi; ++xi)
822 					{
823 						if ((*s) == alphaFF)
824 							(*s) &= alpha00;
825 						s++;
826 					}
827 				}
828 			}
829 			m_imageInfoRecord.type = ImageType7;
830 			m_imageInfoRecord.colorspace = ColorSpaceRGB;
831 			m_pixelFormat = Format_BGRA_8;
832 		}
833 
834 		QFile::remove(tmpFile);
835 
836 		if (extensionIndicatesEPS(ext))
837 		{
838 			m_imageInfoRecord.BBoxX = static_cast<int>(x);
839 			m_imageInfoRecord.BBoxH = static_cast<int>(h);
840 		}
841 		else
842 		{
843 			m_imageInfoRecord.BBoxX = 0;
844 			m_imageInfoRecord.BBoxH = m_image.height();
845 		}
846 		m_image.setDotsPerMeterX ((int) (m_imageInfoRecord.xres / 0.0254));
847 		m_image.setDotsPerMeterY ((int) (m_imageInfoRecord.yres / 0.0254));
848 	}
849 	else
850 	{
851 		qDebug() << "Ghostscript returned result" << retg;
852 	}
853 }
854 
decodeA85(QByteArray & psdata,const QString & tmp)855 void ScImgDataLoader_PS::decodeA85(QByteArray &psdata, const QString& tmp)
856 {
857 	uchar byte;
858 	ushort data;
859 	unsigned long sum = 0;
860 	int quintet = 0;
861 	for (int c = 0; c < tmp.length(); c++)
862 	{
863 		byte = QChar(tmp.at(c)).cell();
864 		if (byte >= '!' && byte <= 'u')
865 		{
866 			sum = sum * 85 + ((unsigned long)byte - '!');
867 			quintet++;
868 			if (quintet == 5)
869 			{
870 				psdata.resize(psdata.size()+4);
871 				data = sum >> 24;
872 				psdata[psdata.size()-4] = data;
873 				data = (sum >> 16) & 0xFF;
874 				psdata[psdata.size()-3] = data;
875 				data = (sum >> 8) & 0xFF;
876 				psdata[psdata.size()-2] = data;
877 				data = sum & 0xFF;
878 				psdata[psdata.size()-1] = data;
879 				quintet = 0;
880 				sum = 0;
881 			}
882 		}
883 		else if (byte == 'z')
884 		{
885 			psdata.resize(psdata.size()+4);
886 			psdata[psdata.size()-4] = 0;
887 			psdata[psdata.size()-3] = 0;
888 			psdata[psdata.size()-2] = 0;
889 			psdata[psdata.size()-1] = 0;
890 		}
891 		else if (byte == '~')
892 		{
893 			if (quintet)
894 			{
895 				int i;
896 				for (i = 0; i < 5 - quintet; i++)
897 					sum *= 85;
898 				if (quintet > 1)
899 					sum += (0xFFFFFF >> ((quintet - 2) * 8));
900 				for (i = 0; i < quintet - 1; i++)
901 				{
902 					data = (sum >> (24 - 8 * i)) & 0xFF;
903 					psdata.resize(psdata.size()+1);
904 					psdata[psdata.size()-1] = data;
905 				}
906 			}
907 			break;
908 		}
909 	}
910 }
911 
912 typedef struct my_error_mgr
913 {
914 	struct jpeg_error_mgr pub;            /* "public" fields */
915 	jmp_buf setjmp_buffer;  /* for return to caller */
916 } *my_error_ptr;
917 
my_error_exit(j_common_ptr cinfo)918 static void my_error_exit (j_common_ptr cinfo)
919 {
920 	my_error_ptr myerr = (my_error_ptr) cinfo->err;
921 	(*cinfo->err->output_message) (cinfo);
922 	longjmp (myerr->setjmp_buffer, 1);
923 }
924 
loadPSjpeg(const QString & fn)925 bool ScImgDataLoader_PS::loadPSjpeg(const QString& fn)
926 {
927 	if (!QFile::exists(fn))
928 		return false;
929 	struct jpeg_decompress_struct cinfo;
930 	struct my_error_mgr jerr;
931 	FILE *infile;
932 	cinfo.err = jpeg_std_error (&jerr.pub);
933 	jerr.pub.error_exit = my_error_exit;
934 	infile = nullptr;
935 	if (setjmp (jerr.setjmp_buffer))
936 	{
937 		jpeg_destroy_decompress (&cinfo);
938 		if (infile)
939 			fclose (infile);
940 		return false;
941 	}
942 	jpeg_create_decompress (&cinfo);
943 #if defined(Q_OS_WIN32)
944 	if ((infile = _wfopen((const wchar_t*) fn.utf16(), L"rb")) == nullptr)
945 		return false;
946 #else
947 	if ((infile = fopen (fn.toLocal8Bit(), "rb")) == nullptr)
948 		return false;
949 #endif
950 	jpeg_stdio_src(&cinfo, infile);
951 	jpeg_read_header(&cinfo, true);
952 	jpeg_start_decompress(&cinfo);
953 	if ( cinfo.output_components == 3 || cinfo.output_components == 4)
954 		m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 );
955 	else if ( cinfo.output_components == 1 )
956 	{
957 		m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8 );
958 		m_image.setColorCount(256);
959 		for (int i=0; i<256; i++)
960 			m_image.setColor(i, qRgb(i,i,i));
961 	}
962 	if (!m_image.isNull())
963 	{
964 		uchar* data = m_image.bits();
965 		int bpl = m_image.bytesPerLine();
966 		while (cinfo.output_scanline < cinfo.output_height)
967 		{
968 			uchar *d = data + cinfo.output_scanline * bpl;
969 			(void) jpeg_read_scanlines(&cinfo, &d, 1);
970 		}
971 		if ( cinfo.output_components == 3 )
972 		{
973 			uchar *in;
974 			QRgb *out;
975 			for (uint j=0; j<cinfo.output_height; j++)
976 			{
977 				in = m_image.scanLine(j) + cinfo.output_width * 3;
978 				out = (QRgb*) m_image.scanLine(j);
979 				for (uint i=cinfo.output_width; i--; )
980 				{
981 					in -= 3;
982 					out[i] = qRgb(in[0], in[1], in[2]);
983 				}
984 			}
985 			m_pixelFormat = Format_BGRA_8;
986 		}
987 		if ( cinfo.output_components == 4 )
988 		{
989 			QRgb *ptr;
990 			unsigned char c, m, y ,k;
991 			unsigned char *p;
992 			for (int i = 0; i < m_image.height(); i++)
993 			{
994 				ptr = (QRgb*)  m_image.scanLine(i);
995 				if ((cinfo.jpeg_color_space == JCS_YCCK) || ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)))
996 				{
997 					for (int j = 0; j <  m_image.width(); j++)
998 					{
999 						p = (unsigned char *) ptr;
1000 						c = p[0];
1001 						m = p[1];
1002 						y =  p[2];
1003 						k =  p[3];
1004 						*ptr = qRgba(c, m, y, k);
1005 						ptr++;
1006 					}
1007 				}
1008 				else
1009 				{
1010 					for (int j = 0; j <  m_image.width(); j++)
1011 					{
1012 						p = (unsigned char *) ptr;
1013 						c = p[0];
1014 						m = p[1];
1015 						y =  p[2];
1016 						k =  p[3];
1017 						*ptr = qRgba(y, m, c, k);
1018 						ptr++;
1019 					}
1020 				}
1021 			}
1022 			m_pixelFormat = Format_YMCK_8;
1023 		}
1024 		if ( cinfo.output_components == 1 )
1025 		{
1026 			QImage tmpImg = m_image.convertToFormat(QImage::Format_ARGB32);
1027 			m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 );
1028 			QRgb *s;
1029 			QRgb *d;
1030 			for (int yi=0; yi < tmpImg.height(); ++yi)
1031 			{
1032 				s = (QRgb*)(tmpImg.scanLine( yi ));
1033 				d = (QRgb*)(m_image.scanLine( yi ));
1034 				for (int xi=0; xi < tmpImg.width(); ++xi)
1035 				{
1036 					(*d) = (*s);
1037 					s++;
1038 					d++;
1039 				}
1040 			}
1041 			m_pixelFormat = Format_BGRA_8;
1042 		}
1043 	}
1044 	(void) jpeg_finish_decompress(&cinfo);
1045 	fclose (infile);
1046 	jpeg_destroy_decompress (&cinfo);
1047 	return (!m_image.isNull());
1048 }
1049 
loadPSjpeg(const QString & fn,QImage & tmpImg)1050 bool ScImgDataLoader_PS::loadPSjpeg(const QString& fn, QImage &tmpImg)
1051 {
1052 	if (!QFile::exists(fn))
1053 		return false;
1054 	struct jpeg_decompress_struct cinfo;
1055 	struct my_error_mgr         jerr;
1056 	FILE     *infile;
1057 	cinfo.err = jpeg_std_error (&jerr.pub);
1058 	jerr.pub.error_exit = my_error_exit;
1059 	infile = nullptr;
1060 	if (setjmp (jerr.setjmp_buffer))
1061 	{
1062 		jpeg_destroy_decompress (&cinfo);
1063 		if (infile)
1064 			fclose (infile);
1065 		return false;
1066 	}
1067 	jpeg_create_decompress (&cinfo);
1068 #if defined(Q_OS_WIN32)
1069 	if ((infile = _wfopen((const wchar_t*) fn.utf16(), L"rb")) == nullptr)
1070 		return false;
1071 #else
1072 	if ((infile = fopen (fn.toLocal8Bit(), "rb")) == nullptr)
1073 		return false;
1074 #endif
1075 	jpeg_stdio_src(&cinfo, infile);
1076 	jpeg_read_header(&cinfo, true);
1077 	jpeg_start_decompress(&cinfo);
1078 	if ( cinfo.output_components == 3 || cinfo.output_components == 4)
1079 		tmpImg = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 );
1080 	else if ( cinfo.output_components == 1 )
1081 	{
1082 		tmpImg = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8 );
1083 		m_image.setColorCount(256);
1084 		for (int i=0; i<256; i++)
1085 			tmpImg.setColor(i, qRgb(i,i,i));
1086 		m_pixelFormat = Format_GRAY_8;
1087 	}
1088 	if (!tmpImg.isNull())
1089 	{
1090 		uchar* data = tmpImg.bits();
1091 		int bpl = tmpImg.bytesPerLine();
1092 		while (cinfo.output_scanline < cinfo.output_height)
1093 		{
1094 			uchar *d = data + cinfo.output_scanline * bpl;
1095 			(void) jpeg_read_scanlines(&cinfo, &d, 1);
1096 		}
1097 		if ( cinfo.output_components == 3 )
1098 		{
1099 			uchar *in;
1100 			QRgb *out;
1101 			for (uint j=0; j<cinfo.output_height; j++)
1102 			{
1103 				in = tmpImg.scanLine(j) + cinfo.output_width * 3;
1104 				out = (QRgb*) tmpImg.scanLine(j);
1105 				for (uint i=cinfo.output_width; i--; )
1106 				{
1107 					in -= 3;
1108 					out[i] = qRgb(in[0], in[1], in[2]);
1109 				}
1110 			}
1111 			m_pixelFormat = Format_BGRA_8;
1112 		}
1113 		if ( cinfo.output_components == 4 )
1114 		{
1115 			QRgb *ptr;
1116 			unsigned char c, m, y ,k;
1117 			unsigned char *p;
1118 			for (int i = 0; i < tmpImg.height(); i++)
1119 			{
1120 				ptr = (QRgb*)  tmpImg.scanLine(i);
1121 				if ((cinfo.jpeg_color_space == JCS_YCCK) || ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)))
1122 				{
1123 					for (int j = 0; j <  tmpImg.width(); j++)
1124 					{
1125 						p = (unsigned char *) ptr;
1126 						c = p[0];
1127 						m = p[1];
1128 						y =  p[2];
1129 						k =  p[3];
1130 						*ptr = qRgba(c, m, y, k);
1131 						ptr++;
1132 					}
1133 				}
1134 				else
1135 				{
1136 					for (int j = 0; j <  tmpImg.width(); j++)
1137 					{
1138 						p = (unsigned char *) ptr;
1139 						c = p[0];
1140 						m = p[1];
1141 						y =  p[2];
1142 						k =  p[3];
1143 						*ptr = qRgba(y, m, c, k);
1144 						ptr++;
1145 					}
1146 				}
1147 			}
1148 			m_pixelFormat = Format_YMCK_8;
1149 		}
1150 		if ( cinfo.output_components == 1 )
1151 		{
1152 			QImage tmpImg2 = tmpImg.convertToFormat(QImage::Format_ARGB32);
1153 			tmpImg = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 );
1154 			QRgb *s;
1155 			QRgb *d;
1156 			for (int yi=0; yi < tmpImg2.height(); ++yi)
1157 			{
1158 				s = (QRgb*)(tmpImg2.scanLine( yi ));
1159 				d = (QRgb*)(tmpImg.scanLine( yi ));
1160 				for (int xi=0; xi < tmpImg2.width(); ++xi)
1161 				{
1162 					(*d) = (*s);
1163 					s++;
1164 					d++;
1165 				}
1166 			}
1167 		}
1168 	}
1169 	(void) jpeg_finish_decompress(&cinfo);
1170 	fclose (infile);
1171 	jpeg_destroy_decompress (&cinfo);
1172 	return (!tmpImg.isNull());
1173 }
1174 
loadPhotoshopBinary(const QString & fn)1175 void ScImgDataLoader_PS::loadPhotoshopBinary(const QString& fn)
1176 {
1177 	double x, y, b, h;
1178 	ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
1179 	ts2 >> x >> y >> b >> h;
1180 	QString tmpFile(QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc1.jpg"));
1181 	QFile f2(tmpFile);
1182 	QString tmp;
1183 	m_image = QImage(m_psXSize, m_psYSize, QImage::Format_ARGB32);
1184 	m_image.fill(qRgba(0, 0, 0, 0));
1185 	m_imageInfoRecord.xres = qRound(m_psXSize / b * 72.0);
1186 	m_imageInfoRecord.yres = qRound(m_psYSize / h * 72.0);
1187 	QByteArray psdata;
1188 	QFile f(fn);
1189 	int yCount = 0;
1190 	if (!f.open(QIODevice::ReadOnly))
1191 	{
1192 		qDebug()<<"Failed to open QFile f in ScImgDataLoader_PS::loadPhotoshopBinary";
1193 		return;
1194 	}
1195 	if (m_psDataType > 2)
1196 	{
1197 		if (!f2.open(QIODevice::WriteOnly))
1198 		{
1199 			qDebug()<<"Failed to open QFile f2 in ScImgDataLoader_PS::loadPhotoshopBinary";
1200 			return;
1201 		}
1202 	}
1203 	QDataStream ts(&f);
1204 	while (!ts.atEnd())
1205 	{
1206 		tmp = readLineFromDataStream(ts);
1207 		if (tmp == m_psCommand)
1208 		{
1209 			if (m_psDataType == 1)
1210 			{
1211 				QRgb *p;
1212 				uchar cc, cm, cy, ck;
1213 				for (int yh = 0; yh < m_image.height(); ++yh )
1214 				{
1215 					if (m_psMode == 4)
1216 						psdata.resize(m_psXSize * (4 + m_psChannel));
1217 					else
1218 						psdata.resize(m_psXSize * (3 + m_psChannel));
1219 					f.read(psdata.data(), psdata.size());
1220 					p = (QRgb *)m_image.scanLine( yh );
1221 					for (int xh = 0; xh < m_image.width(); ++xh )
1222 					{
1223 						cc = psdata[xh];
1224 						cm = psdata[m_psXSize+xh];
1225 						cy = psdata[m_psXSize*2+xh];
1226 						ck = psdata[m_psXSize*3+xh];
1227 						if (m_psMode == 4)
1228 							*p = qRgba(cc, cm, cy, ck);
1229 						else
1230 							*p = qRgba(cc, cm, cy, 255);
1231 						p++;
1232 					}
1233 				}
1234 			}
1235 			else if (m_psDataType > 1)
1236 			{
1237 				while (!ts.atEnd())
1238 				{
1239 					tmp = readLineFromDataStream(ts);
1240 					if ((tmp.isEmpty()) || (tmp.startsWith("%%EndBinary")))
1241 						break;
1242 					if (m_psDataType == 2)
1243 					{
1244 						for (int a = 0; a < tmp.length(); a += 2)
1245 						{
1246 							bool ok;
1247 							ushort data = tmp.midRef(a, 2).toUShort(&ok, 16);
1248 							psdata.resize(psdata.size()+1);
1249 							psdata[psdata.size()-1] = data;
1250 						}
1251 					}
1252 					else
1253 					{
1254 						decodeA85(psdata, tmp);
1255 						f2.write(psdata.data(), psdata.size());
1256 						psdata.resize(0);
1257 					}
1258 				}
1259 				if (m_psDataType > 2)
1260 				{
1261 					f2.close();
1262 					loadPSjpeg(tmpFile);
1263 					QFile::remove(tmpFile);
1264 				}
1265 				else
1266 				{
1267 					QRgb *p;
1268 					uchar cc, cm, cy, ck;
1269 					for (int yh = 0; yh < m_image.height(); ++yh )
1270 					{
1271 						p = (QRgb *)m_image.scanLine( yh );
1272 						for (int xh = 0; xh < m_image.width(); ++xh )
1273 						{
1274 							cc = psdata[yCount+xh];
1275 							cm = psdata[yCount+m_psXSize+xh];
1276 							cy = psdata[yCount+m_psXSize*2+xh];
1277 							if (m_psMode == 4)
1278 							{
1279 								ck = psdata[yCount+m_psXSize*3+xh];
1280 								*p = qRgba(cc, cm, cy, ck);
1281 							}
1282 							else
1283 								*p = qRgba(cc, cm, cy, 255);
1284 							p++;
1285 						}
1286 						if (m_psMode == 4)
1287 							yCount += m_psXSize * (4 + m_psChannel);
1288 						else
1289 							yCount += m_psXSize * (3 + m_psChannel);
1290 					}
1291 				}
1292 			}
1293 			if (m_psMode == 4)
1294 			{
1295 				m_imageInfoRecord.colorspace = ColorSpaceCMYK;
1296 				m_pixelFormat = Format_YMCK_8;
1297 			}
1298 			else
1299 			{
1300 				m_imageInfoRecord.colorspace = ColorSpaceRGB;
1301 				m_pixelFormat = Format_BGRA_8;
1302 			}
1303 			m_imageInfoRecord.type  = ImageType7;
1304 			m_imageInfoRecord.BBoxX = 0;
1305 			m_imageInfoRecord.BBoxH = m_image.height();
1306 			m_image.setDotsPerMeterX ((int) (m_imageInfoRecord.xres / 0.0254));
1307 			m_image.setDotsPerMeterY ((int) (m_imageInfoRecord.yres / 0.0254));
1308 			f.close();
1309 			return;
1310 		}
1311 	}
1312 	f.close();
1313 }
1314 
loadPhotoshopBinary(const QString & fn,QImage & tmpImg)1315 void ScImgDataLoader_PS::loadPhotoshopBinary(const QString& fn, QImage &tmpImg)
1316 {
1317 	double x, y, b, h;
1318 	ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
1319 	ts2 >> x >> y >> b >> h;
1320 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc1.jpg");
1321 	QFile f2(tmpFile);
1322 	QString tmp;
1323 	tmpImg = QImage(m_psXSize, m_psYSize, QImage::Format_ARGB32);
1324 	tmpImg.fill(qRgba(0, 0, 0, 0));
1325 	QByteArray psdata;
1326 	QFile f(fn);
1327 	int yCount = 0;
1328 	if (f.open(QIODevice::ReadOnly))
1329 	{
1330 		if (m_psDataType > 2)
1331 		{
1332 			f2.open(QIODevice::WriteOnly);
1333 		}
1334 		QDataStream ts(&f);
1335 		while (!ts.atEnd())
1336 		{
1337 			tmp = readLineFromDataStream(ts);
1338 			if (tmp == m_psCommand)
1339 			{
1340 				if (m_psDataType == 1)
1341 				{
1342 					QRgb *p;
1343 					uchar cc, cm, cy, ck;
1344 					for (int yh = 0; yh < tmpImg.height(); ++yh )
1345 					{
1346 						if (m_psMode == 4)
1347 							psdata.resize(m_psXSize * (4 + m_psChannel));
1348 						else
1349 							psdata.resize(m_psXSize * (3 + m_psChannel));
1350 						f.read(psdata.data(), psdata.size());
1351 						p = (QRgb *)tmpImg.scanLine( yh );
1352 						for (int xh = 0; xh < tmpImg.width(); ++xh )
1353 						{
1354 							cc = psdata[xh];
1355 							cm = psdata[m_psXSize+xh];
1356 							cy = psdata[m_psXSize*2+xh];
1357 							if (m_psMode == 4)
1358 							{
1359 								ck = psdata[m_psXSize*3+xh];
1360 								*p = qRgba(cc, cm, cy, ck);
1361 							}
1362 							else
1363 								*p = qRgba(cc, cm, cy, 255);
1364 							p++;
1365 						}
1366 					}
1367 				}
1368 				else if (m_psDataType > 1)
1369 				{
1370 					while (!ts.atEnd())
1371 					{
1372 						tmp = readLineFromDataStream(ts);
1373 						if ((tmp.isEmpty()) || (tmp.startsWith("%%EndBinary")))
1374 							break;
1375 						if (m_psDataType == 2)
1376 						{
1377 							for (int a = 0; a < tmp.length(); a += 2)
1378 							{
1379 								bool ok;
1380 								ushort data = tmp.midRef(a, 2).toUShort(&ok, 16);
1381 								psdata.resize(psdata.size()+1);
1382 								psdata[psdata.size()-1] = data;
1383 							}
1384 						}
1385 						else
1386 						{
1387 							decodeA85(psdata, tmp);
1388 							f2.write(psdata.data(), psdata.size());
1389 							psdata.resize(0);
1390 						}
1391 					}
1392 					if (m_psDataType > 2)
1393 					{
1394 						f2.close();
1395 						loadPSjpeg(tmpFile, tmpImg);
1396 						QFile::remove(tmpFile);
1397 					}
1398 					else
1399 					{
1400 						QRgb *p;
1401 						uchar cc, cm, cy, ck;
1402 						for (int yh = 0; yh < tmpImg.height(); ++yh )
1403 						{
1404 							p = (QRgb *)tmpImg.scanLine( yh );
1405 							for (int xh = 0; xh < tmpImg.width(); ++xh )
1406 							{
1407 								cc = psdata[yCount+xh];
1408 								cm = psdata[yCount+m_psXSize+xh];
1409 								cy = psdata[yCount+m_psXSize*2+xh];
1410 								if (m_psMode == 4)
1411 								{
1412 									ck = psdata[yCount+m_psXSize*3+xh];
1413 									*p = qRgba(cc, cm, cy, ck);
1414 								}
1415 								else
1416 									*p = qRgba(cc, cm, cy, 255);
1417 								p++;
1418 							}
1419 							if (m_psMode == 4)
1420 								yCount += m_psXSize * (4 + m_psChannel);
1421 							else
1422 								yCount += m_psXSize * (3 + m_psChannel);
1423 						}
1424 					}
1425 				}
1426 				f.close();
1427 				return;
1428 			}
1429 		}
1430 		f.close();
1431 	}
1432 }
1433 
loadDCS2(const QString & fn,int gsRes)1434 void ScImgDataLoader_PS::loadDCS2(const QString& fn, int gsRes)
1435 {
1436 	QStringList args;
1437 	double x, y, b, h;
1438 	QFileInfo fi(fn);
1439 	QString ext = fi.suffix().toLower();
1440 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc1.png");
1441 	QString tmpFile2 = QDir::toNativeSeparators(ScPaths::tempFileDir() + "tmp.eps");
1442 	QString baseFile = fi.absolutePath();
1443 	QString picFile = QDir::toNativeSeparators(fn);
1444 	float xres = gsRes;
1445 	float yres = gsRes;
1446 	ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
1447 	ts2 >> x >> y >> b >> h;
1448 	xres = gsRes;
1449 	yres = gsRes;
1450 	if ((m_isPhotoshop) && (m_hasPhotoshopImageData))
1451 	{
1452 		m_image = QImage(m_psXSize, m_psYSize, QImage::Format_ARGB32);
1453 		xres = m_psXSize / b * 72.0;
1454 		yres = m_psYSize / h * 72.0;
1455 	}
1456 	else
1457 		m_image = QImage( qRound(b * gsRes / 72.0), qRound(h * gsRes / 72.0), QImage::Format_ARGB32 );
1458 	m_image.fill(qRgba(0, 0, 0, 0));
1459 	if (!m_isDCS2multi)
1460 	{
1461 		for (QMap<QString, plateOffsets>::Iterator it = m_colorPlates2.begin(); it != m_colorPlates2.end(); ++it)
1462 		{
1463 			QByteArray imgc(it.value().len, ' ');
1464 			QFile f(picFile);
1465 			if (f.open(QIODevice::ReadOnly))
1466 			{
1467 				f.seek(it.value().pos);
1468 				f.read(imgc.data(), it.value().len);
1469 			}
1470 			f.close();
1471 			QFile f2(tmpFile2);
1472 			if (f2.open(QIODevice::WriteOnly))
1473 				f2.write(imgc.data(), it.value().len);
1474 			f2.close();
1475 			imgc.resize(0);
1476 			if ((m_isPhotoshop) && (m_hasPhotoshopImageData))
1477 			{
1478 				QImage tmpImg;
1479 				loadPhotoshopBinary(tmpFile2, tmpImg);
1480 				blendImages(tmpImg, m_CustColors[it.key()]);
1481 			}
1482 			else
1483 			{
1484 				args.append("-dEPSCrop");
1485 				args.append("-r"+QString::number(gsRes));
1486 				args.append("-sOutputFile="+tmpFile);
1487 				args.append(tmpFile2);
1488 				int retg = callGS(args);
1489 				if (retg == 0)
1490 				{
1491 					QImage tmpImg;
1492 					tmpImg.load(tmpFile);
1493 					blendImages(tmpImg, m_CustColors[it.key()]);
1494 					QFile::remove(tmpFile);
1495 				}
1496 			}
1497 			QFile::remove(tmpFile2);
1498 		}
1499 	}
1500 	else
1501 	{
1502 		for (QMap<QString, QString>::Iterator it = m_colorPlates.begin(); it != m_colorPlates.end(); ++it)
1503 		{
1504 			tmpFile2 = QDir::toNativeSeparators(baseFile+"/"+it.value());
1505 			if ((m_isPhotoshop) && (m_hasPhotoshopImageData))
1506 			{
1507 				QImage tmpImg;
1508 				loadPhotoshopBinary(tmpFile2, tmpImg);
1509 				blendImages(tmpImg, m_CustColors[it.key()]);
1510 			}
1511 			else
1512 			{
1513 				args.append("-dEPSCrop");
1514 				args.append("-r"+QString::number(gsRes));
1515 				args.append("-sOutputFile="+tmpFile);
1516 				args.append(tmpFile2);
1517 				int retg = callGS(args);
1518 				if (retg == 0)
1519 				{
1520 					QImage tmpImg;
1521 					tmpImg.load(tmpFile);
1522 					blendImages(tmpImg, m_CustColors[it.key()]);
1523 					QFile::remove(tmpFile);
1524 				}
1525 				args.clear();
1526 			}
1527 		}
1528 	}
1529 	if (extensionIndicatesEPS(ext))
1530 	{
1531 		m_imageInfoRecord.BBoxX = static_cast<int>(x);
1532 		m_imageInfoRecord.BBoxH = static_cast<int>(h);
1533 	}
1534 	else
1535 	{
1536 		m_imageInfoRecord.BBoxX = 0;
1537 		m_imageInfoRecord.BBoxH = m_image.height();
1538 	}
1539 	m_imageInfoRecord.xres = qRound(xres);
1540 	m_imageInfoRecord.yres = qRound(yres);
1541 	m_imageInfoRecord.colorspace = ColorSpaceCMYK;
1542 	m_imageInfoRecord.type = ImageType7;
1543 	m_image.setDotsPerMeterX ((int) (xres / 0.0254));
1544 	m_image.setDotsPerMeterY ((int) (yres / 0.0254));
1545 	m_pixelFormat = Format_YMCK_8;
1546 }
1547 
loadDCS1(const QString & fn,int gsRes)1548 void ScImgDataLoader_PS::loadDCS1(const QString& fn, int gsRes)
1549 {
1550 	QStringList args;
1551 	double x, y, b, h;
1552 	QFileInfo fi = QFileInfo(fn);
1553 	QString ext = fi.suffix().toLower();
1554 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc1.png");
1555 	QString baseFile = fi.absolutePath();
1556 	QString picFile;
1557 	float xres = gsRes;
1558 	float yres = gsRes;
1559 	ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
1560 	ts2 >> x >> y >> b >> h;
1561 	xres = gsRes;
1562 	yres = gsRes;
1563 	m_image = QImage( qRound(b * gsRes / 72.0), qRound(h * gsRes / 72.0), QImage::Format_ARGB32 );
1564 	m_image.fill(qRgba(0, 0, 0, 0));
1565 	bool isEncapPS=extensionIndicatesEPS(ext);
1566 	if (isEncapPS)
1567 		args.append("-dEPSCrop");
1568 	args.append("-r"+QString::number(gsRes));
1569 	args.append("-sOutputFile="+tmpFile);
1570 	picFile = QDir::toNativeSeparators(baseFile+"/"+m_colorPlates["Cyan"]);
1571 	args.append(picFile);
1572 	int retg = callGS(args);
1573 	if (retg == 0)
1574 	{
1575 		QImage tmpImg;
1576 		tmpImg.load(tmpFile);
1577 		blendImages(tmpImg, ScColor(255, 0, 0, 0));
1578 		QFile::remove(tmpFile);
1579 	}
1580 	args.clear();
1581 
1582 	if (isEncapPS)
1583 		args.append("-dEPSCrop");
1584 	args.append("-r"+QString::number(gsRes));
1585 	args.append("-sOutputFile="+tmpFile);
1586 	picFile = QDir::toNativeSeparators(baseFile+"/"+m_colorPlates["Magenta"]);
1587 	args.append(picFile);
1588 	retg = callGS(args);
1589 	if (retg == 0)
1590 	{
1591 		QImage tmpImg;
1592 		tmpImg.load(tmpFile);
1593 		blendImages(tmpImg, ScColor(0, 255, 0, 0));
1594 		QFile::remove(tmpFile);
1595 	}
1596 	args.clear();
1597 
1598 	if (isEncapPS)
1599 		args.append("-dEPSCrop");
1600 	args.append("-r"+QString::number(gsRes));
1601 	args.append("-sOutputFile="+tmpFile);
1602 	picFile = QDir::toNativeSeparators(baseFile+"/"+m_colorPlates["Yellow"]);
1603 	args.append(picFile);
1604 	retg = callGS(args);
1605 	if (retg == 0)
1606 	{
1607 		QImage tmpImg;
1608 		tmpImg.load(tmpFile);
1609 		blendImages(tmpImg, ScColor(0, 0, 255, 0));
1610 		QFile::remove(tmpFile);
1611 	}
1612 	args.clear();
1613 
1614 	if (isEncapPS)
1615 		args.append("-dEPSCrop");
1616 	args.append("-r"+QString::number(gsRes));
1617 	args.append("-sOutputFile="+tmpFile);
1618 	picFile = QDir::toNativeSeparators(baseFile+"/"+m_colorPlates["Black"]);
1619 	args.append(picFile);
1620 	retg = callGS(args);
1621 	if (retg == 0)
1622 	{
1623 		QImage tmpImg;
1624 		tmpImg.load(tmpFile);
1625 		blendImages(tmpImg, ScColor(0, 0, 0, 255));
1626 		QFile::remove(tmpFile);
1627 	}
1628 	args.clear();
1629 
1630 	if (isEncapPS)
1631 	{
1632 		m_imageInfoRecord.BBoxX = static_cast<int>(x);
1633 		m_imageInfoRecord.BBoxH = static_cast<int>(h);
1634 	}
1635 	else
1636 	{
1637 		m_imageInfoRecord.BBoxX = 0;
1638 		m_imageInfoRecord.BBoxH = m_image.height();
1639 	}
1640 	m_imageInfoRecord.xres = gsRes;
1641 	m_imageInfoRecord.yres = gsRes;
1642 	m_imageInfoRecord.colorspace = ColorSpaceCMYK;
1643 	m_imageInfoRecord.type = ImageType7;
1644 	m_image.setDotsPerMeterX ((int) (xres / 0.0254));
1645 	m_image.setDotsPerMeterY ((int) (yres / 0.0254));
1646 	m_pixelFormat = Format_YMCK_8;
1647 }
1648 
blendImages(QImage & source,ScColor col)1649 void ScImgDataLoader_PS::blendImages(QImage &source, ScColor col)
1650 {
1651 	int h = source.height();
1652 	int w = source.width();
1653 	int cyan, c, m, yc, k, cc, mm, yy, kk;
1654 	col.getCMYK(&c, &m, &yc, &k);
1655 	QRgb *p;
1656 	QRgb *pq;
1657 	for (int y=0; y < h; ++y )
1658 	{
1659 		p = (QRgb *)m_image.scanLine( y );
1660 		pq = (QRgb *)source.scanLine( y );
1661 		for (int x=0; x < w; ++x )
1662 		{
1663 			cyan = 255 - qRed(*pq);
1664 			if (cyan != 0)
1665 			{
1666 				(c == 0) ? cc = qRed(*p) : cc = qMin(c * cyan / 255 + qRed(*p), 255);
1667 				(m == 0) ? mm = qGreen(*p) : mm = qMin(m * cyan / 255 + qGreen(*p), 255);
1668 				(yc == 0) ? yy = qBlue(*p) : yy = qMin(yc * cyan / 255 + qBlue(*p), 255);
1669 				(k == 0) ? kk = qAlpha(*p) : kk = qMin(k * cyan / 255 + qAlpha(*p), 255);
1670 				*p = qRgba(cc, mm, yy, kk);
1671 			}
1672 			p++;
1673 			pq++;
1674 		}
1675 	}
1676 }
1677 
preloadAlphaChannel(const QString & fn,int page,int gsRes,bool & hasAlpha)1678 bool ScImgDataLoader_PS::preloadAlphaChannel(const QString& fn, int page, int gsRes, bool& hasAlpha)
1679 {
1680 	float xres, yres;
1681 
1682 	initialize();
1683 
1684 	hasAlpha = false;
1685 	QFileInfo fi = QFileInfo(fn);
1686 	if (!fi.exists())
1687 		return false;
1688 	QString ext = fi.suffix().toLower();
1689 	QString tmpFile = QDir::toNativeSeparators(ScPaths::tempFileDir() + QString("sc%1.png").arg(qMax(1, page)));
1690 	QString tmpFiles = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc%d.png");
1691 	QString picFile = QDir::toNativeSeparators(fn);
1692 	double x, y, b, h;
1693 	bool found = false;
1694 	found = parseData(fn);
1695 	if (found)
1696 	{
1697 		ScTextStream ts2(&m_BBox, QIODevice::ReadOnly);
1698 		ts2 >> x >> y >> b >> h;
1699 		h = h * gsRes / 72.0;
1700 		QStringList args;
1701 		xres = gsRes;
1702 		yres = gsRes;
1703 		if (extensionIndicatesEPS(ext))
1704 		{
1705 			if (!m_BBoxInTrailer)
1706 				args.append("-dEPSCrop");
1707 		}
1708 		args.append("-r"+QString::number(gsRes));
1709 		args.append("-sOutputFile="+tmpFiles);
1710 		args.append(picFile);
1711 //		qDebug() << "scimgdataloader_ps(alpha):" << args;
1712 		int retg = callGS(args);
1713 		if (retg == 0)
1714 		{
1715 			m_image.load(tmpFile);
1716 			if ((extensionIndicatesEPS(ext) && m_BBoxInTrailer) || (m_isRotated))
1717 			{
1718 				int ex = qRound(x * gsRes / 72.0);
1719 				int ey = qRound(m_image.height() - h);
1720 				int ew = qRound((b - x) * gsRes / 72.0);
1721 				int eh = qRound(h - y * gsRes / 72.0);
1722 				m_image = m_image.copy(ex, ey, ew, eh);
1723 			}
1724 			if ((!ScCore->havePNGAlpha()) || (m_isRotated))
1725 			{
1726 				int wi = m_image.width();
1727 				int hi = m_image.height();
1728 				QRgb alphaFF = qRgba(255,255,255,255);
1729 				QRgb alpha00 = qRgba(255,255,255,  0);
1730 				QRgb *s;
1731 				for (int yi = 0; yi < hi; ++yi)
1732 				{
1733 					s = (QRgb*)(m_image.scanLine(yi));
1734 					for (int xi = 0; xi < wi; ++xi)
1735 					{
1736 						if ((*s) == alphaFF)
1737 							(*s) &= alpha00;
1738 						s++;
1739 					}
1740 				}
1741 			}
1742 
1743 			QStringList files = QStringList("sc*.png");
1744 			files = QDir(ScPaths::tempFileDir()).entryList(files);
1745 			for (int i=0; i < files.count(); ++i)
1746 				QFile::remove(QDir::toNativeSeparators(ScPaths::tempFileDir() + files[i]));
1747 
1748 			hasAlpha = true;
1749 			m_imageInfoRecord.actualPageNumber = page;
1750 			m_imageInfoRecord.type = ImageTypeEPS;
1751 			m_imageInfoRecord.colorspace = ColorSpaceRGB;
1752 			m_image.setDotsPerMeterX ((int) (xres / 0.0254));
1753 			m_image.setDotsPerMeterY ((int) (yres / 0.0254));
1754 			return true;
1755 		}
1756 		qDebug() << "Ghostscript returned result" << retg;
1757 		return false;
1758 	}
1759 	m_imageInfoRecord.actualPageNumber = page;
1760 	return true;
1761 }
1762