KImgIO
eps.cpp
Go to the documentation of this file.
00001 00007 #include "eps.h" 00008 #include <unistd.h> 00009 #include <stdio.h> 00010 #include <QtGui/QImage> 00011 #include <QtCore/QFile> 00012 #include <QtGui/QPainter> 00013 #include <QtGui/QPrinter> 00014 #include <QtCore/QTextStream> 00015 #include <QtCore/QTemporaryFile> 00016 #include <kapplication.h> 00017 #include <kdebug.h> 00018 00019 #define BUFLEN 200 00020 00021 #define BBOX "%%BoundingBox:" 00022 #define BBOX_LEN strlen(BBOX) 00023 00024 static bool seekToCodeStart( QIODevice * io, quint32 & ps_offset, quint32 & ps_size ) 00025 { 00026 char buf[4]; // We at most need to read 4 bytes at a time 00027 ps_offset=0L; 00028 ps_size=0L; 00029 00030 if ( io->read(buf, 2)!=2 ) // Read first two bytes 00031 { 00032 kError(399) << "kimgio EPS: EPS file has less than 2 bytes." << endl; 00033 return false; 00034 } 00035 00036 if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic 00037 { 00038 kDebug(399) << "kimgio EPS: normal EPS file"; 00039 } 00040 else if ( buf[0]==char(0xc5) && buf[1]==char(0xd0) ) // Check start of MS-DOS EPS magic 00041 { // May be a MS-DOS EPS file 00042 if ( io->read(buf+2, 2)!=2 ) // Read further bytes of MS-DOS EPS magic 00043 { 00044 kError(399) << "kimgio EPS: potential MS-DOS EPS file has less than 4 bytes." << endl; 00045 return false; 00046 } 00047 if ( buf[2]==char(0xd3) && buf[3]==char(0xc6) ) // Check last bytes of MS-DOS EPS magic 00048 { 00049 if (io->read(buf, 4)!=4) // Get offset of PostScript code in the MS-DOS EPS file. 00050 { 00051 kError(399) << "kimgio EPS: cannot read offset of MS-DOS EPS file" << endl; 00052 return false; 00053 } 00054 ps_offset // Offset is in little endian 00055 = ((unsigned char) buf[0]) 00056 + ((unsigned char) buf[1] << 8) 00057 + ((unsigned char) buf[2] << 16) 00058 + ((unsigned char) buf[3] << 24); 00059 if (io->read(buf, 4)!=4) // Get size of PostScript code in the MS-DOS EPS file. 00060 { 00061 kError(399) << "kimgio EPS: cannot read size of MS-DOS EPS file" << endl; 00062 return false; 00063 } 00064 ps_size // Size is in little endian 00065 = ((unsigned char) buf[0]) 00066 + ((unsigned char) buf[1] << 8) 00067 + ((unsigned char) buf[2] << 16) 00068 + ((unsigned char) buf[3] << 24); 00069 kDebug(399) << "kimgio EPS: Offset: " << ps_offset <<" Size: " << ps_size; 00070 if ( !io->seek(ps_offset) ) // Get offset of PostScript code in the MS-DOS EPS file. 00071 { 00072 kError(399) << "kimgio EPS: cannot seek in MS-DOS EPS file" << endl; 00073 return false; 00074 } 00075 if ( io->read(buf, 2)!=2 ) // Read first two bytes of what should be the Postscript code 00076 { 00077 kError(399) << "kimgio EPS: PostScript code has less than 2 bytes." << endl; 00078 return false; 00079 } 00080 if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic 00081 { 00082 kDebug(399) << "kimgio EPS: MS-DOS EPS file"; 00083 } 00084 else 00085 { 00086 kError(399) << "kimgio EPS: supposed Postscript code of a MS-DOS EPS file doe not start with %!." << endl; 00087 return false; 00088 } 00089 } 00090 else 00091 { 00092 kError(399) << "kimgio EPS: wrong magic for potential MS-DOS EPS file!" << endl; 00093 return false; 00094 } 00095 } 00096 else 00097 { 00098 kError(399) << "kimgio EPS: not an EPS file!" << endl; 00099 return false; 00100 } 00101 return true; 00102 } 00103 00104 static bool bbox ( QIODevice *io, int *x1, int *y1, int *x2, int *y2) 00105 { 00106 char buf[BUFLEN+1]; 00107 00108 bool ret = false; 00109 00110 while (io->readLine(buf, BUFLEN) > 0) 00111 { 00112 if (strncmp (buf, BBOX, BBOX_LEN) == 0) 00113 { 00114 // Some EPS files have non-integer values for the bbox 00115 // We don't support that currently, but at least we parse it 00116 float _x1, _y1, _x2, _y2; 00117 if ( sscanf (buf, "%*s %f %f %f %f", 00118 &_x1, &_y1, &_x2, &_y2) == 4) { 00119 kDebug(399) << "kimgio EPS BBOX: " << _x1 << " " << _y1 << " " << _x2 << " " << _y2; 00120 *x1=(int)_x1; *y1=(int)_y1; *x2=(int)_x2; *y2=(int)_y2; 00121 ret = true; 00122 break; 00123 } 00124 } 00125 } 00126 00127 return ret; 00128 } 00129 00130 EPSHandler::EPSHandler() 00131 { 00132 } 00133 00134 bool EPSHandler::canRead() const 00135 { 00136 if (canRead(device())) { 00137 setFormat("eps"); 00138 return true; 00139 } 00140 return false; 00141 } 00142 00143 bool EPSHandler::read(QImage *image) 00144 { 00145 kDebug(399) << "kimgio EPS: starting..."; 00146 00147 FILE * ghostfd; 00148 int x1, y1, x2, y2; 00149 //QTime dt; 00150 //dt.start(); 00151 00152 QString cmdBuf; 00153 QString tmp; 00154 00155 QIODevice* io = device(); 00156 quint32 ps_offset, ps_size; 00157 00158 // find start of PostScript code 00159 if ( !seekToCodeStart(io, ps_offset, ps_size) ) 00160 return false; 00161 00162 // find bounding box 00163 if ( !bbox (io, &x1, &y1, &x2, &y2)) { 00164 kError(399) << "kimgio EPS: no bounding box found!" << endl; 00165 return false; 00166 } 00167 00168 QTemporaryFile tmpFile; 00169 if( !tmpFile.open() ) { 00170 kError(399) << "kimgio EPS: no temp file!" << endl; 00171 return false; 00172 } 00173 00174 // x1, y1 -> translation 00175 // x2, y2 -> new size 00176 00177 x2 -= x1; 00178 y2 -= y1; 00179 //kDebug(399) << "origin point: " << x1 << "," << y1 << " size:" << x2 << "," << y2; 00180 double xScale = 1.0; 00181 double yScale = 1.0; 00182 int wantedWidth = x2; 00183 int wantedHeight = y2; 00184 00185 // create GS command line 00186 00187 cmdBuf = "gs -sOutputFile="; 00188 cmdBuf += tmpFile.fileName(); 00189 cmdBuf += " -q -g"; 00190 tmp.setNum( wantedWidth ); 00191 cmdBuf += tmp; 00192 tmp.setNum( wantedHeight ); 00193 cmdBuf += 'x'; 00194 cmdBuf += tmp; 00195 cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ppm -c " 00196 "0 0 moveto " 00197 "1000 0 lineto " 00198 "1000 1000 lineto " 00199 "0 1000 lineto " 00200 "1 1 254 255 div setrgbcolor fill " 00201 "0 0 0 setrgbcolor - -c showpage quit"; 00202 00203 // run ghostview 00204 00205 ghostfd = popen (QFile::encodeName(cmdBuf), "w"); 00206 00207 if ( ghostfd == 0 ) { 00208 kError(399) << "kimgio EPS: no GhostScript?" << endl; 00209 return false; 00210 } 00211 00212 fprintf (ghostfd, "\n%d %d translate\n", -qRound(x1*xScale), -qRound(y1*yScale)); 00213 00214 // write image to gs 00215 00216 io->reset(); // Go back to start of file to give all the file to GhostScript 00217 if (ps_offset>0L) // We have an offset 00218 io->seek(ps_offset); 00219 QByteArray buffer ( io->readAll() ); 00220 00221 // If we have no MS-DOS EPS file or if the size seems wrong, then choose the buffer size 00222 if (ps_size<=0 || ps_size>(unsigned int)buffer.size()) 00223 ps_size=buffer.size(); 00224 00225 fwrite(buffer.data(), sizeof(char), ps_size, ghostfd); 00226 buffer.resize(0); 00227 00228 pclose ( ghostfd ); 00229 00230 // load image 00231 if( image->load (tmpFile.fileName()) ) { 00232 kDebug(399) << "kimgio EPS: success!"; 00233 //kDebug(399) << "Loading EPS took " << (float)(dt.elapsed()) / 1000 << " seconds"; 00234 return true; 00235 } 00236 00237 kError(399) << "kimgio EPS: no image!" << endl; 00238 return false; 00239 } 00240 00241 00242 // Sven Wiegand <SWiegand@tfh-berlin.de> -- eps output filter (from KSnapshot) 00243 bool EPSHandler::write(const QImage &image) 00244 { 00245 QPrinter psOut(QPrinter::PrinterResolution); 00246 QPainter p; 00247 00248 // making some definitions (papersize, output to file, filename): 00249 psOut.setCreator( "KDE " KDE_VERSION_STRING ); 00250 if ( psOut.outputFileName().isEmpty() ) 00251 psOut.setOutputFileName( "untitled_printer_document" ); 00252 00253 // Extension must be .eps so that Qt generates EPS file 00254 QTemporaryFile tmpFile("XXXXXXXX.eps"); 00255 if ( !tmpFile.open() ) 00256 return false; 00257 00258 psOut.setOutputFileName(tmpFile.fileName()); 00259 psOut.setOutputFormat(QPrinter::PostScriptFormat); 00260 psOut.setFullPage(true); 00261 00262 // painting the pixmap to the "printer" which is a file 00263 p.begin( &psOut ); 00264 // Qt uses the clip rect for the bounding box 00265 p.setClipRect( 0, 0, image.width(), image.height()); 00266 p.drawImage( QPoint( 0, 0 ), image ); 00267 p.end(); 00268 00269 // Copy file to imageio struct 00270 QFile inFile(tmpFile.fileName()); 00271 inFile.open( QIODevice::ReadOnly ); 00272 00273 QTextStream in( &inFile ); 00274 in.setCodec( "ISO-8859-1" ); 00275 QTextStream out( device() ); 00276 out.setCodec( "ISO-8859-1" ); 00277 00278 QString szInLine = in.readLine(); 00279 out << szInLine << '\n'; 00280 00281 while( !in.atEnd() ){ 00282 szInLine = in.readLine(); 00283 out << szInLine << '\n'; 00284 } 00285 00286 inFile.close(); 00287 00288 return true; 00289 } 00290 00291 QByteArray EPSHandler::name() const 00292 { 00293 return "eps"; 00294 } 00295 00296 bool EPSHandler::canRead(QIODevice *device) 00297 { 00298 if (!device) { 00299 qWarning("EPSHandler::canRead() called with no device"); 00300 return false; 00301 } 00302 00303 qint64 oldPos = device->pos(); 00304 00305 QByteArray head = device->readLine(64); 00306 int readBytes = head.size(); 00307 if (device->isSequential()) { 00308 while (readBytes > 0) 00309 device->ungetChar(head[readBytes-- - 1]); 00310 } else { 00311 device->seek(oldPos); 00312 } 00313 00314 return head.contains("%!PS-Adobe"); 00315 } 00316 00317 class EPSPlugin : public QImageIOPlugin 00318 { 00319 public: 00320 QStringList keys() const; 00321 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 00322 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 00323 }; 00324 00325 QStringList EPSPlugin::keys() const 00326 { 00327 return QStringList() << "eps" << "EPS" << "epsi" << "EPSI" << "epsf" << "EPSF"; 00328 } 00329 00330 QImageIOPlugin::Capabilities EPSPlugin::capabilities(QIODevice *device, const QByteArray &format) const 00331 { 00332 if (format == "eps" || format == "epsi" || format == "EPS" || format == "EPSI" || 00333 format == "epsf" || format == "EPSF") 00334 return Capabilities(CanRead | CanWrite); 00335 if (!format.isEmpty()) 00336 return 0; 00337 if (!device->isOpen()) 00338 return 0; 00339 00340 Capabilities cap; 00341 if (device->isReadable() && EPSHandler::canRead(device)) 00342 cap |= CanRead; 00343 if (device->isWritable()) 00344 cap |= CanWrite; 00345 return cap; 00346 } 00347 00348 QImageIOHandler *EPSPlugin::create(QIODevice *device, const QByteArray &format) const 00349 { 00350 QImageIOHandler *handler = new EPSHandler; 00351 handler->setDevice(device); 00352 handler->setFormat(format); 00353 return handler; 00354 } 00355 00356 Q_EXPORT_STATIC_PLUGIN(EPSPlugin) 00357 Q_EXPORT_PLUGIN2(eps, EPSPlugin)
KDE 4.6 API Reference