// -------------------------------------------------------------------- // Various utility classes // -------------------------------------------------------------------- /* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2004 Otfried Cheong Ipe is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ipeutils.h" #include "ipepage.h" #include "ipegroup.h" #include "iperef.h" #include "ipeimage.h" #include "ipetext.h" #include // -------------------------------------------------------------------- /*! \class IpeBitmapFinder \ingroup high \brief A visitor that recursively scans objects and collects all bitmaps. */ void IpeBitmapFinder::ScanPage(const IpePage *page) { for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) (*this)(*it); } void IpeBitmapFinder::VisitGroup(const IpeGroup *obj) { for (IpeGroup::const_iterator it = obj->begin(); it != obj->end(); ++it) (*it)->Accept(*this); } void IpeBitmapFinder::VisitReference(const IpeReference *obj) { if (obj->Object()) obj->Object()->Accept(*this); } void IpeBitmapFinder::VisitImage(const IpeImage *obj) { iBitmaps.push_back(obj->Bitmap()); } // -------------------------------------------------------------------- /*! \class IpeBBoxPainter \ingroup high \brief Paint objects using this painter to compute an accurate bounding box. The IpeObjct::BBox member function computes a bounding box useful for distance calculations and optimizations. To find a bounding box that is accurate for the actual \b drawn object, paint the object using an IpeBBoxPainter, and retrieve the box with BBox. */ IpeBBoxPainter::IpeBBoxPainter(const IpeStyleSheet *style) : IpePainter(style) { // nothing else } void IpeBBoxPainter::DoMoveTo(const IpeVector &v) { iV = Matrix() * v; Add(iV); } void IpeBBoxPainter::DoLineTo(const IpeVector &v) { iV = Matrix() * v; Add(iV); } void IpeBBoxPainter::DoCurveTo(const IpeVector &v1, const IpeVector &v2, const IpeVector &v3) { IpeBezier bez(iV, Matrix() * v1, Matrix() * v2, Matrix() * v3); IpeRect bb = bez.BBox(); double lw = 0.0; if (LineWidth()) lw = Repository()->ToScalar(LineWidth()).ToDouble(); iBBox.AddPoint(bb.Min() - IpeVector(lw, lw)); iBBox.AddPoint(bb.Max() + IpeVector(lw, lw)); iV = Matrix() * v3; } void IpeBBoxPainter::DoDrawBitmap(IpeBitmap) { iBBox.AddPoint(Matrix() * IpeVector(0.0, 0.0)); iBBox.AddPoint(Matrix() * IpeVector(0.0, 1.0)); iBBox.AddPoint(Matrix() * IpeVector(1.0, 1.0)); iBBox.AddPoint(Matrix() * IpeVector(1.0, 0.0)); } void IpeBBoxPainter::DoDrawText(const IpeText *text) { iBBox.AddPoint(Matrix() * IpeVector(0,0)); iBBox.AddPoint(Matrix() * IpeVector(0, text->TotalHeight())); iBBox.AddPoint(Matrix() * IpeVector(text->Width(), text->TotalHeight())); iBBox.AddPoint(Matrix() * IpeVector(text->Width(), 0)); } void IpeBBoxPainter::Add(const IpeVector &pos) { double lw = 0.0; if (LineWidth()) lw = Repository()->ToScalar(LineWidth()).ToDouble(); iBBox.AddPoint(IpeVector(pos.iX - lw, pos.iY - lw)); iBBox.AddPoint(IpeVector(pos.iX + lw, pos.iY + lw)); } // -------------------------------------------------------------------- /*! \class IpeA85Stream \ingroup high \brief Filter stream adding ASCII85 encoding. */ inline uint A85Word(const uchar *p) { return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; } inline void A85Encode(uint w, char *p) { p[4] = char(w % 85 + 33); w /= 85; p[3] = char(w % 85 + 33); w /= 85; p[2] = char(w % 85 + 33); w /= 85; p[1] = char(w % 85 + 33); p[0] = char(w / 85 + 33); } IpeA85Stream::IpeA85Stream(IpeStream &stream) : iStream(stream) { iN = 0; iCol = 0; } void IpeA85Stream::PutChar(char ch) { iCh[iN++] = ch; if (iN == 4) { // encode and write uint w = A85Word(iCh); if (w == 0) { iStream.PutChar('z'); ++iCol; } else { char buf[6]; buf[5] = '\0'; A85Encode(w, buf); iStream.PutCString(buf); iCol += 5; } if (iCol > 70) { iStream.PutChar('\n'); iCol = 0; } iN = 0; } } void IpeA85Stream::Close() { if (iN) { for (int k = iN; k < 4; ++k) iCh[k] = 0; uint w = A85Word(iCh); char buf[6]; A85Encode(w, buf); buf[iN + 1] = '\0'; iStream.PutCString(buf); } iStream.PutCString("~>\n"); iStream.Close(); } // -------------------------------------------------------------------- /*! \class IpeA85Source \ingroup high \brief Filter source adding ASCII85 decoding. */ IpeA85Source::IpeA85Source(IpeDataSource &source) : iSource(source) { iEof = false; iN = 0; // number of characters buffered iIndex = 0; // next character to return } int IpeA85Source::GetChar() { if (iIndex < iN) return iBuf[iIndex++]; if (iEof) return EOF; int ch; do { ch = iSource.GetChar(); } while (ch == '\n' || ch == '\r' || ch == ' '); if (ch == '~' || ch == EOF) { iEof = true; iN = 0; // no more data, immediate EOF return EOF; } iIndex = 1; iN = 4; if (ch == 'z') { iBuf[0] = iBuf[1] = iBuf[2] = iBuf[3] = 0; return iBuf[0]; } int c[5]; c[0] = ch; for (int k = 1; k < 5; ++k) { do { c[k] = iSource.GetChar(); } while (c[k] == '\n' || c[k] == '\r' || c[k] == ' '); if (c[k] == '~' || c[k] == EOF) { iN = k - 1; iEof = true; break; } } for (int k = iN + 1; k < 5; ++k) c[k] = 0x21 + 84; uint t = 0; for (int k = 0; k < 5; ++k) t = t * 85 + (c[k] - 0x21); for (int k = 3; k >= 0; --k) { iBuf[k] = char(t & 0xff); t >>= 8; } return iBuf[0]; }; // -------------------------------------------------------------------- /*! \class IpeDeflateStream \ingroup high \brief Filter stream adding flate compression. */ struct IpeDeflateStream::Private { z_stream iFlate; }; IpeDeflateStream::IpeDeflateStream(IpeStream &stream, int level) : iStream(stream), iIn(0x400), iOut(0x400) // create buffers { iPriv = new Private; z_streamp z = &iPriv->iFlate; z->zalloc = 0; z->zfree = 0; z->opaque = 0; int err = deflateInit(z, level); if (err != Z_OK) { ipeDebug("deflateInit returns error %d", err); assert(false); } iN = 0; } IpeDeflateStream::~IpeDeflateStream() { if (iPriv) { z_streamp z = &iPriv->iFlate; deflateEnd(z); delete iPriv; } } void IpeDeflateStream::PutChar(char ch) { iIn[iN++] = ch; if (iN < iIn.size()) return; // compress and write z_streamp z = &iPriv->iFlate; z->next_in = (Bytef *) iIn.data(); z->avail_in = iIn.size(); while (z->avail_in) { z->next_out = (Bytef *) iOut.data(); z->avail_out = iOut.size(); int err = deflate(z, Z_NO_FLUSH); if (err != Z_OK) { ipeDebug("deflate returns error %d", err); assert(false); } // save output iStream.PutRaw(iOut.data(), z->next_out - (Bytef *) iOut.data()); } iN = 0; } void IpeDeflateStream::Close() { // compress and write remaining data z_streamp z = &iPriv->iFlate; z->next_in = (Bytef *) iIn.data(); z->avail_in = iN; int err; do { z->next_out = (Bytef *) iOut.data(); z->avail_out = iOut.size(); err = deflate(z, Z_FINISH); if (err != Z_OK && err != Z_STREAM_END) { ipeDebug("deflate returns error %d", err); assert(false); } iStream.PutRaw(iOut.data(), z->next_out - (Bytef *) iOut.data()); } while (err == Z_OK); err = deflateEnd(z); if (err != Z_OK) { ipeDebug("deflateEnd returns error %d", err); assert(false); } delete iPriv; iPriv = 0; // make sure no more writing possible iStream.Close(); } //! Deflate a buffer in a single run. /*! The returned buffer may be larger than necessary: \a deflatedSize is set to the number of bytes actually used. */ IpeBuffer IpeDeflateStream::Deflate(const char *data, int size, int &deflatedSize, int compressLevel) { ulong dfsize = ulong(size * 1.001 + 13); IpeBuffer deflatedData(dfsize); int err = compress2((Bytef *) deflatedData.data(), &dfsize, (const Bytef *) data, size, compressLevel); if (err != Z_OK) { ipeDebug("Zlib compress2 returns errror %d", err); assert(false); } deflatedSize = dfsize; return deflatedData; } // -------------------------------------------------------------------- /*! \class IpeInflateSource \ingroup high \brief Filter source adding flate decompression. */ struct IpeInflateSource::Private { z_stream iFlate; }; IpeInflateSource::IpeInflateSource(IpeDataSource &source) : iSource(source), iIn(0x400), iOut(0x400) { iPriv = new Private; z_streamp z = &iPriv->iFlate; z->zalloc = Z_NULL; z->zfree = Z_NULL; z->opaque = Z_NULL; FillBuffer(); int err = inflateInit(z); if (err != Z_OK) { ipeDebug("inflateInit returns error %d", err); delete iPriv; iPriv = 0; // set EOF return; } iP = iOut.data(); z->next_out = (Bytef *) iP; } IpeInflateSource::~IpeInflateSource() { if (iPriv) { z_streamp z = &iPriv->iFlate; inflateEnd(z); delete iPriv; } } void IpeInflateSource::FillBuffer() { char *p = iIn.data(); char *p1 = iIn.data() + iIn.size(); z_streamp z = &iPriv->iFlate; z->next_in = (Bytef *) p; z->avail_in = 0; while (p < p1) { int ch = iSource.GetChar(); if (ch == EOF) return; *p++ = char(ch); z->avail_in++; } } //! Get one more character, or EOF. int IpeInflateSource::GetChar() { if (!iPriv) return EOF; z_streamp z = &iPriv->iFlate; if (iP < (char *) z->next_out) return *iP++; // next to decompress some data if (z->avail_in == 0) FillBuffer(); if (z->avail_in > 0) { // data is available z->next_out = (Bytef *) iOut.data(); z->avail_out = iOut.size(); int err = inflate(z, Z_NO_FLUSH); if (err != Z_OK && err != Z_STREAM_END) { ipeDebug("inflate returns error %d", err); inflateEnd(z); delete iPriv; iPriv = 0; // set EOF return EOF; } iP = iOut.data(); if (iP < (char *) z->next_out) return *iP++; // didn't get any new data, must be EOF } // FillBuffer didn't get any data, must be EOF, so we are done inflateEnd(z); delete iPriv; iPriv = 0; return EOF; } // --------------------------------------------------------------------