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