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