fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kimgio / eps.cpp
blob780016cb6619ea6bd1f09f6c7a46e97886ec2383
1 /**
2 * QImageIO Routines to read/write EPS images.
3 * copyright (c) 1998 Dirk Schoenberger <dirk.schoenberger@freenet.de>
5 * This library is distributed under the conditions of the GNU LGPL.
6 */
7 #include "eps.h"
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <QtGui/QImage>
11 #include <QtCore/QFile>
12 #include <QtGui/QPainter>
13 #include <QtGui/QPrinter>
14 #include <QtCore/QTextStream>
15 #include <QtCore/QTemporaryFile>
16 #include <kapplication.h>
17 #include <kdebug.h>
19 #define BUFLEN 200
21 #define BBOX "%%BoundingBox:"
22 #define BBOX_LEN strlen(BBOX)
24 static bool seekToCodeStart( QIODevice * io, quint32 & ps_offset, quint32 & ps_size )
26 char buf[4]; // We at most need to read 4 bytes at a time
27 ps_offset=0L;
28 ps_size=0L;
30 if ( io->read(buf, 2)!=2 ) // Read first two bytes
32 kError(399) << "kimgio EPS: EPS file has less than 2 bytes." << endl;
33 return false;
36 if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic
38 kDebug(399) << "kimgio EPS: normal EPS file";
40 else if ( buf[0]==char(0xc5) && buf[1]==char(0xd0) ) // Check start of MS-DOS EPS magic
41 { // May be a MS-DOS EPS file
42 if ( io->read(buf+2, 2)!=2 ) // Read further bytes of MS-DOS EPS magic
44 kError(399) << "kimgio EPS: potential MS-DOS EPS file has less than 4 bytes." << endl;
45 return false;
47 if ( buf[2]==char(0xd3) && buf[3]==char(0xc6) ) // Check last bytes of MS-DOS EPS magic
49 if (io->read(buf, 4)!=4) // Get offset of PostScript code in the MS-DOS EPS file.
51 kError(399) << "kimgio EPS: cannot read offset of MS-DOS EPS file" << endl;
52 return false;
54 ps_offset // Offset is in little endian
55 = ((unsigned char) buf[0])
56 + ((unsigned char) buf[1] << 8)
57 + ((unsigned char) buf[2] << 16)
58 + ((unsigned char) buf[3] << 24);
59 if (io->read(buf, 4)!=4) // Get size of PostScript code in the MS-DOS EPS file.
61 kError(399) << "kimgio EPS: cannot read size of MS-DOS EPS file" << endl;
62 return false;
64 ps_size // Size is in little endian
65 = ((unsigned char) buf[0])
66 + ((unsigned char) buf[1] << 8)
67 + ((unsigned char) buf[2] << 16)
68 + ((unsigned char) buf[3] << 24);
69 kDebug(399) << "kimgio EPS: Offset: " << ps_offset <<" Size: " << ps_size;
70 if ( !io->seek(ps_offset) ) // Get offset of PostScript code in the MS-DOS EPS file.
72 kError(399) << "kimgio EPS: cannot seek in MS-DOS EPS file" << endl;
73 return false;
75 if ( io->read(buf, 2)!=2 ) // Read first two bytes of what should be the Postscript code
77 kError(399) << "kimgio EPS: PostScript code has less than 2 bytes." << endl;
78 return false;
80 if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic
82 kDebug(399) << "kimgio EPS: MS-DOS EPS file";
84 else
86 kError(399) << "kimgio EPS: supposed Postscript code of a MS-DOS EPS file doe not start with %!." << endl;
87 return false;
90 else
92 kError(399) << "kimgio EPS: wrong magic for potential MS-DOS EPS file!" << endl;
93 return false;
96 else
98 kError(399) << "kimgio EPS: not an EPS file!" << endl;
99 return false;
101 return true;
104 static bool bbox ( QIODevice *io, int *x1, int *y1, int *x2, int *y2)
106 char buf[BUFLEN+1];
108 bool ret = false;
110 while (io->readLine(buf, BUFLEN) > 0)
112 if (strncmp (buf, BBOX, BBOX_LEN) == 0)
114 // Some EPS files have non-integer values for the bbox
115 // We don't support that currently, but at least we parse it
116 float _x1, _y1, _x2, _y2;
117 if ( sscanf (buf, "%*s %f %f %f %f",
118 &_x1, &_y1, &_x2, &_y2) == 4) {
119 kDebug(399) << "kimgio EPS BBOX: " << _x1 << " " << _y1 << " " << _x2 << " " << _y2;
120 *x1=(int)_x1; *y1=(int)_y1; *x2=(int)_x2; *y2=(int)_y2;
121 ret = true;
122 break;
127 return ret;
130 EPSHandler::EPSHandler()
134 bool EPSHandler::canRead() const
136 if (canRead(device())) {
137 setFormat("eps");
138 return true;
140 return false;
143 bool EPSHandler::read(QImage *image)
145 kDebug(399) << "kimgio EPS: starting...";
147 FILE * ghostfd;
148 int x1, y1, x2, y2;
149 //QTime dt;
150 //dt.start();
152 QString cmdBuf;
153 QString tmp;
155 QIODevice* io = device();
156 quint32 ps_offset, ps_size;
158 // find start of PostScript code
159 if ( !seekToCodeStart(io, ps_offset, ps_size) )
160 return false;
162 // find bounding box
163 if ( !bbox (io, &x1, &y1, &x2, &y2)) {
164 kError(399) << "kimgio EPS: no bounding box found!" << endl;
165 return false;
168 QTemporaryFile tmpFile;
169 if( !tmpFile.open() ) {
170 kError(399) << "kimgio EPS: no temp file!" << endl;
171 return false;
174 // x1, y1 -> translation
175 // x2, y2 -> new size
177 x2 -= x1;
178 y2 -= y1;
179 //kDebug(399) << "origin point: " << x1 << "," << y1 << " size:" << x2 << "," << y2;
180 double xScale = 1.0;
181 double yScale = 1.0;
182 int wantedWidth = x2;
183 int wantedHeight = y2;
185 // create GS command line
187 cmdBuf = "gs -sOutputFile=";
188 cmdBuf += tmpFile.fileName();
189 cmdBuf += " -q -g";
190 tmp.setNum( wantedWidth );
191 cmdBuf += tmp;
192 tmp.setNum( wantedHeight );
193 cmdBuf += 'x';
194 cmdBuf += tmp;
195 cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ppm -c "
196 "0 0 moveto "
197 "1000 0 lineto "
198 "1000 1000 lineto "
199 "0 1000 lineto "
200 "1 1 254 255 div setrgbcolor fill "
201 "0 0 0 setrgbcolor - -c showpage quit";
203 // run ghostview
205 ghostfd = popen (QFile::encodeName(cmdBuf), "w");
207 if ( ghostfd == 0 ) {
208 kError(399) << "kimgio EPS: no GhostScript?" << endl;
209 return false;
212 fprintf (ghostfd, "\n%d %d translate\n", -qRound(x1*xScale), -qRound(y1*yScale));
214 // write image to gs
216 io->reset(); // Go back to start of file to give all the file to GhostScript
217 if (ps_offset>0L) // We have an offset
218 io->seek(ps_offset);
219 QByteArray buffer ( io->readAll() );
221 // If we have no MS-DOS EPS file or if the size seems wrong, then choose the buffer size
222 if (ps_size<=0 || ps_size>(unsigned int)buffer.size())
223 ps_size=buffer.size();
225 fwrite(buffer.data(), sizeof(char), ps_size, ghostfd);
226 buffer.resize(0);
228 pclose ( ghostfd );
230 // load image
231 if( image->load (tmpFile.fileName()) ) {
232 kDebug(399) << "kimgio EPS: success!";
233 //kDebug(399) << "Loading EPS took " << (float)(dt.elapsed()) / 1000 << " seconds";
234 return true;
237 kError(399) << "kimgio EPS: no image!" << endl;
238 return false;
242 // Sven Wiegand <SWiegand@tfh-berlin.de> -- eps output filter (from KSnapshot)
243 bool EPSHandler::write(const QImage &image)
245 QPrinter psOut(QPrinter::PrinterResolution);
246 QPainter p;
248 // making some definitions (papersize, output to file, filename):
249 psOut.setCreator( "KDE " KDE_VERSION_STRING );
250 if ( psOut.outputFileName().isEmpty() )
251 psOut.setOutputFileName( "untitled_printer_document" );
253 // Extension must be .eps so that Qt generates EPS file
254 QTemporaryFile tmpFile("XXXXXXXX.eps");
255 if ( !tmpFile.open() )
256 return false;
258 psOut.setOutputFileName(tmpFile.fileName());
259 psOut.setFullPage(true);
261 // painting the pixmap to the "printer" which is a file
262 p.begin( &psOut );
263 // Qt uses the clip rect for the bounding box
264 p.setClipRect( 0, 0, image.width(), image.height());
265 p.drawImage( QPoint( 0, 0 ), image );
266 p.end();
268 // Copy file to imageio struct
269 QFile inFile(tmpFile.fileName());
270 inFile.open( QIODevice::ReadOnly );
272 QTextStream in( &inFile );
273 in.setCodec( "ISO-8859-1" );
274 QTextStream out( device() );
275 out.setCodec( "ISO-8859-1" );
277 QString szInLine = in.readLine();
278 out << szInLine << '\n';
280 while( !in.atEnd() ){
281 szInLine = in.readLine();
282 out << szInLine << '\n';
285 inFile.close();
287 return true;
290 QByteArray EPSHandler::name() const
292 return "eps";
295 bool EPSHandler::canRead(QIODevice *device)
297 if (!device) {
298 qWarning("EPSHandler::canRead() called with no device");
299 return false;
302 qint64 oldPos = device->pos();
304 QByteArray head = device->readLine(64);
305 int readBytes = head.size();
306 if (device->isSequential()) {
307 while (readBytes > 0)
308 device->ungetChar(head[readBytes-- - 1]);
309 } else {
310 device->seek(oldPos);
313 return head.contains("%!PS-Adobe");
316 class EPSPlugin : public QImageIOPlugin
318 public:
319 QStringList keys() const;
320 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
321 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
324 QStringList EPSPlugin::keys() const
326 return QStringList() << "eps" << "EPS" << "epsi" << "EPSI" << "epsf" << "EPSF";
329 QImageIOPlugin::Capabilities EPSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
331 if (format == "eps" || format == "epsi" || format == "EPS" || format == "EPSI" ||
332 format == "epsf" || format == "EPSF")
333 return Capabilities(CanRead | CanWrite);
334 if (!format.isEmpty())
335 return 0;
336 if (!device->isOpen())
337 return 0;
339 Capabilities cap;
340 if (device->isReadable() && EPSHandler::canRead(device))
341 cap |= CanRead;
342 if (device->isWritable())
343 cap |= CanWrite;
344 return cap;
347 QImageIOHandler *EPSPlugin::create(QIODevice *device, const QByteArray &format) const
349 QImageIOHandler *handler = new EPSHandler;
350 handler->setDevice(device);
351 handler->setFormat(format);
352 return handler;
355 Q_EXPORT_STATIC_PLUGIN(EPSPlugin)
356 Q_EXPORT_PLUGIN2(eps, EPSPlugin)