diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kofficecore/KoPictureEps.cpp | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kofficecore/KoPictureEps.cpp')
-rw-r--r-- | lib/kofficecore/KoPictureEps.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/lib/kofficecore/KoPictureEps.cpp b/lib/kofficecore/KoPictureEps.cpp new file mode 100644 index 00000000..752e2a45 --- /dev/null +++ b/lib/kofficecore/KoPictureEps.cpp @@ -0,0 +1,447 @@ +/* This file is part of the KDE project + Copyright (c) 2001 Simon Hausmann <[email protected]> + Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <unistd.h> +#include <stdio.h> + +#include <qbuffer.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qapplication.h> +#include <qdragobject.h> + +#include <kglobal.h> +#include <kdebug.h> +#include <kdeversion.h> +#if ! KDE_IS_VERSION( 3,1,90 ) +#include <kdebugclasses.h> +#endif +#include <ktempfile.h> +#include <kprocess.h> + +#include "KoPictureKey.h" +#include "KoPictureBase.h" +#include "KoPictureEps.h" + + +KoPictureEps::KoPictureEps(void) : m_psStreamStart(0), m_psStreamLength(0), m_cacheIsInFastMode(true) +{ + // Forbid QPixmap to cache the X-Window resources (Yes, it is slower!) + m_cachedPixmap.setOptimization(QPixmap::MemoryOptim); +} + +KoPictureEps::~KoPictureEps(void) +{ +} + +KoPictureBase* KoPictureEps::newCopy(void) const +{ + return new KoPictureEps(*this); +} + +KoPictureType::Type KoPictureEps::getType(void) const +{ + return KoPictureType::TypeEps; +} + +bool KoPictureEps::isNull(void) const +{ + return m_rawData.isNull(); +} + +QImage KoPictureEps::scaleWithGhostScript(const QSize& size, const int resolutionx, const int resolutiony ) +{ + if (!m_boundingBox.width() || !m_boundingBox.height()) + { + kdDebug(30003) << "EPS image has a null size! (in KoPictureEps::scaleWithGhostScript)" << endl; + return QImage(); + } + + // ### TODO: do not call GhostScript up to three times for each re-scaling (one call of GhostScript should be enough to know which device is available: gs --help) + // png16m is better, but not always available -> fallback to bmp16m, then fallback to ppm (256 colors) + // ### TODO: pcx24b is also a true colour format + // ### TODO: support alpha (other gs devices needed) + + const char* deviceTable[] = { "png16m", "bmp16m", "ppm", 0 }; + + QImage img; + + for ( int i = 0; deviceTable[i]; ++i) + { + if ( tryScaleWithGhostScript( img, size, resolutionx, resolutiony, deviceTable[i] ) != -1 ) + { + return img; + } + + } + + kdError(30003) << "Image from GhostScript cannot be loaded (in KoPictureEps::scaleWithGhostScript)" << endl; + return img; +} + +// Helper method for scaleWithGhostScript. Returns 1 on success, 0 on error, -1 if nothing generated +// (in which case another 'output device' can be tried) +int KoPictureEps::tryScaleWithGhostScript(QImage &image, const QSize& size, const int resolutionx, const int resolutiony, const char* device ) +// Based on the code of the file kdelibs/kimgio/eps.cpp +{ + kdDebug(30003) << "Sampling with GhostScript, using device \"" << device << "\" (in KoPictureEps::tryScaleWithGhostScript)" << endl; + + KTempFile tmpFile; + tmpFile.setAutoDelete(true); + + if ( tmpFile.status() ) + { + kdError(30003) << "No KTempFile! (in KoPictureEps::tryScaleWithGhostScript)" << endl; + return 0; // error + } + + const int wantedWidth = size.width(); + const int wantedHeight = size.height(); + const double xScale = double(size.width()) / double(m_boundingBox.width()); + const double yScale = double(size.height()) / double(m_boundingBox.height()); + + // create GS command line + + QString cmdBuf ( "gs -sOutputFile=" ); + cmdBuf += KProcess::quote(tmpFile.name()); + cmdBuf += " -q -g"; + cmdBuf += QString::number( wantedWidth ); + cmdBuf += "x"; + cmdBuf += QString::number( wantedHeight ); + + if ( ( resolutionx > 0) && ( resolutiony > 0) ) + { +#if 0 + // Do not play with resolution for now. + // It brings more problems at print than solutions + cmdBuf += " -r"; + cmdBuf += QString::number( resolutionx ); + cmdBuf += "x"; + cmdBuf += QString::number( resolutiony ); +#endif + } + + cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE="; + cmdBuf += device; + //cmdBuf += " -c 255 255 255 setrgbcolor fill 0 0 0 setrgbcolor"; + cmdBuf += " -"; + cmdBuf += " -c showpage quit"; + + // run ghostview + + FILE* ghostfd = popen (QFile::encodeName(cmdBuf), "w"); + + if ( ghostfd == 0 ) + { + kdError(30003) << "No connection to GhostScript (in KoPictureEps::tryScaleWithGhostScript)" << endl; + return 0; // error + } + + // The translation is needed as GhostScript (7.07) cannot handle negative values in the bounding box otherwise. + fprintf (ghostfd, "\n%d %d translate\n", -qRound(m_boundingBox.left()*xScale), -qRound(m_boundingBox.top()*yScale)); + fprintf (ghostfd, "%g %g scale\n", xScale, yScale); + + // write image to gs + + fwrite( m_rawData.data() + m_psStreamStart, sizeof(char), m_psStreamLength, ghostfd); + + pclose ( ghostfd ); + + // load image + if( !image.load (tmpFile.name()) ) + { + // It failed - maybe the device isn't supported by gs + return -1; + } + if ( image.size() != size ) // this can happen due to rounding problems + { + //kdDebug(30003) << "fixing size to " << size.width() << "x" << size.height() + // << " (was " << image.width() << "x" << image.height() << ")" << endl; + image = image.scale( size ); // hmm, smoothScale instead? + } + kdDebug(30003) << "Image parameters: " << image.width() << "x" << image.height() << "x" << image.depth() << endl; + return 1; // success +} + +void KoPictureEps::scaleAndCreatePixmap(const QSize& size, bool fastMode, const int resolutionx, const int resolutiony ) +{ + kdDebug(30003) << "KoPictureEps::scaleAndCreatePixmap " << size << " " << (fastMode?QString("fast"):QString("slow")) + << " resolutionx: " << resolutionx << " resolutiony: " << resolutiony << endl; + if ((size==m_cachedSize) + && ((fastMode) || (!m_cacheIsInFastMode))) + { + // The cached pixmap has already the right size + // and: + // - we are in fast mode (We do not care if the re-size was done slowly previously) + // - the re-size was already done in slow mode + kdDebug(30003) << "Already cached!" << endl; + return; + } + + // Slow mode can be very slow, especially at high zoom levels -> configurable + if ( !isSlowResizeModeAllowed() ) + { + kdDebug(30003) << "User has disallowed slow mode!" << endl; + fastMode = true; + } + + // We cannot use fast mode, if nothing was ever cached. + if ( fastMode && !m_cachedSize.isEmpty()) + { + kdDebug(30003) << "Fast scaling!" << endl; + // Slower than caching a QImage, but faster than re-sampling! + QImage image( m_cachedPixmap.convertToImage() ); + m_cachedPixmap=image.scale( size ); + m_cacheIsInFastMode=true; + m_cachedSize=size; + } + else + { + QTime time; + time.start(); + + QApplication::setOverrideCursor( Qt::waitCursor ); + m_cachedPixmap = scaleWithGhostScript( size, resolutionx, resolutiony ); + QApplication::restoreOverrideCursor(); + m_cacheIsInFastMode=false; + m_cachedSize=size; + + kdDebug(30003) << "Time: " << (time.elapsed()/1000.0) << " s" << endl; + } + kdDebug(30003) << "New size: " << size << endl; +} + +void KoPictureEps::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode) +{ + if ( !width || !height ) + return; + + QSize screenSize( width, height ); + //kdDebug() << "KoPictureEps::draw screenSize=" << screenSize.width() << "x" << screenSize.height() << endl; + + QPaintDeviceMetrics metrics (painter.device()); + kdDebug(30003) << "Metrics: X: " << metrics.logicalDpiX() << " x Y: " << metrics.logicalDpiX() << " (in KoPictureEps::draw)" << endl; + + if ( painter.device()->isExtDev() ) // Is it an external device (i.e. printer) + { + kdDebug(30003) << "Drawing for a printer (in KoPictureEps::draw)" << endl; + // For printing, always re-sample the image, as a printer has never the same resolution than a display. + QImage image( scaleWithGhostScript( screenSize, metrics.logicalDpiX(), metrics.logicalDpiY() ) ); + // sx,sy,sw,sh is meant to be used as a cliprect on the pixmap, but drawImage + // translates it to the (x,y) point -> we need (x+sx, y+sy). + painter.drawImage( x + sx, y + sy, image, sx, sy, sw, sh ); + } + else // No, it is simply a display + { + scaleAndCreatePixmap(screenSize, fastMode, metrics.logicalDpiX(), metrics.logicalDpiY() ); + + // sx,sy,sw,sh is meant to be used as a cliprect on the pixmap, but drawPixmap + // translates it to the (x,y) point -> we need (x+sx, y+sy). + painter.drawPixmap( x + sx, y + sy, m_cachedPixmap, sx, sy, sw, sh ); + } +} + +bool KoPictureEps::extractPostScriptStream( void ) +{ + kdDebug(30003) << "KoPictureEps::extractPostScriptStream" << endl; + QDataStream data( m_rawData, IO_ReadOnly ); + data.setByteOrder( QDataStream::LittleEndian ); + Q_UINT32 magic, offset, length; + data >> magic; + data >> offset; + data >> length; + if ( !length ) + { + kdError(30003) << "Length of PS stream is zero!" << endl; + return false; + } + if ( offset+length>m_rawData.size() ) + { + kdError(30003) << "Data stream of the EPSF file is longer than file: " << offset << "+" << length << ">" << m_rawData.size() << endl; + return false; + } + m_psStreamStart = offset; + m_psStreamLength = length; + return true; +} + +QString KoPictureEps::readLine( const QByteArray& array, const uint start, const uint length, uint& pos, bool& lastCharWasCr ) +{ + QString strLine; + const uint finish = kMin( start + length, array.size() ); + for ( ; pos < finish; ++pos ) // We are starting at pos + { + const char ch = array[ pos ]; // Read one character + if ( ch == '\n' ) + { + if ( lastCharWasCr ) + { + // We have a line feed following a Carriage Return + // As the Carriage Return has already ended the previous line, + // discard this Line Feed. + lastCharWasCr = false; + } + else + { + // We have a normal Line Feed, therefore we end the line + break; + } + } + else if ( ch == '\r' ) + { + // We have a Carriage Return, therefore we end the line + lastCharWasCr = true; + break; + } + else if ( ch == char(12) ) // Form Feed + { // ### TODO: can a FF happen in PostScript? + // Ignore the form feed + continue; + } + else + { + strLine += ch; + lastCharWasCr = false; + } + } + return strLine; +} + + +bool KoPictureEps::loadData(const QByteArray& array, const QString& /* extension */ ) +{ + + kdDebug(30003) << "KoPictureEps::load" << endl; + // First, read the raw data + m_rawData=array; + + if (m_rawData.isNull()) + { + kdError(30003) << "No data was loaded!" << endl; + return false; + } + + if ( ( m_rawData[0]==char(0xc5) ) && ( m_rawData[1]==char(0xd0) ) + && ( m_rawData[2]==char(0xd3) ) && ( m_rawData[3]==char(0xc6) ) ) + { + // We have a so-called "MS-DOS EPS file", we have to extract the PostScript stream + if (!extractPostScriptStream()) // Changes m_rawData + return false; + } + else + { + m_psStreamStart = 0; + m_psStreamLength = m_rawData.size(); + } + + QString lineBox; // Line with the bounding box + bool lastWasCr = false; // Was the last character of the line a carriage return? + uint pos = m_psStreamStart; // We start to search the bounding box at the start of the PostScript stream + QString line( readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr ) ); + kdDebug(30003) << "Header: " << line << endl; + if (!line.startsWith("%!")) + { + kdError(30003) << "Not a PostScript file!" << endl; + return false; + } + QRect rect; + bool lineIsBoundingBox = false; // Does "line" has a %%BoundingBox line? + for(;;) + { + ++pos; // Get over the previous line end (CR or LF) + line = readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr ); + kdDebug(30003) << "Checking line: " << line << endl; + // ### TODO: it seems that the bounding box can be delayed with "(atend)" in the trailer (GhostScript 7.07 does not support it either.) + if (line.startsWith("%%BoundingBox:")) + { + lineIsBoundingBox = true; + break; + } + // ### TODO: also abort on %%EndComments + // ### TODO: %? , where ? is non-white-space printable, does not end the comment! + else if (!line.startsWith("%%")) + break; // Not a EPS comment anymore, so abort as we are not in the EPS header anymore + } + if ( !lineIsBoundingBox ) + { + kdError(30003) << "KoPictureEps::load: could not find a bounding box!" << endl; + return false; + } + // Floating point values are not allowed in a Bounding Box, but ther are many such files out there... + QRegExp exp("(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)"); + if ( exp.search(line) == -1 ) + { + // ### TODO: it might be an "(atend)" and the bounding box is in the trailer + // (but GhostScript 7.07 does not support a bounding box in the trailer.) + // Note: in Trailer, it is the last BoundingBox that counts not the first! + kdError(30003) << "Not standard bounding box: " << line << endl; + return false; + } + kdDebug(30003) << "Reg. Exp. Found: " << exp.capturedTexts() << endl; + rect.setLeft((int)exp.cap(1).toDouble()); + rect.setTop((int)exp.cap(2).toDouble()); + rect.setRight((int)exp.cap(3).toDouble()); + rect.setBottom((int)exp.cap(4).toDouble()); + m_boundingBox=rect; + m_originalSize=rect.size(); + kdDebug(30003) << "Rect: " << rect << " Size: " << m_originalSize << endl; + return true; +} + +bool KoPictureEps::save(QIODevice* io) const +{ + // We save the raw data, to avoid damaging the file by many load/save cycles + Q_ULONG size=io->writeBlock(m_rawData); // WARNING: writeBlock returns Q_LONG but size() Q_ULONG! + return (size==m_rawData.size()); +} + +QSize KoPictureEps::getOriginalSize(void) const +{ + return m_originalSize; +} + +QPixmap KoPictureEps::generatePixmap(const QSize& size, bool smoothScale) +{ + scaleAndCreatePixmap(size,!smoothScale, 0, 0); + return m_cachedPixmap; +} + +QString KoPictureEps::getMimeType(const QString&) const +{ + return "image/x-eps"; +} + +QImage KoPictureEps::generateImage(const QSize& size) +{ + // 0, 0 == resolution unknown + return scaleWithGhostScript(size, 0, 0); +} + +void KoPictureEps::clearCache(void) +{ + m_cachedPixmap.resize(0, 0); + m_cacheIsInFastMode=true; + m_cachedSize=QSize(); +} |