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.
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>
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
30 if ( io
->read(buf
, 2)!=2 ) // Read first two bytes
32 kError(399) << "kimgio EPS: EPS file has less than 2 bytes." << endl
;
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
;
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
;
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
;
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
;
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
;
80 if ( buf
[0]=='%' && buf
[1]=='!' ) // Check %! magic
82 kDebug(399) << "kimgio EPS: MS-DOS EPS file";
86 kError(399) << "kimgio EPS: supposed Postscript code of a MS-DOS EPS file doe not start with %!." << endl
;
92 kError(399) << "kimgio EPS: wrong magic for potential MS-DOS EPS file!" << endl
;
98 kError(399) << "kimgio EPS: not an EPS file!" << endl
;
104 static bool bbox ( QIODevice
*io
, int *x1
, int *y1
, int *x2
, int *y2
)
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
;
130 EPSHandler::EPSHandler()
134 bool EPSHandler::canRead() const
136 if (canRead(device())) {
143 bool EPSHandler::read(QImage
*image
)
145 kDebug(399) << "kimgio EPS: starting...";
155 QIODevice
* io
= device();
156 quint32 ps_offset
, ps_size
;
158 // find start of PostScript code
159 if ( !seekToCodeStart(io
, ps_offset
, ps_size
) )
163 if ( !bbox (io
, &x1
, &y1
, &x2
, &y2
)) {
164 kError(399) << "kimgio EPS: no bounding box found!" << endl
;
168 QTemporaryFile tmpFile
;
169 if( !tmpFile
.open() ) {
170 kError(399) << "kimgio EPS: no temp file!" << endl
;
174 // x1, y1 -> translation
175 // x2, y2 -> new size
179 //kDebug(399) << "origin point: " << x1 << "," << y1 << " size:" << x2 << "," << y2;
182 int wantedWidth
= x2
;
183 int wantedHeight
= y2
;
185 // create GS command line
187 cmdBuf
= "gs -sOutputFile=";
188 cmdBuf
+= tmpFile
.fileName();
190 tmp
.setNum( wantedWidth
);
192 tmp
.setNum( wantedHeight
);
195 cmdBuf
+= " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ppm -c "
200 "1 1 254 255 div setrgbcolor fill "
201 "0 0 0 setrgbcolor - -c showpage quit";
205 ghostfd
= popen (QFile::encodeName(cmdBuf
), "w");
207 if ( ghostfd
== 0 ) {
208 kError(399) << "kimgio EPS: no GhostScript?" << endl
;
212 fprintf (ghostfd
, "\n%d %d translate\n", -qRound(x1
*xScale
), -qRound(y1
*yScale
));
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
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
);
231 if( image
->load (tmpFile
.fileName()) ) {
232 kDebug(399) << "kimgio EPS: success!";
233 //kDebug(399) << "Loading EPS took " << (float)(dt.elapsed()) / 1000 << " seconds";
237 kError(399) << "kimgio EPS: no image!" << endl
;
242 // Sven Wiegand <SWiegand@tfh-berlin.de> -- eps output filter (from KSnapshot)
243 bool EPSHandler::write(const QImage
&image
)
245 QPrinter
psOut(QPrinter::PrinterResolution
);
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() )
258 psOut
.setOutputFileName(tmpFile
.fileName());
259 psOut
.setFullPage(true);
261 // painting the pixmap to the "printer" which is a file
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
);
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';
290 QByteArray
EPSHandler::name() const
295 bool EPSHandler::canRead(QIODevice
*device
)
298 qWarning("EPSHandler::canRead() called with no device");
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]);
310 device
->seek(oldPos
);
313 return head
.contains("%!PS-Adobe");
316 class EPSPlugin
: public QImageIOPlugin
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())
336 if (!device
->isOpen())
340 if (device
->isReadable() && EPSHandler::canRead(device
))
342 if (device
->isWritable())
347 QImageIOHandler
*EPSPlugin::create(QIODevice
*device
, const QByteArray
&format
) const
349 QImageIOHandler
*handler
= new EPSHandler
;
350 handler
->setDevice(device
);
351 handler
->setFormat(format
);
355 Q_EXPORT_STATIC_PLUGIN(EPSPlugin
)
356 Q_EXPORT_PLUGIN2(eps
, EPSPlugin
)