00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <unistd.h>
00022 #include <stdio.h>
00023
00024 #include <qbuffer.h>
00025 #include <qpainter.h>
00026 #include <qpaintdevicemetrics.h>
00027 #include <qfile.h>
00028 #include <qtextstream.h>
00029 #include <qregexp.h>
00030 #include <qimage.h>
00031 #include <qpixmap.h>
00032 #include <qapplication.h>
00033 #include <qdragobject.h>
00034
00035 #include <kglobal.h>
00036 #include <kdebug.h>
00037 #include <kdeversion.h>
00038 #if ! KDE_IS_VERSION( 3,1,90 )
00039 #include <kdebugclasses.h>
00040 #endif
00041 #include <ktempfile.h>
00042 #include <kprocess.h>
00043
00044 #include "KoPictureKey.h"
00045 #include "KoPictureBase.h"
00046 #include "KoPictureEps.h"
00047
00048
00049 KoPictureEps::KoPictureEps(void) : m_psStreamStart(0), m_psStreamLength(0), m_cacheIsInFastMode(true)
00050 {
00051
00052 m_cachedPixmap.setOptimization(QPixmap::MemoryOptim);
00053 }
00054
00055 KoPictureEps::~KoPictureEps(void)
00056 {
00057 }
00058
00059 KoPictureBase* KoPictureEps::newCopy(void) const
00060 {
00061 return new KoPictureEps(*this);
00062 }
00063
00064 KoPictureType::Type KoPictureEps::getType(void) const
00065 {
00066 return KoPictureType::TypeEps;
00067 }
00068
00069 bool KoPictureEps::isNull(void) const
00070 {
00071 return m_rawData.isNull();
00072 }
00073
00074 QImage KoPictureEps::scaleWithGhostScript(const QSize& size, const int resolutionx, const int resolutiony )
00075 {
00076 if (!m_boundingBox.width() || !m_boundingBox.height())
00077 {
00078 kdDebug(30003) << "EPS image has a null size! (in KoPictureEps::scaleWithGhostScript)" << endl;
00079 return QImage();
00080 }
00081
00082
00083
00084
00085
00086
00087 const char* deviceTable[] = { "png16m", "bmp16m", "ppm", 0 };
00088
00089 QImage img;
00090
00091 for ( int i = 0; deviceTable[i]; ++i)
00092 {
00093 if ( tryScaleWithGhostScript( img, size, resolutionx, resolutiony, deviceTable[i] ) != -1 )
00094 {
00095 return img;
00096 }
00097
00098 }
00099
00100 kdError(30003) << "Image from GhostScript cannot be loaded (in KoPictureEps::scaleWithGhostScript)" << endl;
00101 return img;
00102 }
00103
00104
00105
00106 int KoPictureEps::tryScaleWithGhostScript(QImage &image, const QSize& size, const int resolutionx, const int resolutiony, const char* device )
00107
00108 {
00109 kdDebug(30003) << "Sampling with GhostScript, using device \"" << device << "\" (in KoPictureEps::tryScaleWithGhostScript)" << endl;
00110
00111 KTempFile tmpFile;
00112 tmpFile.setAutoDelete(true);
00113
00114 if ( tmpFile.status() )
00115 {
00116 kdError(30003) << "No KTempFile! (in KoPictureEps::tryScaleWithGhostScript)" << endl;
00117 return 0;
00118 }
00119
00120 const int wantedWidth = size.width();
00121 const int wantedHeight = size.height();
00122 const double xScale = double(size.width()) / double(m_boundingBox.width());
00123 const double yScale = double(size.height()) / double(m_boundingBox.height());
00124
00125
00126
00127 QString cmdBuf ( "gs -sOutputFile=" );
00128 cmdBuf += KProcess::quote(tmpFile.name());
00129 cmdBuf += " -q -g";
00130 cmdBuf += QString::number( wantedWidth );
00131 cmdBuf += "x";
00132 cmdBuf += QString::number( wantedHeight );
00133
00134 if ( ( resolutionx > 0) && ( resolutiony > 0) )
00135 {
00136 #if 0
00137
00138
00139 cmdBuf += " -r";
00140 cmdBuf += QString::number( resolutionx );
00141 cmdBuf += "x";
00142 cmdBuf += QString::number( resolutiony );
00143 #endif
00144 }
00145
00146 cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=";
00147 cmdBuf += device;
00148
00149 cmdBuf += " -";
00150 cmdBuf += " -c showpage quit";
00151
00152
00153
00154 FILE* ghostfd = popen (QFile::encodeName(cmdBuf), "w");
00155
00156 if ( ghostfd == 0 )
00157 {
00158 kdError(30003) << "No connection to GhostScript (in KoPictureEps::tryScaleWithGhostScript)" << endl;
00159 return 0;
00160 }
00161
00162
00163 fprintf (ghostfd, "\n%d %d translate\n", -qRound(m_boundingBox.left()*xScale), -qRound(m_boundingBox.top()*yScale));
00164 fprintf (ghostfd, "%g %g scale\n", xScale, yScale);
00165
00166
00167
00168 fwrite( m_rawData.data() + m_psStreamStart, sizeof(char), m_psStreamLength, ghostfd);
00169
00170 pclose ( ghostfd );
00171
00172
00173 if( !image.load (tmpFile.name()) )
00174 {
00175
00176 return -1;
00177 }
00178 if ( image.size() != size )
00179 {
00180
00181
00182 image = image.scale( size );
00183 }
00184 kdDebug(30003) << "Image parameters: " << image.width() << "x" << image.height() << "x" << image.depth() << endl;
00185 return 1;
00186 }
00187
00188 void KoPictureEps::scaleAndCreatePixmap(const QSize& size, bool fastMode, const int resolutionx, const int resolutiony )
00189 {
00190 kdDebug(30003) << "KoPictureEps::scaleAndCreatePixmap " << size << " " << (fastMode?QString("fast"):QString("slow"))
00191 << " resolutionx: " << resolutionx << " resolutiony: " << resolutiony << endl;
00192 if ((size==m_cachedSize)
00193 && ((fastMode) || (!m_cacheIsInFastMode)))
00194 {
00195
00196
00197
00198
00199 kdDebug(30003) << "Already cached!" << endl;
00200 return;
00201 }
00202
00203
00204 if ( !isSlowResizeModeAllowed() )
00205 {
00206 kdDebug(30003) << "User has disallowed slow mode!" << endl;
00207 fastMode = true;
00208 }
00209
00210
00211 if ( fastMode && !m_cachedSize.isEmpty())
00212 {
00213 kdDebug(30003) << "Fast scaling!" << endl;
00214
00215 QImage image( m_cachedPixmap.convertToImage() );
00216 m_cachedPixmap=image.scale( size );
00217 m_cacheIsInFastMode=true;
00218 m_cachedSize=size;
00219 }
00220 else
00221 {
00222 QTime time;
00223 time.start();
00224
00225 QApplication::setOverrideCursor( Qt::waitCursor );
00226 m_cachedPixmap = scaleWithGhostScript( size, resolutionx, resolutiony );
00227 QApplication::restoreOverrideCursor();
00228 m_cacheIsInFastMode=false;
00229 m_cachedSize=size;
00230
00231 kdDebug(30003) << "Time: " << (time.elapsed()/1000.0) << " s" << endl;
00232 }
00233 kdDebug(30003) << "New size: " << size << endl;
00234 }
00235
00236 void KoPictureEps::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
00237 {
00238 if ( !width || !height )
00239 return;
00240
00241 QSize screenSize( width, height );
00242
00243
00244 QPaintDeviceMetrics metrics (painter.device());
00245 kdDebug(30003) << "Metrics: X: " << metrics.logicalDpiX() << " x Y: " << metrics.logicalDpiX() << " (in KoPictureEps::draw)" << endl;
00246
00247 if ( painter.device()->isExtDev() )
00248 {
00249 kdDebug(30003) << "Drawing for a printer (in KoPictureEps::draw)" << endl;
00250
00251 QImage image( scaleWithGhostScript( screenSize, metrics.logicalDpiX(), metrics.logicalDpiY() ) );
00252
00253
00254 painter.drawImage( x + sx, y + sy, image, sx, sy, sw, sh );
00255 }
00256 else
00257 {
00258 scaleAndCreatePixmap(screenSize, fastMode, metrics.logicalDpiX(), metrics.logicalDpiY() );
00259
00260
00261
00262 painter.drawPixmap( x + sx, y + sy, m_cachedPixmap, sx, sy, sw, sh );
00263 }
00264 }
00265
00266 bool KoPictureEps::extractPostScriptStream( void )
00267 {
00268 kdDebug(30003) << "KoPictureEps::extractPostScriptStream" << endl;
00269 QDataStream data( m_rawData, IO_ReadOnly );
00270 data.setByteOrder( QDataStream::LittleEndian );
00271 Q_UINT32 magic, offset, length;
00272 data >> magic;
00273 data >> offset;
00274 data >> length;
00275 if ( !length )
00276 {
00277 kdError(30003) << "Length of PS stream is zero!" << endl;
00278 return false;
00279 }
00280 if ( offset+length>m_rawData.size() )
00281 {
00282 kdError(30003) << "Data stream of the EPSF file is longer than file: " << offset << "+" << length << ">" << m_rawData.size() << endl;
00283 return false;
00284 }
00285 m_psStreamStart = offset;
00286 m_psStreamLength = length;
00287 return true;
00288 }
00289
00290 QString KoPictureEps::readLine( const QByteArray& array, const uint start, const uint length, uint& pos, bool& lastCharWasCr )
00291 {
00292 QString strLine;
00293 const uint finish = kMin( start + length, array.size() );
00294 for ( ; pos < finish; ++pos )
00295 {
00296 const char ch = array[ pos ];
00297 if ( ch == '\n' )
00298 {
00299 if ( lastCharWasCr )
00300 {
00301
00302
00303
00304 lastCharWasCr = false;
00305 }
00306 else
00307 {
00308
00309 break;
00310 }
00311 }
00312 else if ( ch == '\r' )
00313 {
00314
00315 lastCharWasCr = true;
00316 break;
00317 }
00318 else if ( ch == char(12) )
00319 {
00320
00321 continue;
00322 }
00323 else
00324 {
00325 strLine += ch;
00326 lastCharWasCr = false;
00327 }
00328 }
00329 return strLine;
00330 }
00331
00332
00333 bool KoPictureEps::loadData(const QByteArray& array, const QString& )
00334 {
00335
00336 kdDebug(30003) << "KoPictureEps::load" << endl;
00337
00338 m_rawData=array;
00339
00340 if (m_rawData.isNull())
00341 {
00342 kdError(30003) << "No data was loaded!" << endl;
00343 return false;
00344 }
00345
00346 if ( ( m_rawData[0]==char(0xc5) ) && ( m_rawData[1]==char(0xd0) )
00347 && ( m_rawData[2]==char(0xd3) ) && ( m_rawData[3]==char(0xc6) ) )
00348 {
00349
00350 if (!extractPostScriptStream())
00351 return false;
00352 }
00353 else
00354 {
00355 m_psStreamStart = 0;
00356 m_psStreamLength = m_rawData.size();
00357 }
00358
00359 QString lineBox;
00360 bool lastWasCr = false;
00361 uint pos = m_psStreamStart;
00362 QString line( readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr ) );
00363 kdDebug(30003) << "Header: " << line << endl;
00364 if (!line.startsWith("%!"))
00365 {
00366 kdError(30003) << "Not a PostScript file!" << endl;
00367 return false;
00368 }
00369 QRect rect;
00370 bool lineIsBoundingBox = false;
00371 for(;;)
00372 {
00373 ++pos;
00374 line = readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr );
00375 kdDebug(30003) << "Checking line: " << line << endl;
00376
00377 if (line.startsWith("%%BoundingBox:"))
00378 {
00379 lineIsBoundingBox = true;
00380 break;
00381 }
00382
00383
00384 else if (!line.startsWith("%%"))
00385 break;
00386 }
00387 if ( !lineIsBoundingBox )
00388 {
00389 kdError(30003) << "KoPictureEps::load: could not find a bounding box!" << endl;
00390 return false;
00391 }
00392
00393 QRegExp exp("(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)");
00394 if ( exp.search(line) == -1 )
00395 {
00396
00397
00398
00399 kdError(30003) << "Not standard bounding box: " << line << endl;
00400 return false;
00401 }
00402 kdDebug(30003) << "Reg. Exp. Found: " << exp.capturedTexts() << endl;
00403 rect.setLeft((int)exp.cap(1).toDouble());
00404 rect.setTop((int)exp.cap(2).toDouble());
00405 rect.setRight((int)exp.cap(3).toDouble());
00406 rect.setBottom((int)exp.cap(4).toDouble());
00407 m_boundingBox=rect;
00408 m_originalSize=rect.size();
00409 kdDebug(30003) << "Rect: " << rect << " Size: " << m_originalSize << endl;
00410 return true;
00411 }
00412
00413 bool KoPictureEps::save(QIODevice* io) const
00414 {
00415
00416 Q_ULONG size=io->writeBlock(m_rawData);
00417 return (size==m_rawData.size());
00418 }
00419
00420 QSize KoPictureEps::getOriginalSize(void) const
00421 {
00422 return m_originalSize;
00423 }
00424
00425 QPixmap KoPictureEps::generatePixmap(const QSize& size, bool smoothScale)
00426 {
00427 scaleAndCreatePixmap(size,!smoothScale, 0, 0);
00428 return m_cachedPixmap;
00429 }
00430
00431 QString KoPictureEps::getMimeType(const QString&) const
00432 {
00433 return "image/x-eps";
00434 }
00435
00436 QImage KoPictureEps::generateImage(const QSize& size)
00437 {
00438
00439 return scaleWithGhostScript(size, 0, 0);
00440 }
00441
00442 void KoPictureEps::clearCache(void)
00443 {
00444 m_cachedPixmap.resize(0, 0);
00445 m_cacheIsInFastMode=true;
00446 m_cachedSize=QSize();
00447 }